Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / libwebp / enc / vp8l.c
blobc2bb13d545119649d5e78af4814479c9526860ca
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // main entry for the lossless encoder.
12 // Author: Vikas Arora (vikaas.arora@gmail.com)
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
19 #include "./backward_references.h"
20 #include "./vp8enci.h"
21 #include "./vp8li.h"
22 #include "../dsp/lossless.h"
23 #include "../utils/bit_writer.h"
24 #include "../utils/huffman_encode.h"
25 #include "../utils/utils.h"
26 #include "../webp/format_constants.h"
28 #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
29 #define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
30 #define MAX_COLORS_FOR_GRAPH 64
32 // -----------------------------------------------------------------------------
33 // Palette
35 static int CompareColors(const void* p1, const void* p2) {
36 const uint32_t a = *(const uint32_t*)p1;
37 const uint32_t b = *(const uint32_t*)p2;
38 assert(a != b);
39 return (a < b) ? -1 : 1;
42 // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
43 // creates a palette and returns true, else returns false.
44 static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
45 uint32_t palette[MAX_PALETTE_SIZE],
46 int* const palette_size) {
47 int i, x, y, key;
48 int num_colors = 0;
49 uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 };
50 uint32_t colors[MAX_PALETTE_SIZE * 4];
51 static const uint32_t kHashMul = 0x1e35a7bd;
52 const uint32_t* argb = pic->argb;
53 const int width = pic->width;
54 const int height = pic->height;
55 uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
57 for (y = 0; y < height; ++y) {
58 for (x = 0; x < width; ++x) {
59 if (argb[x] == last_pix) {
60 continue;
62 last_pix = argb[x];
63 key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT;
64 while (1) {
65 if (!in_use[key]) {
66 colors[key] = last_pix;
67 in_use[key] = 1;
68 ++num_colors;
69 if (num_colors > MAX_PALETTE_SIZE) {
70 return 0;
72 break;
73 } else if (colors[key] == last_pix) {
74 // The color is already there.
75 break;
76 } else {
77 // Some other color sits there.
78 // Do linear conflict resolution.
79 ++key;
80 key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer.
84 argb += pic->argb_stride;
87 // TODO(skal): could we reuse in_use[] to speed up EncodePalette()?
88 num_colors = 0;
89 for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) {
90 if (in_use[i]) {
91 palette[num_colors] = colors[i];
92 ++num_colors;
96 qsort(palette, num_colors, sizeof(*palette), CompareColors);
97 *palette_size = num_colors;
98 return 1;
101 static int AnalyzeEntropy(const uint32_t* argb,
102 int width, int height, int argb_stride,
103 double* const nonpredicted_bits,
104 double* const predicted_bits) {
105 int x, y;
106 const uint32_t* last_line = NULL;
107 uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
109 VP8LHistogramSet* const histo_set = VP8LAllocateHistogramSet(2, 0);
110 if (histo_set == NULL) return 0;
112 for (y = 0; y < height; ++y) {
113 for (x = 0; x < width; ++x) {
114 const uint32_t pix = argb[x];
115 const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
116 if (pix_diff == 0) continue;
117 if (last_line != NULL && pix == last_line[x]) {
118 continue;
120 last_pix = pix;
122 const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
123 const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
124 VP8LHistogramAddSinglePixOrCopy(histo_set->histograms[0], &pix_token);
125 VP8LHistogramAddSinglePixOrCopy(histo_set->histograms[1],
126 &pix_diff_token);
129 last_line = argb;
130 argb += argb_stride;
132 *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(histo_set->histograms[0]);
133 *predicted_bits = VP8LHistogramEstimateBitsBulk(histo_set->histograms[1]);
134 VP8LFreeHistogramSet(histo_set);
135 return 1;
138 static int AnalyzeAndInit(VP8LEncoder* const enc, WebPImageHint image_hint) {
139 const WebPPicture* const pic = enc->pic_;
140 const int width = pic->width;
141 const int height = pic->height;
142 const int pix_cnt = width * height;
143 // we round the block size up, so we're guaranteed to have
144 // at max MAX_REFS_BLOCK_PER_IMAGE blocks used:
145 int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1;
146 assert(pic != NULL && pic->argb != NULL);
148 enc->use_palette_ =
149 AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
151 if (image_hint == WEBP_HINT_GRAPH) {
152 if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
153 enc->use_palette_ = 0;
157 if (!enc->use_palette_) {
158 if (image_hint == WEBP_HINT_PHOTO) {
159 enc->use_predict_ = 1;
160 enc->use_cross_color_ = 1;
161 } else {
162 double non_pred_entropy, pred_entropy;
163 if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride,
164 &non_pred_entropy, &pred_entropy)) {
165 return 0;
167 if (pred_entropy < 0.95 * non_pred_entropy) {
168 enc->use_predict_ = 1;
169 enc->use_cross_color_ = 1;
173 if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0;
175 // palette-friendly input typically uses less literals
176 // -> reduce block size a bit
177 if (enc->use_palette_) refs_block_size /= 2;
178 VP8LBackwardRefsInit(&enc->refs_[0], refs_block_size);
179 VP8LBackwardRefsInit(&enc->refs_[1], refs_block_size);
181 return 1;
184 // Returns false in case of memory error.
185 static int GetHuffBitLengthsAndCodes(
186 const VP8LHistogramSet* const histogram_image,
187 HuffmanTreeCode* const huffman_codes) {
188 int i, k;
189 int ok = 0;
190 uint64_t total_length_size = 0;
191 uint8_t* mem_buf = NULL;
192 const int histogram_image_size = histogram_image->size;
193 int max_num_symbols = 0;
194 uint8_t* buf_rle = NULL;
195 HuffmanTree* huff_tree = NULL;
197 // Iterate over all histograms and get the aggregate number of codes used.
198 for (i = 0; i < histogram_image_size; ++i) {
199 const VP8LHistogram* const histo = histogram_image->histograms[i];
200 HuffmanTreeCode* const codes = &huffman_codes[5 * i];
201 for (k = 0; k < 5; ++k) {
202 const int num_symbols =
203 (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) :
204 (k == 4) ? NUM_DISTANCE_CODES : 256;
205 codes[k].num_symbols = num_symbols;
206 total_length_size += num_symbols;
210 // Allocate and Set Huffman codes.
212 uint16_t* codes;
213 uint8_t* lengths;
214 mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
215 sizeof(*lengths) + sizeof(*codes));
216 if (mem_buf == NULL) goto End;
218 codes = (uint16_t*)mem_buf;
219 lengths = (uint8_t*)&codes[total_length_size];
220 for (i = 0; i < 5 * histogram_image_size; ++i) {
221 const int bit_length = huffman_codes[i].num_symbols;
222 huffman_codes[i].codes = codes;
223 huffman_codes[i].code_lengths = lengths;
224 codes += bit_length;
225 lengths += bit_length;
226 if (max_num_symbols < bit_length) {
227 max_num_symbols = bit_length;
232 buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols);
233 huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols,
234 sizeof(*huff_tree));
235 if (buf_rle == NULL || huff_tree == NULL) goto End;
237 // Create Huffman trees.
238 for (i = 0; i < histogram_image_size; ++i) {
239 HuffmanTreeCode* const codes = &huffman_codes[5 * i];
240 VP8LHistogram* const histo = histogram_image->histograms[i];
241 VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0);
242 VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1);
243 VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2);
244 VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3);
245 VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4);
247 ok = 1;
248 End:
249 WebPSafeFree(huff_tree);
250 WebPSafeFree(buf_rle);
251 if (!ok) {
252 WebPSafeFree(mem_buf);
253 memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
255 return ok;
258 static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
259 VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
260 // RFC 1951 will calm you down if you are worried about this funny sequence.
261 // This sequence is tuned from that, but more weighted for lower symbol count,
262 // and more spiking histograms.
263 static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
264 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
266 int i;
267 // Throw away trailing zeros:
268 int codes_to_store = CODE_LENGTH_CODES;
269 for (; codes_to_store > 4; --codes_to_store) {
270 if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
271 break;
274 VP8LWriteBits(bw, 4, codes_to_store - 4);
275 for (i = 0; i < codes_to_store; ++i) {
276 VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]);
280 static void ClearHuffmanTreeIfOnlyOneSymbol(
281 HuffmanTreeCode* const huffman_code) {
282 int k;
283 int count = 0;
284 for (k = 0; k < huffman_code->num_symbols; ++k) {
285 if (huffman_code->code_lengths[k] != 0) {
286 ++count;
287 if (count > 1) return;
290 for (k = 0; k < huffman_code->num_symbols; ++k) {
291 huffman_code->code_lengths[k] = 0;
292 huffman_code->codes[k] = 0;
296 static void StoreHuffmanTreeToBitMask(
297 VP8LBitWriter* const bw,
298 const HuffmanTreeToken* const tokens, const int num_tokens,
299 const HuffmanTreeCode* const huffman_code) {
300 int i;
301 for (i = 0; i < num_tokens; ++i) {
302 const int ix = tokens[i].code;
303 const int extra_bits = tokens[i].extra_bits;
304 VP8LWriteBits(bw, huffman_code->code_lengths[ix], huffman_code->codes[ix]);
305 switch (ix) {
306 case 16:
307 VP8LWriteBits(bw, 2, extra_bits);
308 break;
309 case 17:
310 VP8LWriteBits(bw, 3, extra_bits);
311 break;
312 case 18:
313 VP8LWriteBits(bw, 7, extra_bits);
314 break;
319 // 'huff_tree' and 'tokens' are pre-alloacted buffers.
320 static void StoreFullHuffmanCode(VP8LBitWriter* const bw,
321 HuffmanTree* const huff_tree,
322 HuffmanTreeToken* const tokens,
323 const HuffmanTreeCode* const tree) {
324 uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
325 uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
326 const int max_tokens = tree->num_symbols;
327 int num_tokens;
328 HuffmanTreeCode huffman_code;
329 huffman_code.num_symbols = CODE_LENGTH_CODES;
330 huffman_code.code_lengths = code_length_bitdepth;
331 huffman_code.codes = code_length_bitdepth_symbols;
333 VP8LWriteBits(bw, 1, 0);
334 num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
336 uint32_t histogram[CODE_LENGTH_CODES] = { 0 };
337 uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 };
338 int i;
339 for (i = 0; i < num_tokens; ++i) {
340 ++histogram[tokens[i].code];
343 VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code);
346 StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
347 ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
349 int trailing_zero_bits = 0;
350 int trimmed_length = num_tokens;
351 int write_trimmed_length;
352 int length;
353 int i = num_tokens;
354 while (i-- > 0) {
355 const int ix = tokens[i].code;
356 if (ix == 0 || ix == 17 || ix == 18) {
357 --trimmed_length; // discount trailing zeros
358 trailing_zero_bits += code_length_bitdepth[ix];
359 if (ix == 17) {
360 trailing_zero_bits += 3;
361 } else if (ix == 18) {
362 trailing_zero_bits += 7;
364 } else {
365 break;
368 write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
369 length = write_trimmed_length ? trimmed_length : num_tokens;
370 VP8LWriteBits(bw, 1, write_trimmed_length);
371 if (write_trimmed_length) {
372 const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
373 const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2;
374 VP8LWriteBits(bw, 3, nbitpairs - 1);
375 assert(trimmed_length >= 2);
376 VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
378 StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
382 // 'huff_tree' and 'tokens' are pre-alloacted buffers.
383 static void StoreHuffmanCode(VP8LBitWriter* const bw,
384 HuffmanTree* const huff_tree,
385 HuffmanTreeToken* const tokens,
386 const HuffmanTreeCode* const huffman_code) {
387 int i;
388 int count = 0;
389 int symbols[2] = { 0, 0 };
390 const int kMaxBits = 8;
391 const int kMaxSymbol = 1 << kMaxBits;
393 // Check whether it's a small tree.
394 for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
395 if (huffman_code->code_lengths[i] != 0) {
396 if (count < 2) symbols[count] = i;
397 ++count;
401 if (count == 0) { // emit minimal tree for empty cases
402 // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
403 VP8LWriteBits(bw, 4, 0x01);
404 } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
405 VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
406 VP8LWriteBits(bw, 1, count - 1);
407 if (symbols[0] <= 1) {
408 VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value.
409 VP8LWriteBits(bw, 1, symbols[0]);
410 } else {
411 VP8LWriteBits(bw, 1, 1);
412 VP8LWriteBits(bw, 8, symbols[0]);
414 if (count == 2) {
415 VP8LWriteBits(bw, 8, symbols[1]);
417 } else {
418 StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code);
422 static void WriteHuffmanCode(VP8LBitWriter* const bw,
423 const HuffmanTreeCode* const code,
424 int code_index) {
425 const int depth = code->code_lengths[code_index];
426 const int symbol = code->codes[code_index];
427 VP8LWriteBits(bw, depth, symbol);
430 static WebPEncodingError StoreImageToBitMask(
431 VP8LBitWriter* const bw, int width, int histo_bits,
432 VP8LBackwardRefs* const refs,
433 const uint16_t* histogram_symbols,
434 const HuffmanTreeCode* const huffman_codes) {
435 // x and y trace the position in the image.
436 int x = 0;
437 int y = 0;
438 const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
439 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
440 while (VP8LRefsCursorOk(&c)) {
441 const PixOrCopy* const v = c.cur_pos;
442 const int histogram_ix = histogram_symbols[histo_bits ?
443 (y >> histo_bits) * histo_xsize +
444 (x >> histo_bits) : 0];
445 const HuffmanTreeCode* const codes = huffman_codes + 5 * histogram_ix;
446 if (PixOrCopyIsCacheIdx(v)) {
447 const int code = PixOrCopyCacheIdx(v);
448 const int literal_ix = 256 + NUM_LENGTH_CODES + code;
449 WriteHuffmanCode(bw, codes, literal_ix);
450 } else if (PixOrCopyIsLiteral(v)) {
451 static const int order[] = { 1, 2, 0, 3 };
452 int k;
453 for (k = 0; k < 4; ++k) {
454 const int code = PixOrCopyLiteral(v, order[k]);
455 WriteHuffmanCode(bw, codes + k, code);
457 } else {
458 int bits, n_bits;
459 int code, distance;
461 VP8LPrefixEncode(v->len, &code, &n_bits, &bits);
462 WriteHuffmanCode(bw, codes, 256 + code);
463 VP8LWriteBits(bw, n_bits, bits);
465 distance = PixOrCopyDistance(v);
466 VP8LPrefixEncode(distance, &code, &n_bits, &bits);
467 WriteHuffmanCode(bw, codes + 4, code);
468 VP8LWriteBits(bw, n_bits, bits);
470 x += PixOrCopyLength(v);
471 while (x >= width) {
472 x -= width;
473 ++y;
475 VP8LRefsCursorNext(&c);
477 return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK;
480 // Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
481 static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
482 const uint32_t* const argb,
483 VP8LHashChain* const hash_chain,
484 VP8LBackwardRefs refs_array[2],
485 int width, int height,
486 int quality) {
487 int i;
488 int max_tokens = 0;
489 WebPEncodingError err = VP8_ENC_OK;
490 VP8LBackwardRefs* refs;
491 HuffmanTreeToken* tokens = NULL;
492 HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
493 const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
494 VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
495 HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
496 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
497 if (histogram_image == NULL || huff_tree == NULL) {
498 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
499 goto Error;
502 // Calculate backward references from ARGB image.
503 refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, 1,
504 hash_chain, refs_array);
505 if (refs == NULL) {
506 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
507 goto Error;
509 // Build histogram image and symbols from backward references.
510 VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
512 // Create Huffman bit lengths and codes for each histogram image.
513 assert(histogram_image->size == 1);
514 if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
515 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
516 goto Error;
519 // No color cache, no Huffman image.
520 VP8LWriteBits(bw, 1, 0);
522 // Find maximum number of symbols for the huffman tree-set.
523 for (i = 0; i < 5; ++i) {
524 HuffmanTreeCode* const codes = &huffman_codes[i];
525 if (max_tokens < codes->num_symbols) {
526 max_tokens = codes->num_symbols;
530 tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
531 if (tokens == NULL) {
532 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
533 goto Error;
536 // Store Huffman codes.
537 for (i = 0; i < 5; ++i) {
538 HuffmanTreeCode* const codes = &huffman_codes[i];
539 StoreHuffmanCode(bw, huff_tree, tokens, codes);
540 ClearHuffmanTreeIfOnlyOneSymbol(codes);
543 // Store actual literals.
544 err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols,
545 huffman_codes);
547 Error:
548 WebPSafeFree(tokens);
549 WebPSafeFree(huff_tree);
550 VP8LFreeHistogramSet(histogram_image);
551 WebPSafeFree(huffman_codes[0].codes);
552 return err;
555 static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
556 const uint32_t* const argb,
557 VP8LHashChain* const hash_chain,
558 VP8LBackwardRefs refs_array[2],
559 int width, int height, int quality,
560 int cache_bits,
561 int histogram_bits) {
562 WebPEncodingError err = VP8_ENC_OK;
563 const int use_2d_locality = 1;
564 const int use_color_cache = (cache_bits > 0);
565 const uint32_t histogram_image_xysize =
566 VP8LSubSampleSize(width, histogram_bits) *
567 VP8LSubSampleSize(height, histogram_bits);
568 VP8LHistogramSet* histogram_image =
569 VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits);
570 int histogram_image_size = 0;
571 size_t bit_array_size = 0;
572 HuffmanTree* huff_tree = NULL;
573 HuffmanTreeToken* tokens = NULL;
574 HuffmanTreeCode* huffman_codes = NULL;
575 VP8LBackwardRefs refs;
576 VP8LBackwardRefs* best_refs;
577 uint16_t* const histogram_symbols =
578 (uint16_t*)WebPSafeMalloc(histogram_image_xysize,
579 sizeof(*histogram_symbols));
580 assert(histogram_bits >= MIN_HUFFMAN_BITS);
581 assert(histogram_bits <= MAX_HUFFMAN_BITS);
583 VP8LBackwardRefsInit(&refs, refs_array[0].block_size_);
584 if (histogram_image == NULL || histogram_symbols == NULL) {
585 VP8LFreeHistogramSet(histogram_image);
586 WebPSafeFree(histogram_symbols);
587 return 0;
590 // 'best_refs' is the reference to the best backward refs and points to one
591 // of refs_array[0] or refs_array[1].
592 // Calculate backward references from ARGB image.
593 best_refs = VP8LGetBackwardReferences(width, height, argb, quality,
594 cache_bits, use_2d_locality,
595 hash_chain, refs_array);
596 if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) {
597 goto Error;
599 // Build histogram image and symbols from backward references.
600 if (!VP8LGetHistoImageSymbols(width, height, &refs,
601 quality, histogram_bits, cache_bits,
602 histogram_image,
603 histogram_symbols)) {
604 goto Error;
606 // Create Huffman bit lengths and codes for each histogram image.
607 histogram_image_size = histogram_image->size;
608 bit_array_size = 5 * histogram_image_size;
609 huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
610 sizeof(*huffman_codes));
611 if (huffman_codes == NULL ||
612 !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
613 goto Error;
615 // Free combined histograms.
616 VP8LFreeHistogramSet(histogram_image);
617 histogram_image = NULL;
619 // Color Cache parameters.
620 VP8LWriteBits(bw, 1, use_color_cache);
621 if (use_color_cache) {
622 VP8LWriteBits(bw, 4, cache_bits);
625 // Huffman image + meta huffman.
627 const int write_histogram_image = (histogram_image_size > 1);
628 VP8LWriteBits(bw, 1, write_histogram_image);
629 if (write_histogram_image) {
630 uint32_t* const histogram_argb =
631 (uint32_t*)WebPSafeMalloc(histogram_image_xysize,
632 sizeof(*histogram_argb));
633 int max_index = 0;
634 uint32_t i;
635 if (histogram_argb == NULL) goto Error;
636 for (i = 0; i < histogram_image_xysize; ++i) {
637 const int symbol_index = histogram_symbols[i] & 0xffff;
638 histogram_argb[i] = 0xff000000 | (symbol_index << 8);
639 if (symbol_index >= max_index) {
640 max_index = symbol_index + 1;
643 histogram_image_size = max_index;
645 VP8LWriteBits(bw, 3, histogram_bits - 2);
646 err = EncodeImageNoHuffman(bw, histogram_argb, hash_chain, refs_array,
647 VP8LSubSampleSize(width, histogram_bits),
648 VP8LSubSampleSize(height, histogram_bits),
649 quality);
650 WebPSafeFree(histogram_argb);
651 if (err != VP8_ENC_OK) goto Error;
655 // Store Huffman codes.
657 int i;
658 int max_tokens = 0;
659 huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * CODE_LENGTH_CODES,
660 sizeof(*huff_tree));
661 if (huff_tree == NULL) goto Error;
662 // Find maximum number of symbols for the huffman tree-set.
663 for (i = 0; i < 5 * histogram_image_size; ++i) {
664 HuffmanTreeCode* const codes = &huffman_codes[i];
665 if (max_tokens < codes->num_symbols) {
666 max_tokens = codes->num_symbols;
669 tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens,
670 sizeof(*tokens));
671 if (tokens == NULL) goto Error;
672 for (i = 0; i < 5 * histogram_image_size; ++i) {
673 HuffmanTreeCode* const codes = &huffman_codes[i];
674 StoreHuffmanCode(bw, huff_tree, tokens, codes);
675 ClearHuffmanTreeIfOnlyOneSymbol(codes);
679 // Store actual literals.
680 err = StoreImageToBitMask(bw, width, histogram_bits, &refs,
681 histogram_symbols, huffman_codes);
683 Error:
684 WebPSafeFree(tokens);
685 WebPSafeFree(huff_tree);
686 VP8LFreeHistogramSet(histogram_image);
687 VP8LBackwardRefsClear(&refs);
688 if (huffman_codes != NULL) {
689 WebPSafeFree(huffman_codes->codes);
690 WebPSafeFree(huffman_codes);
692 WebPSafeFree(histogram_symbols);
693 return err;
696 // -----------------------------------------------------------------------------
697 // Transforms
699 // Check if it would be a good idea to subtract green from red and blue. We
700 // only impact entropy in red/blue components, don't bother to look at others.
701 static WebPEncodingError EvalAndApplySubtractGreen(VP8LEncoder* const enc,
702 int width, int height,
703 VP8LBitWriter* const bw) {
704 if (!enc->use_palette_) {
705 int i;
706 const uint32_t* const argb = enc->argb_;
707 double bit_cost_before, bit_cost_after;
708 // Allocate histogram with cache_bits = 1.
709 VP8LHistogram* const histo = VP8LAllocateHistogram(1);
710 if (histo == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
711 for (i = 0; i < width * height; ++i) {
712 const uint32_t c = argb[i];
713 ++histo->red_[(c >> 16) & 0xff];
714 ++histo->blue_[(c >> 0) & 0xff];
716 bit_cost_before = VP8LHistogramEstimateBits(histo);
718 VP8LHistogramInit(histo, 1);
719 for (i = 0; i < width * height; ++i) {
720 const uint32_t c = argb[i];
721 const int green = (c >> 8) & 0xff;
722 ++histo->red_[((c >> 16) - green) & 0xff];
723 ++histo->blue_[((c >> 0) - green) & 0xff];
725 bit_cost_after = VP8LHistogramEstimateBits(histo);
726 VP8LFreeHistogram(histo);
728 // Check if subtracting green yields low entropy.
729 enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
730 if (enc->use_subtract_green_) {
731 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
732 VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
733 VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
736 return VP8_ENC_OK;
739 static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
740 int width, int height, int quality,
741 VP8LBitWriter* const bw) {
742 const int pred_bits = enc->transform_bits_;
743 const int transform_width = VP8LSubSampleSize(width, pred_bits);
744 const int transform_height = VP8LSubSampleSize(height, pred_bits);
746 VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_,
747 enc->transform_data_);
748 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
749 VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
750 assert(pred_bits >= 2);
751 VP8LWriteBits(bw, 3, pred_bits - 2);
752 return EncodeImageNoHuffman(bw, enc->transform_data_,
753 (VP8LHashChain*)&enc->hash_chain_,
754 (VP8LBackwardRefs*)enc->refs_, // cast const away
755 transform_width, transform_height,
756 quality);
759 static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
760 int width, int height,
761 int quality,
762 VP8LBitWriter* const bw) {
763 const int ccolor_transform_bits = enc->transform_bits_;
764 const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
765 const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
767 VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
768 enc->argb_, enc->transform_data_);
769 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
770 VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
771 assert(ccolor_transform_bits >= 2);
772 VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
773 return EncodeImageNoHuffman(bw, enc->transform_data_,
774 (VP8LHashChain*)&enc->hash_chain_,
775 (VP8LBackwardRefs*)enc->refs_, // cast const away
776 transform_width, transform_height,
777 quality);
780 // -----------------------------------------------------------------------------
782 static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
783 size_t riff_size, size_t vp8l_size) {
784 uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
785 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
786 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
788 PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
789 PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
790 if (!pic->writer(riff, sizeof(riff), pic)) {
791 return VP8_ENC_ERROR_BAD_WRITE;
793 return VP8_ENC_OK;
796 static int WriteImageSize(const WebPPicture* const pic,
797 VP8LBitWriter* const bw) {
798 const int width = pic->width - 1;
799 const int height = pic->height - 1;
800 assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
802 VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, width);
803 VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, height);
804 return !bw->error_;
807 static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
808 VP8LWriteBits(bw, 1, has_alpha);
809 VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION);
810 return !bw->error_;
813 static WebPEncodingError WriteImage(const WebPPicture* const pic,
814 VP8LBitWriter* const bw,
815 size_t* const coded_size) {
816 WebPEncodingError err = VP8_ENC_OK;
817 const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
818 const size_t webpll_size = VP8LBitWriterNumBytes(bw);
819 const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
820 const size_t pad = vp8l_size & 1;
821 const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
823 err = WriteRiffHeader(pic, riff_size, vp8l_size);
824 if (err != VP8_ENC_OK) goto Error;
826 if (!pic->writer(webpll_data, webpll_size, pic)) {
827 err = VP8_ENC_ERROR_BAD_WRITE;
828 goto Error;
831 if (pad) {
832 const uint8_t pad_byte[1] = { 0 };
833 if (!pic->writer(pad_byte, 1, pic)) {
834 err = VP8_ENC_ERROR_BAD_WRITE;
835 goto Error;
838 *coded_size = CHUNK_HEADER_SIZE + riff_size;
839 return VP8_ENC_OK;
841 Error:
842 return err;
845 // -----------------------------------------------------------------------------
847 // Allocates the memory for argb (W x H) buffer, 2 rows of context for
848 // prediction and transform data.
849 static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
850 int width, int height) {
851 WebPEncodingError err = VP8_ENC_OK;
852 const int tile_size = 1 << enc->transform_bits_;
853 const uint64_t image_size = width * height;
854 const uint64_t argb_scratch_size = tile_size * width + width;
855 const int transform_data_size =
856 VP8LSubSampleSize(width, enc->transform_bits_) *
857 VP8LSubSampleSize(height, enc->transform_bits_);
858 const uint64_t total_size =
859 image_size + argb_scratch_size + (uint64_t)transform_data_size;
860 uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem));
861 if (mem == NULL) {
862 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
863 goto Error;
865 enc->argb_ = mem;
866 mem += image_size;
867 enc->argb_scratch_ = mem;
868 mem += argb_scratch_size;
869 enc->transform_data_ = mem;
870 enc->current_width_ = width;
872 Error:
873 return err;
876 static void ApplyPalette(uint32_t* src, uint32_t* dst,
877 uint32_t src_stride, uint32_t dst_stride,
878 const uint32_t* palette, int palette_size,
879 int width, int height, int xbits, uint8_t* row) {
880 int i, x, y;
881 int use_LUT = 1;
882 for (i = 0; i < palette_size; ++i) {
883 if ((palette[i] & 0xffff00ffu) != 0) {
884 use_LUT = 0;
885 break;
889 if (use_LUT) {
890 uint8_t inv_palette[MAX_PALETTE_SIZE] = { 0 };
891 for (i = 0; i < palette_size; ++i) {
892 const int color = (palette[i] >> 8) & 0xff;
893 inv_palette[color] = i;
895 for (y = 0; y < height; ++y) {
896 for (x = 0; x < width; ++x) {
897 const int color = (src[x] >> 8) & 0xff;
898 row[x] = inv_palette[color];
900 VP8LBundleColorMap(row, width, xbits, dst);
901 src += src_stride;
902 dst += dst_stride;
904 } else {
905 // Use 1 pixel cache for ARGB pixels.
906 uint32_t last_pix = palette[0];
907 int last_idx = 0;
908 for (y = 0; y < height; ++y) {
909 for (x = 0; x < width; ++x) {
910 const uint32_t pix = src[x];
911 if (pix != last_pix) {
912 for (i = 0; i < palette_size; ++i) {
913 if (pix == palette[i]) {
914 last_idx = i;
915 last_pix = pix;
916 break;
920 row[x] = last_idx;
922 VP8LBundleColorMap(row, width, xbits, dst);
923 src += src_stride;
924 dst += dst_stride;
929 // Note: Expects "enc->palette_" to be set properly.
930 // Also, "enc->palette_" will be modified after this call and should not be used
931 // later.
932 static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
933 VP8LEncoder* const enc, int quality) {
934 WebPEncodingError err = VP8_ENC_OK;
935 int i;
936 const WebPPicture* const pic = enc->pic_;
937 uint32_t* src = pic->argb;
938 uint32_t* dst;
939 const int width = pic->width;
940 const int height = pic->height;
941 uint32_t* const palette = enc->palette_;
942 const int palette_size = enc->palette_size_;
943 uint8_t* row = NULL;
944 int xbits;
946 // Replace each input pixel by corresponding palette index.
947 // This is done line by line.
948 if (palette_size <= 4) {
949 xbits = (palette_size <= 2) ? 3 : 2;
950 } else {
951 xbits = (palette_size <= 16) ? 1 : 0;
954 err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
955 if (err != VP8_ENC_OK) goto Error;
956 dst = enc->argb_;
958 row = (uint8_t*)WebPSafeMalloc(width, sizeof(*row));
959 if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
961 ApplyPalette(src, dst, pic->argb_stride, enc->current_width_,
962 palette, palette_size, width, height, xbits, row);
964 // Save palette to bitstream.
965 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
966 VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM);
967 assert(palette_size >= 1);
968 VP8LWriteBits(bw, 8, palette_size - 1);
969 for (i = palette_size - 1; i >= 1; --i) {
970 palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
972 err = EncodeImageNoHuffman(bw, palette, &enc->hash_chain_, enc->refs_,
973 palette_size, 1, quality);
975 Error:
976 WebPSafeFree(row);
977 return err;
980 // -----------------------------------------------------------------------------
982 static int GetHistoBits(int method, int use_palette, int width, int height) {
983 const int hist_size = VP8LGetHistogramSize(MAX_COLOR_CACHE_BITS);
984 // Make tile size a function of encoding method (Range: 0 to 6).
985 int histo_bits = (use_palette ? 9 : 7) - method;
986 while (1) {
987 const int huff_image_size = VP8LSubSampleSize(width, histo_bits) *
988 VP8LSubSampleSize(height, histo_bits);
989 if ((uint64_t)huff_image_size * hist_size <= MAX_HUFF_IMAGE_SIZE) break;
990 ++histo_bits;
992 return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
993 (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
996 static int GetTransformBits(int method, int histo_bits) {
997 const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5;
998 return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
1001 static int GetCacheBits(float quality) {
1002 return (quality <= 25.f) ? 0 : 7;
1005 static void FinishEncParams(VP8LEncoder* const enc) {
1006 const WebPConfig* const config = enc->config_;
1007 const WebPPicture* const pic = enc->pic_;
1008 const int method = config->method;
1009 const float quality = config->quality;
1010 const int use_palette = enc->use_palette_;
1011 enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height);
1012 enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_);
1013 enc->cache_bits_ = GetCacheBits(quality);
1016 // -----------------------------------------------------------------------------
1017 // VP8LEncoder
1019 static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
1020 const WebPPicture* const picture) {
1021 VP8LEncoder* const enc = (VP8LEncoder*)WebPSafeCalloc(1ULL, sizeof(*enc));
1022 if (enc == NULL) {
1023 WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
1024 return NULL;
1026 enc->config_ = config;
1027 enc->pic_ = picture;
1029 VP8LDspInit();
1031 return enc;
1034 static void VP8LEncoderDelete(VP8LEncoder* enc) {
1035 if (enc != NULL) {
1036 VP8LHashChainClear(&enc->hash_chain_);
1037 VP8LBackwardRefsClear(&enc->refs_[0]);
1038 VP8LBackwardRefsClear(&enc->refs_[1]);
1039 WebPSafeFree(enc->argb_);
1040 WebPSafeFree(enc);
1044 // -----------------------------------------------------------------------------
1045 // Main call
1047 WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
1048 const WebPPicture* const picture,
1049 VP8LBitWriter* const bw) {
1050 WebPEncodingError err = VP8_ENC_OK;
1051 const int quality = (int)config->quality;
1052 const int width = picture->width;
1053 const int height = picture->height;
1054 VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
1055 const size_t byte_position = VP8LBitWriterNumBytes(bw);
1057 if (enc == NULL) {
1058 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1059 goto Error;
1062 // ---------------------------------------------------------------------------
1063 // Analyze image (entropy, num_palettes etc)
1065 if (!AnalyzeAndInit(enc, config->image_hint)) {
1066 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1067 goto Error;
1070 FinishEncParams(enc);
1072 if (enc->use_palette_) {
1073 err = EncodePalette(bw, enc, quality);
1074 if (err != VP8_ENC_OK) goto Error;
1075 // Color cache is disabled for palette.
1076 enc->cache_bits_ = 0;
1079 // In case image is not packed.
1080 if (enc->argb_ == NULL) {
1081 int y;
1082 err = AllocateTransformBuffer(enc, width, height);
1083 if (err != VP8_ENC_OK) goto Error;
1084 assert(enc->argb_ != NULL);
1085 for (y = 0; y < height; ++y) {
1086 memcpy(enc->argb_ + y * width,
1087 picture->argb + y * picture->argb_stride,
1088 width * sizeof(*enc->argb_));
1090 enc->current_width_ = width;
1093 // ---------------------------------------------------------------------------
1094 // Apply transforms and write transform data.
1096 err = EvalAndApplySubtractGreen(enc, enc->current_width_, height, bw);
1097 if (err != VP8_ENC_OK) goto Error;
1099 if (enc->use_predict_) {
1100 err = ApplyPredictFilter(enc, enc->current_width_, height, quality, bw);
1101 if (err != VP8_ENC_OK) goto Error;
1104 if (enc->use_cross_color_) {
1105 err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw);
1106 if (err != VP8_ENC_OK) goto Error;
1109 VP8LWriteBits(bw, 1, !TRANSFORM_PRESENT); // No more transforms.
1111 // ---------------------------------------------------------------------------
1112 // Estimate the color cache size.
1114 if (enc->cache_bits_ > 0) {
1115 if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
1116 height, quality, &enc->hash_chain_,
1117 &enc->refs_[0], &enc->cache_bits_)) {
1118 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1119 goto Error;
1123 // ---------------------------------------------------------------------------
1124 // Encode and write the transformed image.
1126 err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
1127 enc->current_width_, height, quality,
1128 enc->cache_bits_, enc->histo_bits_);
1129 if (err != VP8_ENC_OK) goto Error;
1131 if (picture->stats != NULL) {
1132 WebPAuxStats* const stats = picture->stats;
1133 stats->lossless_features = 0;
1134 if (enc->use_predict_) stats->lossless_features |= 1;
1135 if (enc->use_cross_color_) stats->lossless_features |= 2;
1136 if (enc->use_subtract_green_) stats->lossless_features |= 4;
1137 if (enc->use_palette_) stats->lossless_features |= 8;
1138 stats->histogram_bits = enc->histo_bits_;
1139 stats->transform_bits = enc->transform_bits_;
1140 stats->cache_bits = enc->cache_bits_;
1141 stats->palette_size = enc->palette_size_;
1142 stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position);
1145 Error:
1146 VP8LEncoderDelete(enc);
1147 return err;
1150 int VP8LEncodeImage(const WebPConfig* const config,
1151 const WebPPicture* const picture) {
1152 int width, height;
1153 int has_alpha;
1154 size_t coded_size;
1155 int percent = 0;
1156 int initial_size;
1157 WebPEncodingError err = VP8_ENC_OK;
1158 VP8LBitWriter bw;
1160 if (picture == NULL) return 0;
1162 if (config == NULL || picture->argb == NULL) {
1163 err = VP8_ENC_ERROR_NULL_PARAMETER;
1164 WebPEncodingSetError(picture, err);
1165 return 0;
1168 width = picture->width;
1169 height = picture->height;
1170 // Initialize BitWriter with size corresponding to 16 bpp to photo images and
1171 // 8 bpp for graphical images.
1172 initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
1173 width * height : width * height * 2;
1174 if (!VP8LBitWriterInit(&bw, initial_size)) {
1175 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1176 goto Error;
1179 if (!WebPReportProgress(picture, 1, &percent)) {
1180 UserAbort:
1181 err = VP8_ENC_ERROR_USER_ABORT;
1182 goto Error;
1184 // Reset stats (for pure lossless coding)
1185 if (picture->stats != NULL) {
1186 WebPAuxStats* const stats = picture->stats;
1187 memset(stats, 0, sizeof(*stats));
1188 stats->PSNR[0] = 99.f;
1189 stats->PSNR[1] = 99.f;
1190 stats->PSNR[2] = 99.f;
1191 stats->PSNR[3] = 99.f;
1192 stats->PSNR[4] = 99.f;
1195 // Write image size.
1196 if (!WriteImageSize(picture, &bw)) {
1197 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1198 goto Error;
1201 has_alpha = WebPPictureHasTransparency(picture);
1202 // Write the non-trivial Alpha flag and lossless version.
1203 if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
1204 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1205 goto Error;
1208 if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
1210 // Encode main image stream.
1211 err = VP8LEncodeStream(config, picture, &bw);
1212 if (err != VP8_ENC_OK) goto Error;
1214 // TODO(skal): have a fine-grained progress report in VP8LEncodeStream().
1215 if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
1217 // Finish the RIFF chunk.
1218 err = WriteImage(picture, &bw, &coded_size);
1219 if (err != VP8_ENC_OK) goto Error;
1221 if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
1223 // Save size.
1224 if (picture->stats != NULL) {
1225 picture->stats->coded_size += (int)coded_size;
1226 picture->stats->lossless_size = (int)coded_size;
1229 if (picture->extra_info != NULL) {
1230 const int mb_w = (width + 15) >> 4;
1231 const int mb_h = (height + 15) >> 4;
1232 memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
1235 Error:
1236 if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1237 VP8LBitWriterDestroy(&bw);
1238 if (err != VP8_ENC_OK) {
1239 WebPEncodingSetError(picture, err);
1240 return 0;
1242 return 1;
1245 //------------------------------------------------------------------------------