Index: third_party/libwebp/enc/vp8l.c |
diff --git a/third_party/libwebp/enc/vp8l.c b/third_party/libwebp/enc/vp8l.c |
index 0d0fe65e54906f5910a6654ffc21e44e24fab82b..5077167be1a39f09dc3c0ef491e3a3a97a78c521 100644 |
--- a/third_party/libwebp/enc/vp8l.c |
+++ b/third_party/libwebp/enc/vp8l.c |
@@ -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 |
@@ -36,7 +37,8 @@ extern "C" { |
static int CompareColors(const void* p1, const void* p2) { |
const uint32_t a = *(const uint32_t*)p1; |
const uint32_t b = *(const uint32_t*)p2; |
- return (a < b) ? -1 : (a > b) ? 1 : 0; |
+ assert(a != b); |
+ return (a < b) ? -1 : 1; |
} |
// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, |
@@ -98,11 +100,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 +116,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 +133,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 +145,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 (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_DEFAULT) { |
+ 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, &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; |
} |
- } else if (image_hint == WEBP_HINT_PHOTO) { |
- enc->use_predict_ = 1; |
- enc->use_cross_color_ = 1; |
} |
} |
+ |
return 1; |
} |
@@ -208,7 +221,7 @@ static int GetHuffBitLengthsAndCodes( |
} |
// Create Huffman trees. |
- for (i = 0; i < histogram_image_size; ++i) { |
+ for (i = 0; ok && (i < histogram_image_size); ++i) { |
HuffmanTreeCode* const codes = &huffman_codes[5 * i]; |
VP8LHistogram* const histo = histogram_image->histograms[i]; |
ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0); |
@@ -219,7 +232,11 @@ static int GetHuffBitLengthsAndCodes( |
} |
End: |
- if (!ok) free(mem_buf); |
+ if (!ok) { |
+ free(mem_buf); |
+ // If one VP8LCreateHuffmanTree() above fails, we need to clean up behind. |
+ memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes)); |
+ } |
return ok; |
} |
@@ -394,9 +411,10 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, |
} |
static void WriteHuffmanCode(VP8LBitWriter* const bw, |
- const HuffmanTreeCode* const code, int index) { |
- const int depth = code->code_lengths[index]; |
- const int symbol = code->codes[index]; |
+ const HuffmanTreeCode* const code, |
+ int code_index) { |
+ const int depth = code->code_lengths[code_index]; |
+ const int symbol = code->codes[code_index]; |
VP8LWriteBits(bw, depth, symbol); |
} |
@@ -517,7 +535,12 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, |
sizeof(*histogram_symbols)); |
assert(histogram_bits >= MIN_HUFFMAN_BITS); |
assert(histogram_bits <= MAX_HUFFMAN_BITS); |
- if (histogram_image == NULL || histogram_symbols == NULL) goto Error; |
+ |
+ if (histogram_image == NULL || histogram_symbols == NULL) { |
+ free(histogram_image); |
+ free(histogram_symbols); |
+ return 0; |
+ } |
// Calculate backward references from ARGB image. |
if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits, |
@@ -540,6 +563,9 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, |
!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { |
goto Error; |
} |
+ // Free combined histograms. |
+ free(histogram_image); |
+ histogram_image = NULL; |
// Color Cache parameters. |
VP8LWriteBits(bw, 1, use_color_cache); |
@@ -559,10 +585,10 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, |
uint32_t i; |
if (histogram_argb == NULL) goto Error; |
for (i = 0; i < histogram_image_xysize; ++i) { |
- const int index = histogram_symbols[i] & 0xffff; |
- histogram_argb[i] = 0xff000000 | (index << 8); |
- if (index >= max_index) { |
- max_index = index + 1; |
+ const int symbol_index = histogram_symbols[i] & 0xffff; |
+ histogram_argb[i] = 0xff000000 | (symbol_index << 8); |
+ if (symbol_index >= max_index) { |
+ max_index = symbol_index + 1; |
} |
} |
histogram_image_size = max_index; |
@@ -586,9 +612,6 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, |
ClearHuffmanTreeIfOnlyOneSymbol(codes); |
} |
} |
- // Free combined histograms. |
- free(histogram_image); |
- histogram_image = NULL; |
// Store actual literals. |
StoreImageToBitMask(bw, width, histogram_bits, &refs, |
@@ -596,7 +619,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, |
ok = 1; |
Error: |
- if (!ok) free(histogram_image); |
+ free(histogram_image); |
VP8LClearBackwardRefs(&refs); |
if (huffman_codes != NULL) { |
@@ -694,13 +717,6 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc, |
// ----------------------------------------------------------------------------- |
-static void PutLE32(uint8_t* const data, uint32_t val) { |
- data[0] = (val >> 0) & 0xff; |
- data[1] = (val >> 8) & 0xff; |
- data[2] = (val >> 16) & 0xff; |
- data[3] = (val >> 24) & 0xff; |
-} |
- |
static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, |
size_t riff_size, size_t vp8l_size) { |
uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = { |
@@ -795,30 +811,24 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, |
return err; |
} |
-// Bundles multiple (2, 4 or 8) pixels into a single pixel. |
-// Returns the new xsize. |
-static void BundleColorMap(const WebPPicture* const pic, |
- int xbits, uint32_t* bundled_argb, int xs) { |
- int y; |
- const int bit_depth = 1 << (3 - xbits); |
- uint32_t code = 0; |
- const uint32_t* argb = pic->argb; |
- const int width = pic->width; |
- const int height = pic->height; |
- |
- for (y = 0; y < height; ++y) { |
- int x; |
+// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. |
+static void BundleColorMap(const uint8_t* const row, int width, |
+ int xbits, uint32_t* const dst) { |
+ int x; |
+ if (xbits > 0) { |
+ const int bit_depth = 1 << (3 - xbits); |
+ const int mask = (1 << xbits) - 1; |
+ uint32_t code = 0xff000000; |
for (x = 0; x < width; ++x) { |
- const int mask = (1 << xbits) - 1; |
const int xsub = x & mask; |
if (xsub == 0) { |
- code = 0; |
+ code = 0xff000000; |
} |
- // TODO(vikasa): simplify the bundling logic. |
- code |= (argb[x] & 0xff00) << (bit_depth * xsub); |
- bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; |
+ code |= row[x] << (8 + bit_depth * xsub); |
+ dst[x >> xbits] = code; |
} |
- argb += pic->argb_stride; |
+ } else { |
+ for (x = 0; x < width; ++x) dst[x] = 0xff000000 | (row[x] << 8); |
} |
} |
@@ -830,24 +840,43 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, |
WebPEncodingError err = VP8_ENC_OK; |
int i, x, y; |
const WebPPicture* const pic = enc->pic_; |
- uint32_t* argb = pic->argb; |
+ uint32_t* src = pic->argb; |
+ uint32_t* dst; |
const int width = pic->width; |
const int height = pic->height; |
uint32_t* const palette = enc->palette_; |
const int palette_size = enc->palette_size_; |
+ uint8_t* row = NULL; |
+ int xbits; |
// Replace each input pixel by corresponding palette index. |
+ // This is done line by line. |
+ if (palette_size <= 4) { |
+ xbits = (palette_size <= 2) ? 3 : 2; |
+ } else { |
+ xbits = (palette_size <= 16) ? 1 : 0; |
+ } |
+ |
+ err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); |
+ if (err != VP8_ENC_OK) goto Error; |
+ dst = enc->argb_; |
+ |
+ row = WebPSafeMalloc((uint64_t)width, sizeof(*row)); |
+ if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; |
+ |
for (y = 0; y < height; ++y) { |
for (x = 0; x < width; ++x) { |
- const uint32_t pix = argb[x]; |
+ const uint32_t pix = src[x]; |
for (i = 0; i < palette_size; ++i) { |
if (pix == palette[i]) { |
- argb[x] = 0xff000000u | (i << 8); |
+ row[x] = i; |
break; |
} |
} |
} |
- argb += pic->argb_stride; |
+ BundleColorMap(row, width, xbits, dst); |
+ src += pic->argb_stride; |
+ dst += enc->current_width_; |
} |
// Save palette to bitstream. |
@@ -863,20 +892,8 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, |
goto Error; |
} |
- if (palette_size <= 16) { |
- // Image can be packed (multiple pixels per uint32_t). |
- int xbits = 1; |
- if (palette_size <= 2) { |
- xbits = 3; |
- } else if (palette_size <= 4) { |
- xbits = 2; |
- } |
- err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); |
- if (err != VP8_ENC_OK) goto Error; |
- BundleColorMap(pic, xbits, enc->argb_, enc->current_width_); |
- } |
- |
Error: |
+ free(row); |
return err; |
} |
@@ -886,13 +903,13 @@ static int GetHistoBits(const WebPConfig* const config, |
const WebPPicture* const pic) { |
const int width = pic->width; |
const int height = pic->height; |
- const size_t hist_size = sizeof(VP8LHistogram); |
+ const uint64_t hist_size = sizeof(VP8LHistogram); |
// Make tile size a function of encoding method (Range: 0 to 6). |
int histo_bits = 7 - config->method; |
while (1) { |
- const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * |
- VP8LSubSampleSize(height, histo_bits) * |
- hist_size; |
+ const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * |
+ VP8LSubSampleSize(height, histo_bits) * |
+ hist_size; |
if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; |
++histo_bits; |
} |
@@ -961,6 +978,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; |
} |