• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 14784588603

01 May 2025 10:21PM UTC coverage: 70.78% (+0.003%) from 70.777%
14784588603

Pull #12246

github

web-flow
Merge ca591d9cb into 7f3406edd
Pull Request #12246: gdal vector concat: fix memleak

102 of 114 new or added lines in 2 files covered. (89.47%)

59 existing lines in 27 files now uncovered.

563062 of 795511 relevant lines covered (70.78%)

222874.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.59
/frmts/gtiff/libtiff/tif_webp.c
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
#define LSTATE_INIT_DECODE 0x01
41
#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
#define LState(tif) ((WebPState *)(tif)->tif_data)
73
#define DecoderState(tif) LState(tif)
74
#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,
16,171✔
80
                              const WebPPicture *const picture)
81
{
82
    static const char module[] = "TWebPDatasetWriter";
83
    TIFF *tif = (TIFF *)(picture->custom_ptr);
16,171✔
84

85
    if ((tif->tif_rawcc + (tmsize_t)data_size) > tif->tif_rawdatasize)
16,171✔
86
    {
87
        TIFFErrorExtR(
×
88
            tif, module, "Buffer too small by %" TIFF_SIZE_FORMAT " bytes.",
89
            (size_t)(tif->tif_rawcc + data_size - tif->tif_rawdatasize));
×
90
        return 0;
×
91
    }
92
    else
93
    {
94
        _TIFFmemcpy(tif->tif_rawcp, data, data_size);
16,171✔
95
        tif->tif_rawcc += data_size;
16,171✔
96
        tif->tif_rawcp += data_size;
16,171✔
97
        return 1;
16,171✔
98
    }
99
}
100

101
/*
102
 * Encode a chunk of pixels.
103
 */
104
static int TWebPEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
4,975✔
105
{
106
    static const char module[] = "TWebPEncode";
107
    WebPState *sp = EncoderState(tif);
4,975✔
108
    (void)s;
109

110
    assert(sp != NULL);
4,975✔
111
    assert(sp->state == LSTATE_INIT_ENCODE);
4,975✔
112

113
    if ((uint64_t)sp->buffer_offset + (uint64_t)cc > sp->buffer_size)
4,975✔
114
    {
115
        TIFFErrorExtR(tif, module, "Too many bytes to be written");
×
116
        return 0;
×
117
    }
118

119
    memcpy(sp->pBuffer + sp->buffer_offset, bp, cc);
4,975✔
120
    sp->buffer_offset += (unsigned)cc;
4,975✔
121

122
    return 1;
4,975✔
123
}
124

125
static int TWebPDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
15,414✔
126
{
127
    static const char module[] = "WebPDecode";
128
    VP8StatusCode status = VP8_STATUS_OK;
15,414✔
129
    WebPState *sp = DecoderState(tif);
15,414✔
130
    uint32_t segment_width, segment_height;
131
    bool decode_whole_strile = false;
15,414✔
132

133
    (void)s;
134

135
    assert(sp != NULL);
15,414✔
136
    assert(sp->state == LSTATE_INIT_DECODE);
15,414✔
137

138
    if (sp->read_error)
15,414✔
139
    {
140
        memset(op, 0, (size_t)occ);
×
141
        TIFFErrorExtR(tif, module,
×
142
                      "ZIPDecode: Scanline %" PRIu32 " cannot be read due to "
143
                      "previous error",
144
                      tif->tif_row);
145
        return 0;
×
146
    }
147

148
    if (sp->psDecoder == NULL)
15,414✔
149
    {
150
        TIFFDirectory *td = &tif->tif_dir;
5,474✔
151
        uint32_t buffer_size;
152

153
        if (isTiled(tif))
5,474✔
154
        {
155
            segment_width = td->td_tilewidth;
810✔
156
            segment_height = td->td_tilelength;
810✔
157
        }
158
        else
159
        {
160
            segment_width = td->td_imagewidth;
4,664✔
161
            segment_height = td->td_imagelength - tif->tif_row;
4,664✔
162
            if (segment_height > td->td_rowsperstrip)
4,664✔
163
                segment_height = td->td_rowsperstrip;
4,601✔
164
        }
165

166
        int webp_width, webp_height;
167
        if (!WebPGetInfo(tif->tif_rawcp,
5,474✔
168
                         (uint64_t)tif->tif_rawcc > UINT32_MAX
5,474✔
169
                             ? UINT32_MAX
170
                             : (uint32_t)tif->tif_rawcc,
5,474✔
171
                         &webp_width, &webp_height))
172
        {
173
            memset(op, 0, (size_t)occ);
×
174
            sp->read_error = 1;
×
175
            TIFFErrorExtR(tif, module, "WebPGetInfo() failed");
×
176
            return 0;
×
177
        }
178
        if ((uint32_t)webp_width != segment_width ||
5,474✔
179
            (uint32_t)webp_height != segment_height)
5,474✔
180
        {
181
            memset(op, 0, (size_t)occ);
×
182
            sp->read_error = 1;
×
183
            TIFFErrorExtR(
×
184
                tif, module, "WebP blob dimension is %dx%d. Expected %ux%u",
185
                webp_width, webp_height, segment_width, segment_height);
186
            return 0;
×
187
        }
188

189
#if WEBP_DECODER_ABI_VERSION >= 0x0002
190
        WebPDecoderConfig config;
191
        if (!WebPInitDecoderConfig(&config))
5,474✔
192
        {
193
            memset(op, 0, (size_t)occ);
×
194
            sp->read_error = 1;
×
195
            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
×
196
            return 0;
×
197
        }
198

199
        const bool bWebPGetFeaturesOK =
5,474✔
200
            WebPGetFeatures(tif->tif_rawcp,
5,474✔
201
                            (uint64_t)tif->tif_rawcc > UINT32_MAX
5,474✔
202
                                ? UINT32_MAX
203
                                : (uint32_t)tif->tif_rawcc,
5,474✔
204
                            &config.input) == VP8_STATUS_OK;
205

206
        WebPFreeDecBuffer(&config.output);
5,474✔
207

208
        if (!bWebPGetFeaturesOK)
5,474✔
209
        {
210
            memset(op, 0, (size_t)occ);
×
211
            sp->read_error = 1;
×
212
            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
×
213
            return 0;
×
214
        }
215

216
        const int webp_bands = config.input.has_alpha ? 4 : 3;
5,474✔
217
        if (webp_bands != sp->nSamples &&
5,474✔
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
            !(webp_bands == 3 && sp->nSamples == 4))
1✔
224
        {
225
            memset(op, 0, (size_t)occ);
×
226
            sp->read_error = 1;
×
227
            TIFFErrorExtR(tif, module,
×
228
                          "WebP blob band count is %d. Expected %d", webp_bands,
229
                          sp->nSamples);
×
230
            return 0;
×
231
        }
232
#endif
233

234
        buffer_size = segment_width * segment_height * sp->nSamples;
5,474✔
235
        if (occ == (tmsize_t)buffer_size)
5,474✔
236
        {
237
            /* If decoding the whole strip/tile, we can directly use the */
238
            /* output buffer */
239
            decode_whole_strile = true;
5,469✔
240
        }
241
        else if (sp->pBuffer == NULL || buffer_size > sp->buffer_size)
5✔
242
        {
243
            if (sp->pBuffer != NULL)
3✔
244
            {
245
                _TIFFfreeExt(tif, sp->pBuffer);
×
246
                sp->pBuffer = NULL;
×
247
            }
248

249
            sp->pBuffer = _TIFFmallocExt(tif, buffer_size);
3✔
250
            if (!sp->pBuffer)
3✔
251
            {
252
                TIFFErrorExtR(tif, module, "Cannot allocate buffer");
×
253
                memset(op, 0, (size_t)occ);
×
254
                sp->read_error = 1;
×
255
                return 0;
×
256
            }
257
            sp->buffer_size = buffer_size;
3✔
258
        }
259

260
        sp->last_y = 0;
5,474✔
261

262
        WebPInitDecBuffer(&sp->sDecBuffer);
5,474✔
263

264
        sp->sDecBuffer.is_external_memory = 1;
5,474✔
265
        sp->sDecBuffer.width = segment_width;
5,474✔
266
        sp->sDecBuffer.height = segment_height;
5,474✔
267
        sp->sDecBuffer.u.RGBA.rgba = decode_whole_strile ? op : sp->pBuffer;
5,474✔
268
        sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples;
5,474✔
269
        sp->sDecBuffer.u.RGBA.size = buffer_size;
5,474✔
270

271
        if (sp->nSamples > 3)
5,474✔
272
        {
273
            sp->sDecBuffer.colorspace = MODE_RGBA;
2✔
274
        }
275
        else
276
        {
277
            sp->sDecBuffer.colorspace = MODE_RGB;
5,472✔
278
        }
279

280
        sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer);
5,474✔
281

282
        if (sp->psDecoder == NULL)
5,474✔
283
        {
284
            memset(op, 0, (size_t)occ);
×
285
            sp->read_error = 1;
×
286
            TIFFErrorExtR(tif, module, "Unable to allocate WebP decoder.");
×
287
            return 0;
×
288
        }
289
    }
290

291
    if (occ % sp->sDecBuffer.u.RGBA.stride)
15,414✔
292
    {
293
        // read_error not set here as this is a usage issue that can be
294
        // recovered in a following call.
295
        memset(op, 0, (size_t)occ);
×
296
        /* Do not set read_error as could potentially be recovered */
297
        TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
×
298
        return 0;
×
299
    }
300

301
    status = WebPIAppend(sp->psDecoder, tif->tif_rawcp, tif->tif_rawcc);
15,414✔
302

303
    if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED)
15,414✔
304
    {
305
        if (status == VP8_STATUS_INVALID_PARAM)
×
306
        {
307
            TIFFErrorExtR(tif, module, "Invalid parameter used.");
×
308
        }
309
        else if (status == VP8_STATUS_OUT_OF_MEMORY)
×
310
        {
311
            TIFFErrorExtR(tif, module, "Out of memory.");
×
312
        }
313
        else
314
        {
315
            TIFFErrorExtR(tif, module, "Unrecognized error.");
×
316
        }
317
        memset(op, 0, (size_t)occ);
×
318
        sp->read_error = 1;
×
319
        return 0;
×
320
    }
321
    else
322
    {
323
        int current_y, stride;
324
        uint8_t *buf;
325

326
        /* Returns the RGB/A image decoded so far */
327
        buf = WebPIDecGetRGB(sp->psDecoder, &current_y, NULL, NULL, &stride);
15,414✔
328

329
        if ((buf != NULL) &&
15,414✔
330
            (occ <= (tmsize_t)stride * (current_y - sp->last_y)))
15,414✔
331
        {
332
            const int numberOfExpectedLines =
15,414✔
333
                (int)(occ / sp->sDecBuffer.u.RGBA.stride);
15,414✔
334
            if (decode_whole_strile)
15,414✔
335
            {
336
                if (current_y != numberOfExpectedLines)
5,469✔
337
                {
338
                    memset(op, 0, (size_t)occ);
×
339
                    sp->read_error = 1;
×
340
                    TIFFErrorExtR(tif, module,
×
341
                                  "Unable to decode WebP data: less lines than "
342
                                  "expected.");
343
                    return 0;
×
344
                }
345
            }
346
            else
347
            {
348
                memcpy(op, buf + (sp->last_y * stride), occ);
9,945✔
349
            }
350

351
            tif->tif_rawcp += tif->tif_rawcc;
15,414✔
352
            tif->tif_rawcc = 0;
15,414✔
353
            sp->last_y += numberOfExpectedLines;
15,414✔
354

355
            if (decode_whole_strile)
15,414✔
356
            {
357
                /* We can now free the decoder as we're completely done */
358
                if (sp->psDecoder != NULL)
5,469✔
359
                {
360
                    WebPIDelete(sp->psDecoder);
5,469✔
361
                    WebPFreeDecBuffer(&sp->sDecBuffer);
5,469✔
362
                    sp->psDecoder = NULL;
5,469✔
363
                }
364
            }
365
            return 1;
15,414✔
366
        }
367
        else
368
        {
369
            memset(op, 0, (size_t)occ);
×
370
            sp->read_error = 1;
×
371
            TIFFErrorExtR(tif, module, "Unable to decode WebP data.");
×
372
            return 0;
×
373
        }
374
    }
375
}
376

377
static int TWebPFixupTags(TIFF *tif)
406✔
378
{
379
    (void)tif;
380
    if (tif->tif_dir.td_planarconfig != PLANARCONFIG_CONTIG)
406✔
381
    {
382
        static const char module[] = "TWebPFixupTags";
383
        TIFFErrorExtR(tif, module,
×
384
                      "TIFF WEBP requires data to be stored contiguously in "
385
                      "RGB e.g. RGBRGBRGB "
386
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
387
                      "or RGBARGBARGBA"
388
#endif
389
        );
390
        return 0;
×
391
    }
392
    return 1;
406✔
393
}
394

395
static int TWebPSetupDecode(TIFF *tif)
97✔
396
{
397
    static const char module[] = "WebPSetupDecode";
398
    uint16_t nBitsPerSample = tif->tif_dir.td_bitspersample;
97✔
399
    uint16_t sampleFormat = tif->tif_dir.td_sampleformat;
97✔
400

401
    WebPState *sp = DecoderState(tif);
97✔
402
    assert(sp != NULL);
97✔
403

404
    sp->nSamples = tif->tif_dir.td_samplesperpixel;
97✔
405

406
    /* check band count */
407
    if (sp->nSamples != 3
97✔
408
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
409
        && sp->nSamples != 4
2✔
410
#endif
411
    )
412
    {
413
        TIFFErrorExtR(tif, module,
×
414
                      "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
415
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
416
                      "or 4 (RGBA) "
417
#endif
418
                      "bands.",
419
                      sp->nSamples);
×
420
        return 0;
×
421
    }
422

423
    /* check bits per sample and data type */
424
    if ((nBitsPerSample != 8) && (sampleFormat != 1))
97✔
425
    {
426
        TIFFErrorExtR(tif, module, "WEBP driver requires 8 bit unsigned data");
×
427
        return 0;
×
428
    }
429

430
    /* if we were last encoding, terminate this mode */
431
    if (sp->state & LSTATE_INIT_ENCODE)
97✔
432
    {
433
        WebPPictureFree(&sp->sPicture);
9✔
434
        if (sp->pBuffer != NULL)
9✔
435
        {
436
            _TIFFfreeExt(tif, sp->pBuffer);
9✔
437
            sp->pBuffer = NULL;
9✔
438
        }
439
        sp->buffer_offset = 0;
9✔
440
        sp->state = 0;
9✔
441
    }
442

443
    sp->state |= LSTATE_INIT_DECODE;
97✔
444

445
    return 1;
97✔
446
}
447

448
/*
449
 * Setup state for decoding a strip.
450
 */
451
static int TWebPPreDecode(TIFF *tif, uint16_t s)
5,474✔
452
{
453
    static const char module[] = "TWebPPreDecode";
454
    uint32_t segment_width, segment_height;
455
    WebPState *sp = DecoderState(tif);
5,474✔
456
    TIFFDirectory *td = &tif->tif_dir;
5,474✔
457
    (void)s;
458
    assert(sp != NULL);
5,474✔
459

460
    if (isTiled(tif))
5,474✔
461
    {
462
        segment_width = td->td_tilewidth;
810✔
463
        segment_height = td->td_tilelength;
810✔
464
    }
465
    else
466
    {
467
        segment_width = td->td_imagewidth;
4,664✔
468
        segment_height = td->td_imagelength - tif->tif_row;
4,664✔
469
        if (segment_height > td->td_rowsperstrip)
4,664✔
470
            segment_height = td->td_rowsperstrip;
4,601✔
471
    }
472

473
    if (segment_width > 16383 || segment_height > 16383)
5,474✔
474
    {
475
        TIFFErrorExtR(tif, module,
×
476
                      "WEBP maximum image dimensions are 16383 x 16383.");
477
        return 0;
×
478
    }
479

480
    if ((sp->state & LSTATE_INIT_DECODE) == 0)
5,474✔
481
        tif->tif_setupdecode(tif);
9✔
482

483
    if (sp->psDecoder != NULL)
5,474✔
484
    {
485
        WebPIDelete(sp->psDecoder);
2✔
486
        WebPFreeDecBuffer(&sp->sDecBuffer);
2✔
487
        sp->psDecoder = NULL;
2✔
488
    }
489

490
    sp->read_error = 0;
5,474✔
491

492
    return 1;
5,474✔
493
}
494

495
static int TWebPSetupEncode(TIFF *tif)
64✔
496
{
497
    static const char module[] = "WebPSetupEncode";
498
    uint16_t nBitsPerSample = tif->tif_dir.td_bitspersample;
64✔
499
    uint16_t sampleFormat = tif->tif_dir.td_sampleformat;
64✔
500

501
    WebPState *sp = EncoderState(tif);
64✔
502
    assert(sp != NULL);
64✔
503

504
    sp->nSamples = tif->tif_dir.td_samplesperpixel;
64✔
505

506
    /* check band count */
507
    if (sp->nSamples != 3
64✔
508
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
509
        && sp->nSamples != 4
1✔
510
#endif
511
    )
512
    {
513
        TIFFErrorExtR(tif, module,
×
514
                      "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
515
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
516
                      "or 4 (RGBA) "
517
#endif
518
                      "bands.",
519
                      sp->nSamples);
×
520
        return 0;
×
521
    }
522

523
    /* check bits per sample and data type */
524
    if ((nBitsPerSample != 8) || (sampleFormat != SAMPLEFORMAT_UINT))
64✔
525
    {
526
        TIFFErrorExtR(tif, module, "WEBP driver requires 8 bit unsigned data");
×
527
        return 0;
×
528
    }
529

530
    if (sp->state & LSTATE_INIT_DECODE)
64✔
531
    {
532
        WebPIDelete(sp->psDecoder);
×
533
        WebPFreeDecBuffer(&sp->sDecBuffer);
×
534
        sp->psDecoder = NULL;
×
535
        sp->last_y = 0;
×
536
        sp->state = 0;
×
537
    }
538

539
    sp->state |= LSTATE_INIT_ENCODE;
64✔
540

541
    if (!WebPPictureInit(&sp->sPicture))
64✔
542
    {
543
        TIFFErrorExtR(tif, module, "Error initializing WebP picture.");
×
544
        return 0;
×
545
    }
546

547
    if (!WebPConfigInitInternal(&sp->sEncoderConfig, WEBP_PRESET_DEFAULT,
64✔
548
                                (float)sp->quality_level,
64✔
549
                                WEBP_ENCODER_ABI_VERSION))
550
    {
551
        TIFFErrorExtR(tif, module,
×
552
                      "Error creating WebP encoder configuration.");
553
        return 0;
×
554
    }
555

556
// WebPConfigInitInternal above sets lossless to false
557
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
558
    sp->sEncoderConfig.lossless = sp->lossless;
64✔
559
    if (sp->lossless)
64✔
560
    {
561
        sp->sPicture.use_argb = 1;
15✔
562
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
563
        sp->sEncoderConfig.exact = sp->lossless_exact;
15✔
564
#endif
565
    }
566
#endif
567

568
    if (!WebPValidateConfig(&sp->sEncoderConfig))
64✔
569
    {
570
        TIFFErrorExtR(tif, module, "Error with WebP encoder configuration.");
×
571
        return 0;
×
572
    }
573

574
    return 1;
64✔
575
}
576

577
/*
578
 * Reset encoding state at the start of a strip.
579
 */
580
static int TWebPPreEncode(TIFF *tif, uint16_t s)
2,975✔
581
{
582
    static const char module[] = "TWebPPreEncode";
583
    uint32_t segment_width, segment_height;
584
    WebPState *sp = EncoderState(tif);
2,975✔
585
    TIFFDirectory *td = &tif->tif_dir;
2,975✔
586

587
    (void)s;
588

589
    assert(sp != NULL);
2,975✔
590
    if (sp->state != LSTATE_INIT_ENCODE)
2,975✔
591
        tif->tif_setupencode(tif);
×
592

593
    /*
594
     * Set encoding parameters for this strip/tile.
595
     */
596
    if (isTiled(tif))
2,975✔
597
    {
598
        segment_width = td->td_tilewidth;
385✔
599
        segment_height = td->td_tilelength;
385✔
600
    }
601
    else
602
    {
603
        segment_width = td->td_imagewidth;
2,590✔
604
        segment_height = td->td_imagelength - tif->tif_row;
2,590✔
605
        if (segment_height > td->td_rowsperstrip)
2,590✔
606
            segment_height = td->td_rowsperstrip;
2,553✔
607
    }
608

609
    if (segment_width > 16383 || segment_height > 16383)
2,975✔
610
    {
611
        TIFFErrorExtR(tif, module,
×
612
                      "WEBP maximum image dimensions are 16383 x 16383.");
613
        return 0;
×
614
    }
615

616
    /* set up buffer for raw data */
617
    /* given above check and that nSamples <= 4, buffer_size is <= 1 GB */
618
    sp->buffer_size = segment_width * segment_height * sp->nSamples;
2,975✔
619

620
    if (sp->pBuffer != NULL)
2,975✔
621
    {
622
        _TIFFfreeExt(tif, sp->pBuffer);
2,911✔
623
        sp->pBuffer = NULL;
2,911✔
624
    }
625

626
    sp->pBuffer = _TIFFmallocExt(tif, sp->buffer_size);
2,975✔
627
    if (!sp->pBuffer)
2,975✔
628
    {
629
        TIFFErrorExtR(tif, module, "Cannot allocate buffer");
×
630
        return 0;
×
631
    }
632
    sp->buffer_offset = 0;
2,975✔
633

634
    sp->sPicture.width = segment_width;
2,975✔
635
    sp->sPicture.height = segment_height;
2,975✔
636
    sp->sPicture.writer = TWebPDatasetWriter;
2,975✔
637
    sp->sPicture.custom_ptr = tif;
2,975✔
638

639
    return 1;
2,975✔
640
}
641

642
/*
643
 * Finish off an encoded strip by flushing it.
644
 */
645
static int TWebPPostEncode(TIFF *tif)
2,975✔
646
{
647
    static const char module[] = "WebPPostEncode";
648
    int64_t stride;
649
    WebPState *sp = EncoderState(tif);
2,975✔
650
    assert(sp != NULL);
2,975✔
651

652
    assert(sp->state == LSTATE_INIT_ENCODE);
2,975✔
653

654
    stride = (int64_t)sp->sPicture.width * sp->nSamples;
2,975✔
655

656
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
657
    if (sp->nSamples == 4)
2,975✔
658
    {
659
        if (!WebPPictureImportRGBA(&sp->sPicture, sp->pBuffer, (int)stride))
1✔
660
        {
661
            TIFFErrorExtR(tif, module, "WebPPictureImportRGBA() failed");
×
662
            return 0;
×
663
        }
664
    }
665
    else
666
#endif
667
        if (!WebPPictureImportRGB(&sp->sPicture, sp->pBuffer, (int)stride))
2,974✔
668
    {
669
        TIFFErrorExtR(tif, module, "WebPPictureImportRGB() failed");
×
670
        return 0;
×
671
    }
672

673
    if (!WebPEncode(&sp->sEncoderConfig, &sp->sPicture))
2,975✔
674
    {
675

676
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
677
        const char *pszErrorMsg = NULL;
×
678
        switch (sp->sPicture.error_code)
×
679
        {
680
            case VP8_ENC_ERROR_OUT_OF_MEMORY:
×
681
                pszErrorMsg = "Out of memory";
×
682
                break;
×
683
            case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
×
684
                pszErrorMsg = "Out of memory while flushing bits";
×
685
                break;
×
686
            case VP8_ENC_ERROR_NULL_PARAMETER:
×
687
                pszErrorMsg = "A pointer parameter is NULL";
×
688
                break;
×
689
            case VP8_ENC_ERROR_INVALID_CONFIGURATION:
×
690
                pszErrorMsg = "Configuration is invalid";
×
691
                break;
×
692
            case VP8_ENC_ERROR_BAD_DIMENSION:
×
693
                pszErrorMsg = "Picture has invalid width/height";
×
694
                break;
×
695
            case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
×
696
                pszErrorMsg = "Partition is bigger than 512k. Try using less "
×
697
                              "SEGMENTS, or increase PARTITION_LIMIT value";
698
                break;
×
699
            case VP8_ENC_ERROR_PARTITION_OVERFLOW:
×
700
                pszErrorMsg = "Partition is bigger than 16M";
×
701
                break;
×
702
            case VP8_ENC_ERROR_BAD_WRITE:
×
703
                pszErrorMsg = "Error while fludshing bytes";
×
704
                break;
×
705
            case VP8_ENC_ERROR_FILE_TOO_BIG:
×
706
                pszErrorMsg = "File is bigger than 4G";
×
707
                break;
×
708
            case VP8_ENC_ERROR_USER_ABORT:
×
709
                pszErrorMsg = "User interrupted";
×
710
                break;
×
711
            default:
×
712
                TIFFErrorExtR(tif, module,
×
713
                              "WebPEncode returned an unknown error code: %d",
714
                              sp->sPicture.error_code);
×
715
                pszErrorMsg = "Unknown WebP error type.";
×
716
                break;
×
717
        }
718
        TIFFErrorExtR(tif, module, "WebPEncode() failed : %s", pszErrorMsg);
×
719
#else
720
        TIFFErrorExtR(tif, module, "Error in WebPEncode()");
721
#endif
722
        return 0;
×
723
    }
724

725
    sp->sPicture.custom_ptr = NULL;
2,975✔
726

727
    if (!TIFFFlushData1(tif))
2,975✔
728
    {
UNCOV
729
        TIFFErrorExtR(tif, module, "Error flushing TIFF WebP encoder.");
×
730
        return 0;
×
731
    }
732

733
    return 1;
2,975✔
734
}
735

736
static void TWebPCleanup(TIFF *tif)
509✔
737
{
738
    WebPState *sp = LState(tif);
509✔
739

740
    assert(sp != 0);
509✔
741

742
    tif->tif_tagmethods.vgetfield = sp->vgetparent;
509✔
743
    tif->tif_tagmethods.vsetfield = sp->vsetparent;
509✔
744

745
    if (sp->state & LSTATE_INIT_ENCODE)
509✔
746
    {
747
        WebPPictureFree(&sp->sPicture);
55✔
748
    }
749

750
    if (sp->psDecoder != NULL)
509✔
751
    {
752
        WebPIDelete(sp->psDecoder);
3✔
753
        WebPFreeDecBuffer(&sp->sDecBuffer);
3✔
754
        sp->psDecoder = NULL;
3✔
755
        sp->last_y = 0;
3✔
756
    }
757

758
    if (sp->pBuffer != NULL)
509✔
759
    {
760
        _TIFFfreeExt(tif, sp->pBuffer);
58✔
761
        sp->pBuffer = NULL;
58✔
762
    }
763

764
    _TIFFfreeExt(tif, tif->tif_data);
509✔
765
    tif->tif_data = NULL;
509✔
766

767
    _TIFFSetDefaultCompressionState(tif);
509✔
768
}
509✔
769

770
static int TWebPVSetField(TIFF *tif, uint32_t tag, va_list ap)
4,661✔
771
{
772
    static const char module[] = "WebPVSetField";
773
    WebPState *sp = LState(tif);
4,661✔
774

775
    switch (tag)
4,661✔
776
    {
777
        case TIFFTAG_WEBP_LEVEL:
174✔
778
            sp->quality_level = (int)va_arg(ap, int);
174✔
779
            if (sp->quality_level <= 0 || sp->quality_level > 100.0f)
174✔
780
            {
781
                TIFFWarningExtR(tif, module,
×
782
                                "WEBP_LEVEL should be between 1 and 100");
783
            }
784
            return 1;
174✔
785
        case TIFFTAG_WEBP_LOSSLESS:
63✔
786
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
787
            sp->lossless = va_arg(ap, int);
63✔
788
            if (sp->lossless)
63✔
789
            {
790
                sp->quality_level = 100;
63✔
791
            }
792
            return 1;
63✔
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
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
×
801
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
802
            sp->lossless_exact = va_arg(ap, int);
×
803
            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
        default:
4,424✔
812
            return (*sp->vsetparent)(tif, tag, ap);
4,424✔
813
    }
814
    /*NOTREACHED*/
815
}
816

817
static int TWebPVGetField(TIFF *tif, uint32_t tag, va_list ap)
17,228✔
818
{
819
    WebPState *sp = LState(tif);
17,228✔
820

821
    switch (tag)
17,228✔
822
    {
823
        case TIFFTAG_WEBP_LEVEL:
×
824
            *va_arg(ap, int *) = sp->quality_level;
×
825
            break;
×
826
        case TIFFTAG_WEBP_LOSSLESS:
×
827
            *va_arg(ap, int *) = sp->lossless;
×
828
            break;
×
829
        case TIFFTAG_WEBP_LOSSLESS_EXACT:
×
830
            *va_arg(ap, int *) = sp->lossless_exact;
×
831
            break;
×
832
        default:
17,228✔
833
            return (*sp->vgetparent)(tif, tag, ap);
17,228✔
834
    }
835
    return 1;
×
836
}
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)
509✔
848
{
849
    static const char module[] = "TIFFInitWebP";
850
    WebPState *sp;
851

852
    (void)scheme;
853
    assert(scheme == COMPRESSION_WEBP);
509✔
854

855
    /*
856
     * Merge codec-specific tag information.
857
     */
858
    if (!_TIFFMergeFields(tif, TWebPFields, TIFFArrayCount(TWebPFields)))
509✔
859
    {
860
        TIFFErrorExtR(tif, module, "Merging WebP codec-specific tags failed");
×
861
        return 0;
×
862
    }
863

864
    /*
865
     * Allocate state block so tag methods have storage to record values.
866
     */
867
    tif->tif_data = (uint8_t *)_TIFFmallocExt(tif, sizeof(WebPState));
509✔
868
    if (tif->tif_data == NULL)
509✔
869
        goto bad;
×
870
    sp = LState(tif);
509✔
871

872
    /*
873
     * Override parent get/set field methods.
874
     */
875
    sp->vgetparent = tif->tif_tagmethods.vgetfield;
509✔
876
    tif->tif_tagmethods.vgetfield = TWebPVGetField; /* hook for codec tags */
509✔
877
    sp->vsetparent = tif->tif_tagmethods.vsetfield;
509✔
878
    tif->tif_tagmethods.vsetfield = TWebPVSetField; /* hook for codec tags */
509✔
879

880
    /* Default values for codec-specific fields */
881
    sp->quality_level = 75; /* default comp. level */
509✔
882
    sp->lossless = 0;       /* default to false */
509✔
883
    sp->lossless_exact = 1; /* exact lossless mode (if lossless enabled) */
509✔
884
    sp->state = 0;
509✔
885
    sp->nSamples = 0;
509✔
886
    sp->psDecoder = NULL;
509✔
887
    sp->last_y = 0;
509✔
888

889
    sp->buffer_offset = 0;
509✔
890
    sp->pBuffer = NULL;
509✔
891

892
    /*
893
     * Install codec methods.
894
     * Notes:
895
     * encoderow is not supported
896
     */
897
    tif->tif_fixuptags = TWebPFixupTags;
509✔
898
    tif->tif_setupdecode = TWebPSetupDecode;
509✔
899
    tif->tif_predecode = TWebPPreDecode;
509✔
900
    tif->tif_decoderow = TWebPDecode;
509✔
901
    tif->tif_decodestrip = TWebPDecode;
509✔
902
    tif->tif_decodetile = TWebPDecode;
509✔
903
    tif->tif_setupencode = TWebPSetupEncode;
509✔
904
    tif->tif_preencode = TWebPPreEncode;
509✔
905
    tif->tif_postencode = TWebPPostEncode;
509✔
906
    tif->tif_encoderow = TWebPEncode;
509✔
907
    tif->tif_encodestrip = TWebPEncode;
509✔
908
    tif->tif_encodetile = TWebPEncode;
509✔
909
    tif->tif_cleanup = TWebPCleanup;
509✔
910

911
    return 1;
509✔
912
bad:
×
913
    TIFFErrorExtR(tif, module, "No space for WebP state block");
×
914
    return 0;
×
915
}
916

917
#endif /* WEBP_SUPPORT */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc