add extra meaning to WebPDecBuffer::is_external_memory

If value is '2', it means the buffer is a 'slow' one, like GPU-mapped memory.

This change is backward compatible (setting is_external_memory to 2
will be a no-op in previous libraries)

dwebp:  add flags to force a particular colorspace format
new flags is:
   -pixel_format {RGB,RGBA,BGR,BGRA,ARGB,RGBA_4444,RGB_565,
                  rgbA,bgrA,Argb,rgbA_4444,YUV,YUVA}
and also,external_memory {0,1,2}
These flags are mostly for debuggging purpose, and hence are not documented.

Change-Id: Iac88ce1e10b35163dd7af57f9660f062f5d8ed5e
This commit is contained in:
Pascal Massimino 2016-03-09 08:58:58 +01:00
parent a90edffb7e
commit abdb109f3b
7 changed files with 296 additions and 46 deletions

View File

@ -67,8 +67,13 @@ typedef enum {
PGM, PGM,
BMP, BMP,
TIFF, TIFF,
YUV, RAW_YUV,
ALPHA_PLANE_ONLY // this is for experimenting only 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; } OutputFileFormat;
#ifdef HAVE_WINCODEC_H #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; const uint32_t height = buffer->height;
uint8_t* const rgb = buffer->u.RGBA.rgba; uint8_t* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride; 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, return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
MAKE_REFGUID(GUID_ContainerFormatPng), MAKE_REFGUID(GUID_ContainerFormatPng),
@ -193,7 +198,7 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
const uint32_t height = buffer->height; const uint32_t height = buffer->height;
uint8_t* const rgb = buffer->u.RGBA.rgba; uint8_t* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride; 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_structp png;
volatile png_infop info; volatile png_infop info;
png_uint_32 y; png_uint_32 y;
@ -259,6 +264,24 @@ static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
return 1; 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) { static void PutLE16(uint8_t* const dst, uint32_t value) {
dst[0] = (value >> 0) & 0xff; dst[0] = (value >> 0) & 0xff;
dst[1] = (value >> 8) & 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 #define BMP_HEADER_SIZE 54
static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { 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 width = buffer->width;
const uint32_t height = buffer->height; const uint32_t height = buffer->height;
const uint8_t* const rgba = buffer->u.RGBA.rgba; 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) #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { 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 width = buffer->width;
const uint32_t height = buffer->height; const uint32_t height = buffer->height;
const uint8_t* const rgba = buffer->u.RGBA.rgba; 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 // 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 // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for
// viewing the samples, esp. for odd dimensions. // 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, static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer,
OutputFileFormat format) { OutputFileFormat format) {
const int width = buffer->width; 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; const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
int ok = 1; int ok = 1;
int y; 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_width = (width + 1) / 2;
const int uv_height = (height + 1) / 2; const int uv_height = (height + 1) / 2;
const int out_stride = (width + pad) & ~pad; 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 #ifdef HAVE_WINCODEC_H
ok &= WritePNG(out_file, use_stdout, buffer); ok &= WritePNG(out_file, use_stdout, buffer);
#else #else
@ -495,14 +520,17 @@ static int SaveOutput(const WebPDecBuffer* const buffer,
#endif #endif
} else if (format == PAM) { } else if (format == PAM) {
ok &= WritePPM(fout, buffer, 1); ok &= WritePPM(fout, buffer, 1);
} else if (format == PPM) { } else if (format == PPM || format == RGB || format == BGR) {
ok &= WritePPM(fout, buffer, 0); ok &= WritePPM(fout, buffer, 0);
} else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
ok &= Write16bAsPGM(fout, buffer);
} else if (format == BMP) { } else if (format == BMP) {
ok &= WriteBMP(fout, buffer); ok &= WriteBMP(fout, buffer);
} else if (format == TIFF) { } else if (format == TIFF) {
ok &= WriteTIFF(fout, buffer); ok &= WriteTIFF(fout, buffer);
} else if (format == PGM || format == YUV) { } else if (format == PGM || format == RAW_YUV ||
ok &= WritePGMOrYUV(fout, buffer, format); format == YUV || format == YUVA) {
ok &= WritePGMOrYUV(fout, buffer, format == RAW_YUV ? RAW_YUV : PGM);
} else if (format == ALPHA_PLANE_ONLY) { } else if (format == ALPHA_PLANE_ONLY) {
ok &= WriteAlphaPlane(fout, buffer); ok &= WriteAlphaPlane(fout, buffer);
} }
@ -569,6 +597,70 @@ static const char* const kFormatType[] = {
"unspecified", "lossy", "lossless" "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 main(int argc, const char *argv[]) {
int ok = 0; int ok = 0;
const char *in_file = NULL; const char *in_file = NULL;
@ -578,6 +670,10 @@ int main(int argc, const char *argv[]) {
WebPDecBuffer* const output_buffer = &config.output; WebPDecBuffer* const output_buffer = &config.output;
WebPBitstreamFeatures* const bitstream = &config.input; WebPBitstreamFeatures* const bitstream = &config.input;
OutputFileFormat format = PNG; OutputFileFormat format = PNG;
uint8_t* external_buffer = NULL;
int use_external_memory = 0;
const uint8_t* data = NULL;
int incremental = 0; int incremental = 0;
int c; int c;
@ -617,7 +713,32 @@ int main(int argc, const char *argv[]) {
} else if (!strcmp(argv[c], "-pgm")) { } else if (!strcmp(argv[c], "-pgm")) {
format = PGM; format = PGM;
} else if (!strcmp(argv[c], "-yuv")) { } 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")) { } else if (!strcmp(argv[c], "-mt")) {
config.options.use_threads = 1; config.options.use_threads = 1;
} else if (!strcmp(argv[c], "-alpha_dither")) { } else if (!strcmp(argv[c], "-alpha_dither")) {
@ -676,7 +797,6 @@ int main(int argc, const char *argv[]) {
{ {
VP8StatusCode status = VP8_STATUS_OK; VP8StatusCode status = VP8_STATUS_OK;
size_t data_size = 0; size_t data_size = 0;
const uint8_t* data = NULL;
if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) { if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) {
return -1; return -1;
} }
@ -703,15 +823,33 @@ int main(int argc, const char *argv[]) {
bitstream->has_alpha ? MODE_rgbA : MODE_RGB; bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
break; break;
case PGM: case PGM:
case YUV: case RAW_YUV:
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
break; break;
case ALPHA_PLANE_ONLY: case ALPHA_PLANE_ONLY:
output_buffer->colorspace = MODE_YUVA; output_buffer->colorspace = MODE_YUVA;
break; break;
default: // forced modes:
free((void*)data); case RGB: output_buffer->colorspace = MODE_RGB; break;
return -1; 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) { if (incremental) {
@ -720,7 +858,6 @@ int main(int argc, const char *argv[]) {
status = ExUtilDecodeWebP(data, data_size, verbose, &config); status = ExUtilDecodeWebP(data, data_size, verbose, &config);
} }
free((void*)data);
ok = (status == VP8_STATUS_OK); ok = (status == VP8_STATUS_OK);
if (!ok) { if (!ok) {
ExUtilPrintWebPError(in_file, status); ExUtilPrintWebPError(in_file, status);
@ -750,6 +887,8 @@ int main(int argc, const char *argv[]) {
} }
Exit: Exit:
WebPFreeDecBuffer(output_buffer); WebPFreeDecBuffer(output_buffer);
free((void*)external_buffer);
free((void*)data);
return ok ? 0 : -1; return ok ? 0 : -1;
} }

View File

@ -92,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
return VP8_STATUS_INVALID_PARAM; 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; uint8_t* output;
int uv_stride = 0, a_stride = 0; int uv_stride = 0, a_stride = 0;
uint64_t uv_size = 0, a_size = 0, total_size; uint64_t uv_size = 0, a_size = 0, total_size;
@ -227,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
void WebPFreeDecBuffer(WebPDecBuffer* buffer) { void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
if (buffer != NULL) { if (buffer != NULL) {
if (!buffer->is_external_memory) { if (buffer->is_external_memory <= 0) {
WebPSafeFree(buffer->private_memory); WebPSafeFree(buffer->private_memory);
} }
buffer->private_memory = NULL; 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);
}
//------------------------------------------------------------------------------

View File

@ -70,7 +70,9 @@ struct WebPIDecoder {
VP8Io io_; VP8Io io_;
MemBuffer mem_; // input memory buffer. 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. size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
int last_mb_y_; // last row reached for intra-mode decoding 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; idec->state_ = STATE_DONE;
if (options != NULL && options->flip) { if (options != NULL && options->flip) {
return WebPFlipBuffer(output); const VP8StatusCode status = WebPFlipBuffer(output);
} else { if (status != VP8_STATUS_OK) return status;
return VP8_STATUS_OK;
} }
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)); WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
if (idec == NULL) { if (idec == NULL) {
return NULL; return NULL;
@ -593,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
VP8InitIo(&idec->io_); VP8InitIo(&idec->io_);
WebPResetDecParams(&idec->params_); WebPResetDecParams(&idec->params_);
idec->params_.output = (output_buffer != NULL) ? output_buffer if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
: &idec->output_; 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. WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
return idec; return idec;
} }
//------------------------------------------------------------------------------
// Public functions
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
return NewDecoder(output_buffer, NULL);
}
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
WebPDecoderConfig* config) { WebPDecoderConfig* config) {
WebPIDecoder* idec; 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: // Parse the bitstream's features, if requested:
if (data != NULL && data_size > 0 && config != NULL) { if (data != NULL && data_size > 0) {
if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) { if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
return NULL; return NULL;
} }
} }
// Create an instance of the incremental decoder // 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) { if (idec == NULL) {
return NULL; return NULL;
} }
@ -645,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) {
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
size_t output_buffer_size, int output_stride) { 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; WebPIDecoder* idec;
if (mode >= MODE_YUV) return NULL; 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_buffer_size = 0;
output_stride = 0; output_stride = 0;
} else { // A buffer was passed. Validate the other params. } 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* u, size_t u_size, int u_stride,
uint8_t* v, size_t v_size, int v_stride, uint8_t* v, size_t v_size, int v_stride,
uint8_t* a, size_t a_size, int a_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; WebPIDecoder* idec;
WEBP_CSP_MODE colorspace; 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_size = u_size = v_size = a_size = 0;
luma_stride = u_stride = v_stride = a_stride = 0; luma_stride = u_stride = v_stride = a_stride = 0;
u = v = a = NULL; u = v = a = NULL;
@ -783,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
if (idec->state_ <= STATE_VP8_PARTS0) { if (idec->state_ <= STATE_VP8_PARTS0) {
return NULL; return NULL;
} }
if (idec->final_output_ != NULL) {
return NULL; // not yet slow-copied
}
return idec->params_.output; return idec->params_.output;
} }
@ -792,7 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
const WebPDecBuffer* const src = GetOutputBuffer(idec); const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (left != NULL) *left = 0; if (left != NULL) *left = 0;
if (top != NULL) *top = 0; if (top != NULL) *top = 0;
if (src) { if (src != NULL) {
if (width != NULL) *width = src->width; if (width != NULL) *width = src->width;
if (height != NULL) *height = idec->params_.last_y; if (height != NULL) *height = idec->params_.last_y;
} else { } else {

View File

@ -32,7 +32,7 @@ extern "C" {
// version numbers // version numbers
#define DEC_MAJ_VERSION 0 #define DEC_MAJ_VERSION 0
#define DEC_MIN_VERSION 5 #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). // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y), // Constraints are: We need to store one 16x16 block of luma samples (y),

View File

@ -514,6 +514,8 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
WebPFreeDecBuffer(params->output); WebPFreeDecBuffer(params->output);
} else { } else {
if (params->options != NULL && params->options->flip) { 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); status = WebPFlipBuffer(params->output);
} }
} }
@ -758,9 +760,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
} }
WebPResetDecParams(&params); WebPResetDecParams(&params);
params.output = &config->output;
params.options = &config->options; params.options = &config->options;
status = DecodeInto(data, data_size, &params); 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, &params);
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, &params);
}
return status; return status;
} }
@ -826,4 +843,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -45,11 +45,20 @@ struct WebPDecParams {
OutputFunc emit; // output RGB or YUV samples OutputFunc emit; // output RGB or YUV samples
OutputAlphaFunc emit_alpha; // output alpha channel OutputAlphaFunc emit_alpha; // output alpha channel
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values 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. // Should be called first, before any use of the WebPDecParams object.
void WebPResetDecParams(WebPDecParams* const params); void WebPResetDecParams(WebPDecParams* const params);
// Delete all memory (after an error occurred, for instance)
void WebPFreeDecParams(WebPDecParams* const params);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Header parsing helpers // Header parsing helpers
@ -107,13 +116,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the // 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, void WebPCopyDecBuffer(const WebPDecBuffer* const src,
WebPDecBuffer* const dst); WebPDecBuffer* const dst);
// Copy and transfer ownership from src to dst (beware of parameter order!) // Copy and transfer ownership from src to dst (beware of parameter order!)
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); 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 #ifdef __cplusplus

View File

@ -197,7 +197,10 @@ struct WebPYUVABuffer { // view as YUVA
struct WebPDecBuffer { struct WebPDecBuffer {
WEBP_CSP_MODE colorspace; // Colorspace. WEBP_CSP_MODE colorspace; // Colorspace.
int width, height; // Dimensions. 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 { union {
WebPRGBABuffer RGBA; WebPRGBABuffer RGBA;
WebPYUVABuffer YUVA; WebPYUVABuffer YUVA;
@ -205,7 +208,7 @@ struct WebPDecBuffer {
uint32_t pad[4]; // padding for later use uint32_t pad[4]; // padding for later use
uint8_t* private_memory; // Internally allocated memory (only when 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. // externally, but accessed via the buffer union.
}; };
@ -269,7 +272,7 @@ typedef enum VP8StatusCode {
// that of the returned WebPIDecoder object. // that of the returned WebPIDecoder object.
// The supplied 'output_buffer' content MUST NOT be changed between calls to // The supplied 'output_buffer' content MUST NOT be changed between calls to
// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is // 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 // stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
// within valid bounds. // within valid bounds.
// All other fields of WebPDecBuffer MUST remain constant between calls. // All other fields of WebPDecBuffer MUST remain constant between calls.