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:
parent
a90edffb7e
commit
abdb109f3b
177
examples/dwebp.c
177
examples/dwebp.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
|
@ -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(¶ms);
|
WebPResetDecParams(¶ms);
|
||||||
params.output = &config->output;
|
|
||||||
params.options = &config->options;
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
@ -826,4 +843,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user