| 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;
|
| }
|
|
|
|
|