Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/tiff/libtiff/tif_webp.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2018, Mapbox
3
 * Author: <norman.barker at mapbox.com>
4
 *
5
 * Permission to use, copy, modify, distribute, and sell this software and
6
 * its documentation for any purpose is hereby granted without fee, provided
7
 * that (i) the above copyright notices and this permission notice appear in
8
 * all copies of the software and related documentation, and (ii) the names of
9
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
10
 * publicity relating to the software without the specific, prior written
11
 * permission of Sam Leffler and Silicon Graphics.
12
 *
13
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16
 *
17
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22
 * OF THIS SOFTWARE.
23
 */
24
25
#include "tiffiop.h"
26
#ifdef WEBP_SUPPORT
27
/*
28
 * TIFF Library.
29
 *
30
 * WEBP Compression Support
31
 *
32
 */
33
34
#include "webp/decode.h"
35
#include "webp/encode.h"
36
37
#include <stdbool.h>
38
#include <stdio.h>
39
40
0
#define LSTATE_INIT_DECODE 0x01
41
0
#define LSTATE_INIT_ENCODE 0x02
42
/*
43
 * State block for each open TIFF
44
 * file using WEBP compression/decompression.
45
 */
46
typedef struct
47
{
48
    uint16_t nSamples; /* number of samples per pixel */
49
50
    int read_error; /* whether a read error has occurred, and which should cause
51
                       further reads in the same strip/tile to be aborted */
52
    int lossless;   /* lossy/lossless compression */
53
    int lossless_exact;   /* lossless exact mode. If TRUE, R,G,B values in areas
54
                             with alpha = 0 will be preserved */
55
    int quality_level;    /* compression level */
56
    WebPPicture sPicture; /* WebP Picture */
57
    WebPConfig sEncoderConfig;  /* WebP encoder config */
58
    uint8_t *pBuffer;           /* buffer to hold raw data on encoding */
59
    unsigned int buffer_offset; /* current offset into the buffer */
60
    unsigned int buffer_size;
61
62
    WebPIDecoder *psDecoder;  /* WebPIDecoder */
63
    WebPDecBuffer sDecBuffer; /* Decoder buffer */
64
    int last_y;               /* Last row decoded */
65
66
    int state; /* state flags */
67
68
    TIFFVGetMethod vgetparent; /* super-class method */
69
    TIFFVSetMethod vsetparent; /* super-class method */
70
} WebPState;
71
72
0
#define LState(tif) ((WebPState *)(tif)->tif_data)
73
0
#define DecoderState(tif) LState(tif)
74
0
#define EncoderState(tif) LState(tif)
75
76
static int TWebPEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s);
77
static int TWebPDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s);
78
79
static int TWebPDatasetWriter(const uint8_t *data, size_t data_size,
80
                              const WebPPicture *const picture)
81
0
{
82
0
    static const char module[] = "TWebPDatasetWriter";
83
0
    TIFF *tif = (TIFF *)(picture->custom_ptr);
84
85
0
    if ((tif->tif_rawcc + (tmsize_t)data_size) > tif->tif_rawdatasize)
86
0
    {
87
0
        TIFFErrorExtR(
88
0
            tif, module, "Buffer too small by %" TIFF_SIZE_FORMAT " bytes.",
89
0
            (size_t)(tif->tif_rawcc + data_size - tif->tif_rawdatasize));
90
0
        return 0;
91
0
    }
92
0
    else
93
0
    {
94
0
        _TIFFmemcpy(tif->tif_rawcp, data, data_size);
95
0
        tif->tif_rawcc += data_size;
96
0
        tif->tif_rawcp += data_size;
97
0
        return 1;
98
0
    }
99
0
}
100
101
/*
102
 * Encode a chunk of pixels.
103
 */
104
static int TWebPEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
105
0
{
106
0
    static const char module[] = "TWebPEncode";
107
0
    WebPState *sp = EncoderState(tif);
108
0
    (void)s;
109
110
0
    assert(sp != NULL);
111
0
    assert(sp->state == LSTATE_INIT_ENCODE);
112
113
0
    if ((uint64_t)sp->buffer_offset + (uint64_t)cc > sp->buffer_size)
114
0
    {
115
0
        TIFFErrorExtR(tif, module, "Too many bytes to be written");
116
0
        return 0;
117
0
    }
118
119
0
    memcpy(sp->pBuffer + sp->buffer_offset, bp, cc);
120
0
    sp->buffer_offset += (unsigned)cc;
121
122
0
    return 1;
123
0
}
124
125
static int TWebPDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
126
0
{
127
0
    static const char module[] = "WebPDecode";
128
0
    VP8StatusCode status = VP8_STATUS_OK;
129
0
    WebPState *sp = DecoderState(tif);
130
0
    uint32_t segment_width, segment_height;
131
0
    bool decode_whole_strile = false;
132
133
0
    (void)s;
134
135
0
    assert(sp != NULL);
136
0
    assert(sp->state == LSTATE_INIT_DECODE);
137
138
0
    if (sp->read_error)
139
0
    {
140
0
        memset(op, 0, (size_t)occ);
141
0
        TIFFErrorExtR(tif, module,
142
0
                      "ZIPDecode: Scanline %" PRIu32 " cannot be read due to "
143
0
                      "previous error",
144
0
                      tif->tif_row);
145
0
        return 0;
146
0
    }
147
148
0
    if (sp->psDecoder == NULL)
149
0
    {
150
0
        TIFFDirectory *td = &tif->tif_dir;
151
0
        uint32_t buffer_size;
152
153
0
        if (isTiled(tif))
154
0
        {
155
0
            segment_width = td->td_tilewidth;
156
0
            segment_height = td->td_tilelength;
157
0
        }
158
0
        else
159
0
        {
160
0
            segment_width = td->td_imagewidth;
161
0
            segment_height = td->td_imagelength - tif->tif_row;
162
0
            if (segment_height > td->td_rowsperstrip)
163
0
                segment_height = td->td_rowsperstrip;
164
0
        }
165
166
0
        int webp_width, webp_height;
167
0
        if (!WebPGetInfo(tif->tif_rawcp,
168
0
                         (uint64_t)tif->tif_rawcc > UINT32_MAX
169
0
                             ? UINT32_MAX
170
0
                             : (uint32_t)tif->tif_rawcc,
171
0
                         &webp_width, &webp_height))
172
0
        {
173
0
            memset(op, 0, (size_t)occ);
174
0
            sp->read_error = 1;
175
0
            TIFFErrorExtR(tif, module, "WebPGetInfo() failed");
176
0
            return 0;
177
0
        }
178
0
        if ((uint32_t)webp_width != segment_width ||
179
0
            (uint32_t)webp_height != segment_height)
180
0
        {
181
0
            memset(op, 0, (size_t)occ);
182
0
            sp->read_error = 1;
183
0
            TIFFErrorExtR(
184
0
                tif, module, "WebP blob dimension is %dx%d. Expected %ux%u",
185
0
                webp_width, webp_height, segment_width, segment_height);
186
0
            return 0;
187
0
        }
188
189
0
#if WEBP_DECODER_ABI_VERSION >= 0x0002
190
0
        WebPDecoderConfig config;
191
0
        if (!WebPInitDecoderConfig(&config))
192
0
        {
193
0
            memset(op, 0, (size_t)occ);
194
0
            sp->read_error = 1;
195
0
            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
196
0
            return 0;
197
0
        }
198
199
0
        const bool bWebPGetFeaturesOK =
200
0
            WebPGetFeatures(tif->tif_rawcp,
201
0
                            (uint64_t)tif->tif_rawcc > UINT32_MAX
202
0
                                ? UINT32_MAX
203
0
                                : (uint32_t)tif->tif_rawcc,
204
0
                            &config.input) == VP8_STATUS_OK;
205
206
0
        WebPFreeDecBuffer(&config.output);
207
208
0
        if (!bWebPGetFeaturesOK)
209
0
        {
210
0
            memset(op, 0, (size_t)occ);
211
0
            sp->read_error = 1;
212
0
            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
213
0
            return 0;
214
0
        }
215
216
0
        const int webp_bands = config.input.has_alpha ? 4 : 3;
217
0
        if (webp_bands != sp->nSamples &&
218
            /* We accept the situation where the WebP blob has only 3 bands,
219
             * whereas the raster is 4 bands. This can happen when the alpha
220
             * channel is fully opaque, and WebP decoding works fine in that
221
             * situation.
222
             */
223
0
            !(webp_bands == 3 && sp->nSamples == 4))
224
0
        {
225
0
            memset(op, 0, (size_t)occ);
226
0
            sp->read_error = 1;
227
0
            TIFFErrorExtR(tif, module,
228
0
                          "WebP blob band count is %d. Expected %d", webp_bands,
229
0
                          sp->nSamples);
230
0
            return 0;
231
0
        }
232
0
#endif
233
234
0
        buffer_size = segment_width * segment_height * sp->nSamples;
235
0
        if (occ == (tmsize_t)buffer_size)
236
0
        {
237
            /* If decoding the whole strip/tile, we can directly use the */
238
            /* output buffer */
239
0
            decode_whole_strile = true;
240
0
        }
241
0
        else if (sp->pBuffer == NULL || buffer_size > sp->buffer_size)
242
0
        {
243
0
            if (sp->pBuffer != NULL)
244
0
            {
245
0
                _TIFFfreeExt(tif, sp->pBuffer);
246
0
                sp->pBuffer = NULL;
247
0
            }
248
249
0
            sp->pBuffer = _TIFFmallocExt(tif, buffer_size);
250
0
            if (!sp->pBuffer)
251
0
            {
252
0
                TIFFErrorExtR(tif, module, "Cannot allocate buffer");
253
0
                memset(op, 0, (size_t)occ);
254
0
                sp->read_error = 1;
255
0
                return 0;
256
0
            }
257
0
            sp->buffer_size = buffer_size;
258
0
        }
259
260
0
        sp->last_y = 0;
261
262
0
        WebPInitDecBuffer(&sp->sDecBuffer);
263
264
0
        sp->sDecBuffer.is_external_memory = 1;
265
0
        sp->sDecBuffer.width = segment_width;
266
0
        sp->sDecBuffer.height = segment_height;
267
0
        sp->sDecBuffer.u.RGBA.rgba = decode_whole_strile ? op : sp->pBuffer;
268
0
        sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples;
269
0
        sp->sDecBuffer.u.RGBA.size = buffer_size;
270
271
0
        if (sp->nSamples > 3)
272
0
        {
273
0
            sp->sDecBuffer.colorspace = MODE_RGBA;
274
0
        }
275
0
        else
276
0
        {
277
0
            sp->sDecBuffer.colorspace = MODE_RGB;
278
0
        }
279
280
0
        sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer);
281
282
0
        if (sp->psDecoder == NULL)
283
0
        {
284
0
            memset(op, 0, (size_t)occ);
285
0
            sp->read_error = 1;
286
0
            TIFFErrorExtR(tif, module, "Unable to allocate WebP decoder.");
287
0
            return 0;
288
0
        }
289
0
    }
290
291
0
    if (occ % sp->sDecBuffer.u.RGBA.stride)
292
0
    {
293
        // read_error not set here as this is a usage issue that can be
294
        // recovered in a following call.
295
0
        memset(op, 0, (size_t)occ);
296
        /* Do not set read_error as could potentially be recovered */
297
0
        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
298
0
        return 0;
299
0
    }
300
301
0
    status = WebPIAppend(sp->psDecoder, tif->tif_rawcp, tif->tif_rawcc);
302
303
0
    if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED)
304
0
    {
305
0
        if (status == VP8_STATUS_INVALID_PARAM)
306
0
        {
307
0
            TIFFErrorExtR(tif, module, "Invalid parameter used.");
308
0
        }
309
0
        else if (status == VP8_STATUS_OUT_OF_MEMORY)
310
0
        {
311
0
            TIFFErrorExtR(tif, module, "Out of memory.");
312
0
        }
313
0
        else
314
0
        {
315
0
            TIFFErrorExtR(tif, module, "Unrecognized error.");
316
0
        }
317
0
        memset(op, 0, (size_t)occ);
318
0
        sp->read_error = 1;
319
0
        return 0;
320
0
    }
321
0
    else
322
0
    {
323
0
        int current_y, stride;
324
0
        uint8_t *buf;
325
326
        /* Returns the RGB/A image decoded so far */
327
0
        buf = WebPIDecGetRGB(sp->psDecoder, &current_y, NULL, NULL, &stride);
328
329
0
        if ((buf != NULL) &&
330
0
            (occ <= (tmsize_t)stride * (current_y - sp->last_y)))
331
0
        {
332
0
            const int numberOfExpectedLines =
333
0
                (int)(occ / sp->sDecBuffer.u.RGBA.stride);
334
0
            if (decode_whole_strile)
335
0
            {
336
0
                if (current_y != numberOfExpectedLines)
337
0
                {
338
0
                    memset(op, 0, (size_t)occ);
339
0
                    sp->read_error = 1;
340
0
                    TIFFErrorExtR(tif, module,
341
0
                                  "Unable to decode WebP data: less lines than "
342
0
                                  "expected.");
343
0
                    return 0;
344
0
                }
345
0
            }
346
0
            else
347
0
            {
348
0
                memcpy(op, buf + (sp->last_y * stride), occ);
349
0
            }
350
351
0
            tif->tif_rawcp += tif->tif_rawcc;
352
0
            tif->tif_rawcc = 0;
353
0
            sp->last_y += numberOfExpectedLines;
354
355
0
            if (decode_whole_strile)
356
0
            {
357
                /* We can now free the decoder as we're completely done */
358
0
                if (sp->psDecoder != NULL)
359
0
                {
360
0
                    WebPIDelete(sp->psDecoder);
361
0
                    WebPFreeDecBuffer(&sp->sDecBuffer);
362
0
                    sp->psDecoder = NULL;
363
0
                }
364
0
            }
365
0
            return 1;
366
0
        }
367
0
        else
368
0
        {
369
0
            memset(op, 0, (size_t)occ);
370
0
            sp->read_error = 1;
371
0
            TIFFErrorExtR(tif, module, "Unable to decode WebP data.");
372
0
            return 0;
373
0
        }
374
0
    }
375
0
}
376
377
static int TWebPFixupTags(TIFF *tif)
378
0
{
379
0
    (void)tif;
380
0
    if (tif->tif_dir.td_planarconfig != PLANARCONFIG_CONTIG)
381
0
    {
382
0
        static const char module[] = "TWebPFixupTags";
383
0
        TIFFErrorExtR(tif, module,
384
0
                      "TIFF WEBP requires data to be stored contiguously in "
385
0
                      "RGB e.g. RGBRGBRGB "
386
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
387
0
                      "or RGBARGBARGBA"
388
0
#endif
389
0
        );
390
0
        return 0;
391
0
    }
392
0
    return 1;
393
0
}
394
395
static int TWebPSetupDecode(TIFF *tif)
396
0
{
397
0
    static const char module[] = "WebPSetupDecode";
398
0
    uint16_t nBitsPerSample = tif->tif_dir.td_bitspersample;
399
0
    uint16_t sampleFormat = tif->tif_dir.td_sampleformat;
400
401
0
    WebPState *sp = DecoderState(tif);
402
0
    assert(sp != NULL);
403
404
0
    sp->nSamples = tif->tif_dir.td_samplesperpixel;
405
406
    /* check band count */
407
0
    if (sp->nSamples != 3
408
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
409
0
        && sp->nSamples != 4
410
0
#endif
411
0
    )
412
0
    {
413
0
        TIFFErrorExtR(tif, module,
414
0
                      "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
415
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
416
0
                      "or 4 (RGBA) "
417
0
#endif
418
0
                      "bands.",
419
0
                      sp->nSamples);
420
0
        return 0;
421
0
    }
422
423
    /* check bits per sample and data type */
424
0
    if ((nBitsPerSample != 8) && (sampleFormat != 1))
425
0
    {
426
0
        TIFFErrorExtR(tif, module, "WEBP driver requires 8 bit unsigned data");
427
0
        return 0;
428
0
    }
429
430
    /* if we were last encoding, terminate this mode */
431
0
    if (sp->state & LSTATE_INIT_ENCODE)
432
0
    {
433
0
        WebPPictureFree(&sp->sPicture);
434
0
        if (sp->pBuffer != NULL)
435
0
        {
436
0
            _TIFFfreeExt(tif, sp->pBuffer);
437
0
            sp->pBuffer = NULL;
438
0
        }
439
0
        sp->buffer_offset = 0;
440
0
        sp->state = 0;
441
0
    }
442
443
0
    sp->state |= LSTATE_INIT_DECODE;
444
445
0
    return 1;
446
0
}
447
448
/*
449
 * Setup state for decoding a strip.
450
 */
451
static int TWebPPreDecode(TIFF *tif, uint16_t s)
452
0
{
453
0
    static const char module[] = "TWebPPreDecode";
454
0
    uint32_t segment_width, segment_height;
455
0
    WebPState *sp = DecoderState(tif);
456
0
    TIFFDirectory *td = &tif->tif_dir;
457
0
    (void)s;
458
0
    assert(sp != NULL);
459
460
0
    if (isTiled(tif))
461
0
    {
462
0
        segment_width = td->td_tilewidth;
463
0
        segment_height = td->td_tilelength;
464
0
    }
465
0
    else
466
0
    {
467
0
        segment_width = td->td_imagewidth;
468
0
        segment_height = td->td_imagelength - tif->tif_row;
469
0
        if (segment_height > td->td_rowsperstrip)
470
0
            segment_height = td->td_rowsperstrip;
471
0
    }
472
473
0
    if (segment_width > 16383 || segment_height > 16383)
474
0
    {
475
0
        TIFFErrorExtR(tif, module,
476
0
                      "WEBP maximum image dimensions are 16383 x 16383.");
477
0
        return 0;
478
0
    }
479
480
0
    if ((sp->state & LSTATE_INIT_DECODE) == 0)
481
0
        tif->tif_setupdecode(tif);
482
483
0
    if (sp->psDecoder != NULL)
484
0
    {
485
0
        WebPIDelete(sp->psDecoder);
486
0
        WebPFreeDecBuffer(&sp->sDecBuffer);
487
0
        sp->psDecoder = NULL;
488
0
    }
489
490
0
    sp->read_error = 0;
491
492
0
    return 1;
493
0
}
494
495
static int TWebPSetupEncode(TIFF *tif)
496
0
{
497
0
    static const char module[] = "WebPSetupEncode";
498
0
    uint16_t nBitsPerSample = tif->tif_dir.td_bitspersample;
499
0
    uint16_t sampleFormat = tif->tif_dir.td_sampleformat;
500
501
0
    WebPState *sp = EncoderState(tif);
502
0
    assert(sp != NULL);
503
504
0
    sp->nSamples = tif->tif_dir.td_samplesperpixel;
505
506
    /* check band count */
507
0
    if (sp->nSamples != 3
508
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
509
0
        && sp->nSamples != 4
510
0
#endif
511
0
    )
512
0
    {
513
0
        TIFFErrorExtR(tif, module,
514
0
                      "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
515
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
516
0
                      "or 4 (RGBA) "
517
0
#endif
518
0
                      "bands.",
519
0
                      sp->nSamples);
520
0
        return 0;
521
0
    }
522
523
    /* check bits per sample and data type */
524
0
    if ((nBitsPerSample != 8) || (sampleFormat != SAMPLEFORMAT_UINT))
525
0
    {
526
0
        TIFFErrorExtR(tif, module, "WEBP driver requires 8 bit unsigned data");
527
0
        return 0;
528
0
    }
529
530
0
    if (sp->state & LSTATE_INIT_DECODE)
531
0
    {
532
0
        WebPIDelete(sp->psDecoder);
533
0
        WebPFreeDecBuffer(&sp->sDecBuffer);
534
0
        sp->psDecoder = NULL;
535
0
        sp->last_y = 0;
536
0
        sp->state = 0;
537
0
    }
538
539
0
    sp->state |= LSTATE_INIT_ENCODE;
540
541
0
    if (!WebPPictureInit(&sp->sPicture))
542
0
    {
543
0
        TIFFErrorExtR(tif, module, "Error initializing WebP picture.");
544
0
        return 0;
545
0
    }
546
547
0
    if (!WebPConfigInitInternal(&sp->sEncoderConfig, WEBP_PRESET_DEFAULT,
548
0
                                (float)sp->quality_level,
549
0
                                WEBP_ENCODER_ABI_VERSION))
550
0
    {
551
0
        TIFFErrorExtR(tif, module,
552
0
                      "Error creating WebP encoder configuration.");
553
0
        return 0;
554
0
    }
555
556
// WebPConfigInitInternal above sets lossless to false
557
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
558
0
    sp->sEncoderConfig.lossless = sp->lossless;
559
0
    if (sp->lossless)
560
0
    {
561
0
        sp->sPicture.use_argb = 1;
562
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
563
0
        sp->sEncoderConfig.exact = sp->lossless_exact;
564
0
#endif
565
0
    }
566
0
#endif
567
568
0
    if (!WebPValidateConfig(&sp->sEncoderConfig))
569
0
    {
570
0
        TIFFErrorExtR(tif, module, "Error with WebP encoder configuration.");
571
0
        return 0;
572
0
    }
573
574
0
    return 1;
575
0
}
576
577
/*
578
 * Reset encoding state at the start of a strip.
579
 */
580
static int TWebPPreEncode(TIFF *tif, uint16_t s)
581
0
{
582
0
    static const char module[] = "TWebPPreEncode";
583
0
    uint32_t segment_width, segment_height;
584
0
    WebPState *sp = EncoderState(tif);
585
0
    TIFFDirectory *td = &tif->tif_dir;
586
587
0
    (void)s;
588
589
0
    assert(sp != NULL);
590
0
    if (sp->state != LSTATE_INIT_ENCODE)
591
0
        tif->tif_setupencode(tif);
592
593
    /*
594
     * Set encoding parameters for this strip/tile.
595
     */
596
0
    if (isTiled(tif))
597
0
    {
598
0
        segment_width = td->td_tilewidth;
599
0
        segment_height = td->td_tilelength;
600
0
    }
601
0
    else
602
0
    {
603
0
        segment_width = td->td_imagewidth;
604
0
        segment_height = td->td_imagelength - tif->tif_row;
605
0
        if (segment_height > td->td_rowsperstrip)
606
0
            segment_height = td->td_rowsperstrip;
607
0
    }
608
609
0
    if (segment_width > 16383 || segment_height > 16383)
610
0
    {
611
0
        TIFFErrorExtR(tif, module,
612
0
                      "WEBP maximum image dimensions are 16383 x 16383.");
613
0
        return 0;
614
0
    }
615
616
    /* set up buffer for raw data */
617
    /* given above check and that nSamples <= 4, buffer_size is <= 1 GB */
618
0
    sp->buffer_size = segment_width * segment_height * sp->nSamples;
619
620
0
    if (sp->pBuffer != NULL)
621
0
    {
622
0
        _TIFFfreeExt(tif, sp->pBuffer);
623
0
        sp->pBuffer = NULL;
624
0
    }
625
626
0
    sp->pBuffer = _TIFFmallocExt(tif, sp->buffer_size);
627
0
    if (!sp->pBuffer)
628
0
    {
629
0
        TIFFErrorExtR(tif, module, "Cannot allocate buffer");
630
0
        return 0;
631
0
    }
632
0
    sp->buffer_offset = 0;
633
634
0
    sp->sPicture.width = segment_width;
635
0
    sp->sPicture.height = segment_height;
636
0
    sp->sPicture.writer = TWebPDatasetWriter;
637
0
    sp->sPicture.custom_ptr = tif;
638
639
0
    return 1;
640
0
}
641
642
/*
643
 * Finish off an encoded strip by flushing it.
644
 */
645
static int TWebPPostEncode(TIFF *tif)
646
0
{
647
0
    static const char module[] = "WebPPostEncode";
648
0
    int64_t stride;
649
0
    WebPState *sp = EncoderState(tif);
650
0
    assert(sp != NULL);
651
652
0
    assert(sp->state == LSTATE_INIT_ENCODE);
653
654
0
    stride = (int64_t)sp->sPicture.width * sp->nSamples;
655
656
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
657
0
    if (sp->nSamples == 4)
658
0
    {
659
0
        if (!WebPPictureImportRGBA(&sp->sPicture, sp->pBuffer, (int)stride))
660
0
        {
661
0
            TIFFErrorExtR(tif, module, "WebPPictureImportRGBA() failed");
662
0
            return 0;
663
0
        }
664
0
    }
665
0
    else
666
0
#endif
667
0
        if (!WebPPictureImportRGB(&sp->sPicture, sp->pBuffer, (int)stride))
668
0
    {
669
0
        TIFFErrorExtR(tif, module, "WebPPictureImportRGB() failed");
670
0
        return 0;
671
0
    }
672
673
0
    if (!WebPEncode(&sp->sEncoderConfig, &sp->sPicture))
674
0
    {
675
676
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
677
0
        const char *pszErrorMsg = NULL;
678
0
        switch (sp->sPicture.error_code)
679
0
        {
680
0
            case VP8_ENC_ERROR_OUT_OF_MEMORY:
681
0
                pszErrorMsg = "Out of memory";
682
0
                break;
683
0
            case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
684
0
                pszErrorMsg = "Out of memory while flushing bits";
685
0
                break;
686
0
            case VP8_ENC_ERROR_NULL_PARAMETER:
687
0
                pszErrorMsg = "A pointer parameter is NULL";
688
0
                break;
689
0
            case VP8_ENC_ERROR_INVALID_CONFIGURATION:
690
0
                pszErrorMsg = "Configuration is invalid";
691
0
                break;
692
0
            case VP8_ENC_ERROR_BAD_DIMENSION:
693
0
                pszErrorMsg = "Picture has invalid width/height";
694
0
                break;
695
0
            case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
696
0
                pszErrorMsg = "Partition is bigger than 512k. Try using less "
697
0
                              "SEGMENTS, or increase PARTITION_LIMIT value";
698
0
                break;
699
0
            case VP8_ENC_ERROR_PARTITION_OVERFLOW:
700
0
                pszErrorMsg = "Partition is bigger than 16M";
701
0
                break;
702
0
            case VP8_ENC_ERROR_BAD_WRITE:
703
0
                pszErrorMsg = "Error while fludshing bytes";
704
0
                break;
705
0
            case VP8_ENC_ERROR_FILE_TOO_BIG:
706
0
                pszErrorMsg = "File is bigger than 4G";
707
0
                break;
708
0
            case VP8_ENC_ERROR_USER_ABORT:
709
0
                pszErrorMsg = "User interrupted";
710
0
                break;
711
0
            default:
712
0
                TIFFErrorExtR(tif, module,
713
0
                              "WebPEncode returned an unknown error code: %d",
714
0
                              sp->sPicture.error_code);
715
0
                pszErrorMsg = "Unknown WebP error type.";
716
0
                break;
717
0
        }
718
0
        TIFFErrorExtR(tif, module, "WebPEncode() failed : %s", pszErrorMsg);
719
#else
720
        TIFFErrorExtR(tif, module, "Error in WebPEncode()");
721
#endif
722
0
        return 0;
723
0
    }
724
725
0
    sp->sPicture.custom_ptr = NULL;
726
727
0
    if (!TIFFFlushData1(tif))
728
0
    {
729
0
        TIFFErrorExtR(tif, module, "Error flushing TIFF WebP encoder.");
730
0
        return 0;
731
0
    }
732
733
0
    return 1;
734
0
}
735
736
static void TWebPCleanup(TIFF *tif)
737
0
{
738
0
    WebPState *sp = LState(tif);
739
740
0
    assert(sp != 0);
741
742
0
    tif->tif_tagmethods.vgetfield = sp->vgetparent;
743
0
    tif->tif_tagmethods.vsetfield = sp->vsetparent;
744
745
0
    if (sp->state & LSTATE_INIT_ENCODE)
746
0
    {
747
0
        WebPPictureFree(&sp->sPicture);
748
0
    }
749
750
0
    if (sp->psDecoder != NULL)
751
0
    {
752
0
        WebPIDelete(sp->psDecoder);
753
0
        WebPFreeDecBuffer(&sp->sDecBuffer);
754
0
        sp->psDecoder = NULL;
755
0
        sp->last_y = 0;
756
0
    }
757
758
0
    if (sp->pBuffer != NULL)
759
0
    {
760
0
        _TIFFfreeExt(tif, sp->pBuffer);
761
0
        sp->pBuffer = NULL;
762
0
    }
763
764
0
    _TIFFfreeExt(tif, tif->tif_data);
765
0
    tif->tif_data = NULL;
766
767
0
    _TIFFSetDefaultCompressionState(tif);
768
0
}
769
770
static int TWebPVSetField(TIFF *tif, uint32_t tag, va_list ap)
771
0
{
772
0
    static const char module[] = "WebPVSetField";
773
0
    WebPState *sp = LState(tif);
774
775
0
    switch (tag)
776
0
    {
777
0
        case TIFFTAG_WEBP_LEVEL:
778
0
            sp->quality_level = (int)va_arg(ap, int);
779
0
            if (sp->quality_level <= 0 || sp->quality_level > 100.0f)
780
0
            {
781
0
                TIFFWarningExtR(tif, module,
782
0
                                "WEBP_LEVEL should be between 1 and 100");
783
0
            }
784
0
            return 1;
785
0
        case TIFFTAG_WEBP_LOSSLESS:
786
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
787
0
            sp->lossless = va_arg(ap, int);
788
0
            if (sp->lossless)
789
0
            {
790
0
                sp->quality_level = 100;
791
0
            }
792
0
            return 1;
793
#else
794
            TIFFErrorExtR(
795
                tif, module,
796
                "Need to upgrade WEBP driver, this version doesn't support "
797
                "lossless compression.");
798
            return 0;
799
#endif
800
0
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
801
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
802
0
            sp->lossless_exact = va_arg(ap, int);
803
0
            return 1;
804
#else
805
            TIFFErrorExtR(
806
                tif, module,
807
                "Need to upgrade WEBP driver, this version doesn't support "
808
                "lossless compression.");
809
            return 0;
810
#endif
811
0
        default:
812
0
            return (*sp->vsetparent)(tif, tag, ap);
813
0
    }
814
    /*NOTREACHED*/
815
0
}
816
817
static int TWebPVGetField(TIFF *tif, uint32_t tag, va_list ap)
818
0
{
819
0
    WebPState *sp = LState(tif);
820
821
0
    switch (tag)
822
0
    {
823
0
        case TIFFTAG_WEBP_LEVEL:
824
0
            *va_arg(ap, int *) = sp->quality_level;
825
0
            break;
826
0
        case TIFFTAG_WEBP_LOSSLESS:
827
0
            *va_arg(ap, int *) = sp->lossless;
828
0
            break;
829
0
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
830
0
            *va_arg(ap, int *) = sp->lossless_exact;
831
0
            break;
832
0
        default:
833
0
            return (*sp->vgetparent)(tif, tag, ap);
834
0
    }
835
0
    return 1;
836
0
}
837
838
static const TIFFField TWebPFields[] = {
839
    {TIFFTAG_WEBP_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, FIELD_PSEUDO, TRUE,
840
     FALSE, "WEBP quality", NULL},
841
    {TIFFTAG_WEBP_LOSSLESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, FIELD_PSEUDO,
842
     TRUE, FALSE, "WEBP lossless/lossy", NULL},
843
    {TIFFTAG_WEBP_LOSSLESS_EXACT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
844
     FIELD_PSEUDO, TRUE, FALSE, "WEBP exact lossless", NULL},
845
};
846
847
int TIFFInitWebP(TIFF *tif, int scheme)
848
0
{
849
0
    static const char module[] = "TIFFInitWebP";
850
0
    WebPState *sp;
851
852
0
    (void)scheme;
853
0
    assert(scheme == COMPRESSION_WEBP);
854
855
    /*
856
     * Merge codec-specific tag information.
857
     */
858
0
    if (!_TIFFMergeFields(tif, TWebPFields, TIFFArrayCount(TWebPFields)))
859
0
    {
860
0
        TIFFErrorExtR(tif, module, "Merging WebP codec-specific tags failed");
861
0
        return 0;
862
0
    }
863
864
    /*
865
     * Allocate state block so tag methods have storage to record values.
866
     */
867
0
    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(WebPState));
868
0
    if (tif->tif_data == NULL)
869
0
        goto bad;
870
0
    sp = LState(tif);
871
872
    /*
873
     * Override parent get/set field methods.
874
     */
875
0
    sp->vgetparent = tif->tif_tagmethods.vgetfield;
876
0
    tif->tif_tagmethods.vgetfield = TWebPVGetField; /* hook for codec tags */
877
0
    sp->vsetparent = tif->tif_tagmethods.vsetfield;
878
0
    tif->tif_tagmethods.vsetfield = TWebPVSetField; /* hook for codec tags */
879
880
    /* Default values for codec-specific fields */
881
0
    sp->quality_level = 75; /* default comp. level */
882
0
    sp->lossless = 0;       /* default to false */
883
0
    sp->lossless_exact = 1; /* exact lossless mode (if lossless enabled) */
884
0
    sp->state = 0;
885
0
    sp->nSamples = 0;
886
0
    sp->psDecoder = NULL;
887
0
    sp->last_y = 0;
888
889
0
    sp->buffer_offset = 0;
890
0
    sp->pBuffer = NULL;
891
892
    /*
893
     * Install codec methods.
894
     * Notes:
895
     * encoderow is not supported
896
     */
897
0
    tif->tif_fixuptags = TWebPFixupTags;
898
0
    tif->tif_setupdecode = TWebPSetupDecode;
899
0
    tif->tif_predecode = TWebPPreDecode;
900
0
    tif->tif_decoderow = TWebPDecode;
901
0
    tif->tif_decodestrip = TWebPDecode;
902
0
    tif->tif_decodetile = TWebPDecode;
903
0
    tif->tif_setupencode = TWebPSetupEncode;
904
0
    tif->tif_preencode = TWebPPreEncode;
905
0
    tif->tif_postencode = TWebPPostEncode;
906
0
    tif->tif_encoderow = TWebPEncode;
907
0
    tif->tif_encodestrip = TWebPEncode;
908
0
    tif->tif_encodetile = TWebPEncode;
909
0
    tif->tif_cleanup = TWebPCleanup;
910
911
0
    return 1;
912
0
bad:
913
0
    TIFFErrorExtR(tif, module, "No space for WebP state block");
914
0
    return 0;
915
0
}
916
917
#endif /* WEBP_SUPPORT */