From e6d7e0b76d7bb6233fa128bce0167227e3e966c6 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 23 Jun 2022 11:52:28 +0200 Subject: WEBP codec: avoid temporary buffer and memcpy() on whole tile/strip decoding --- libtiff/tif_webp.c | 123 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 95 insertions(+), 28 deletions(-) diff --git a/libtiff/tif_webp.c b/libtiff/tif_webp.c index 60ebaca2..377ff2f5 100644 --- a/libtiff/tif_webp.c +++ b/libtiff/tif_webp.c @@ -35,6 +35,7 @@ #include "webp/encode.h" #include +#include #define LSTATE_INIT_DECODE 0x01 #define LSTATE_INIT_ENCODE 0x02 @@ -125,11 +126,77 @@ TWebPDecode(TIFF* tif, uint8_t* op, tmsize_t occ, uint16_t s) static const char module[] = "WebPDecode"; VP8StatusCode status = VP8_STATUS_OK; WebPState *sp = DecoderState(tif); + uint32_t segment_width, segment_height; + bool decode_whole_strile = false; + (void) s; assert(sp != NULL); assert(sp->state == LSTATE_INIT_DECODE); + if( sp->psDecoder == NULL ) + { + TIFFDirectory* td = &tif->tif_dir; + uint32_t buffer_size; + + if (isTiled(tif)) { + segment_width = td->td_tilewidth; + segment_height = td->td_tilelength; + } else { + segment_width = td->td_imagewidth; + segment_height = td->td_imagelength - tif->tif_row; + if (segment_height > td->td_rowsperstrip) + segment_height = td->td_rowsperstrip; + } + + buffer_size = segment_width * segment_height * sp->nSamples; + if( occ == (tmsize_t)buffer_size ) + { + /* If decoding the whole strip/tile, we can directly use the */ + /* output buffer */ + decode_whole_strile = true; + } + else if( sp->pBuffer == NULL || buffer_size > sp->buffer_size ) + { + if (sp->pBuffer != NULL) { + _TIFFfree(sp->pBuffer); + sp->pBuffer = NULL; + } + + sp->pBuffer = _TIFFmalloc(buffer_size); + if( !sp->pBuffer) { + TIFFErrorExt(tif->tif_clientdata, module, "Cannot allocate buffer"); + return 0; + } + sp->buffer_size = buffer_size; + } + + sp->last_y = 0; + + WebPInitDecBuffer(&sp->sDecBuffer); + + sp->sDecBuffer.is_external_memory = 1; + sp->sDecBuffer.width = segment_width; + sp->sDecBuffer.height = segment_height; + sp->sDecBuffer.u.RGBA.rgba = decode_whole_strile ? op : sp->pBuffer; + sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples; + sp->sDecBuffer.u.RGBA.size = buffer_size; + + if (sp->nSamples > 3) { + sp->sDecBuffer.colorspace = MODE_RGBA; + } else { + sp->sDecBuffer.colorspace = MODE_RGB; + } + + sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer); + + if (sp->psDecoder == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "Unable to allocate WebP decoder."); + return 0; + } + } + if (occ % sp->sDecBuffer.u.RGBA.stride) { TIFFErrorExt(tif->tif_clientdata, module, @@ -160,13 +227,37 @@ TWebPDecode(TIFF* tif, uint8_t* op, tmsize_t occ, uint16_t s) if ((buf != NULL) && (occ <= (tmsize_t)stride * (current_y - sp->last_y))) { - memcpy(op, - buf + (sp->last_y * stride), - occ); + const int numberOfExpectedLines = (int)(occ / sp->sDecBuffer.u.RGBA.stride); + if( decode_whole_strile ) + { + if( current_y != numberOfExpectedLines ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Unable to decode WebP data: less lines than expected."); + return 0; + } + } + else + { + memcpy(op, + buf + (sp->last_y * stride), + occ); + } tif->tif_rawcp += tif->tif_rawcc; tif->tif_rawcc = 0; - sp->last_y += (int)(occ / sp->sDecBuffer.u.RGBA.stride); + sp->last_y += numberOfExpectedLines; + + if( decode_whole_strile ) + { + /* We can now free the decoder as we're completely done */ + if (sp->psDecoder != NULL) + { + WebPIDelete(sp->psDecoder); + WebPFreeDecBuffer(&sp->sDecBuffer); + sp->psDecoder = NULL; + } + } return 1; } else { TIFFErrorExt(tif->tif_clientdata, module, "Unable to decode WebP data."); @@ -282,30 +373,6 @@ TWebPPreDecode(TIFF* tif, uint16_t s) sp->psDecoder = NULL; } - sp->last_y = 0; - - WebPInitDecBuffer(&sp->sDecBuffer); - - sp->sDecBuffer.is_external_memory = 0; - sp->sDecBuffer.width = segment_width; - sp->sDecBuffer.height = segment_height; - sp->sDecBuffer.u.RGBA.stride = segment_width * sp->nSamples; - sp->sDecBuffer.u.RGBA.size = segment_width * sp->nSamples * segment_height; - - if (sp->nSamples > 3) { - sp->sDecBuffer.colorspace = MODE_RGBA; - } else { - sp->sDecBuffer.colorspace = MODE_RGB; - } - - sp->psDecoder = WebPINewDecoder(&sp->sDecBuffer); - - if (sp->psDecoder == NULL) { - TIFFErrorExt(tif->tif_clientdata, module, - "Unable to allocate WebP decoder."); - return 0; - } - return 1; } -- cgit v1.2.1