2010-09-30 15:34:38 +02:00
|
|
|
// 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"
|
2011-03-09 04:23:16 +01:00
|
|
|
#include "webpi.h"
|
2010-09-30 15:34:38 +02:00
|
|
|
#include "yuv.h"
|
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
2010-11-03 22:27:51 +01:00
|
|
|
#define FANCY_UPSCALING // undefined to remove fancy upscaling support
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// 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.
|
2011-03-09 04:23:16 +01:00
|
|
|
uint32_t WebPCheckRIFFHeader(const uint8_t** data_ptr,
|
|
|
|
uint32_t *data_size_ptr) {
|
2010-09-30 15:34:38 +02:00
|
|
|
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
|
2010-10-06 01:58:47 +02:00
|
|
|
} else {
|
|
|
|
const uint32_t riff_size = get_le32(*data_ptr + 4);
|
2011-02-16 22:24:55 +01:00
|
|
|
if (riff_size < 12) {
|
|
|
|
return 0; // we should have at least one chunk
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
if (memcmp(*data_ptr + 12, "VP8 ", 4)) {
|
|
|
|
return 0; // invalid compression format
|
|
|
|
}
|
|
|
|
chunk_size = get_le32(*data_ptr + 16);
|
2011-02-16 22:24:55 +01:00
|
|
|
if (chunk_size > riff_size - 12) {
|
2010-10-06 01:58:47 +02:00
|
|
|
return 0; // inconsistent size information.
|
|
|
|
}
|
2011-02-17 18:59:34 +01:00
|
|
|
// We have a RIFF container. Skip it.
|
2010-10-06 01:58:47 +02:00
|
|
|
*data_ptr += 20;
|
|
|
|
*data_size_ptr -= 20;
|
2011-02-16 22:24:55 +01:00
|
|
|
// Note: we don't report error for odd-sized chunks.
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
return chunk_size;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
return *data_size_ptr;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2010-11-03 22:27:51 +01:00
|
|
|
// Fancy upscaling
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2010-11-03 22:27:51 +01:00
|
|
|
#ifdef FANCY_UPSCALING
|
|
|
|
|
|
|
|
// Given samples laid out in a square as:
|
|
|
|
// [a b]
|
|
|
|
// [c d]
|
|
|
|
// we interpolate u/v as:
|
|
|
|
// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16
|
|
|
|
// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
|
|
|
|
|
|
|
|
// We process u and v together stashed into 32bit (16bit each).
|
|
|
|
#define LOAD_UV(u,v) ((u) | ((v) << 16))
|
|
|
|
|
2010-12-14 22:52:07 +01:00
|
|
|
#define UPSCALE_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
|
|
|
static inline void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
|
|
|
const uint8_t* top_u, const uint8_t* top_v, \
|
|
|
|
const uint8_t* cur_u, const uint8_t* cur_v, \
|
|
|
|
uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
|
|
|
|
int x; \
|
|
|
|
const int last_pixel_pair = (len - 1) >> 1; \
|
|
|
|
uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
|
|
|
|
uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
|
|
|
|
if (top_y) { \
|
|
|
|
const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
|
|
|
|
FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
|
|
|
|
} \
|
|
|
|
if (bottom_y) { \
|
|
|
|
const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
|
|
|
|
FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
|
|
|
|
} \
|
|
|
|
for (x = 1; x <= last_pixel_pair; ++x) { \
|
|
|
|
const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \
|
|
|
|
const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \
|
|
|
|
/* precompute invariant values associated with first and second diagonals*/\
|
|
|
|
const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
|
|
|
|
const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
|
|
|
|
const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
|
|
|
|
if (top_y) { \
|
|
|
|
const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
|
|
|
|
const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
|
|
|
|
FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
|
|
|
|
top_dst + (2 * x - 1) * XSTEP); \
|
|
|
|
FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
|
|
|
|
top_dst + (2 * x - 0) * XSTEP); \
|
|
|
|
} \
|
|
|
|
if (bottom_y) { \
|
|
|
|
const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
|
|
|
|
const uint32_t uv1 = (diag_12 + uv) >> 1; \
|
|
|
|
FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
|
|
|
|
bottom_dst + (2 * x - 1) * XSTEP); \
|
|
|
|
FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \
|
|
|
|
bottom_dst + (2 * x + 0) * XSTEP); \
|
|
|
|
} \
|
|
|
|
tl_uv = t_uv; \
|
|
|
|
l_uv = uv; \
|
|
|
|
} \
|
|
|
|
if (!(len & 1)) { \
|
|
|
|
if (top_y) { \
|
|
|
|
const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
|
|
|
|
FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
|
|
|
|
top_dst + (len - 1) * XSTEP); \
|
|
|
|
} \
|
|
|
|
if (bottom_y) { \
|
|
|
|
const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
|
|
|
|
FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
|
|
|
|
bottom_dst + (len - 1) * XSTEP); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
}
|
2010-11-03 22:27:51 +01:00
|
|
|
|
|
|
|
// All variants implemented.
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
UPSCALE_FUNC(UpscaleRgbLinePair, VP8YuvToRgb, 3)
|
|
|
|
UPSCALE_FUNC(UpscaleBgrLinePair, VP8YuvToBgr, 3)
|
|
|
|
UPSCALE_FUNC(UpscaleRgbaLinePair, VP8YuvToRgb, 4)
|
|
|
|
UPSCALE_FUNC(UpscaleBgraLinePair, VP8YuvToBgr, 4)
|
2010-11-03 22:27:51 +01:00
|
|
|
|
|
|
|
// Main driver function.
|
2010-12-14 22:52:07 +01:00
|
|
|
static inline
|
|
|
|
void UpscaleLinePair(const uint8_t* top_y, const uint8_t* bottom_y,
|
|
|
|
const uint8_t* top_u, const uint8_t* top_v,
|
|
|
|
const uint8_t* cur_u, const uint8_t* cur_v,
|
|
|
|
uint8_t* top_dst, uint8_t* bottom_dst, int len,
|
2011-03-09 04:23:16 +01:00
|
|
|
WEBP_CSP_MODE mode) {
|
2010-12-14 22:52:07 +01:00
|
|
|
if (mode == MODE_RGB) {
|
|
|
|
UpscaleRgbLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
|
|
|
|
top_dst, bottom_dst, len);
|
|
|
|
} else if (mode == MODE_BGR) {
|
|
|
|
UpscaleBgrLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
|
|
|
|
top_dst, bottom_dst, len);
|
|
|
|
} else if (mode == MODE_RGBA) {
|
|
|
|
UpscaleRgbaLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
|
|
|
|
top_dst, bottom_dst, len);
|
2010-11-03 22:27:51 +01:00
|
|
|
} else {
|
2010-12-14 22:52:07 +01:00
|
|
|
assert(mode == MODE_BGRA);
|
|
|
|
UpscaleBgraLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
|
|
|
|
top_dst, bottom_dst, len);
|
2010-11-03 22:27:51 +01:00
|
|
|
}
|
|
|
|
}
|
2010-12-14 22:52:07 +01:00
|
|
|
|
2010-11-03 22:27:51 +01:00
|
|
|
#undef LOAD_UV
|
|
|
|
#undef UPSCALE_FUNC
|
|
|
|
|
|
|
|
#endif // FANCY_UPSCALING
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Main conversion driver.
|
|
|
|
|
2011-02-16 23:33:16 +01:00
|
|
|
static int CustomPut(const VP8Io* io) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams *p = (WebPDecParams*)io->opaque;
|
2010-11-03 22:27:51 +01:00
|
|
|
const int w = io->width;
|
2010-09-30 15:34:38 +02:00
|
|
|
const int mb_h = io->mb_h;
|
2010-11-03 22:27:51 +01:00
|
|
|
const int uv_w = (w + 1) / 2;
|
|
|
|
assert(!(io->mb_y & 1));
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-02-16 23:33:16 +01:00
|
|
|
if (w <= 0 || mb_h <= 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-20 21:09:07 +01:00
|
|
|
|
2011-03-24 01:24:31 +01:00
|
|
|
p->last_y = io->mb_y + io->mb_h; // a priori guess
|
2010-09-30 15:34:38 +02:00
|
|
|
if (p->mode == MODE_YUV) {
|
2010-11-03 22:27:51 +01:00
|
|
|
uint8_t* const y_dst = p->output + io->mb_y * p->stride;
|
|
|
|
uint8_t* const u_dst = p->u + (io->mb_y >> 1) * p->u_stride;
|
|
|
|
uint8_t* const v_dst = p->v + (io->mb_y >> 1) * p->v_stride;
|
|
|
|
int j;
|
2010-10-06 01:58:47 +02:00
|
|
|
for (j = 0; j < mb_h; ++j) {
|
2010-11-03 22:27:51 +01:00
|
|
|
memcpy(y_dst + j * p->stride, io->y + j * io->y_stride, w);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
for (j = 0; j < (mb_h + 1) / 2; ++j) {
|
2010-09-30 15:34:38 +02:00
|
|
|
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 {
|
2010-11-03 22:27:51 +01:00
|
|
|
uint8_t* dst = p->output + io->mb_y * p->stride;
|
|
|
|
if (io->fancy_upscaling) {
|
|
|
|
#ifdef FANCY_UPSCALING
|
2010-12-14 22:52:07 +01:00
|
|
|
const uint8_t* cur_y = io->y;
|
2010-11-03 22:27:51 +01:00
|
|
|
const uint8_t* cur_u = io->u;
|
|
|
|
const uint8_t* cur_v = io->v;
|
|
|
|
const uint8_t* top_u = p->top_u;
|
|
|
|
const uint8_t* top_v = p->top_v;
|
|
|
|
int y = io->mb_y;
|
2010-12-14 22:52:07 +01:00
|
|
|
int y_end = io->mb_y + io->mb_h;
|
|
|
|
if (y == 0) {
|
|
|
|
// First line is special cased. We mirror the u/v samples at boundary.
|
|
|
|
UpscaleLinePair(NULL, cur_y, cur_u, cur_v, cur_u, cur_v,
|
|
|
|
NULL, dst, w, p->mode);
|
2010-11-03 22:27:51 +01:00
|
|
|
} else {
|
2010-12-14 22:52:07 +01:00
|
|
|
// We can finish the left-over line from previous call
|
|
|
|
UpscaleLinePair(p->top_y, cur_y, top_u, top_v, cur_u, cur_v,
|
|
|
|
dst - p->stride, dst, w, p->mode);
|
|
|
|
}
|
|
|
|
// Loop over each output pairs of row.
|
|
|
|
for (; y + 2 < y_end; y += 2) {
|
2010-11-03 22:27:51 +01:00
|
|
|
top_u = cur_u;
|
|
|
|
top_v = cur_v;
|
2010-12-14 22:52:07 +01:00
|
|
|
cur_u += io->uv_stride;
|
|
|
|
cur_v += io->uv_stride;
|
|
|
|
dst += 2 * p->stride;
|
|
|
|
cur_y += 2 * io->y_stride;
|
|
|
|
UpscaleLinePair(cur_y - io->y_stride, cur_y,
|
|
|
|
top_u, top_v, cur_u, cur_v,
|
|
|
|
dst - p->stride, dst, w, p->mode);
|
2010-11-03 22:27:51 +01:00
|
|
|
}
|
2010-12-14 22:52:07 +01:00
|
|
|
// move to last row
|
|
|
|
cur_y += io->y_stride;
|
|
|
|
if (y_end != io->height) {
|
|
|
|
// Save the unfinished samples for next call (as we're not done yet).
|
|
|
|
memcpy(p->top_y, cur_y, w * sizeof(*p->top_y));
|
|
|
|
memcpy(p->top_u, cur_u, uv_w * sizeof(*p->top_u));
|
|
|
|
memcpy(p->top_v, cur_v, uv_w * sizeof(*p->top_v));
|
2011-03-24 01:24:31 +01:00
|
|
|
// The fancy upscaler leaves a row unfinished behind
|
|
|
|
// (except for the very last row)
|
|
|
|
p->last_y -= 1;
|
2010-11-03 22:27:51 +01:00
|
|
|
} else {
|
2010-12-14 22:52:07 +01:00
|
|
|
// Process the very last row of even-sized picture
|
|
|
|
if (!(y_end & 1)) {
|
|
|
|
UpscaleLinePair(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
|
|
|
dst + p->stride, NULL, w, p->mode);
|
2010-11-03 22:27:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
assert(0); // shouldn't happen.
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
// Point-sampling U/V upscaler.
|
|
|
|
int j;
|
|
|
|
for (j = 0; j < mb_h; ++j) {
|
|
|
|
const uint8_t* y_src = io->y + j * io->y_stride;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 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) {
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
VP8YuvToRgb(y, u, v, dst + i * 4);
|
2010-11-03 22:27:51 +01:00
|
|
|
} else {
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
VP8YuvToBgr(y, u, v, dst + i * 4);
|
2010-11-03 22:27:51 +01:00
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-11-03 22:27:51 +01:00
|
|
|
dst += p->stride;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
|
|
|
|
// Alpha handling
|
|
|
|
if (p->mode == MODE_RGBA || p->mode == MODE_BGRA) {
|
|
|
|
int i, j;
|
|
|
|
uint8_t* dst = p->output + io->mb_y * p->stride + 3;
|
|
|
|
const uint8_t* alpha = io->a;
|
|
|
|
const int has_alpha = (alpha != NULL);
|
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
|
|
if (has_alpha) {
|
|
|
|
for (j = 0; j < mb_h; ++j) {
|
|
|
|
for (i = 0; i < w; ++i) {
|
|
|
|
dst[4 * i] = alpha[i];
|
|
|
|
}
|
|
|
|
alpha += io->width;
|
|
|
|
dst += p->stride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!has_alpha) { // fill-in with 0xFFs
|
|
|
|
for (j = 0; j < mb_h; ++j) {
|
|
|
|
for (i = 0; i < w; ++i) {
|
|
|
|
dst[4 * i] = 0xff;
|
|
|
|
}
|
|
|
|
dst += p->stride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-16 23:33:16 +01:00
|
|
|
return 1;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
2010-11-03 22:27:51 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static int CustomSetup(VP8Io* io) {
|
|
|
|
#ifdef FANCY_UPSCALING
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams *p = (WebPDecParams*)io->opaque;
|
2010-11-03 22:27:51 +01:00
|
|
|
p->top_y = p->top_u = p->top_v = NULL;
|
|
|
|
if (p->mode != MODE_YUV) {
|
|
|
|
const int uv_width = (io->width + 1) >> 1;
|
|
|
|
p->top_y = (uint8_t*)malloc(io->width + 2 * uv_width);
|
|
|
|
if (p->top_y == NULL) {
|
|
|
|
return 0; // memory error.
|
|
|
|
}
|
|
|
|
p->top_u = p->top_y + io->width;
|
|
|
|
p->top_v = p->top_u + uv_width;
|
|
|
|
io->fancy_upscaling = 1; // activate fancy upscaling
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CustomTeardown(const VP8Io* io) {
|
|
|
|
#ifdef FANCY_UPSCALING
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams *p = (WebPDecParams*)io->opaque;
|
2010-11-03 22:27:51 +01:00
|
|
|
if (p->top_y) {
|
|
|
|
free(p->top_y);
|
|
|
|
p->top_y = p->top_u = p->top_v = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-03-09 04:23:16 +01:00
|
|
|
void WebPInitCustomIo(VP8Io* const io) {
|
|
|
|
io->put = CustomPut;
|
|
|
|
io->setup = CustomSetup;
|
|
|
|
io->teardown = CustomTeardown;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2011-03-13 09:22:57 +01:00
|
|
|
// Init/Check/Free decoding parameters and buffer
|
2011-03-09 04:23:16 +01:00
|
|
|
|
|
|
|
int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
|
|
|
|
int* height, WebPDecParams* const params) {
|
2011-03-24 02:03:53 +01:00
|
|
|
int w, h;
|
2011-03-09 04:23:16 +01:00
|
|
|
|
|
|
|
if (!WebPGetInfo(data, data_size, &w, &h)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (width) *width = w;
|
|
|
|
if (height) *height = h;
|
|
|
|
|
2011-03-24 02:03:53 +01:00
|
|
|
if (!params->external_buffer) {
|
|
|
|
int stride;
|
|
|
|
int uv_stride = 0;
|
|
|
|
int size;
|
|
|
|
int uv_size = 0;
|
|
|
|
uint8_t* output;
|
|
|
|
WEBP_CSP_MODE mode = params->mode;
|
|
|
|
|
|
|
|
// initialize output buffer, now that dimensions are known.
|
|
|
|
stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
|
|
|
|
: (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
|
|
|
|
: w;
|
|
|
|
size = stride * h;
|
|
|
|
|
|
|
|
if (mode == MODE_YUV) {
|
|
|
|
uv_stride = (w + 1) / 2;
|
|
|
|
uv_size = uv_stride * ((h + 1) / 2);
|
|
|
|
}
|
2011-03-09 04:23:16 +01:00
|
|
|
|
2011-03-24 02:03:53 +01:00
|
|
|
output = (uint8_t*)malloc(size + 2 * uv_size);
|
|
|
|
if (!output) {
|
|
|
|
return 0;
|
|
|
|
}
|
2011-03-09 04:23:16 +01:00
|
|
|
|
2011-03-24 02:03:53 +01:00
|
|
|
params->output = output;
|
|
|
|
params->stride = stride;
|
|
|
|
params->output_size = size;
|
|
|
|
if (mode == MODE_YUV) {
|
|
|
|
params->u = output + size;
|
|
|
|
params->u_stride = uv_stride;
|
|
|
|
params->output_u_size = uv_size;
|
|
|
|
params->v = output + size + uv_size;
|
|
|
|
params->v_stride = uv_stride;
|
|
|
|
params->output_v_size = uv_size;
|
|
|
|
}
|
2011-03-09 04:23:16 +01:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-03-24 02:03:53 +01:00
|
|
|
int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params) {
|
2011-03-09 04:23:16 +01:00
|
|
|
int ok = 1;
|
|
|
|
WEBP_CSP_MODE mode = params->mode;
|
2011-03-24 02:03:53 +01:00
|
|
|
ok &= (params->stride * io->height <= params->output_size);
|
2011-03-09 04:23:16 +01:00
|
|
|
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 {
|
|
|
|
// 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->stride >= io->width);
|
|
|
|
ok &= (params->u_stride >= (io->width + 1) / 2) &&
|
|
|
|
(params->v_stride >= (io->width + 1) / 2);
|
2011-03-24 02:03:53 +01:00
|
|
|
ok &= (u_size <= params->output_u_size &&
|
|
|
|
v_size <= params->output_v_size);
|
2011-03-09 04:23:16 +01:00
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
void WebPResetDecParams(WebPDecParams* const params) {
|
|
|
|
assert(params);
|
|
|
|
memset(params, 0, sizeof(*params));
|
|
|
|
}
|
|
|
|
|
2011-03-13 09:22:57 +01:00
|
|
|
void WebPClearDecParams(WebPDecParams* params) {
|
2011-05-20 04:07:01 +02:00
|
|
|
assert(params);
|
2011-03-24 02:03:53 +01:00
|
|
|
if (!params->external_buffer) {
|
|
|
|
free(params->output);
|
|
|
|
}
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(params);
|
2011-03-13 09:22:57 +01:00
|
|
|
}
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// "Into" variants
|
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
static uint8_t* DecodeInto(const uint8_t* data, uint32_t data_size,
|
2011-03-24 02:03:53 +01:00
|
|
|
WebPDecParams* params) {
|
2010-09-30 15:34:38 +02:00
|
|
|
VP8Decoder* dec = VP8New();
|
2010-10-06 01:58:47 +02:00
|
|
|
VP8Io io;
|
|
|
|
int ok = 1;
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (dec == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
VP8InitIo(&io);
|
|
|
|
io.data = data;
|
|
|
|
io.data_size = data_size;
|
|
|
|
|
|
|
|
io.opaque = params;
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPInitCustomIo(&io);
|
2010-09-30 15:34:38 +02:00
|
|
|
|
|
|
|
if (!VP8GetHeaders(dec, &io)) {
|
|
|
|
VP8Delete(dec);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-03-09 04:23:16 +01:00
|
|
|
// check output buffers
|
2011-03-24 02:03:53 +01:00
|
|
|
ok = WebPCheckDecParams(&io, params);
|
2010-09-30 15:34:38 +02:00
|
|
|
if (!ok) {
|
|
|
|
VP8Delete(dec);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
if (params->mode != MODE_YUV) {
|
2010-09-30 15:34:38 +02:00
|
|
|
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) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (output == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
|
|
|
params.mode = MODE_RGB;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.output = output;
|
|
|
|
params.stride = output_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = output_size;
|
2011-05-20 04:07:01 +02:00
|
|
|
return DecodeInto(data, data_size, ¶ms);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
|
|
|
|
uint8_t* output, int output_size,
|
|
|
|
int output_stride) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (output == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
|
|
|
params.mode = MODE_RGBA;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.output = output;
|
|
|
|
params.stride = output_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = output_size;
|
2011-05-20 04:07:01 +02:00
|
|
|
return DecodeInto(data, data_size, ¶ms);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
|
|
|
|
uint8_t* output, int output_size,
|
|
|
|
int output_stride) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (output == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
|
|
|
params.mode = MODE_BGR;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.output = output;
|
|
|
|
params.stride = output_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = output_size;
|
2011-05-20 04:07:01 +02:00
|
|
|
return DecodeInto(data, data_size, ¶ms);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
|
|
|
|
uint8_t* output, int output_size,
|
|
|
|
int output_stride) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (output == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
|
|
|
params.mode = MODE_BGRA;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.output = output;
|
|
|
|
params.stride = output_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = output_size;
|
2011-05-20 04:07:01 +02:00
|
|
|
return DecodeInto(data, data_size, ¶ms);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (luma == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
|
|
|
params.mode = MODE_YUV;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.output = luma;
|
|
|
|
params.stride = luma_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = luma_size;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.u = u;
|
|
|
|
params.u_stride = u_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_u_size = u_size;
|
2010-09-30 15:34:38 +02:00
|
|
|
params.v = v;
|
|
|
|
params.v_stride = v_stride;
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_v_size = v_size;
|
2011-05-20 04:07:01 +02:00
|
|
|
return DecodeInto(data, data_size, ¶ms);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2011-03-09 04:23:16 +01:00
|
|
|
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
|
|
|
|
uint32_t data_size, int* width, int* height,
|
|
|
|
WebPDecParams* params_out) {
|
2011-03-14 18:29:02 +01:00
|
|
|
uint8_t* output;
|
2011-03-25 23:04:11 +01:00
|
|
|
WebPDecParams params;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-05-20 04:07:01 +02:00
|
|
|
WebPResetDecParams(¶ms);
|
2011-03-09 04:23:16 +01:00
|
|
|
params.mode = mode;
|
|
|
|
if (!WebPInitDecParams(data, data_size, width, height, ¶ms)) {
|
2010-09-30 15:34:38 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-03-09 04:23:16 +01:00
|
|
|
|
2011-03-24 02:03:53 +01:00
|
|
|
params.output_size = params.stride * (*height);
|
|
|
|
params.output_u_size = params.output_v_size =
|
|
|
|
params.u_stride * ((*height + 1) / 2);
|
2011-05-20 04:07:01 +02:00
|
|
|
output = DecodeInto(data, data_size, ¶ms);
|
2011-03-14 18:29:02 +01:00
|
|
|
if (!output) {
|
2011-03-13 09:22:57 +01:00
|
|
|
WebPClearDecParams(¶ms);
|
|
|
|
}
|
2011-03-14 18:29:02 +01:00
|
|
|
if (params_out) {
|
|
|
|
*params_out = params;
|
|
|
|
}
|
|
|
|
return output;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2011-03-09 04:23:16 +01:00
|
|
|
WebPDecParams params;
|
2010-09-30 15:34:38 +02:00
|
|
|
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) {
|
2011-03-09 04:23:16 +01:00
|
|
|
const uint32_t chunk_size = WebPCheckRIFFHeader(&data, &data_size);
|
2010-09-30 15:34:38 +02:00
|
|
|
if (!chunk_size) {
|
|
|
|
return 0; // unsupported RIFF header
|
|
|
|
}
|
|
|
|
// Validate raw video data
|
|
|
|
if (data_size < 10) {
|
|
|
|
return 0; // not enough data
|
|
|
|
}
|
2011-05-20 04:07:01 +02:00
|
|
|
return VP8GetInfo(data, chunk_size, width, height);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
} // extern "C"
|
|
|
|
#endif
|