diff --git a/examples/dwebp.c b/examples/dwebp.c index 77181874..f3d15c32 100644 --- a/examples/dwebp.c +++ b/examples/dwebp.c @@ -67,8 +67,13 @@ typedef enum { PGM, BMP, TIFF, - YUV, - ALPHA_PLANE_ONLY // this is for experimenting only + RAW_YUV, + ALPHA_PLANE_ONLY, // this is for experimenting only + // forced colorspace output (for testing, mostly) + RGB, RGBA, BGR, BGRA, ARGB, + RGBA_4444, RGB_565, + rgbA, bgrA, Argb, rgbA_4444, + YUV, YUVA } OutputFileFormat; #ifdef HAVE_WINCODEC_H @@ -175,7 +180,7 @@ static int WritePNG(const char* out_file_name, int use_stdout, const uint32_t height = buffer->height; uint8_t* const rgb = buffer->u.RGBA.rgba; const int stride = buffer->u.RGBA.stride; - const int has_alpha = (buffer->colorspace == MODE_BGRA); + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, MAKE_REFGUID(GUID_ContainerFormatPng), @@ -193,7 +198,7 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { const uint32_t height = buffer->height; uint8_t* const rgb = buffer->u.RGBA.rgba; const int stride = buffer->u.RGBA.stride; - const int has_alpha = (buffer->colorspace == MODE_RGBA); + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); volatile png_structp png; volatile png_infop info; png_uint_32 y; @@ -259,6 +264,24 @@ static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { return 1; } +// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose. +static int Write16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* const rgba = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const uint32_t bytes_per_px = 2; + uint32_t y; + + fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height); + for (y = 0; y < height; ++y) { + if (fwrite(rgba + y * stride, width, bytes_per_px, fout) != bytes_per_px) { + return 0; + } + } + return 1; +} + static void PutLE16(uint8_t* const dst, uint32_t value) { dst[0] = (value >> 0) & 0xff; dst[1] = (value >> 8) & 0xff; @@ -271,7 +294,7 @@ static void PutLE32(uint8_t* const dst, uint32_t value) { #define BMP_HEADER_SIZE 54 static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { - const int has_alpha = (buffer->colorspace != MODE_BGR); + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); const uint32_t width = buffer->width; const uint32_t height = buffer->height; const uint8_t* const rgba = buffer->u.RGBA.rgba; @@ -332,7 +355,7 @@ static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { - const int has_alpha = (buffer->colorspace != MODE_RGB); + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); const uint32_t width = buffer->width; const uint32_t height = buffer->height; const uint8_t* const rgba = buffer->u.RGBA.rgba; @@ -418,7 +441,7 @@ static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { // format=PGM: save a grayscale PGM file using the IMC4 layout // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for // viewing the samples, esp. for odd dimensions. -// format=YUV: just save the Y/U/V/A planes sequentially without header. +// format=RAW_YUV: just save the Y/U/V/A planes sequentially without header. static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, OutputFileFormat format) { const int width = buffer->width; @@ -426,7 +449,7 @@ static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, const WebPYUVABuffer* const yuv = &buffer->u.YUVA; int ok = 1; int y; - const int pad = (format == YUV) ? 0 : 1; + const int pad = (format == RAW_YUV) ? 0 : 1; const int uv_width = (width + 1) / 2; const int uv_height = (height + 1) / 2; const int out_stride = (width + pad) & ~pad; @@ -487,7 +510,9 @@ static int SaveOutput(const WebPDecBuffer* const buffer, } } - if (format == PNG) { + if (format == PNG || + format == RGBA || format == BGRA || format == ARGB || + format == rgbA || format == bgrA || format == Argb) { #ifdef HAVE_WINCODEC_H ok &= WritePNG(out_file, use_stdout, buffer); #else @@ -495,14 +520,17 @@ static int SaveOutput(const WebPDecBuffer* const buffer, #endif } else if (format == PAM) { ok &= WritePPM(fout, buffer, 1); - } else if (format == PPM) { + } else if (format == PPM || format == RGB || format == BGR) { ok &= WritePPM(fout, buffer, 0); + } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { + ok &= Write16bAsPGM(fout, buffer); } else if (format == BMP) { ok &= WriteBMP(fout, buffer); } else if (format == TIFF) { ok &= WriteTIFF(fout, buffer); - } else if (format == PGM || format == YUV) { - ok &= WritePGMOrYUV(fout, buffer, format); + } else if (format == PGM || format == RAW_YUV || + format == YUV || format == YUVA) { + ok &= WritePGMOrYUV(fout, buffer, format == RAW_YUV ? RAW_YUV : PGM); } else if (format == ALPHA_PLANE_ONLY) { ok &= WriteAlphaPlane(fout, buffer); } @@ -569,6 +597,70 @@ static const char* const kFormatType[] = { "unspecified", "lossy", "lossless" }; +static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, + OutputFileFormat format, + int use_external_memory) { + uint8_t* external_buffer = NULL; + WebPDecBuffer* const output_buffer = &config->output; + int w = config->input.width; + int h = config->input.height; + if (config->options.use_scaling) { + w = config->options.scaled_width; + h = config->options.scaled_height; + } else if (config->options.use_cropping) { + w = config->options.crop_width; + h = config->options.crop_height; + } + if (format >= RGB && format <= rgbA_4444) { + const int bpp = (format == RGB || format == BGR) ? 3 + : (format == RGBA_4444 || format == rgbA_4444 || + format == RGB_565) ? 2 + : 4; + uint32_t stride = bpp * w + 7; // <- just for exercising + external_buffer = (uint8_t*)malloc(stride * h); + if (external_buffer == NULL) return NULL; + output_buffer->u.RGBA.stride = stride; + output_buffer->u.RGBA.size = stride * h; + output_buffer->u.RGBA.rgba = external_buffer; + } else { // YUV and YUVA + const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace); + uint8_t* tmp; + uint32_t stride = w + 3; + uint32_t uv_stride = (w + 1) / 2 + 13; + uint32_t total_size = stride * h * (has_alpha ? 2 : 1) + + 2 * uv_stride * (h + 1) / 2; + assert(format >= YUV && format <= YUVA); + external_buffer = (uint8_t*)malloc(total_size); + if (external_buffer == NULL) return NULL; + tmp = external_buffer; + output_buffer->u.YUVA.y = tmp; + output_buffer->u.YUVA.y_stride = stride; + output_buffer->u.YUVA.y_size = stride * h; + tmp += output_buffer->u.YUVA.y_size; + if (has_alpha) { + output_buffer->u.YUVA.a = tmp; + output_buffer->u.YUVA.a_stride = stride; + output_buffer->u.YUVA.a_size = stride * h; + tmp += output_buffer->u.YUVA.a_size; + } else { + output_buffer->u.YUVA.a = NULL; + output_buffer->u.YUVA.a_stride = 0; + } + output_buffer->u.YUVA.u = tmp; + output_buffer->u.YUVA.u_stride = uv_stride; + output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2; + tmp += output_buffer->u.YUVA.u_size; + + output_buffer->u.YUVA.v = tmp; + output_buffer->u.YUVA.v_stride = uv_stride; + output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2; + tmp += output_buffer->u.YUVA.v_size; + assert(tmp <= external_buffer + total_size); + } + output_buffer->is_external_memory = use_external_memory; + return external_buffer; +} + int main(int argc, const char *argv[]) { int ok = 0; const char *in_file = NULL; @@ -578,6 +670,10 @@ int main(int argc, const char *argv[]) { WebPDecBuffer* const output_buffer = &config.output; WebPBitstreamFeatures* const bitstream = &config.input; OutputFileFormat format = PNG; + uint8_t* external_buffer = NULL; + int use_external_memory = 0; + const uint8_t* data = NULL; + int incremental = 0; int c; @@ -617,7 +713,32 @@ int main(int argc, const char *argv[]) { } else if (!strcmp(argv[c], "-pgm")) { format = PGM; } else if (!strcmp(argv[c], "-yuv")) { - format = YUV; + format = RAW_YUV; + } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) { + const char* const fmt = argv[++c]; + if (!strcmp(fmt, "RGB")) format = RGB; + else if (!strcmp(fmt, "RGBA")) format = RGBA; + else if (!strcmp(fmt, "BGR")) format = BGR; + else if (!strcmp(fmt, "BGRA")) format = BGRA; + else if (!strcmp(fmt, "ARGB")) format = ARGB; + else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444; + else if (!strcmp(fmt, "RGB_565")) format = RGB_565; + else if (!strcmp(fmt, "rgbA")) format = rgbA; + else if (!strcmp(fmt, "bgrA")) format = bgrA; + else if (!strcmp(fmt, "Argb")) format = Argb; + else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444; + else if (!strcmp(fmt, "YUV")) format = YUV; + else if (!strcmp(fmt, "YUVA")) format = YUVA; + else { + fprintf(stderr, "Can't parse pixel_format %s\n", fmt); + parse_error = 1; + } + } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) { + use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error); + parse_error |= (use_external_memory > 2 || use_external_memory < 0); + if (parse_error) { + fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]); + } } else if (!strcmp(argv[c], "-mt")) { config.options.use_threads = 1; } else if (!strcmp(argv[c], "-alpha_dither")) { @@ -676,7 +797,6 @@ int main(int argc, const char *argv[]) { { VP8StatusCode status = VP8_STATUS_OK; size_t data_size = 0; - const uint8_t* data = NULL; if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) { return -1; } @@ -703,15 +823,33 @@ int main(int argc, const char *argv[]) { bitstream->has_alpha ? MODE_rgbA : MODE_RGB; break; case PGM: - case YUV: + case RAW_YUV: output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; break; case ALPHA_PLANE_ONLY: output_buffer->colorspace = MODE_YUVA; break; - default: - free((void*)data); - return -1; + // forced modes: + case RGB: output_buffer->colorspace = MODE_RGB; break; + case RGBA: output_buffer->colorspace = MODE_RGBA; break; + case BGR: output_buffer->colorspace = MODE_BGR; break; + case BGRA: output_buffer->colorspace = MODE_BGRA; break; + case ARGB: output_buffer->colorspace = MODE_ARGB; break; + case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break; + case RGB_565: output_buffer->colorspace = MODE_RGB_565; break; + case rgbA: output_buffer->colorspace = MODE_rgbA; break; + case bgrA: output_buffer->colorspace = MODE_bgrA; break; + case Argb: output_buffer->colorspace = MODE_Argb; break; + case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break; + case YUV: output_buffer->colorspace = MODE_YUV; break; + case YUVA: output_buffer->colorspace = MODE_YUVA; break; + default: goto Exit; + } + + if (use_external_memory > 0 && format >= RGB) { + external_buffer = AllocateExternalBuffer(&config, format, + use_external_memory); + if (external_buffer == NULL) goto Exit; } if (incremental) { @@ -720,7 +858,6 @@ int main(int argc, const char *argv[]) { status = ExUtilDecodeWebP(data, data_size, verbose, &config); } - free((void*)data); ok = (status == VP8_STATUS_OK); if (!ok) { ExUtilPrintWebPError(in_file, status); @@ -750,6 +887,8 @@ int main(int argc, const char *argv[]) { } Exit: WebPFreeDecBuffer(output_buffer); + free((void*)external_buffer); + free((void*)data); return ok ? 0 : -1; } diff --git a/src/dec/buffer.c b/src/dec/buffer.c index 9ed2b3fe..547e69b4 100644 --- a/src/dec/buffer.c +++ b/src/dec/buffer.c @@ -92,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { return VP8_STATUS_INVALID_PARAM; } - if (!buffer->is_external_memory && buffer->private_memory == NULL) { + if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { uint8_t* output; int uv_stride = 0, a_stride = 0; uint64_t uv_size = 0, a_size = 0, total_size; @@ -227,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { void WebPFreeDecBuffer(WebPDecBuffer* buffer) { if (buffer != NULL) { - if (!buffer->is_external_memory) { + if (buffer->is_external_memory <= 0) { WebPSafeFree(buffer->private_memory); } buffer->private_memory = NULL; @@ -256,5 +256,45 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { } } -//------------------------------------------------------------------------------ +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, + WebPDecBuffer* const dst_buf) { + assert(src_buf != NULL && dst_buf != NULL); + assert(src_buf->colorspace == dst_buf->colorspace); + dst_buf->width = src_buf->width; + dst_buf->height = src_buf->height; + if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { + return VP8_STATUS_INVALID_PARAM; + } + if (WebPIsRGBMode(src_buf->colorspace)) { + const WebPRGBABuffer* const src = &src_buf->u.RGBA; + const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; + WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, + src_buf->width * kModeBpp[src_buf->colorspace], + src_buf->height); + } else { + const WebPYUVABuffer* const src = &src_buf->u.YUVA; + const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; + WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, + src_buf->width, src_buf->height); + WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + if (WebPIsAlphaMode(src_buf->colorspace)) { + WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, + src_buf->width, src_buf->height); + } + } + return VP8_STATUS_OK; +} + +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features) { + assert(output != NULL); + return (output->is_external_memory >= 2) && + WebPIsPremultipliedMode(output->colorspace) && + (features != NULL && features->has_alpha); +} + +//------------------------------------------------------------------------------ diff --git a/src/dec/idec.c b/src/dec/idec.c index e0cf0c9c..72705a18 100644 --- a/src/dec/idec.c +++ b/src/dec/idec.c @@ -70,7 +70,9 @@ struct WebPIDecoder { VP8Io io_; MemBuffer mem_; // input memory buffer. - WebPDecBuffer output_; // output buffer (when no external one is supplied) + WebPDecBuffer output_; // output buffer (when no external one is supplied, + // or if the external one has slow-memory) + WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually. size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. int last_mb_y_; // last row reached for intra-mode decoding @@ -249,10 +251,16 @@ static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { idec->state_ = STATE_DONE; if (options != NULL && options->flip) { - return WebPFlipBuffer(output); - } else { - return VP8_STATUS_OK; + const VP8StatusCode status = WebPFlipBuffer(output); + if (status != VP8_STATUS_OK) return status; } + if (idec->final_output_ != NULL) { + WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy + WebPFreeDecBuffer(&idec->output_); + *output = *idec->final_output_; + idec->final_output_ = NULL; + } + return VP8_STATUS_OK; } //------------------------------------------------------------------------------ @@ -575,9 +583,10 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) { } //------------------------------------------------------------------------------ -// Public functions +// Internal constructor -WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { +static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, + const WebPBitstreamFeatures* const features) { WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); if (idec == NULL) { return NULL; @@ -593,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { VP8InitIo(&idec->io_); WebPResetDecParams(&idec->params_); - idec->params_.output = (output_buffer != NULL) ? output_buffer - : &idec->output_; + if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { + idec->params_.output = &idec->output_; + idec->final_output_ = output_buffer; + if (output_buffer != NULL) { + idec->params_.output->colorspace = output_buffer->colorspace; + } + } else { + idec->params_.output = output_buffer; + idec->final_output_ = NULL; + } WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. return idec; } +//------------------------------------------------------------------------------ +// Public functions + +WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { + return NewDecoder(output_buffer, NULL); +} + WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, WebPDecoderConfig* config) { WebPIDecoder* idec; + WebPBitstreamFeatures tmp_features; + WebPBitstreamFeatures* const features = + (config == NULL) ? &tmp_features : &config->input; + memset(&tmp_features, 0, sizeof(tmp_features)); // Parse the bitstream's features, if requested: - if (data != NULL && data_size > 0 && config != NULL) { - if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) { + if (data != NULL && data_size > 0) { + if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { return NULL; } } + // Create an instance of the incremental decoder - idec = WebPINewDecoder(config ? &config->output : NULL); + idec = (config != NULL) ? NewDecoder(&config->output, features) + : NewDecoder(NULL, features); if (idec == NULL) { return NULL; } @@ -645,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) { WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, size_t output_buffer_size, int output_stride) { - const int is_external_memory = (output_buffer != NULL); + const int is_external_memory = (output_buffer != NULL) ? 1 : 0; WebPIDecoder* idec; if (mode >= MODE_YUV) return NULL; - if (!is_external_memory) { // Overwrite parameters to sane values. + if (is_external_memory == 0) { // Overwrite parameters to sane values. output_buffer_size = 0; output_stride = 0; } else { // A buffer was passed. Validate the other params. @@ -671,11 +701,11 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, int u_stride, uint8_t* v, size_t v_size, int v_stride, uint8_t* a, size_t a_size, int a_stride) { - const int is_external_memory = (luma != NULL); + const int is_external_memory = (luma != NULL) ? 1 : 0; WebPIDecoder* idec; WEBP_CSP_MODE colorspace; - if (!is_external_memory) { // Overwrite parameters to sane values. + if (is_external_memory == 0) { // Overwrite parameters to sane values. luma_size = u_size = v_size = a_size = 0; luma_stride = u_stride = v_stride = a_stride = 0; u = v = a = NULL; @@ -783,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { if (idec->state_ <= STATE_VP8_PARTS0) { return NULL; } + if (idec->final_output_ != NULL) { + return NULL; // not yet slow-copied + } return idec->params_.output; } @@ -792,7 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, const WebPDecBuffer* const src = GetOutputBuffer(idec); if (left != NULL) *left = 0; if (top != NULL) *top = 0; - if (src) { + if (src != NULL) { if (width != NULL) *width = src->width; if (height != NULL) *height = idec->params_.last_y; } else { diff --git a/src/dec/vp8i.h b/src/dec/vp8i.h index 0104f255..59606998 100644 --- a/src/dec/vp8i.h +++ b/src/dec/vp8i.h @@ -32,7 +32,7 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 0 #define DEC_MIN_VERSION 5 -#define DEC_REV_VERSION 0 +#define DEC_REV_VERSION 1 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), diff --git a/src/dec/webp.c b/src/dec/webp.c index eb925a86..0a7162b3 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -514,6 +514,8 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, WebPFreeDecBuffer(params->output); } else { if (params->options != NULL && params->options->flip) { + // This restores the original stride values if options->flip was used + // during the call to WebPAllocateDecBuffer above. status = WebPFlipBuffer(params->output); } } @@ -758,9 +760,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, } WebPResetDecParams(¶ms); - params.output = &config->output; params.options = &config->options; - status = DecodeInto(data, data_size, ¶ms); + params.output = &config->output; + if (WebPAvoidSlowMemory(params.output, &config->input)) { + // decoding to slow memory: use a temporary in-mem buffer to decode into. + WebPDecBuffer in_mem_buffer; + WebPInitDecBuffer(&in_mem_buffer); + in_mem_buffer.colorspace = config->output.colorspace; + in_mem_buffer.width = config->input.width; + in_mem_buffer.height = config->input.height; + params.output = &in_mem_buffer; + status = DecodeInto(data, data_size, ¶ms); + if (status == VP8_STATUS_OK) { // do the slow-copy + status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); + } + WebPFreeDecBuffer(&in_mem_buffer); + } else { + status = DecodeInto(data, data_size, ¶ms); + } return status; } @@ -826,4 +843,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, } //------------------------------------------------------------------------------ - diff --git a/src/dec/webpi.h b/src/dec/webpi.h index c75a2e4a..991b194c 100644 --- a/src/dec/webpi.h +++ b/src/dec/webpi.h @@ -45,11 +45,20 @@ struct WebPDecParams { OutputFunc emit; // output RGB or YUV samples OutputAlphaFunc emit_alpha; // output alpha channel OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values + + WebPDecBuffer* final_output; // In case the user supplied a slow-memory + // output, we decode image in temporary buffer + // (this::output) and copy it here. + WebPDecBuffer tmp_buffer; // this::output will point to this one in case + // of slow memory. }; // Should be called first, before any use of the WebPDecParams object. void WebPResetDecParams(WebPDecParams* const params); +// Delete all memory (after an error occurred, for instance) +void WebPFreeDecParams(WebPDecParams* const params); + //------------------------------------------------------------------------------ // Header parsing helpers @@ -107,13 +116,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height, VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the -// memory (still held by 'src'). +// memory (still held by 'src'). No pixels are copied. void WebPCopyDecBuffer(const WebPDecBuffer* const src, WebPDecBuffer* const dst); // Copy and transfer ownership from src to dst (beware of parameter order!) void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); +// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns +// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy. +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src, + WebPDecBuffer* const dst); + +// Returns true if decoding will be slow with the current configuration +// and bitstream features. +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features); + //------------------------------------------------------------------------------ #ifdef __cplusplus diff --git a/src/webp/decode.h b/src/webp/decode.h index 143e4fbe..b00d15b3 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -197,7 +197,10 @@ struct WebPYUVABuffer { // view as YUVA struct WebPDecBuffer { WEBP_CSP_MODE colorspace; // Colorspace. int width, height; // Dimensions. - int is_external_memory; // If true, 'internal_memory' pointer is not used. + int is_external_memory; // If non-zero, 'internal_memory' pointer is not + // used. If value is '2' or more, the external + // memory is considered 'slow' and multiple + // read/write will be avoided. union { WebPRGBABuffer RGBA; WebPYUVABuffer YUVA; @@ -205,7 +208,7 @@ struct WebPDecBuffer { uint32_t pad[4]; // padding for later use uint8_t* private_memory; // Internally allocated memory (only when - // is_external_memory is false). Should not be used + // is_external_memory is 0). Should not be used // externally, but accessed via the buffer union. }; @@ -269,7 +272,7 @@ typedef enum VP8StatusCode { // that of the returned WebPIDecoder object. // The supplied 'output_buffer' content MUST NOT be changed between calls to // WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is -// set to 1. In such a case, it is allowed to modify the pointers, size and +// not set to 0. In such a case, it is allowed to modify the pointers, size and // stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain // within valid bounds. // All other fields of WebPDecBuffer MUST remain constant between calls.