diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-07 19:03:42 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-07 19:03:42 +0000 |
commit | fd1e1a386c679717a6d0d25952dc38360fac3189 (patch) | |
tree | ecda19e7f453564eb3e34078bed74ee4ac2614f9 /third_party/libwebp/webp.c | |
parent | 0a27eaa85f863e30817ca438132acb4000fec4bd (diff) | |
download | chromium_src-fd1e1a386c679717a6d0d25952dc38360fac3189.zip chromium_src-fd1e1a386c679717a6d0d25952dc38360fac3189.tar.gz chromium_src-fd1e1a386c679717a6d0d25952dc38360fac3189.tar.bz2 |
Add libwebp library source and gyp file.
BUG=58225
TEST=library should build
Review URL: http://codereview.chromium.org/3614010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61828 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/libwebp/webp.c')
-rw-r--r-- | third_party/libwebp/webp.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/third_party/libwebp/webp.c b/third_party/libwebp/webp.c new file mode 100644 index 0000000..4c13fd2 --- /dev/null +++ b/third_party/libwebp/webp.c @@ -0,0 +1,361 @@ +// Copyright 2010 Google Inc. +// +// This code is licensed under the same terms as WebM: +// Software License Agreement: http://www.webmproject.org/license/software/ +// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ +// ----------------------------------------------------------------------------- +// +// Main decoding functions for WEBP images. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include <stdlib.h> +#include "vp8i.h" +#include "yuv.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +//----------------------------------------------------------------------------- +// RIFF layout is: +// 0ffset tag +// 0...3 "RIFF" 4-byte tag +// 4...7 size of image data (including metadata) starting at offset 8 +// 8...11 "WEBP" our form-type signature +// 12..15 "VP8 ": 4-bytes tags, describing the raw video format used +// 16..19 size of the raw VP8 image data, starting at offset 20 +// 20.... the VP8 bytes +// There can be extra chunks after the "VP8 " chunk (ICMT, ICOP, ...) +// All 32-bits sizes are in little-endian order. +// Note: chunk data must be padded to multiple of 2 in size + +static inline uint32_t get_le32(const uint8_t* const data) { + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +// If a RIFF container is detected, validate it and skip over it. +static int CheckRIFFHeader(const uint8_t** data_ptr, uint32_t *data_size_ptr) { + uint32_t chunk_size = 0xffffffffu; + if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) { + if (memcmp(*data_ptr + 8, "WEBP", 4)) { + return 0; // wrong image file signature + } + const uint32_t riff_size = get_le32(*data_ptr + 4); + if (memcmp(*data_ptr + 12, "VP8 ", 4)) { + return 0; // invalid compression format + } + chunk_size = get_le32(*data_ptr + 16); + if ((chunk_size > riff_size + 8) || (chunk_size & 1)) { + return 0; // inconsistent size information. + } + // We have a IFF container. Skip it. + *data_ptr += 20; + *data_size_ptr -= 20; + } + return chunk_size; +} + +//----------------------------------------------------------------------------- + +typedef enum { MODE_RGB = 0, MODE_RGBA = 1, + MODE_BGR = 2, MODE_BGRA = 3, + MODE_YUV = 4 } CSP_MODE; + +typedef struct { + uint8_t* output; // rgb(a) or luma + uint8_t *u, *v; + int stride; // rgb(a) stride or luma stride + int u_stride; + int v_stride; + CSP_MODE mode; +} Params; + +static void CustomPut(const VP8Io* io) { + Params *p = (Params*)io->opaque; + const int mb_w = io->mb_w; + const int mb_h = io->mb_h; + if (p->mode == MODE_YUV) { + uint8_t* const y_dst = p->output + io->mb_x + io->mb_y * p->stride; + for (int j = 0; j < mb_h; ++j) { + memcpy(y_dst + j * p->stride, io->y + j * io->y_stride, mb_w); + } + uint8_t* const u_dst = p->u + (io->mb_x / 2) + (io->mb_y / 2) * p->u_stride; + uint8_t* const v_dst = p->v + (io->mb_x / 2) + (io->mb_y / 2) * p->v_stride; + const int uv_w = (mb_w + 1) / 2; + for (int j = 0; j < (mb_h + 1) / 2; ++j) { + memcpy(u_dst + j * p->u_stride, io->u + j * io->uv_stride, uv_w); + memcpy(v_dst + j * p->v_stride, io->v + j * io->uv_stride, uv_w); + } + } else { + const int psize = (p->mode == MODE_RGB || p->mode == MODE_BGR) ? 3 : 4; + uint8_t* dst = p->output + psize * io->mb_x + io->mb_y * p->stride; + for (int j = 0; j < mb_h; ++j) { + const uint8_t* y_src = io->y + j * io->y_stride; + for (int i = 0; i < mb_w; ++i) { + const int y = y_src[i]; + const int u = io->u[(j / 2) * io->uv_stride + (i / 2)]; + const int v = io->v[(j / 2) * io->uv_stride + (i / 2)]; + if (p->mode == MODE_RGB) { + VP8YuvToRgb(y, u, v, dst + i * 3); + } else if (p->mode == MODE_BGR) { + VP8YuvToBgr(y, u, v, dst + i * 3); + } else if (p->mode == MODE_RGBA) { + VP8YuvToRgba(y, u, v, dst + i * 4); + } else { + VP8YuvToBgra(y, u, v, dst + i * 4); + } + } + dst += p->stride; + } + } +} + + +//----------------------------------------------------------------------------- +// "Into" variants + +static uint8_t* DecodeInto(CSP_MODE mode, + const uint8_t* data, uint32_t data_size, + Params* params, int output_size, + int output_u_size, int output_v_size) { + VP8Decoder* dec = VP8New(); + if (dec == NULL) { + return NULL; + } + + VP8Io io; + VP8InitIo(&io); + io.data = data; + io.data_size = data_size; + + params->mode = mode; + io.opaque = params; + io.put = CustomPut; + + if (!VP8GetHeaders(dec, &io)) { + VP8Delete(dec); + return NULL; + } + // check output buffers + int ok = 1; + ok &= (params->stride * io.height <= output_size); + if (mode == MODE_RGB || mode == MODE_BGR) { + ok &= (params->stride >= io.width * 3); + } else if (mode == MODE_RGBA || mode == MODE_BGRA) { + ok &= (params->stride >= io.width * 4); + } else { + ok &= (params->stride >= io.width); + // some extra checks for U/V + const int u_size = params->u_stride * ((io.height + 1) / 2); + const int v_size = params->v_stride * ((io.height + 1) / 2); + ok &= (params->u_stride >= (io.width + 1) / 2) && + (params->v_stride >= (io.width + 1) / 2); + ok &= (u_size <= output_u_size && v_size <= output_v_size); + } + if (!ok) { + VP8Delete(dec); + return NULL; + } + + if (mode != MODE_YUV) { + VP8YUVInit(); + } + + ok = VP8Decode(dec, &io); + VP8Delete(dec); + return ok ? params->output : NULL; +} + +uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, + uint8_t* output, int output_size, + int output_stride) { + if (output == NULL) { + return NULL; + } + Params params; + params.output = output; + params.stride = output_stride; + return DecodeInto(MODE_RGB, data, data_size, ¶ms, output_size, 0, 0); +} + +uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, + uint8_t* output, int output_size, + int output_stride) { + if (output == NULL) { + return NULL; + } + Params params; + params.output = output; + params.stride = output_stride; + return DecodeInto(MODE_RGBA, data, data_size, ¶ms, output_size, 0, 0); +} + +uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, + uint8_t* output, int output_size, + int output_stride) { + if (output == NULL) { + return NULL; + } + Params params; + params.output = output; + params.stride = output_stride; + return DecodeInto(MODE_BGR, data, data_size, ¶ms, output_size, 0, 0); +} + +uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, + uint8_t* output, int output_size, + int output_stride) { + if (output == NULL) { + return NULL; + } + Params params; + params.output = output; + params.stride = output_stride; + return DecodeInto(MODE_BGRA, data, data_size, ¶ms, output_size, 0, 0); +} + +uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, + uint8_t* luma, int luma_size, int luma_stride, + uint8_t* u, int u_size, int u_stride, + uint8_t* v, int v_size, int v_stride) { + if (luma == NULL) { + return NULL; + } + Params params; + params.output = luma; + params.stride = luma_stride; + params.u = u; + params.u_stride = u_stride; + params.v = v; + params.v_stride = v_stride; + return DecodeInto(MODE_YUV, data, data_size, ¶ms, + luma_size, u_size, v_size); +} + +//----------------------------------------------------------------------------- + +static uint8_t* Decode(CSP_MODE mode, const uint8_t* data, uint32_t data_size, + int* width, int* height, Params* params_out) { + int w, h; + if (!WebPGetInfo(data, data_size, &w, &h)) { + return NULL; + } + if (width) *width = w; + if (height) *height = h; + + // initialize output buffer, now that dimensions are known. + int stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w + : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w + : w; + const int size = stride * h; + int uv_size = 0; + int uv_stride = 0; + if (mode == MODE_YUV) { + uv_stride = (w + 1) / 2; + uv_size = uv_stride * ((h + 1) / 2); + } + uint8_t* const output = (uint8_t*)malloc(size + 2 * uv_size); + if (!output) { + return NULL; + } + Params params = { 0 }; + params.output = output; + params.stride = stride; + if (mode == MODE_YUV) { + params.u = output + size; + params.u_stride = uv_stride; + params.v = output + size + uv_size; + params.v_stride = uv_stride; + } + if (params_out) *params_out = params; + return DecodeInto(mode, data, data_size, ¶ms, size, uv_size, uv_size); +} + +uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size, + int *width, int *height) { + return Decode(MODE_RGB, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size, + int *width, int *height) { + return Decode(MODE_RGBA, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size, + int *width, int *height) { + return Decode(MODE_BGR, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size, + int *width, int *height) { + return Decode(MODE_BGRA, data, data_size, width, height, NULL); +} + +uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size, + int *width, int *height, uint8_t** u, uint8_t** v, + int *stride, int* uv_stride) { + Params params; + uint8_t* const out = Decode(MODE_YUV, data, data_size, + width, height, ¶ms); + + if (out) { + *u = params.u; + *v = params.v; + *stride = params.stride; + *uv_stride = params.u_stride; + assert(params.u_stride == params.v_stride); + } + return out; +} + +//----------------------------------------------------------------------------- +// WebPGetInfo() + +int WebPGetInfo(const uint8_t* data, uint32_t data_size, + int *width, int *height) { + const uint32_t chunk_size = CheckRIFFHeader(&data, &data_size); + if (!chunk_size) { + return 0; // unsupported RIFF header + } + // Validate raw video data + if (data_size < 10) { + return 0; // not enough data + } + // check signature + if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) { + return 0; // Wrong signature. + } + const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16); + const int key_frame = !(bits & 1); + if (!key_frame) { // Not a keyframe. + return 0; + } + const int profile = (bits >> 1) & 7; + const int show_frame = (bits >> 4) & 1; + const uint32_t partition_length = (bits >> 5); + if (profile > 3) { + return 0; // unknown profile + } + if (!show_frame) { + return 0; // first frame is invisible! + } + if (partition_length >= chunk_size) { + return 0; // inconsistent size information. + } + + const int w = ((data[7] << 8) | data[6]) & 0x3fff; + const int h = ((data[9] << 8) | data[8]) & 0x3fff; + if (width) { + *width = w; + } + if (height) { + *height = h; + } + + return 1; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif |