1 // Copyright 2012 Google Inc. All Rights Reserved.
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 // -----------------------------------------------------------------------------
10 // main entry for the lossless encoder.
12 // Author: Vikas Arora (vikaas.arora@gmail.com)
19 #include "./backward_references.h"
20 #include "./vp8enci.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 // -----------------------------------------------------------------------------
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
;
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
) {
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
) {
63 key
= (kHashMul
* last_pix
) >> PALETTE_KEY_RIGHT_SHIFT
;
66 colors
[key
] = last_pix
;
69 if (num_colors
> MAX_PALETTE_SIZE
) {
73 } else if (colors
[key
] == last_pix
) {
74 // The color is already there.
77 // Some other color sits there.
78 // Do linear conflict resolution.
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()?
89 for (i
= 0; i
< (int)(sizeof(in_use
) / sizeof(in_use
[0])); ++i
) {
91 palette
[num_colors
] = colors
[i
];
96 qsort(palette
, num_colors
, sizeof(*palette
), CompareColors
);
97 *palette_size
= num_colors
;
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
) {
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
]) {
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],
132 *nonpredicted_bits
= VP8LHistogramEstimateBitsBulk(histo_set
->histograms
[0]);
133 *predicted_bits
= VP8LHistogramEstimateBitsBulk(histo_set
->histograms
[1]);
134 VP8LFreeHistogramSet(histo_set
);
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
);
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;
162 double non_pred_entropy
, pred_entropy
;
163 if (!AnalyzeEntropy(pic
->argb
, width
, height
, pic
->argb_stride
,
164 &non_pred_entropy
, &pred_entropy
)) {
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
);
184 // Returns false in case of memory error.
185 static int GetHuffBitLengthsAndCodes(
186 const VP8LHistogramSet
* const histogram_image
,
187 HuffmanTreeCode
* const huffman_codes
) {
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.
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
;
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
,
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);
249 WebPSafeFree(huff_tree
);
250 WebPSafeFree(buf_rle
);
252 WebPSafeFree(mem_buf
);
253 memset(huffman_codes
, 0, 5 * histogram_image_size
* sizeof(*huffman_codes
));
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
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) {
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
) {
284 for (k
= 0; k
< huffman_code
->num_symbols
; ++k
) {
285 if (huffman_code
->code_lengths
[k
] != 0) {
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
) {
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
]);
307 VP8LWriteBits(bw
, 2, extra_bits
);
310 VP8LWriteBits(bw
, 3, extra_bits
);
313 VP8LWriteBits(bw
, 7, extra_bits
);
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
;
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 };
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
;
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
];
360 trailing_zero_bits
+= 3;
361 } else if (ix
== 18) {
362 trailing_zero_bits
+= 7;
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
) {
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
;
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]);
411 VP8LWriteBits(bw
, 1, 1);
412 VP8LWriteBits(bw
, 8, symbols
[0]);
415 VP8LWriteBits(bw
, 8, symbols
[1]);
418 StoreFullHuffmanCode(bw
, huff_tree
, tokens
, huffman_code
);
422 static void WriteHuffmanCode(VP8LBitWriter
* const bw
,
423 const HuffmanTreeCode
* const code
,
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.
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 };
453 for (k
= 0; k
< 4; ++k
) {
454 const int code
= PixOrCopyLiteral(v
, order
[k
]);
455 WriteHuffmanCode(bw
, codes
+ k
, code
);
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
);
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
,
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
;
502 // Calculate backward references from ARGB image.
503 refs
= VP8LGetBackwardReferences(width
, height
, argb
, quality
, 0, 1,
504 hash_chain
, refs_array
);
506 err
= VP8_ENC_ERROR_OUT_OF_MEMORY
;
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
;
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
;
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
,
548 WebPSafeFree(tokens
);
549 WebPSafeFree(huff_tree
);
550 VP8LFreeHistogramSet(histogram_image
);
551 WebPSafeFree(huffman_codes
[0].codes
);
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
,
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
);
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
)) {
599 // Build histogram image and symbols from backward references.
600 if (!VP8LGetHistoImageSymbols(width
, height
, &refs
,
601 quality
, histogram_bits
, cache_bits
,
603 histogram_symbols
)) {
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
)) {
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
));
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
),
650 WebPSafeFree(histogram_argb
);
651 if (err
!= VP8_ENC_OK
) goto Error
;
655 // Store Huffman codes.
659 huff_tree
= (HuffmanTree
*)WebPSafeMalloc(3ULL * CODE_LENGTH_CODES
,
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
,
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
);
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
);
696 // -----------------------------------------------------------------------------
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_
) {
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
);
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
,
759 static WebPEncodingError
ApplyCrossColorFilter(const VP8LEncoder
* const enc
,
760 int width
, int height
,
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
,
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
;
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
);
807 static int WriteRealAlphaAndVersion(VP8LBitWriter
* const bw
, int has_alpha
) {
808 VP8LWriteBits(bw
, 1, has_alpha
);
809 VP8LWriteBits(bw
, VP8L_VERSION_BITS
, VP8L_VERSION
);
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
;
832 const uint8_t pad_byte
[1] = { 0 };
833 if (!pic
->writer(pad_byte
, 1, pic
)) {
834 err
= VP8_ENC_ERROR_BAD_WRITE
;
838 *coded_size
= CHUNK_HEADER_SIZE
+ riff_size
;
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
));
862 err
= VP8_ENC_ERROR_OUT_OF_MEMORY
;
867 enc
->argb_scratch_
= mem
;
868 mem
+= argb_scratch_size
;
869 enc
->transform_data_
= mem
;
870 enc
->current_width_
= width
;
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
) {
882 for (i
= 0; i
< palette_size
; ++i
) {
883 if ((palette
[i
] & 0xffff00ffu
) != 0) {
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
);
905 // Use 1 pixel cache for ARGB pixels.
906 uint32_t last_pix
= palette
[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
]) {
922 VP8LBundleColorMap(row
, width
, xbits
, dst
);
929 // Note: Expects "enc->palette_" to be set properly.
930 // Also, "enc->palette_" will be modified after this call and should not be used
932 static WebPEncodingError
EncodePalette(VP8LBitWriter
* const bw
,
933 VP8LEncoder
* const enc
, int quality
) {
934 WebPEncodingError err
= VP8_ENC_OK
;
936 const WebPPicture
* const pic
= enc
->pic_
;
937 uint32_t* src
= pic
->argb
;
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_
;
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;
951 xbits
= (palette_size
<= 16) ? 1 : 0;
954 err
= AllocateTransformBuffer(enc
, VP8LSubSampleSize(width
, xbits
), height
);
955 if (err
!= VP8_ENC_OK
) goto Error
;
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
);
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
;
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;
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 // -----------------------------------------------------------------------------
1019 static VP8LEncoder
* VP8LEncoderNew(const WebPConfig
* const config
,
1020 const WebPPicture
* const picture
) {
1021 VP8LEncoder
* const enc
= (VP8LEncoder
*)WebPSafeCalloc(1ULL, sizeof(*enc
));
1023 WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
1026 enc
->config_
= config
;
1027 enc
->pic_
= picture
;
1034 static void VP8LEncoderDelete(VP8LEncoder
* enc
) {
1036 VP8LHashChainClear(&enc
->hash_chain_
);
1037 VP8LBackwardRefsClear(&enc
->refs_
[0]);
1038 VP8LBackwardRefsClear(&enc
->refs_
[1]);
1039 WebPSafeFree(enc
->argb_
);
1044 // -----------------------------------------------------------------------------
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
);
1058 err
= VP8_ENC_ERROR_OUT_OF_MEMORY
;
1062 // ---------------------------------------------------------------------------
1063 // Analyze image (entropy, num_palettes etc)
1065 if (!AnalyzeAndInit(enc
, config
->image_hint
)) {
1066 err
= VP8_ENC_ERROR_OUT_OF_MEMORY
;
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
) {
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
;
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
);
1146 VP8LEncoderDelete(enc
);
1150 int VP8LEncodeImage(const WebPConfig
* const config
,
1151 const WebPPicture
* const picture
) {
1157 WebPEncodingError err
= VP8_ENC_OK
;
1160 if (picture
== NULL
) return 0;
1162 if (config
== NULL
|| picture
->argb
== NULL
) {
1163 err
= VP8_ENC_ERROR_NULL_PARAMETER
;
1164 WebPEncodingSetError(picture
, err
);
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
;
1179 if (!WebPReportProgress(picture
, 1, &percent
)) {
1181 err
= VP8_ENC_ERROR_USER_ABORT
;
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
;
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
;
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
;
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
));
1236 if (bw
.error_
) err
= VP8_ENC_ERROR_OUT_OF_MEMORY
;
1237 VP8LBitWriterDestroy(&bw
);
1238 if (err
!= VP8_ENC_OK
) {
1239 WebPEncodingSetError(picture
, err
);
1245 //------------------------------------------------------------------------------