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 <stdio.h> 38 39 #define LSTATE_INIT_DECODE 0x01 40 #define LSTATE_INIT_ENCODE 0x02 41 /* 42 * State block for each open TIFF 43 * file using WEBP compression/decompression. 44 */ 45 typedef struct { 46 uint16 nSamples; /* number of samples per pixel */ 47 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; 55 56 WebPIDecoder* psDecoder; /* WebPIDecoder */ 57 WebPDecBuffer sDecBuffer; /* Decoder buffer */ 58 int last_y; /* Last row decoded */ 59 60 int state; /* state flags */ 61 62 TIFFVGetMethod vgetparent; /* super-class method */ 63 TIFFVSetMethod vsetparent; /* super-class method */ 64 } WebPState; 65 66 #define LState(tif) ((WebPState*) (tif)->tif_data) 67 #define DecoderState(tif) LState(tif) 68 #define EncoderState(tif) LState(tif) 69 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); 72 73 static 74 int TWebPDatasetWriter(const uint8_t* data, size_t data_size, 75 const WebPPicture* const picture) 76 { 77 static const char module[] = "TWebPDatasetWriter"; 78 TIFF* tif = (TIFF*)(picture->custom_ptr); 79 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)); 84 return 0; 85 } else { 86 _TIFFmemcpy(tif->tif_rawcp, data, data_size); 87 tif->tif_rawcc += data_size; 88 tif->tif_rawcp += data_size; 89 return 1; 90 } 91 } 92 93 /* 94 * Encode a chunk of pixels. 95 */ 96 static int 97 TWebPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) 98 { 99 static const char module[] = "TWebPEncode"; 100 WebPState *sp = EncoderState(tif); 101 (void) s; 102 103 assert(sp != NULL); 104 assert(sp->state == LSTATE_INIT_ENCODE); 105 106 if( (uint64)sp->buffer_offset + 107 (uint64)cc > sp->buffer_size ) 108 { 109 TIFFErrorExt(tif->tif_clientdata, module, 110 "Too many bytes to be written"); 111 return 0; 112 } 113 114 memcpy(sp->pBuffer + sp->buffer_offset, 115 bp, cc); 116 sp->buffer_offset += (unsigned)cc; 117 118 return 1; 119 120 } 121 122 static int 123 TWebPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) 124 { 125 static const char module[] = "WebPDecode"; 126 VP8StatusCode status = VP8_STATUS_OK; 127 WebPState *sp = DecoderState(tif); 128 (void) s; 129 130 assert(sp != NULL); 131 assert(sp->state == LSTATE_INIT_DECODE); 132 133 if (occ % sp->sDecBuffer.u.RGBA.stride) 134 { 135 TIFFErrorExt(tif->tif_clientdata, module, 136 "Fractional scanlines cannot be read"); 137 return 0; 138 } 139 140 status = WebPIAppend(sp->psDecoder, tif->tif_rawcp, tif->tif_rawcc); 141 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, 148 "Out of memory."); 149 } else { 150 TIFFErrorExt(tif->tif_clientdata, module, 151 "Unrecognized error."); 152 } 153 return 0; 154 } else { 155 int current_y, stride; 156 uint8_t* buf; 157 158 /* Returns the RGB/A image decoded so far */ 159 buf = WebPIDecGetRGB(sp->psDecoder, ¤t_y, NULL, NULL, &stride); 160 161 if ((buf != NULL) && 162 (occ <= stride * (current_y - sp->last_y))) { 163 memcpy(op, 164 buf + (sp->last_y * stride), 165 occ); 166 167 tif->tif_rawcp += tif->tif_rawcc; 168 tif->tif_rawcc = 0; 169 sp->last_y += occ / sp->sDecBuffer.u.RGBA.stride; 170 return 1; 171 } else { 172 TIFFErrorExt(tif->tif_clientdata, module, "Unable to decode WebP data."); 173 return 0; 174 } 175 } 176 } 177 178 static int 179 TWebPFixupTags(TIFF* tif) 180 { 181 (void) 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 187 "or RGBARGBARGBA" 188 #endif 189 ); 190 return 0; 191 } 192 return 1; 193 } 194 195 static int 196 TWebPSetupDecode(TIFF* tif) 197 { 198 static const char module[] = "WebPSetupDecode"; 199 uint16 nBitsPerSample = tif->tif_dir.td_bitspersample; 200 uint16 sampleFormat = tif->tif_dir.td_sampleformat; 201 202 WebPState* sp = DecoderState(tif); 203 assert(sp != NULL); 204 205 sp->nSamples = tif->tif_dir.td_samplesperpixel; 206 207 /* check band count */ 208 if ( sp->nSamples != 3 209 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 210 && sp->nSamples != 4 211 #endif 212 ) 213 { 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 217 "or 4 (RGBA) " 218 #endif 219 "bands.", 220 sp->nSamples ); 221 return 0; 222 } 223 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"); 228 return 0; 229 } 230 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); 236 sp->pBuffer = NULL; 237 } 238 sp->buffer_offset = 0; 239 sp->state = 0; 240 } 241 242 sp->state |= LSTATE_INIT_DECODE; 243 244 return 1; 245 } 246 247 /* 248 * Setup state for decoding a strip. 249 */ 250 static int 251 TWebPPreDecode(TIFF* tif, uint16 s) 252 { 253 static const char module[] = "TWebPPreDecode"; 254 uint32 segment_width, segment_height; 255 WebPState* sp = DecoderState(tif); 256 TIFFDirectory* td = &tif->tif_dir; 257 (void) s; 258 assert(sp != NULL); 259 260 if (isTiled(tif)) { 261 segment_width = td->td_tilewidth; 262 segment_height = td->td_tilelength; 263 } else { 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; 268 } 269 270 if( (sp->state & LSTATE_INIT_DECODE) == 0 ) 271 tif->tif_setupdecode(tif); 272 273 if (sp->psDecoder != NULL) { 274 WebPIDelete(sp->psDecoder); 275 WebPFreeDecBuffer(&sp->sDecBuffer); 276 sp->psDecoder = NULL; 277 } 278 279 sp->last_y = 0; 280 281 WebPInitDecBuffer(&sp->sDecBuffer); 282 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; 288 289 if (sp->nSamples > 3) { 290 sp->sDecBuffer.colorspace = MODE_RGBA; 291 } else { 292 sp->sDecBuffer.colorspace = MODE_RGB; 293 } 294 295 sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer); 296 297 if (sp->psDecoder == NULL) { 298 TIFFErrorExt(tif->tif_clientdata, module, 299 "Unable to allocate WebP decoder."); 300 return 0; 301 } 302 303 return 1; 304 } 305 306 static int 307 TWebPSetupEncode(TIFF* tif) 308 { 309 static const char module[] = "WebPSetupEncode"; 310 uint16 nBitsPerSample = tif->tif_dir.td_bitspersample; 311 uint16 sampleFormat = tif->tif_dir.td_sampleformat; 312 313 WebPState* sp = EncoderState(tif); 314 assert(sp != NULL); 315 316 sp->nSamples = tif->tif_dir.td_samplesperpixel; 317 318 /* check band count */ 319 if ( sp->nSamples != 3 320 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 321 && sp->nSamples != 4 322 #endif 323 ) 324 { 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 328 "or 4 (RGBA) " 329 #endif 330 "bands.", 331 sp->nSamples ); 332 return 0; 333 } 334 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"); 339 return 0; 340 } 341 342 if (sp->state & LSTATE_INIT_DECODE) { 343 WebPIDelete(sp->psDecoder); 344 WebPFreeDecBuffer(&sp->sDecBuffer); 345 sp->psDecoder = NULL; 346 sp->last_y = 0; 347 sp->state = 0; 348 } 349 350 sp->state |= LSTATE_INIT_ENCODE; 351 352 if (!WebPPictureInit(&sp->sPicture)) { 353 TIFFErrorExt(tif->tif_clientdata, module, 354 "Error initializing WebP picture."); 355 return 0; 356 } 357 358 if (!WebPConfigInitInternal(&sp->sEncoderConfig, WEBP_PRESET_DEFAULT, 359 sp->quality_level, 360 WEBP_ENCODER_ABI_VERSION)) { 361 TIFFErrorExt(tif->tif_clientdata, module, 362 "Error creating WebP encoder configuration."); 363 return 0; 364 } 365 366 // WebPConfigInitInternal above sets lossless to false 367 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 368 sp->sEncoderConfig.lossless = sp->lossless; 369 if (sp->lossless) { 370 sp->sPicture.use_argb = 1; 371 } 372 #endif 373 374 if (!WebPValidateConfig(&sp->sEncoderConfig)) { 375 TIFFErrorExt(tif->tif_clientdata, module, 376 "Error with WebP encoder configuration."); 377 return 0; 378 } 379 380 return 1; 381 } 382 383 /* 384 * Reset encoding state at the start of a strip. 385 */ 386 static int 387 TWebPPreEncode(TIFF* tif, uint16 s) 388 { 389 static const char module[] = "TWebPPreEncode"; 390 uint32 segment_width, segment_height; 391 WebPState *sp = EncoderState(tif); 392 TIFFDirectory* td = &tif->tif_dir; 393 394 (void) s; 395 396 assert(sp != NULL); 397 if( sp->state != LSTATE_INIT_ENCODE ) 398 tif->tif_setupencode(tif); 399 400 /* 401 * Set encoding parameters for this strip/tile. 402 */ 403 if (isTiled(tif)) { 404 segment_width = td->td_tilewidth; 405 segment_height = td->td_tilelength; 406 } else { 407 segment_width = td->td_imagewidth; 408 segment_height = td->td_imagelength - tif->tif_row; 409 if (segment_height > td->td_rowsperstrip) 410 segment_height = td->td_rowsperstrip; 411 } 412 413 if( segment_width > 16383 || segment_height > 16383 ) { 414 TIFFErrorExt(tif->tif_clientdata, module, 415 "WEBP maximum image dimensions are 16383 x 16383."); 416 return 0; 417 } 418 419 /* set up buffer for raw data */ 420 /* given above check and that nSamples <= 4, buffer_size is <= 1 GB */ 421 sp->buffer_size = segment_width * segment_height * sp->nSamples; 422 423 if (sp->pBuffer != NULL) { 424 _TIFFfree(sp->pBuffer); 425 sp->pBuffer = NULL; 426 } 427 428 sp->pBuffer = _TIFFmalloc(sp->buffer_size); 429 if( !sp->pBuffer) { 430 TIFFErrorExt(tif->tif_clientdata, module, "Cannot allocate buffer"); 431 return 0; 432 } 433 sp->buffer_offset = 0; 434 435 sp->sPicture.width = segment_width; 436 sp->sPicture.height = segment_height; 437 sp->sPicture.writer = TWebPDatasetWriter; 438 sp->sPicture.custom_ptr = tif; 439 440 return 1; 441 } 442 443 /* 444 * Finish off an encoded strip by flushing it. 445 */ 446 static int 447 TWebPPostEncode(TIFF* tif) 448 { 449 static const char module[] = "WebPPostEncode"; 450 int64_t stride; 451 WebPState *sp = EncoderState(tif); 452 assert(sp != NULL); 453 454 assert(sp->state == LSTATE_INIT_ENCODE); 455 456 stride = (int64_t)sp->sPicture.width * sp->nSamples; 457 458 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 459 if (sp->nSamples == 4) { 460 if (!WebPPictureImportRGBA(&sp->sPicture, sp->pBuffer, (int)stride)) { 461 TIFFErrorExt(tif->tif_clientdata, module, 462 "WebPPictureImportRGBA() failed" ); 463 return 0; 464 } 465 } 466 else 467 #endif 468 if (!WebPPictureImportRGB(&sp->sPicture, sp->pBuffer, (int)stride)) { 469 TIFFErrorExt(tif->tif_clientdata, module, 470 "WebPPictureImportRGB() failed"); 471 return 0; 472 } 473 474 if (!WebPEncode(&sp->sEncoderConfig, &sp->sPicture)) { 475 476 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 477 const char* pszErrorMsg = NULL; 478 switch(sp->sPicture.error_code) { 479 case VP8_ENC_ERROR_OUT_OF_MEMORY: 480 pszErrorMsg = "Out of memory"; break; 481 case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: 482 pszErrorMsg = "Out of memory while flushing bits"; break; 483 case VP8_ENC_ERROR_NULL_PARAMETER: 484 pszErrorMsg = "A pointer parameter is NULL"; break; 485 case VP8_ENC_ERROR_INVALID_CONFIGURATION: 486 pszErrorMsg = "Configuration is invalid"; break; 487 case VP8_ENC_ERROR_BAD_DIMENSION: 488 pszErrorMsg = "Picture has invalid width/height"; break; 489 case VP8_ENC_ERROR_PARTITION0_OVERFLOW: 490 pszErrorMsg = "Partition is bigger than 512k. Try using less " 491 "SEGMENTS, or increase PARTITION_LIMIT value"; 492 break; 493 case VP8_ENC_ERROR_PARTITION_OVERFLOW: 494 pszErrorMsg = "Partition is bigger than 16M"; 495 break; 496 case VP8_ENC_ERROR_BAD_WRITE: 497 pszErrorMsg = "Error while fludshing bytes"; break; 498 case VP8_ENC_ERROR_FILE_TOO_BIG: 499 pszErrorMsg = "File is bigger than 4G"; break; 500 case VP8_ENC_ERROR_USER_ABORT: 501 pszErrorMsg = "User interrupted"; 502 break; 503 default: 504 TIFFErrorExt(tif->tif_clientdata, module, 505 "WebPEncode returned an unknown error code: %d", 506 sp->sPicture.error_code); 507 pszErrorMsg = "Unknown WebP error type."; 508 break; 509 } 510 TIFFErrorExt(tif->tif_clientdata, module, 511 "WebPEncode() failed : %s", pszErrorMsg); 512 #else 513 TIFFErrorExt(tif->tif_clientdata, module, 514 "Error in WebPEncode()"); 515 #endif 516 return 0; 517 } 518 519 sp->sPicture.custom_ptr = NULL; 520 521 if (!TIFFFlushData1(tif)) 522 { 523 TIFFErrorExt(tif->tif_clientdata, module, 524 "Error flushing TIFF WebP encoder."); 525 return 0; 526 } 527 528 return 1; 529 } 530 531 static void 532 TWebPCleanup(TIFF* tif) 533 { 534 WebPState* sp = LState(tif); 535 536 assert(sp != 0); 537 538 tif->tif_tagmethods.vgetfield = sp->vgetparent; 539 tif->tif_tagmethods.vsetfield = sp->vsetparent; 540 541 if (sp->state & LSTATE_INIT_ENCODE) { 542 WebPPictureFree(&sp->sPicture); 543 } 544 545 if (sp->psDecoder != NULL) { 546 WebPIDelete(sp->psDecoder); 547 WebPFreeDecBuffer(&sp->sDecBuffer); 548 sp->psDecoder = NULL; 549 sp->last_y = 0; 550 } 551 552 if (sp->pBuffer != NULL) { 553 _TIFFfree(sp->pBuffer); 554 sp->pBuffer = NULL; 555 } 556 557 _TIFFfree(tif->tif_data); 558 tif->tif_data = NULL; 559 560 _TIFFSetDefaultCompressionState(tif); 561 } 562 563 static int 564 TWebPVSetField(TIFF* tif, uint32 tag, va_list ap) 565 { 566 static const char module[] = "WebPVSetField"; 567 WebPState* sp = LState(tif); 568 569 switch (tag) { 570 case TIFFTAG_WEBP_LEVEL: 571 sp->quality_level = (int) va_arg(ap, int); 572 if( sp->quality_level <= 0 || 573 sp->quality_level > 100.0f ) { 574 TIFFWarningExt(tif->tif_clientdata, module, 575 "WEBP_LEVEL should be between 1 and 100"); 576 } 577 return 1; 578 case TIFFTAG_WEBP_LOSSLESS: 579 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 580 sp->lossless = va_arg(ap, int); 581 if (sp->lossless){ 582 sp->quality_level = 100.0f; 583 } 584 return 1; 585 #else 586 TIFFErrorExt(tif->tif_clientdata, module, 587 "Need to upgrade WEBP driver, this version doesn't support " 588 "lossless compression."); 589 return 0; 590 #endif 591 default: 592 return (*sp->vsetparent)(tif, tag, ap); 593 } 594 /*NOTREACHED*/ 595 } 596 597 static int 598 TWebPVGetField(TIFF* tif, uint32 tag, va_list ap) 599 { 600 WebPState* sp = LState(tif); 601 602 switch (tag) { 603 case TIFFTAG_WEBP_LEVEL: 604 *va_arg(ap, int*) = sp->quality_level; 605 break; 606 case TIFFTAG_WEBP_LOSSLESS: 607 *va_arg(ap, int*) = sp->lossless; 608 break; 609 default: 610 return (*sp->vgetparent)(tif, tag, ap); 611 } 612 return 1; 613 } 614 615 static const TIFFField TWebPFields[] = { 616 { TIFFTAG_WEBP_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, 617 TIFF_SETGET_UNDEFINED, 618 FIELD_PSEUDO, TRUE, FALSE, "WEBP quality", NULL }, 619 { TIFFTAG_WEBP_LOSSLESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, 620 TIFF_SETGET_UNDEFINED, 621 FIELD_PSEUDO, TRUE, FALSE, "WEBP lossless/lossy", NULL 622 }, 623 }; 624 625 int 626 TIFFInitWebP(TIFF* tif, int scheme) 627 { 628 static const char module[] = "TIFFInitWebP"; 629 WebPState* sp; 630 631 assert( scheme == COMPRESSION_WEBP ); 632 633 /* 634 * Merge codec-specific tag information. 635 */ 636 if ( !_TIFFMergeFields(tif, TWebPFields, TIFFArrayCount(TWebPFields)) ) { 637 TIFFErrorExt(tif->tif_clientdata, module, 638 "Merging WebP codec-specific tags failed"); 639 return 0; 640 } 641 642 /* 643 * Allocate state block so tag methods have storage to record values. 644 */ 645 tif->tif_data = (uint8*) _TIFFmalloc(sizeof(WebPState)); 646 if (tif->tif_data == NULL) 647 goto bad; 648 sp = LState(tif); 649 650 /* 651 * Override parent get/set field methods. 652 */ 653 sp->vgetparent = tif->tif_tagmethods.vgetfield; 654 tif->tif_tagmethods.vgetfield = TWebPVGetField; /* hook for codec tags */ 655 sp->vsetparent = tif->tif_tagmethods.vsetfield; 656 tif->tif_tagmethods.vsetfield = TWebPVSetField; /* hook for codec tags */ 657 658 /* Default values for codec-specific fields */ 659 sp->quality_level = 75.0f; /* default comp. level */ 660 sp->lossless = 0; /* default to false */ 661 sp->state = 0; 662 sp->nSamples = 0; 663 sp->psDecoder = NULL; 664 sp->last_y = 0; 665 666 sp->buffer_offset = 0; 667 sp->pBuffer = NULL; 668 669 /* 670 * Install codec methods. 671 * Notes: 672 * encoderow is not supported 673 */ 674 tif->tif_fixuptags = TWebPFixupTags; 675 tif->tif_setupdecode = TWebPSetupDecode; 676 tif->tif_predecode = TWebPPreDecode; 677 tif->tif_decoderow = TWebPDecode; 678 tif->tif_decodestrip = TWebPDecode; 679 tif->tif_decodetile = TWebPDecode; 680 tif->tif_setupencode = TWebPSetupEncode; 681 tif->tif_preencode = TWebPPreEncode; 682 tif->tif_postencode = TWebPPostEncode; 683 tif->tif_encoderow = TWebPEncode; 684 tif->tif_encodestrip = TWebPEncode; 685 tif->tif_encodetile = TWebPEncode; 686 tif->tif_cleanup = TWebPCleanup; 687 688 return 1; 689 bad: 690 TIFFErrorExt(tif->tif_clientdata, module, 691 "No space for WebP state block"); 692 return 0; 693 } 694 695 #endif /* WEBP_SUPPORT */ 696