mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-20 15:59:48 +02:00
Compare commits
11 Commits
v0.2.0-rc1
...
v0.2.0
Author | SHA1 | Date | |
---|---|---|---|
4238bc0adb | |||
c655380c36 | |||
fe1958f17d | |||
681cb30ad2 | |||
f06c1d8f7b | |||
f56e98fd11 | |||
6fe843baeb | |||
528a11af35 | |||
a0a488554d | |||
62dd9bb242 | |||
6f4272b090 |
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
||||
c655380 dec/io.c: cosmetics
|
||||
fe1958f RGBA4444: harmonize lossless/lossy alpha values
|
||||
681cb30 fix RGBA4444 output w/fancy upsampling
|
||||
f06c1d8 Merge "Alignment fix" into 0.2.0
|
||||
f56e98f Alignment fix
|
||||
6fe843b avoid rgb-premultiply if there's only trivial alpha values
|
||||
528a11a fix the ARGB4444 premultiply arithmetic
|
||||
a0a4885 Lossless decoder fix for a special transform order
|
||||
62dd9bb Update encoding heuristic w.r.t palette colors.
|
||||
6f4272b remove unused ApplyInverseTransform()
|
||||
93bf0fa Update ChangeLog (v0.2.0-rc1)
|
||||
5934fc5 update AUTHORS
|
||||
014a711 update NEWS
|
||||
43b0d61 add support for ARGB -> YUVA conversion for lossless decoder
|
||||
|
122
src/dec/io.c
122
src/dec/io.c
@ -111,7 +111,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* top_u = p->tmp_u;
|
||||
const uint8_t* top_v = p->tmp_v;
|
||||
int y = io->mb_y;
|
||||
int y_end = io->mb_y + io->mb_h;
|
||||
const int y_end = io->mb_y + io->mb_h;
|
||||
const int mb_w = io->mb_w;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
|
||||
@ -150,7 +150,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
// Process the very last row of even-sized picture
|
||||
if (!(y_end & 1)) {
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
||||
dst + buf->stride, NULL, mb_w);
|
||||
dst + buf->stride, NULL, mb_w);
|
||||
}
|
||||
}
|
||||
return num_lines_out;
|
||||
@ -184,49 +184,60 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
const uint8_t** alpha, int* const num_rows) {
|
||||
int start_y = io->mb_y;
|
||||
*num_rows = io->mb_h;
|
||||
|
||||
// Compensate for the 1-line delay of the fancy upscaler.
|
||||
// This is similar to EmitFancyRGB().
|
||||
if (io->fancy_upsampling) {
|
||||
if (start_y == 0) {
|
||||
// We don't process the last row yet. It'll be done during the next call.
|
||||
--*num_rows;
|
||||
} else {
|
||||
--start_y;
|
||||
// Fortunately, *alpha data is persistent, so we can go back
|
||||
// one row and finish alpha blending, now that the fancy upscaler
|
||||
// completed the YUV->RGB interpolation.
|
||||
*alpha -= io->width;
|
||||
}
|
||||
if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
|
||||
// If it's the very last call, we process all the remaining rows!
|
||||
*num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||
}
|
||||
}
|
||||
return start_y;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int start_y = io->mb_y;
|
||||
int num_rows = mb_h;
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
// We compensate for the 1-line delay of fancy upscaler.
|
||||
// This is similar to EmitFancyRGB().
|
||||
if (io->fancy_upsampling) {
|
||||
if (start_y == 0) {
|
||||
// We don't process the last row yet. It'll be done during next call.
|
||||
--num_rows;
|
||||
} else {
|
||||
--start_y;
|
||||
// Fortunately, *alpha data is persistent, so we can go back
|
||||
// one row and finish alpha blending, now that the fancy upscaler
|
||||
// completed the YUV->RGB interpolation.
|
||||
alpha -= io->width;
|
||||
}
|
||||
if (io->crop_top + io->mb_y + mb_h == io->crop_bottom) {
|
||||
// If it's the very last call, we process all the remaing rows!
|
||||
num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
{
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) dst[4 * i] = alpha[i];
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
if (WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
|
||||
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -236,22 +247,27 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + io->mb_y * buf->stride;
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_val = VP8Clip4Bits(alpha[i]);
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
||||
const uint32_t alpha_value = alpha[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
if (p->output->colorspace == MODE_rgbA_4444) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, mb_h, buf->stride);
|
||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -396,17 +412,22 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) dst[4 * i] = p->scaler_a.dst[i];
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha) {
|
||||
if (is_premult_alpha && alpha_mask != 0xff) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
@ -421,6 +442,7 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
@ -428,13 +450,14 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_val = VP8Clip4Bits(p->scaler_a.dst[i]);
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha_dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha) {
|
||||
if (is_premult_alpha && alpha_mask != 0x0f) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
@ -471,8 +494,7 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
tmp_size1 += work_size;
|
||||
tmp_size2 += out_width;
|
||||
}
|
||||
p->memory =
|
||||
calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||
p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
@ -569,7 +591,7 @@ static int CustomSetup(VP8Io* io) {
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int CustomPut(const VP8Io* io) {
|
||||
WebPDecParams* p = (WebPDecParams*)io->opaque;
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int num_lines_out;
|
||||
|
@ -615,20 +615,22 @@ static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr,
|
||||
|
||||
typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
|
||||
|
||||
static void ApplyTransforms(VP8LDecoder* const dec, int num_rows,
|
||||
const uint32_t* const rows) {
|
||||
static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows,
|
||||
const uint32_t* const rows) {
|
||||
int n = dec->next_transform_;
|
||||
const int cache_pixs = dec->width_ * num_rows;
|
||||
uint32_t* rows_data = dec->argb_cache_;
|
||||
const int start_row = dec->last_row_;
|
||||
const int end_row = start_row + num_rows;
|
||||
const uint32_t* rows_in = rows;
|
||||
uint32_t* const rows_out = dec->argb_cache_;
|
||||
|
||||
// Inverse transforms.
|
||||
// TODO: most transforms only need to operate on the cropped region only.
|
||||
memcpy(rows_data, rows, cache_pixs * sizeof(*rows_data));
|
||||
memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out));
|
||||
while (n-- > 0) {
|
||||
VP8LTransform* const transform = &dec->transforms_[n];
|
||||
VP8LInverseTransform(transform, start_row, end_row, rows, rows_data);
|
||||
VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out);
|
||||
rows_in = rows_out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,7 +641,7 @@ static void ProcessRows(VP8LDecoder* const dec, int row) {
|
||||
const int num_rows = row - dec->last_row_;
|
||||
|
||||
if (num_rows <= 0) return; // Nothing to be done.
|
||||
ApplyTransforms(dec, num_rows, rows);
|
||||
ApplyInverseTransforms(dec, num_rows, rows);
|
||||
|
||||
// Emit output.
|
||||
{
|
||||
@ -797,19 +799,6 @@ static void ClearTransform(VP8LTransform* const transform) {
|
||||
transform->data_ = NULL;
|
||||
}
|
||||
|
||||
static void ApplyInverseTransforms(VP8LDecoder* const dec, int start_idx,
|
||||
uint32_t* const decoded_data) {
|
||||
int n = dec->next_transform_;
|
||||
assert(start_idx >= 0);
|
||||
while (n-- > start_idx) {
|
||||
VP8LTransform* const transform = &dec->transforms_[n];
|
||||
VP8LInverseTransform(transform, 0, transform->ysize_,
|
||||
decoded_data, decoded_data);
|
||||
ClearTransform(transform);
|
||||
}
|
||||
dec->next_transform_ = start_idx;
|
||||
}
|
||||
|
||||
// For security reason, we need to remap the color map to span
|
||||
// the total possible bundled values, and not just the num_colors.
|
||||
static int ExpandColorMap(int num_colors, VP8LTransform* const transform) {
|
||||
@ -964,7 +953,6 @@ static int DecodeImageStream(int xsize, int ysize,
|
||||
VP8LBitReader* const br = &dec->br_;
|
||||
VP8LMetadata* const hdr = &dec->hdr_;
|
||||
uint32_t* data = NULL;
|
||||
const int transform_start_idx = dec->next_transform_;
|
||||
int color_cache_bits = 0;
|
||||
|
||||
// Read the transforms (may recurse).
|
||||
@ -1024,9 +1012,6 @@ static int DecodeImageStream(int xsize, int ysize,
|
||||
ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, NULL);
|
||||
ok = ok && !br->error_;
|
||||
|
||||
// Apply transforms on the decoded data.
|
||||
if (ok) ApplyInverseTransforms(dec, transform_start_idx, data);
|
||||
|
||||
End:
|
||||
|
||||
if (!ok) {
|
||||
@ -1083,7 +1068,7 @@ static void ExtractAlphaRows(VP8LDecoder* const dec, int row) {
|
||||
const uint32_t* const in = dec->argb_ + dec->width_ * dec->last_row_;
|
||||
|
||||
if (num_rows <= 0) return; // Nothing to be done.
|
||||
ApplyTransforms(dec, num_rows, in);
|
||||
ApplyInverseTransforms(dec, num_rows, in);
|
||||
|
||||
// Extract alpha (which is stored in the green plane).
|
||||
{
|
||||
|
@ -935,7 +935,7 @@ static void ColorIndexInverseTransform(
|
||||
uint32_t packed_pixels = 0;
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
// We need to load fresh 'packed_pixels' once every 'bytes_per_pixels'
|
||||
// We need to load fresh 'packed_pixels' once every 'pixels_per_byte'
|
||||
// increments of x. Fortunately, pixels_per_byte is a power of 2, so
|
||||
// can just use a mask for that, instead of decrementing a counter.
|
||||
if ((x & count_mask) == 0) packed_pixels = ((*src++) >> 8) & 0xff;
|
||||
@ -976,7 +976,21 @@ void VP8LInverseTransform(const VP8LTransform* const transform,
|
||||
ColorSpaceInverseTransform(transform, row_start, row_end, out);
|
||||
break;
|
||||
case COLOR_INDEXING_TRANSFORM:
|
||||
ColorIndexInverseTransform(transform, row_start, row_end, in, out);
|
||||
if (in == out && transform->bits_ > 0) {
|
||||
// Move packed pixels to the end of unpacked region, so that unpacking
|
||||
// can occur seamlessly.
|
||||
// Also, note that this is the only transform that applies on
|
||||
// the effective width of VP8LSubSampleSize(xsize_, bits_). All other
|
||||
// transforms work on effective width of xsize_.
|
||||
const int out_stride = (row_end - row_start) * transform->xsize_;
|
||||
const int in_stride = (row_end - row_start) *
|
||||
VP8LSubSampleSize(transform->xsize_, transform->bits_);
|
||||
uint32_t* const src = out + out_stride - in_stride;
|
||||
memmove(src, out, in_stride * sizeof(*src));
|
||||
ColorIndexInverseTransform(transform, row_start, row_end, src, out);
|
||||
} else {
|
||||
ColorIndexInverseTransform(transform, row_start, row_end, in, out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -271,8 +271,7 @@ static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
|
||||
|
||||
// rgbA4444
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x11)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 12)
|
||||
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||
|
||||
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
||||
return (x & 0xf0) | (x >> 4);
|
||||
@ -282,24 +281,27 @@ static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
|
||||
return (x & 0x0f) | (x << 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
|
||||
return (x * m) >> 16;
|
||||
}
|
||||
|
||||
static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
|
||||
int w, int h, int stride) {
|
||||
while (h-- > 0) {
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint8_t a = dither_lo(rgba4444[2 * i + 1]);
|
||||
const uint8_t a = (rgba4444[2 * i + 1] & 0x0f);
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
const uint8_t r = PREMULTIPLY(dither_hi(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t g = PREMULTIPLY(dither_lo(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t b = PREMULTIPLY(dither_hi(rgba4444[2 * i + 1]), mult);
|
||||
rgba4444[2 * i + 0] = (r & 0xf0) | (g & 0x0f);
|
||||
const uint8_t r = multiply(dither_hi(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t g = multiply(dither_lo(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t b = multiply(dither_hi(rgba4444[2 * i + 1]), mult);
|
||||
rgba4444[2 * i + 0] = (r & 0xf0) | ((g >> 4) & 0x0f);
|
||||
rgba4444[2 * i + 1] = (b & 0xf0) | a;
|
||||
}
|
||||
rgba4444 += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
#undef PREMULTIPLY
|
||||
|
||||
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
|
||||
= ApplyAlphaMultiply;
|
||||
|
@ -90,11 +90,6 @@ static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
|
||||
rgba[3] = 0xff;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t VP8Clip4Bits(uint8_t c) {
|
||||
const uint32_t v = (c + 8) >> 4;
|
||||
return (v > 15) ? 15 : v;
|
||||
}
|
||||
|
||||
// Must be called before everything, to initialize the tables.
|
||||
void VP8YUVInit(void);
|
||||
|
||||
|
@ -29,6 +29,7 @@ extern "C" {
|
||||
|
||||
#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
|
||||
#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
|
||||
#define MAX_COLORS_FOR_GRAPH 64
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Palette
|
||||
@ -98,11 +99,11 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int AnalyzeEntropy(const WebPPicture* const pic,
|
||||
static int AnalyzeEntropy(const uint32_t* argb,
|
||||
int width, int height, int argb_stride,
|
||||
double* const nonpredicted_bits,
|
||||
double* const predicted_bits) {
|
||||
int x, y;
|
||||
const uint32_t* argb = pic->argb;
|
||||
const uint32_t* last_line = NULL;
|
||||
uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
|
||||
|
||||
@ -114,8 +115,8 @@ static int AnalyzeEntropy(const WebPPicture* const pic,
|
||||
|
||||
VP8LHistogramInit(predicted, 0);
|
||||
VP8LHistogramInit(nonpredicted, 0);
|
||||
for (y = 0; y < pic->height; ++y) {
|
||||
for (x = 0; x < pic->width; ++x) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t pix = argb[x];
|
||||
const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
|
||||
if (pix_diff == 0) continue;
|
||||
@ -131,7 +132,7 @@ static int AnalyzeEntropy(const WebPPicture* const pic,
|
||||
}
|
||||
}
|
||||
last_line = argb;
|
||||
argb += pic->argb_stride;
|
||||
argb += argb_stride;
|
||||
}
|
||||
*nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
|
||||
*predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
|
||||
@ -143,24 +144,35 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
||||
const WebPPicture* const pic = enc->pic_;
|
||||
assert(pic != NULL && pic->argb != NULL);
|
||||
|
||||
enc->use_palette_ = (image_hint == WEBP_HINT_GRAPH) ? 0 :
|
||||
enc->use_palette_ =
|
||||
AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
|
||||
if (!enc->use_palette_) {
|
||||
if (image_hint == WEBP_HINT_DEFAULT) {
|
||||
double non_pred_entropy, pred_entropy;
|
||||
if (!AnalyzeEntropy(pic, &non_pred_entropy, &pred_entropy)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pred_entropy < 0.95 * non_pred_entropy) {
|
||||
enc->use_predict_ = 1;
|
||||
enc->use_cross_color_ = 1;
|
||||
}
|
||||
} else if (image_hint == WEBP_HINT_PHOTO) {
|
||||
enc->use_predict_ = 1;
|
||||
enc->use_cross_color_ = 1;
|
||||
if (image_hint == WEBP_HINT_GRAPH) {
|
||||
if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
|
||||
enc->use_palette_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enc->use_palette_) {
|
||||
if (image_hint == WEBP_HINT_PHOTO) {
|
||||
enc->use_predict_ = 1;
|
||||
enc->use_cross_color_ = 1;
|
||||
} else {
|
||||
double non_pred_entropy, pred_entropy;
|
||||
if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
|
||||
&non_pred_entropy, &pred_entropy)) {
|
||||
return 0;
|
||||
}
|
||||
if (pred_entropy < 0.95 * non_pred_entropy) {
|
||||
enc->use_predict_ = 1;
|
||||
// TODO(vikasa): Observed some correlation of cross_color transform with
|
||||
// predict. Need to investigate this further and add separate heuristic
|
||||
// for setting use_cross_color flag.
|
||||
enc->use_cross_color_ = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -961,6 +973,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
||||
if (enc->use_palette_) {
|
||||
err = ApplyPalette(bw, enc, quality);
|
||||
if (err != VP8_ENC_OK) goto Error;
|
||||
// Color cache is disabled for palette.
|
||||
enc->cache_bits_ = 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user