Coverage Report

Created: 2026-08-14 09:29

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