2 * Copyright (c) 2018, Mapbox
3 * Author: <norman.barker at mapbox.com>
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.
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.
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
30 * WEBP Compression Support
34 #include "webp/decode.h"
35 #include "webp/encode.h"
39 #define LSTATE_INIT_DECODE 0x01
40 #define LSTATE_INIT_ENCODE 0x02
42 * State block for each open TIFF
43 * file using WEBP compression/decompression.
46 uint16 nSamples
; /* number of samples per pixel */
48 int lossless
; /* lossy/lossless compression */
49 int quality_level
; /* compression level */
50 WebPPicture sPicture
; /* WebP Picture */
51 WebPConfig sEncoderConfig
; /* WebP encoder config */
52 uint8
* pBuffer
; /* buffer to hold raw data on encoding */
53 unsigned int buffer_offset
; /* current offset into the buffer */
54 unsigned int buffer_size
;
56 WebPIDecoder
* psDecoder
; /* WebPIDecoder */
57 WebPDecBuffer sDecBuffer
; /* Decoder buffer */
58 int last_y
; /* Last row decoded */
60 int state
; /* state flags */
62 TIFFVGetMethod vgetparent
; /* super-class method */
63 TIFFVSetMethod vsetparent
; /* super-class method */
66 #define LState(tif) ((WebPState*) (tif)->tif_data)
67 #define DecoderState(tif) LState(tif)
68 #define EncoderState(tif) LState(tif)
70 static int TWebPEncode(TIFF
* tif
, uint8
* bp
, tmsize_t cc
, uint16 s
);
71 static int TWebPDecode(TIFF
* tif
, uint8
* op
, tmsize_t occ
, uint16 s
);
74 int TWebPDatasetWriter(const uint8_t* data
, size_t data_size
,
75 const WebPPicture
* const picture
)
77 static const char module
[] = "TWebPDatasetWriter";
78 TIFF
* tif
= (TIFF
*)(picture
->custom_ptr
);
80 if ( (tif
->tif_rawcc
+ (tmsize_t
)data_size
) > tif
->tif_rawdatasize
) {
81 TIFFErrorExt(tif
->tif_clientdata
, module
,
82 "Buffer too small by " TIFF_SIZE_FORMAT
" bytes.",
83 (size_t) (tif
->tif_rawcc
+ data_size
- tif
->tif_rawdatasize
));
86 _TIFFmemcpy(tif
->tif_rawcp
, data
, data_size
);
87 tif
->tif_rawcc
+= data_size
;
88 tif
->tif_rawcp
+= data_size
;
94 * Encode a chunk of pixels.
97 TWebPEncode(TIFF
* tif
, uint8
* bp
, tmsize_t cc
, uint16 s
)
99 static const char module
[] = "TWebPEncode";
100 WebPState
*sp
= EncoderState(tif
);
104 assert(sp
->state
== LSTATE_INIT_ENCODE
);
106 if( (uint64
)sp
->buffer_offset
+
107 (uint64
)cc
> sp
->buffer_size
)
109 TIFFErrorExt(tif
->tif_clientdata
, module
,
110 "Too many bytes to be written");
114 memcpy(sp
->pBuffer
+ sp
->buffer_offset
,
116 sp
->buffer_offset
+= (unsigned)cc
;
123 TWebPDecode(TIFF
* tif
, uint8
* op
, tmsize_t occ
, uint16 s
)
125 static const char module
[] = "WebPDecode";
126 VP8StatusCode status
= VP8_STATUS_OK
;
127 WebPState
*sp
= DecoderState(tif
);
131 assert(sp
->state
== LSTATE_INIT_DECODE
);
133 if (occ
% sp
->sDecBuffer
.u
.RGBA
.stride
)
135 TIFFErrorExt(tif
->tif_clientdata
, module
,
136 "Fractional scanlines cannot be read");
140 status
= WebPIAppend(sp
->psDecoder
, tif
->tif_rawcp
, tif
->tif_rawcc
);
142 if (status
!= VP8_STATUS_OK
&& status
!= VP8_STATUS_SUSPENDED
) {
143 if (status
== VP8_STATUS_INVALID_PARAM
) {
144 TIFFErrorExt(tif
->tif_clientdata
, module
,
145 "Invalid parameter used.");
146 } else if (status
== VP8_STATUS_OUT_OF_MEMORY
) {
147 TIFFErrorExt(tif
->tif_clientdata
, module
,
150 TIFFErrorExt(tif
->tif_clientdata
, module
,
151 "Unrecognized error.");
155 int current_y
, stride
;
158 /* Returns the RGB/A image decoded so far */
159 buf
= WebPIDecGetRGB(sp
->psDecoder
, ¤t_y
, NULL
, NULL
, &stride
);
162 (occ
<= stride
* (current_y
- sp
->last_y
))) {
164 buf
+ (sp
->last_y
* stride
),
167 tif
->tif_rawcp
+= tif
->tif_rawcc
;
169 sp
->last_y
+= occ
/ sp
->sDecBuffer
.u
.RGBA
.stride
;
172 TIFFErrorExt(tif
->tif_clientdata
, module
, "Unable to decode WebP data.");
179 TWebPFixupTags(TIFF
* tif
)
182 if (tif
->tif_dir
.td_planarconfig
!= PLANARCONFIG_CONTIG
) {
183 static const char module
[] = "TWebPFixupTags";
184 TIFFErrorExt(tif
->tif_clientdata
, module
,
185 "TIFF WEBP requires data to be stored contiguously in RGB e.g. RGBRGBRGB "
186 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
196 TWebPSetupDecode(TIFF
* tif
)
198 static const char module
[] = "WebPSetupDecode";
199 uint16 nBitsPerSample
= tif
->tif_dir
.td_bitspersample
;
200 uint16 sampleFormat
= tif
->tif_dir
.td_sampleformat
;
202 WebPState
* sp
= DecoderState(tif
);
205 sp
->nSamples
= tif
->tif_dir
.td_samplesperpixel
;
207 /* check band count */
208 if ( sp
->nSamples
!= 3
209 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
214 TIFFErrorExt(tif
->tif_clientdata
, module
,
215 "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
216 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
224 /* check bits per sample and data type */
225 if ((nBitsPerSample
!= 8) && (sampleFormat
!= 1)) {
226 TIFFErrorExt(tif
->tif_clientdata
, module
,
227 "WEBP driver requires 8 bit unsigned data");
231 /* if we were last encoding, terminate this mode */
232 if (sp
->state
& LSTATE_INIT_ENCODE
) {
233 WebPPictureFree(&sp
->sPicture
);
234 if (sp
->pBuffer
!= NULL
) {
235 _TIFFfree(sp
->pBuffer
);
238 sp
->buffer_offset
= 0;
242 sp
->state
|= LSTATE_INIT_DECODE
;
248 * Setup state for decoding a strip.
251 TWebPPreDecode(TIFF
* tif
, uint16 s
)
253 static const char module
[] = "TWebPPreDecode";
254 uint32 segment_width
, segment_height
;
255 WebPState
* sp
= DecoderState(tif
);
256 TIFFDirectory
* td
= &tif
->tif_dir
;
261 segment_width
= td
->td_tilewidth
;
262 segment_height
= td
->td_tilelength
;
264 segment_width
= td
->td_imagewidth
;
265 segment_height
= td
->td_imagelength
- tif
->tif_row
;
266 if (segment_height
> td
->td_rowsperstrip
)
267 segment_height
= td
->td_rowsperstrip
;
270 if( (sp
->state
& LSTATE_INIT_DECODE
) == 0 )
271 tif
->tif_setupdecode(tif
);
273 if (sp
->psDecoder
!= NULL
) {
274 WebPIDelete(sp
->psDecoder
);
275 WebPFreeDecBuffer(&sp
->sDecBuffer
);
276 sp
->psDecoder
= NULL
;
281 WebPInitDecBuffer(&sp
->sDecBuffer
);
283 sp
->sDecBuffer
.is_external_memory
= 0;
284 sp
->sDecBuffer
.width
= segment_width
;
285 sp
->sDecBuffer
.height
= segment_height
;
286 sp
->sDecBuffer
.u
.RGBA
.stride
= segment_width
* sp
->nSamples
;
287 sp
->sDecBuffer
.u
.RGBA
.size
= segment_width
* sp
->nSamples
* segment_height
;
289 if (sp
->nSamples
> 3) {
290 sp
->sDecBuffer
.colorspace
= MODE_RGBA
;
292 sp
->sDecBuffer
.colorspace
= MODE_RGB
;
295 sp
->psDecoder
= WebPINewDecoder(&sp
->sDecBuffer
);
297 if (sp
->psDecoder
== NULL
) {
298 TIFFErrorExt(tif
->tif_clientdata
, module
,
299 "Unable to allocate WebP decoder.");
307 TWebPSetupEncode(TIFF
* tif
)
309 static const char module
[] = "WebPSetupEncode";
310 uint16 nBitsPerSample
= tif
->tif_dir
.td_bitspersample
;
311 uint16 sampleFormat
= tif
->tif_dir
.td_sampleformat
;
313 WebPState
* sp
= EncoderState(tif
);
316 sp
->nSamples
= tif
->tif_dir
.td_samplesperpixel
;
318 /* check band count */
319 if ( sp
->nSamples
!= 3
320 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
325 TIFFErrorExt(tif
->tif_clientdata
, module
,
326 "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
327 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
335 /* check bits per sample and data type */
336 if ((nBitsPerSample
!= 8) && (sampleFormat
!= 1)) {
337 TIFFErrorExt(tif
->tif_clientdata
, module
,
338 "WEBP driver requires 8 bit unsigned data");
342 if (sp
->state
& LSTATE_INIT_DECODE
) {
343 WebPIDelete(sp
->psDecoder
);
344 WebPFreeDecBuffer(&sp
->sDecBuffer
);
345 sp
->psDecoder
= NULL
;
350 sp
->state
|= LSTATE_INIT_ENCODE
;
352 if (!WebPConfigInitInternal(&sp
->sEncoderConfig
, WEBP_PRESET_DEFAULT
,
354 WEBP_ENCODER_ABI_VERSION
)) {
355 TIFFErrorExt(tif
->tif_clientdata
, module
,
356 "Error creating WebP encoder configuration.");
360 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
361 sp
->sEncoderConfig
.lossless
= sp
->lossless
;
364 if (!WebPValidateConfig(&sp
->sEncoderConfig
)) {
365 TIFFErrorExt(tif
->tif_clientdata
, module
,
366 "Error with WebP encoder configuration.");
370 if (!WebPPictureInit(&sp
->sPicture
)) {
371 TIFFErrorExt(tif
->tif_clientdata
, module
,
372 "Error initializing WebP picture.");
380 * Reset encoding state at the start of a strip.
383 TWebPPreEncode(TIFF
* tif
, uint16 s
)
385 static const char module
[] = "TWebPPreEncode";
386 uint32 segment_width
, segment_height
;
387 WebPState
*sp
= EncoderState(tif
);
388 TIFFDirectory
* td
= &tif
->tif_dir
;
393 if( sp
->state
!= LSTATE_INIT_ENCODE
)
394 tif
->tif_setupencode(tif
);
397 * Set encoding parameters for this strip/tile.
400 segment_width
= td
->td_tilewidth
;
401 segment_height
= td
->td_tilelength
;
403 segment_width
= td
->td_imagewidth
;
404 segment_height
= td
->td_imagelength
- tif
->tif_row
;
405 if (segment_height
> td
->td_rowsperstrip
)
406 segment_height
= td
->td_rowsperstrip
;
409 if( segment_width
> 16383 || segment_height
> 16383 ) {
410 TIFFErrorExt(tif
->tif_clientdata
, module
,
411 "WEBP maximum image dimensions are 16383 x 16383.");
415 /* set up buffer for raw data */
416 /* given above check and that nSamples <= 4, buffer_size is <= 1 GB */
417 sp
->buffer_size
= segment_width
* segment_height
* sp
->nSamples
;
418 sp
->pBuffer
= _TIFFmalloc(sp
->buffer_size
);
420 TIFFErrorExt(tif
->tif_clientdata
, module
, "Cannot allocate buffer");
423 sp
->buffer_offset
= 0;
425 sp
->sPicture
.width
= segment_width
;
426 sp
->sPicture
.height
= segment_height
;
427 sp
->sPicture
.writer
= TWebPDatasetWriter
;
428 sp
->sPicture
.custom_ptr
= tif
;
434 * Finish off an encoded strip by flushing it.
437 TWebPPostEncode(TIFF
* tif
)
439 static const char module
[] = "WebPPostEncode";
441 WebPState
*sp
= EncoderState(tif
);
444 assert(sp
->state
== LSTATE_INIT_ENCODE
);
446 stride
= (int64_t)sp
->sPicture
.width
* sp
->nSamples
;
448 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
449 if (sp
->nSamples
== 4) {
450 if (!WebPPictureImportRGBA(&sp
->sPicture
, sp
->pBuffer
, (int)stride
)) {
451 TIFFErrorExt(tif
->tif_clientdata
, module
,
452 "WebPPictureImportRGBA() failed" );
458 if (!WebPPictureImportRGB(&sp
->sPicture
, sp
->pBuffer
, (int)stride
)) {
459 TIFFErrorExt(tif
->tif_clientdata
, module
,
460 "WebPPictureImportRGB() failed");
464 if (!WebPEncode(&sp
->sEncoderConfig
, &sp
->sPicture
)) {
466 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
467 const char* pszErrorMsg
= NULL
;
468 switch(sp
->sPicture
.error_code
) {
469 case VP8_ENC_ERROR_OUT_OF_MEMORY
:
470 pszErrorMsg
= "Out of memory"; break;
471 case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY
:
472 pszErrorMsg
= "Out of memory while flushing bits"; break;
473 case VP8_ENC_ERROR_NULL_PARAMETER
:
474 pszErrorMsg
= "A pointer parameter is NULL"; break;
475 case VP8_ENC_ERROR_INVALID_CONFIGURATION
:
476 pszErrorMsg
= "Configuration is invalid"; break;
477 case VP8_ENC_ERROR_BAD_DIMENSION
:
478 pszErrorMsg
= "Picture has invalid width/height"; break;
479 case VP8_ENC_ERROR_PARTITION0_OVERFLOW
:
480 pszErrorMsg
= "Partition is bigger than 512k. Try using less "
481 "SEGMENTS, or increase PARTITION_LIMIT value";
483 case VP8_ENC_ERROR_PARTITION_OVERFLOW
:
484 pszErrorMsg
= "Partition is bigger than 16M";
486 case VP8_ENC_ERROR_BAD_WRITE
:
487 pszErrorMsg
= "Error while fludshing bytes"; break;
488 case VP8_ENC_ERROR_FILE_TOO_BIG
:
489 pszErrorMsg
= "File is bigger than 4G"; break;
490 case VP8_ENC_ERROR_USER_ABORT
:
491 pszErrorMsg
= "User interrupted";
494 TIFFErrorExt(tif
->tif_clientdata
, module
,
495 "WebPEncode returned an unknown error code: %d",
496 sp
->sPicture
.error_code
);
497 pszErrorMsg
= "Unknown WebP error type.";
500 TIFFErrorExt(tif
->tif_clientdata
, module
,
501 "WebPEncode() failed : %s", pszErrorMsg
);
503 TIFFErrorExt(tif
->tif_clientdata
, module
,
504 "Error in WebPEncode()");
509 sp
->sPicture
.custom_ptr
= NULL
;
511 if (!TIFFFlushData1(tif
))
513 TIFFErrorExt(tif
->tif_clientdata
, module
,
514 "Error flushing TIFF WebP encoder.");
522 TWebPCleanup(TIFF
* tif
)
524 WebPState
* sp
= LState(tif
);
528 tif
->tif_tagmethods
.vgetfield
= sp
->vgetparent
;
529 tif
->tif_tagmethods
.vsetfield
= sp
->vsetparent
;
531 if (sp
->state
& LSTATE_INIT_ENCODE
) {
532 WebPPictureFree(&sp
->sPicture
);
535 if (sp
->psDecoder
!= NULL
) {
536 WebPIDelete(sp
->psDecoder
);
537 WebPFreeDecBuffer(&sp
->sDecBuffer
);
538 sp
->psDecoder
= NULL
;
542 if (sp
->pBuffer
!= NULL
) {
543 _TIFFfree(sp
->pBuffer
);
548 _TIFFfree(tif
->tif_data
);
549 tif
->tif_data
= NULL
;
552 _TIFFSetDefaultCompressionState(tif
);
556 TWebPVSetField(TIFF
* tif
, uint32 tag
, va_list ap
)
558 static const char module
[] = "WebPVSetField";
559 WebPState
* sp
= LState(tif
);
562 case TIFFTAG_WEBP_LEVEL
:
563 sp
->quality_level
= (int) va_arg(ap
, int);
564 if( sp
->quality_level
<= 0 ||
565 sp
->quality_level
> 100.0f
) {
566 TIFFWarningExt(tif
->tif_clientdata
, module
,
567 "WEBP_LEVEL should be between 1 and 100");
570 case TIFFTAG_WEBP_LOSSLESS
:
571 #if WEBP_ENCODER_ABI_VERSION >= 0x0100
572 sp
->lossless
= va_arg(ap
, int);
575 TIFFErrorExt(tif
->tif_clientdata
, module
,
576 "Need to upgrade WEBP driver, this version doesn't support "
577 "lossless compression.");
581 return (*sp
->vsetparent
)(tif
, tag
, ap
);
587 TWebPVGetField(TIFF
* tif
, uint32 tag
, va_list ap
)
589 WebPState
* sp
= LState(tif
);
592 case TIFFTAG_WEBP_LEVEL
:
593 *va_arg(ap
, int*) = sp
->quality_level
;
595 case TIFFTAG_WEBP_LOSSLESS
:
596 *va_arg(ap
, int*) = sp
->lossless
;
599 return (*sp
->vgetparent
)(tif
, tag
, ap
);
604 static const TIFFField TWebPFields
[] = {
605 { TIFFTAG_WEBP_LEVEL
, 0, 0, TIFF_ANY
, 0, TIFF_SETGET_INT
,
606 TIFF_SETGET_UNDEFINED
,
607 FIELD_PSEUDO
, TRUE
, FALSE
, "WEBP quality", NULL
},
608 { TIFFTAG_WEBP_LOSSLESS
, 0, 0, TIFF_ANY
, 0, TIFF_SETGET_INT
,
609 TIFF_SETGET_UNDEFINED
,
610 FIELD_PSEUDO
, TRUE
, FALSE
, "WEBP lossless/lossy", NULL
615 TIFFInitWebP(TIFF
* tif
, int scheme
)
617 static const char module
[] = "TIFFInitWebP";
620 assert( scheme
== COMPRESSION_WEBP
);
623 * Merge codec-specific tag information.
625 if ( !_TIFFMergeFields(tif
, TWebPFields
, TIFFArrayCount(TWebPFields
)) ) {
626 TIFFErrorExt(tif
->tif_clientdata
, module
,
627 "Merging WebP codec-specific tags failed");
632 * Allocate state block so tag methods have storage to record values.
634 tif
->tif_data
= (uint8
*) _TIFFmalloc(sizeof(WebPState
));
635 if (tif
->tif_data
== NULL
)
640 * Override parent get/set field methods.
642 sp
->vgetparent
= tif
->tif_tagmethods
.vgetfield
;
643 tif
->tif_tagmethods
.vgetfield
= TWebPVGetField
; /* hook for codec tags */
644 sp
->vsetparent
= tif
->tif_tagmethods
.vsetfield
;
645 tif
->tif_tagmethods
.vsetfield
= TWebPVSetField
; /* hook for codec tags */
647 /* Default values for codec-specific fields */
648 sp
->quality_level
= 75.0f
; /* default comp. level */
649 sp
->lossless
= 0; /* default to false */
652 sp
->psDecoder
= NULL
;
655 sp
->buffer_offset
= 0;
659 * Install codec methods.
661 * encoderow is not supported
663 tif
->tif_fixuptags
= TWebPFixupTags
;
664 tif
->tif_setupdecode
= TWebPSetupDecode
;
665 tif
->tif_predecode
= TWebPPreDecode
;
666 tif
->tif_decoderow
= TWebPDecode
;
667 tif
->tif_decodestrip
= TWebPDecode
;
668 tif
->tif_decodetile
= TWebPDecode
;
669 tif
->tif_setupencode
= TWebPSetupEncode
;
670 tif
->tif_preencode
= TWebPPreEncode
;
671 tif
->tif_postencode
= TWebPPostEncode
;
672 tif
->tif_encoderow
= TWebPEncode
;
673 tif
->tif_encodestrip
= TWebPEncode
;
674 tif
->tif_encodetile
= TWebPEncode
;
675 tif
->tif_cleanup
= TWebPCleanup
;
679 TIFFErrorExt(tif
->tif_clientdata
, module
,
680 "No space for WebP state block");
684 #endif /* WEBP_SUPPORT */