summaryrefslogtreecommitdiffstats
path: root/third_party/libwebp/enc/vp8l.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebp/enc/vp8l.c')
-rw-r--r--third_party/libwebp/enc/vp8l.c166
1 files changed, 92 insertions, 74 deletions
diff --git a/third_party/libwebp/enc/vp8l.c b/third_party/libwebp/enc/vp8l.c
index 0d0fe65..5077167 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;
}