Coverage Report

Created: 2026-03-30 09:00

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
338
#define LSTATE_INIT_DECODE 0x01
41
1.72k
#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
380k
#define LState(tif) ((WebPState *)(tif)->tif_data)
73
17.6k
#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
17.2k
{
127
17.2k
    static const char module[] = "WebPDecode";
128
17.2k
    VP8StatusCode status = VP8_STATUS_OK;
129
17.2k
    WebPState *sp = DecoderState(tif);
130
17.2k
    uint32_t segment_width, segment_height;
131
17.2k
    bool decode_whole_strile = false;
132
133
17.2k
    (void)s;
134
135
17.2k
    assert(sp != NULL);
136
17.2k
    assert(sp->state == LSTATE_INIT_DECODE);
137
138
17.2k
    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
17.2k
    if (sp->psDecoder == NULL)
149
248
    {
150
248
        TIFFDirectory *td = &tif->tif_dir;
151
248
        uint32_t buffer_size;
152
153
248
        if (isTiled(tif))
154
59
        {
155
59
            segment_width = td->td_tilewidth;
156
59
            segment_height = td->td_tilelength;
157
59
        }
158
189
        else
159
189
        {
160
189
            segment_width = td->td_imagewidth;
161
189
            segment_height = td->td_imagelength - tif->tif_row;
162
189
            if (segment_height > td->td_rowsperstrip)
163
37
                segment_height = td->td_rowsperstrip;
164
189
        }
165
166
248
        int webp_width, webp_height;
167
248
        if (!WebPGetInfo(tif->tif_rawcp,
168
248
                         (uint64_t)tif->tif_rawcc > UINT32_MAX
169
248
                             ? UINT32_MAX
170
248
                             : (uint32_t)tif->tif_rawcc,
171
248
                         &webp_width, &webp_height))
172
82
        {
173
82
            memset(op, 0, (size_t)occ);
174
82
            sp->read_error = 1;
175
82
            TIFFErrorExtR(tif, module, "WebPGetInfo() failed");
176
82
            return 0;
177
82
        }
178
166
        if ((uint32_t)webp_width != segment_width ||
179
152
            (uint32_t)webp_height != segment_height)
180
32
        {
181
32
            memset(op, 0, (size_t)occ);
182
32
            sp->read_error = 1;
183
32
            TIFFErrorExtR(
184
32
                tif, module, "WebP blob dimension is %dx%d. Expected %ux%u",
185
32
                webp_width, webp_height, segment_width, segment_height);
186
32
            return 0;
187
32
        }
188
189
134
#if WEBP_DECODER_ABI_VERSION >= 0x0002
190
134
        WebPDecoderConfig config;
191
134
        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
134
        const bool bWebPGetFeaturesOK =
200
134
            WebPGetFeatures(tif->tif_rawcp,
201
134
                            (uint64_t)tif->tif_rawcc > UINT32_MAX
202
134
                                ? UINT32_MAX
203
134
                                : (uint32_t)tif->tif_rawcc,
204
134
                            &config.input) == VP8_STATUS_OK;
205
206
134
        WebPFreeDecBuffer(&config.output);
207
208
134
        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
134
        const int webp_bands = config.input.has_alpha ? 4 : 3;
217
134
        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
10
            !(webp_bands == 3 && sp->nSamples == 4))
224
3
        {
225
3
            memset(op, 0, (size_t)occ);
226
3
            sp->read_error = 1;
227
3
            TIFFErrorExtR(tif, module,
228
3
                          "WebP blob band count is %d. Expected %d", webp_bands,
229
3
                          sp->nSamples);
230
3
            return 0;
231
3
        }
232
131
#endif
233
234
131
        buffer_size = segment_width * segment_height * sp->nSamples;
235
131
        if (occ == (tmsize_t)buffer_size)
236
56
        {
237
            /* If decoding the whole strip/tile, we can directly use the */
238
            /* output buffer */
239
56
            decode_whole_strile = true;
240
56
        }
241
75
        else if (sp->pBuffer == NULL || buffer_size > sp->buffer_size)
242
26
        {
243
26
            if (sp->pBuffer != NULL)
244
0
            {
245
0
                _TIFFfreeExt(tif, sp->pBuffer);
246
0
                sp->pBuffer = NULL;
247
0
            }
248
249
26
            sp->pBuffer = (uint8_t *)_TIFFmallocExt(tif, buffer_size);
250
26
            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
26
            sp->buffer_size = buffer_size;
258
26
        }
259
260
131
        sp->last_y = 0;
261
262
131
        WebPInitDecBuffer(&sp->sDecBuffer);
263
264
131
        sp->sDecBuffer.is_external_memory = 1;
265
131
        sp->sDecBuffer.width = segment_width;
266
131
        sp->sDecBuffer.height = segment_height;
267
131
        sp->sDecBuffer.u.RGBA.rgba = decode_whole_strile ? op : sp->pBuffer;
268
131
        sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples;
269
131
        sp->sDecBuffer.u.RGBA.size = buffer_size;
270
271
131
        if (sp->nSamples > 3)
272
11
        {
273
11
            sp->sDecBuffer.colorspace = MODE_RGBA;
274
11
        }
275
120
        else
276
120
        {
277
120
            sp->sDecBuffer.colorspace = MODE_RGB;
278
120
        }
279
280
131
        sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer);
281
282
131
        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
131
    }
290
291
17.1k
    if (occ % sp->sDecBuffer.u.RGBA.stride)
292
13
    {
293
        // read_error not set here as this is a usage issue that can be
294
        // recovered in a following call.
295
13
        memset(op, 0, (size_t)occ);
296
        /* Do not set read_error as could potentially be recovered */
297
13
        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
298
13
        return 0;
299
13
    }
300
301
17.1k
    status = WebPIAppend(sp->psDecoder, tif->tif_rawcp, tif->tif_rawcc);
302
303
17.1k
    if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED)
304
13
    {
305
13
        if (status == VP8_STATUS_INVALID_PARAM)
306
0
        {
307
0
            TIFFErrorExtR(tif, module, "Invalid parameter used.");
308
0
        }
309
13
        else if (status == VP8_STATUS_OUT_OF_MEMORY)
310
0
        {
311
0
            TIFFErrorExtR(tif, module, "Out of memory.");
312
0
        }
313
13
        else
314
13
        {
315
13
            TIFFErrorExtR(tif, module, "Unrecognized error.");
316
13
        }
317
13
        memset(op, 0, (size_t)occ);
318
13
        sp->read_error = 1;
319
13
        return 0;
320
13
    }
321
17.1k
    else
322
17.1k
    {
323
17.1k
        int current_y, stride;
324
17.1k
        uint8_t *buf;
325
326
        /* Returns the RGB/A image decoded so far */
327
17.1k
        buf = WebPIDecGetRGB(sp->psDecoder, &current_y, NULL, NULL, &stride);
328
329
17.1k
        if ((buf != NULL) &&
330
17.1k
            (occ <= (tmsize_t)stride * (current_y - sp->last_y)))
331
17.0k
        {
332
17.0k
            const int numberOfExpectedLines =
333
17.0k
                (int)(occ / sp->sDecBuffer.u.RGBA.stride);
334
17.0k
            if (decode_whole_strile)
335
35
            {
336
35
                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
35
            }
346
17.0k
            else
347
17.0k
            {
348
17.0k
                memcpy(op, buf + (sp->last_y * stride), occ);
349
17.0k
            }
350
351
17.0k
            tif->tif_rawcp += tif->tif_rawcc;
352
17.0k
            tif->tif_rawcc = 0;
353
17.0k
            sp->last_y += numberOfExpectedLines;
354
355
17.0k
            if (decode_whole_strile)
356
35
            {
357
                /* We can now free the decoder as we're completely done */
358
35
                if (sp->psDecoder != NULL)
359
35
                {
360
35
                    WebPIDelete(sp->psDecoder);
361
35
                    WebPFreeDecBuffer(&sp->sDecBuffer);
362
35
                    sp->psDecoder = NULL;
363
35
                }
364
35
            }
365
17.0k
            return 1;
366
17.0k
        }
367
50
        else
368
50
        {
369
50
            memset(op, 0, (size_t)occ);
370
50
            sp->read_error = 1;
371
50
            TIFFErrorExtR(tif, module, "Unable to decode WebP data.");
372
50
            return 0;
373
50
        }
374
17.1k
    }
375
17.1k
}
376
377
static int TWebPFixupTags(TIFF *tif)
378
1.60k
{
379
1.60k
    (void)tif;
380
1.60k
    if (tif->tif_dir.td_planarconfig != PLANARCONFIG_CONTIG)
381
452
    {
382
452
        static const char module[] = "TWebPFixupTags";
383
452
        TIFFErrorExtR(tif, module,
384
452
                      "TIFF WEBP requires data to be stored contiguously in "
385
452
                      "RGB e.g. RGBRGBRGB "
386
452
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
387
452
                      "or RGBARGBARGBA"
388
452
#endif
389
452
        );
390
452
        return 0;
391
452
    }
392
1.15k
    return 1;
393
1.60k
}
394
395
static int TWebPSetupDecode(TIFF *tif)
396
150
{
397
150
    static const char module[] = "WebPSetupDecode";
398
150
    uint16_t nBitsPerSample = tif->tif_dir.td_bitspersample;
399
150
    uint16_t sampleFormat = tif->tif_dir.td_sampleformat;
400
401
150
    WebPState *sp = DecoderState(tif);
402
150
    assert(sp != NULL);
403
404
150
    sp->nSamples = tif->tif_dir.td_samplesperpixel;
405
406
    /* check band count */
407
150
    if (sp->nSamples != 3
408
79
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
409
79
        && sp->nSamples != 4
410
150
#endif
411
150
    )
412
49
    {
413
49
        TIFFErrorExtR(tif, module,
414
49
                      "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
415
49
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
416
49
                      "or 4 (RGBA) "
417
49
#endif
418
49
                      "bands.",
419
49
                      sp->nSamples);
420
49
        return 0;
421
49
    }
422
423
    /* check bits per sample and data type */
424
101
    if ((nBitsPerSample != 8) && (sampleFormat != 1))
425
11
    {
426
11
        TIFFErrorExtR(tif, module, "WEBP driver requires 8 bit unsigned data");
427
11
        return 0;
428
11
    }
429
430
    /* if we were last encoding, terminate this mode */
431
90
    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
90
    sp->state |= LSTATE_INIT_DECODE;
444
445
90
    return 1;
446
101
}
447
448
/*
449
 * Setup state for decoding a strip.
450
 */
451
static int TWebPPreDecode(TIFF *tif, uint16_t s)
452
283
{
453
283
    static const char module[] = "TWebPPreDecode";
454
283
    uint32_t segment_width, segment_height;
455
283
    WebPState *sp = DecoderState(tif);
456
283
    TIFFDirectory *td = &tif->tif_dir;
457
283
    (void)s;
458
283
    assert(sp != NULL);
459
460
283
    if (isTiled(tif))
461
66
    {
462
66
        segment_width = td->td_tilewidth;
463
66
        segment_height = td->td_tilelength;
464
66
    }
465
217
    else
466
217
    {
467
217
        segment_width = td->td_imagewidth;
468
217
        segment_height = td->td_imagelength - tif->tif_row;
469
217
        if (segment_height > td->td_rowsperstrip)
470
38
            segment_height = td->td_rowsperstrip;
471
217
    }
472
473
283
    if (segment_width > 16383 || segment_height > 16383)
474
35
    {
475
35
        TIFFErrorExtR(tif, module,
476
35
                      "WEBP maximum image dimensions are 16383 x 16383.");
477
35
        return 0;
478
35
    }
479
480
248
    if ((sp->state & LSTATE_INIT_DECODE) == 0)
481
0
        tif->tif_setupdecode(tif);
482
483
248
    if (sp->psDecoder != NULL)
484
64
    {
485
64
        WebPIDelete(sp->psDecoder);
486
64
        WebPFreeDecBuffer(&sp->sDecBuffer);
487
64
        sp->psDecoder = NULL;
488
64
    }
489
490
248
    sp->read_error = 0;
491
492
248
    return 1;
493
283
}
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 = (uint8_t *)_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
            case VP8_ENC_OK:
712
0
            case VP8_ENC_ERROR_LAST:
713
0
            default:
714
0
                TIFFErrorExtR(tif, module,
715
0
                              "WebPEncode returned an unknown error code: %u",
716
0
                              sp->sPicture.error_code);
717
0
                pszErrorMsg = "Unknown WebP error type.";
718
0
                break;
719
0
        }
720
0
        TIFFErrorExtR(tif, module, "WebPEncode() failed : %s", pszErrorMsg);
721
#else
722
        TIFFErrorExtR(tif, module, "Error in WebPEncode()");
723
#endif
724
0
        return 0;
725
0
    }
726
727
0
    sp->sPicture.custom_ptr = NULL;
728
729
0
    if (!TIFFFlushData1(tif))
730
0
    {
731
0
        TIFFErrorExtR(tif, module, "Error flushing TIFF WebP encoder.");
732
0
        return 0;
733
0
    }
734
735
0
    return 1;
736
0
}
737
738
static void TWebPCleanup(TIFF *tif)
739
1.63k
{
740
1.63k
    WebPState *sp = LState(tif);
741
742
1.63k
    assert(sp != 0);
743
744
1.63k
    tif->tif_tagmethods.vgetfield = sp->vgetparent;
745
1.63k
    tif->tif_tagmethods.vsetfield = sp->vsetparent;
746
747
1.63k
    if (sp->state & LSTATE_INIT_ENCODE)
748
0
    {
749
0
        WebPPictureFree(&sp->sPicture);
750
0
    }
751
752
1.63k
    if (sp->psDecoder != NULL)
753
32
    {
754
32
        WebPIDelete(sp->psDecoder);
755
32
        WebPFreeDecBuffer(&sp->sDecBuffer);
756
32
        sp->psDecoder = NULL;
757
32
        sp->last_y = 0;
758
32
    }
759
760
1.63k
    if (sp->pBuffer != NULL)
761
26
    {
762
26
        _TIFFfreeExt(tif, sp->pBuffer);
763
26
        sp->pBuffer = NULL;
764
26
    }
765
766
1.63k
    _TIFFfreeExt(tif, tif->tif_data);
767
1.63k
    tif->tif_data = NULL;
768
769
1.63k
    _TIFFSetDefaultCompressionState(tif);
770
1.63k
}
771
772
static int TWebPVSetField(TIFF *tif, uint32_t tag, va_list ap)
773
13.9k
{
774
13.9k
    static const char module[] = "WebPVSetField";
775
13.9k
    WebPState *sp = LState(tif);
776
777
13.9k
    switch (tag)
778
13.9k
    {
779
0
        case TIFFTAG_WEBP_LEVEL:
780
0
            sp->quality_level = (int)va_arg(ap, int);
781
0
            if (sp->quality_level <= 0 || sp->quality_level > 100.0f)
782
0
            {
783
0
                TIFFWarningExtR(tif, module,
784
0
                                "WEBP_LEVEL should be between 1 and 100");
785
0
            }
786
0
            return 1;
787
0
        case TIFFTAG_WEBP_LOSSLESS:
788
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
789
0
            sp->lossless = va_arg(ap, int);
790
0
            if (sp->lossless)
791
0
            {
792
0
                sp->quality_level = 100;
793
0
            }
794
0
            return 1;
795
#else
796
            TIFFErrorExtR(
797
                tif, module,
798
                "Need to upgrade WEBP driver, this version doesn't support "
799
                "lossless compression.");
800
            return 0;
801
#endif
802
0
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
803
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
804
0
            sp->lossless_exact = va_arg(ap, int);
805
0
            return 1;
806
#else
807
            TIFFErrorExtR(
808
                tif, module,
809
                "Need to upgrade WEBP driver, this version doesn't support "
810
                "lossless compression.");
811
            return 0;
812
#endif
813
13.9k
        default:
814
13.9k
            return (*sp->vsetparent)(tif, tag, ap);
815
13.9k
    }
816
    /*NOTREACHED*/
817
13.9k
}
818
819
static int TWebPVGetField(TIFF *tif, uint32_t tag, va_list ap)
820
345k
{
821
345k
    WebPState *sp = LState(tif);
822
823
345k
    switch (tag)
824
345k
    {
825
0
        case TIFFTAG_WEBP_LEVEL:
826
0
            *va_arg(ap, int *) = sp->quality_level;
827
0
            break;
828
0
        case TIFFTAG_WEBP_LOSSLESS:
829
0
            *va_arg(ap, int *) = sp->lossless;
830
0
            break;
831
0
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
832
0
            *va_arg(ap, int *) = sp->lossless_exact;
833
0
            break;
834
345k
        default:
835
345k
            return (*sp->vgetparent)(tif, tag, ap);
836
345k
    }
837
0
    return 1;
838
345k
}
839
840
static const TIFFField TWebPFields[] = {
841
    {TIFFTAG_WEBP_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, FIELD_PSEUDO, TRUE,
842
     FALSE, "WEBP quality", NULL},
843
    {TIFFTAG_WEBP_LOSSLESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, FIELD_PSEUDO,
844
     TRUE, FALSE, "WEBP lossless/lossy", NULL},
845
    {TIFFTAG_WEBP_LOSSLESS_EXACT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
846
     FIELD_PSEUDO, TRUE, FALSE, "WEBP exact lossless", NULL},
847
};
848
849
int TIFFInitWebP(TIFF *tif, int scheme)
850
1.63k
{
851
1.63k
    static const char module[] = "TIFFInitWebP";
852
1.63k
    WebPState *sp;
853
854
1.63k
    (void)scheme;
855
1.63k
    assert(scheme == COMPRESSION_WEBP);
856
857
    /*
858
     * Merge codec-specific tag information.
859
     */
860
1.63k
    if (!_TIFFMergeFields(tif, TWebPFields, TIFFArrayCount(TWebPFields)))
861
0
    {
862
0
        TIFFErrorExtR(tif, module, "Merging WebP codec-specific tags failed");
863
0
        return 0;
864
0
    }
865
866
    /*
867
     * Allocate state block so tag methods have storage to record values.
868
     */
869
1.63k
    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(WebPState));
870
1.63k
    if (tif->tif_data == NULL)
871
0
        goto bad;
872
1.63k
    sp = LState(tif);
873
874
    /*
875
     * Override parent get/set field methods.
876
     */
877
1.63k
    sp->vgetparent = tif->tif_tagmethods.vgetfield;
878
1.63k
    tif->tif_tagmethods.vgetfield = TWebPVGetField; /* hook for codec tags */
879
1.63k
    sp->vsetparent = tif->tif_tagmethods.vsetfield;
880
1.63k
    tif->tif_tagmethods.vsetfield = TWebPVSetField; /* hook for codec tags */
881
882
    /* Default values for codec-specific fields */
883
1.63k
    sp->quality_level = 75; /* default comp. level */
884
1.63k
    sp->lossless = 0;       /* default to false */
885
1.63k
    sp->lossless_exact = 1; /* exact lossless mode (if lossless enabled) */
886
1.63k
    sp->state = 0;
887
1.63k
    sp->nSamples = 0;
888
1.63k
    sp->psDecoder = NULL;
889
1.63k
    sp->last_y = 0;
890
891
1.63k
    sp->buffer_offset = 0;
892
1.63k
    sp->pBuffer = NULL;
893
894
    /*
895
     * Install codec methods.
896
     * Notes:
897
     * encoderow is not supported
898
     */
899
1.63k
    tif->tif_fixuptags = TWebPFixupTags;
900
1.63k
    tif->tif_setupdecode = TWebPSetupDecode;
901
1.63k
    tif->tif_predecode = TWebPPreDecode;
902
1.63k
    tif->tif_decoderow = TWebPDecode;
903
1.63k
    tif->tif_decodestrip = TWebPDecode;
904
1.63k
    tif->tif_decodetile = TWebPDecode;
905
1.63k
    tif->tif_setupencode = TWebPSetupEncode;
906
1.63k
    tif->tif_preencode = TWebPPreEncode;
907
1.63k
    tif->tif_postencode = TWebPPostEncode;
908
1.63k
    tif->tif_encoderow = TWebPEncode;
909
1.63k
    tif->tif_encodestrip = TWebPEncode;
910
1.63k
    tif->tif_encodetile = TWebPEncode;
911
1.63k
    tif->tif_cleanup = TWebPCleanup;
912
913
1.63k
    return 1;
914
0
bad:
915
0
    TIFFErrorExtR(tif, module, "No space for WebP state block");
916
0
    return 0;
917
1.63k
}
918
919
#endif /* WEBP_SUPPORT */