1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // This code is licensed under the same terms as WebM:
4 // Software License Agreement: http://www.webmproject.org/license/software/
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // main entry for the lossless encoder.
9 //
10 // Author: Vikas Arora (vikaas.arora@gmail.com)
11 //
12
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include "./backward_references.h"
18 #include "./vp8enci.h"
19 #include "./vp8li.h"
20 #include "../dsp/lossless.h"
21 #include "../utils/bit_writer.h"
22 #include "../utils/huffman_encode.h"
23 #include "../utils/utils.h"
24 #include "webp/format_constants.h"
25
26 #if defined(__cplusplus) || defined(c_plusplus)
27 extern "C" {
28 #endif
29
30 #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
31 #define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
32 #define MAX_COLORS_FOR_GRAPH 64
33
34 // -----------------------------------------------------------------------------
35 // Palette
36
CompareColors(const void * p1,const void * p2)37 static int CompareColors(const void* p1, const void* p2) {
38 const uint32_t a = *(const uint32_t*)p1;
39 const uint32_t b = *(const uint32_t*)p2;
40 return (a < b) ? -1 : (a > b) ? 1 : 0;
41 }
42
43 // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
44 // creates a palette and returns true, else returns false.
AnalyzeAndCreatePalette(const WebPPicture * const pic,uint32_t palette[MAX_PALETTE_SIZE],int * const palette_size)45 static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
46 uint32_t palette[MAX_PALETTE_SIZE],
47 int* const palette_size) {
48 int i, x, y, key;
49 int num_colors = 0;
50 uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 };
51 uint32_t colors[MAX_PALETTE_SIZE * 4];
52 static const uint32_t kHashMul = 0x1e35a7bd;
53 const uint32_t* argb = pic->argb;
54 const int width = pic->width;
55 const int height = pic->height;
56 uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
57
58 for (y = 0; y < height; ++y) {
59 for (x = 0; x < width; ++x) {
60 if (argb[x] == last_pix) {
61 continue;
62 }
63 last_pix = argb[x];
64 key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT;
65 while (1) {
66 if (!in_use[key]) {
67 colors[key] = last_pix;
68 in_use[key] = 1;
69 ++num_colors;
70 if (num_colors > MAX_PALETTE_SIZE) {
71 return 0;
72 }
73 break;
74 } else if (colors[key] == last_pix) {
75 // The color is already there.
76 break;
77 } else {
78 // Some other color sits there.
79 // Do linear conflict resolution.
80 ++key;
81 key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer.
82 }
83 }
84 }
85 argb += pic->argb_stride;
86 }
87
88 // TODO(skal): could we reuse in_use[] to speed up ApplyPalette()?
89 num_colors = 0;
90 for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) {
91 if (in_use[i]) {
92 palette[num_colors] = colors[i];
93 ++num_colors;
94 }
95 }
96
97 qsort(palette, num_colors, sizeof(*palette), CompareColors);
98 *palette_size = num_colors;
99 return 1;
100 }
101
AnalyzeEntropy(const uint32_t * argb,int width,int height,int argb_stride,double * const nonpredicted_bits,double * const predicted_bits)102 static int AnalyzeEntropy(const uint32_t* argb,
103 int width, int height, int argb_stride,
104 double* const nonpredicted_bits,
105 double* const predicted_bits) {
106 int x, y;
107 const uint32_t* last_line = NULL;
108 uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
109
110 VP8LHistogram* nonpredicted = NULL;
111 VP8LHistogram* predicted =
112 (VP8LHistogram*)malloc(2 * sizeof(*predicted));
113 if (predicted == NULL) return 0;
114 nonpredicted = predicted + 1;
115
116 VP8LHistogramInit(predicted, 0);
117 VP8LHistogramInit(nonpredicted, 0);
118 for (y = 0; y < height; ++y) {
119 for (x = 0; x < width; ++x) {
120 const uint32_t pix = argb[x];
121 const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
122 if (pix_diff == 0) continue;
123 if (last_line != NULL && pix == last_line[x]) {
124 continue;
125 }
126 last_pix = pix;
127 {
128 const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
129 const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
130 VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token);
131 VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token);
132 }
133 }
134 last_line = argb;
135 argb += argb_stride;
136 }
137 *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
138 *predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
139 free(predicted);
140 return 1;
141 }
142
VP8LEncAnalyze(VP8LEncoder * const enc,WebPImageHint image_hint)143 static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
144 const WebPPicture* const pic = enc->pic_;
145 assert(pic != NULL && pic->argb != NULL);
146
147 enc->use_palette_ =
148 AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
149
150 if (image_hint == WEBP_HINT_GRAPH) {
151 if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
152 enc->use_palette_ = 0;
153 }
154 }
155
156 if (!enc->use_palette_) {
157 if (image_hint == WEBP_HINT_PHOTO) {
158 enc->use_predict_ = 1;
159 enc->use_cross_color_ = 1;
160 } else {
161 double non_pred_entropy, pred_entropy;
162 if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
163 &non_pred_entropy, &pred_entropy)) {
164 return 0;
165 }
166 if (pred_entropy < 0.95 * non_pred_entropy) {
167 enc->use_predict_ = 1;
168 // TODO(vikasa): Observed some correlation of cross_color transform with
169 // predict. Need to investigate this further and add separate heuristic
170 // for setting use_cross_color flag.
171 enc->use_cross_color_ = 1;
172 }
173 }
174 }
175
176 return 1;
177 }
178
GetHuffBitLengthsAndCodes(const VP8LHistogramSet * const histogram_image,HuffmanTreeCode * const huffman_codes)179 static int GetHuffBitLengthsAndCodes(
180 const VP8LHistogramSet* const histogram_image,
181 HuffmanTreeCode* const huffman_codes) {
182 int i, k;
183 int ok = 1;
184 uint64_t total_length_size = 0;
185 uint8_t* mem_buf = NULL;
186 const int histogram_image_size = histogram_image->size;
187
188 // Iterate over all histograms and get the aggregate number of codes used.
189 for (i = 0; i < histogram_image_size; ++i) {
190 const VP8LHistogram* const histo = histogram_image->histograms[i];
191 HuffmanTreeCode* const codes = &huffman_codes[5 * i];
192 for (k = 0; k < 5; ++k) {
193 const int num_symbols = (k == 0) ? VP8LHistogramNumCodes(histo)
194 : (k == 4) ? NUM_DISTANCE_CODES
195 : 256;
196 codes[k].num_symbols = num_symbols;
197 total_length_size += num_symbols;
198 }
199 }
200
201 // Allocate and Set Huffman codes.
202 {
203 uint16_t* codes;
204 uint8_t* lengths;
205 mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
206 sizeof(*lengths) + sizeof(*codes));
207 if (mem_buf == NULL) {
208 ok = 0;
209 goto End;
210 }
211 codes = (uint16_t*)mem_buf;
212 lengths = (uint8_t*)&codes[total_length_size];
213 for (i = 0; i < 5 * histogram_image_size; ++i) {
214 const int bit_length = huffman_codes[i].num_symbols;
215 huffman_codes[i].codes = codes;
216 huffman_codes[i].code_lengths = lengths;
217 codes += bit_length;
218 lengths += bit_length;
219 }
220 }
221
222 // Create Huffman trees.
223 for (i = 0; i < histogram_image_size; ++i) {
224 HuffmanTreeCode* const codes = &huffman_codes[5 * i];
225 VP8LHistogram* const histo = histogram_image->histograms[i];
226 ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0);
227 ok = ok && VP8LCreateHuffmanTree(histo->red_, 15, codes + 1);
228 ok = ok && VP8LCreateHuffmanTree(histo->blue_, 15, codes + 2);
229 ok = ok && VP8LCreateHuffmanTree(histo->alpha_, 15, codes + 3);
230 ok = ok && VP8LCreateHuffmanTree(histo->distance_, 15, codes + 4);
231 }
232
233 End:
234 if (!ok) free(mem_buf);
235 return ok;
236 }
237
StoreHuffmanTreeOfHuffmanTreeToBitMask(VP8LBitWriter * const bw,const uint8_t * code_length_bitdepth)238 static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
239 VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
240 // RFC 1951 will calm you down if you are worried about this funny sequence.
241 // This sequence is tuned from that, but more weighted for lower symbol count,
242 // and more spiking histograms.
243 static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
244 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
245 };
246 int i;
247 // Throw away trailing zeros:
248 int codes_to_store = CODE_LENGTH_CODES;
249 for (; codes_to_store > 4; --codes_to_store) {
250 if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
251 break;
252 }
253 }
254 VP8LWriteBits(bw, 4, codes_to_store - 4);
255 for (i = 0; i < codes_to_store; ++i) {
256 VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]);
257 }
258 }
259
ClearHuffmanTreeIfOnlyOneSymbol(HuffmanTreeCode * const huffman_code)260 static void ClearHuffmanTreeIfOnlyOneSymbol(
261 HuffmanTreeCode* const huffman_code) {
262 int k;
263 int count = 0;
264 for (k = 0; k < huffman_code->num_symbols; ++k) {
265 if (huffman_code->code_lengths[k] != 0) {
266 ++count;
267 if (count > 1) return;
268 }
269 }
270 for (k = 0; k < huffman_code->num_symbols; ++k) {
271 huffman_code->code_lengths[k] = 0;
272 huffman_code->codes[k] = 0;
273 }
274 }
275
StoreHuffmanTreeToBitMask(VP8LBitWriter * const bw,const HuffmanTreeToken * const tokens,const int num_tokens,const HuffmanTreeCode * const huffman_code)276 static void StoreHuffmanTreeToBitMask(
277 VP8LBitWriter* const bw,
278 const HuffmanTreeToken* const tokens, const int num_tokens,
279 const HuffmanTreeCode* const huffman_code) {
280 int i;
281 for (i = 0; i < num_tokens; ++i) {
282 const int ix = tokens[i].code;
283 const int extra_bits = tokens[i].extra_bits;
284 VP8LWriteBits(bw, huffman_code->code_lengths[ix], huffman_code->codes[ix]);
285 switch (ix) {
286 case 16:
287 VP8LWriteBits(bw, 2, extra_bits);
288 break;
289 case 17:
290 VP8LWriteBits(bw, 3, extra_bits);
291 break;
292 case 18:
293 VP8LWriteBits(bw, 7, extra_bits);
294 break;
295 }
296 }
297 }
298
StoreFullHuffmanCode(VP8LBitWriter * const bw,const HuffmanTreeCode * const tree)299 static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
300 const HuffmanTreeCode* const tree) {
301 int ok = 0;
302 uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
303 uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
304 const int max_tokens = tree->num_symbols;
305 int num_tokens;
306 HuffmanTreeCode huffman_code;
307 HuffmanTreeToken* const tokens =
308 (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens));
309 if (tokens == NULL) return 0;
310
311 huffman_code.num_symbols = CODE_LENGTH_CODES;
312 huffman_code.code_lengths = code_length_bitdepth;
313 huffman_code.codes = code_length_bitdepth_symbols;
314
315 VP8LWriteBits(bw, 1, 0);
316 num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
317 {
318 int histogram[CODE_LENGTH_CODES] = { 0 };
319 int i;
320 for (i = 0; i < num_tokens; ++i) {
321 ++histogram[tokens[i].code];
322 }
323
324 if (!VP8LCreateHuffmanTree(histogram, 7, &huffman_code)) {
325 goto End;
326 }
327 }
328
329 StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
330 ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
331 {
332 int trailing_zero_bits = 0;
333 int trimmed_length = num_tokens;
334 int write_trimmed_length;
335 int length;
336 int i = num_tokens;
337 while (i-- > 0) {
338 const int ix = tokens[i].code;
339 if (ix == 0 || ix == 17 || ix == 18) {
340 --trimmed_length; // discount trailing zeros
341 trailing_zero_bits += code_length_bitdepth[ix];
342 if (ix == 17) {
343 trailing_zero_bits += 3;
344 } else if (ix == 18) {
345 trailing_zero_bits += 7;
346 }
347 } else {
348 break;
349 }
350 }
351 write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
352 length = write_trimmed_length ? trimmed_length : num_tokens;
353 VP8LWriteBits(bw, 1, write_trimmed_length);
354 if (write_trimmed_length) {
355 const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
356 const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2;
357 VP8LWriteBits(bw, 3, nbitpairs - 1);
358 assert(trimmed_length >= 2);
359 VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
360 }
361 StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
362 }
363 ok = 1;
364 End:
365 free(tokens);
366 return ok;
367 }
368
StoreHuffmanCode(VP8LBitWriter * const bw,const HuffmanTreeCode * const huffman_code)369 static int StoreHuffmanCode(VP8LBitWriter* const bw,
370 const HuffmanTreeCode* const huffman_code) {
371 int i;
372 int count = 0;
373 int symbols[2] = { 0, 0 };
374 const int kMaxBits = 8;
375 const int kMaxSymbol = 1 << kMaxBits;
376
377 // Check whether it's a small tree.
378 for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
379 if (huffman_code->code_lengths[i] != 0) {
380 if (count < 2) symbols[count] = i;
381 ++count;
382 }
383 }
384
385 if (count == 0) { // emit minimal tree for empty cases
386 // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
387 VP8LWriteBits(bw, 4, 0x01);
388 return 1;
389 } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
390 VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
391 VP8LWriteBits(bw, 1, count - 1);
392 if (symbols[0] <= 1) {
393 VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value.
394 VP8LWriteBits(bw, 1, symbols[0]);
395 } else {
396 VP8LWriteBits(bw, 1, 1);
397 VP8LWriteBits(bw, 8, symbols[0]);
398 }
399 if (count == 2) {
400 VP8LWriteBits(bw, 8, symbols[1]);
401 }
402 return 1;
403 } else {
404 return StoreFullHuffmanCode(bw, huffman_code);
405 }
406 }
407
WriteHuffmanCode(VP8LBitWriter * const bw,const HuffmanTreeCode * const code,int index)408 static void WriteHuffmanCode(VP8LBitWriter* const bw,
409 const HuffmanTreeCode* const code, int index) {
410 const int depth = code->code_lengths[index];
411 const int symbol = code->codes[index];
412 VP8LWriteBits(bw, depth, symbol);
413 }
414
StoreImageToBitMask(VP8LBitWriter * const bw,int width,int histo_bits,const VP8LBackwardRefs * const refs,const uint16_t * histogram_symbols,const HuffmanTreeCode * const huffman_codes)415 static void StoreImageToBitMask(
416 VP8LBitWriter* const bw, int width, int histo_bits,
417 const VP8LBackwardRefs* const refs,
418 const uint16_t* histogram_symbols,
419 const HuffmanTreeCode* const huffman_codes) {
420 // x and y trace the position in the image.
421 int x = 0;
422 int y = 0;
423 const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
424 int i;
425 for (i = 0; i < refs->size; ++i) {
426 const PixOrCopy* const v = &refs->refs[i];
427 const int histogram_ix = histogram_symbols[histo_bits ?
428 (y >> histo_bits) * histo_xsize +
429 (x >> histo_bits) : 0];
430 const HuffmanTreeCode* const codes = huffman_codes + 5 * histogram_ix;
431 if (PixOrCopyIsCacheIdx(v)) {
432 const int code = PixOrCopyCacheIdx(v);
433 const int literal_ix = 256 + NUM_LENGTH_CODES + code;
434 WriteHuffmanCode(bw, codes, literal_ix);
435 } else if (PixOrCopyIsLiteral(v)) {
436 static const int order[] = { 1, 2, 0, 3 };
437 int k;
438 for (k = 0; k < 4; ++k) {
439 const int code = PixOrCopyLiteral(v, order[k]);
440 WriteHuffmanCode(bw, codes + k, code);
441 }
442 } else {
443 int bits, n_bits;
444 int code, distance;
445
446 PrefixEncode(v->len, &code, &n_bits, &bits);
447 WriteHuffmanCode(bw, codes, 256 + code);
448 VP8LWriteBits(bw, n_bits, bits);
449
450 distance = PixOrCopyDistance(v);
451 PrefixEncode(distance, &code, &n_bits, &bits);
452 WriteHuffmanCode(bw, codes + 4, code);
453 VP8LWriteBits(bw, n_bits, bits);
454 }
455 x += PixOrCopyLength(v);
456 while (x >= width) {
457 x -= width;
458 ++y;
459 }
460 }
461 }
462
463 // Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
EncodeImageNoHuffman(VP8LBitWriter * const bw,const uint32_t * const argb,int width,int height,int quality)464 static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
465 const uint32_t* const argb,
466 int width, int height, int quality) {
467 int i;
468 int ok = 0;
469 VP8LBackwardRefs refs;
470 HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
471 const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
472 VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
473 if (histogram_image == NULL) return 0;
474
475 // Calculate backward references from ARGB image.
476 if (!VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, &refs)) {
477 goto Error;
478 }
479 // Build histogram image and symbols from backward references.
480 VP8LHistogramStoreRefs(&refs, histogram_image->histograms[0]);
481
482 // Create Huffman bit lengths and codes for each histogram image.
483 assert(histogram_image->size == 1);
484 if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
485 goto Error;
486 }
487
488 // No color cache, no Huffman image.
489 VP8LWriteBits(bw, 1, 0);
490
491 // Store Huffman codes.
492 for (i = 0; i < 5; ++i) {
493 HuffmanTreeCode* const codes = &huffman_codes[i];
494 if (!StoreHuffmanCode(bw, codes)) {
495 goto Error;
496 }
497 ClearHuffmanTreeIfOnlyOneSymbol(codes);
498 }
499
500 // Store actual literals.
501 StoreImageToBitMask(bw, width, 0, &refs, histogram_symbols, huffman_codes);
502 ok = 1;
503
504 Error:
505 free(histogram_image);
506 VP8LClearBackwardRefs(&refs);
507 free(huffman_codes[0].codes);
508 return ok;
509 }
510
EncodeImageInternal(VP8LBitWriter * const bw,const uint32_t * const argb,int width,int height,int quality,int cache_bits,int histogram_bits)511 static int EncodeImageInternal(VP8LBitWriter* const bw,
512 const uint32_t* const argb,
513 int width, int height, int quality,
514 int cache_bits, int histogram_bits) {
515 int ok = 0;
516 const int use_2d_locality = 1;
517 const int use_color_cache = (cache_bits > 0);
518 const uint32_t histogram_image_xysize =
519 VP8LSubSampleSize(width, histogram_bits) *
520 VP8LSubSampleSize(height, histogram_bits);
521 VP8LHistogramSet* histogram_image =
522 VP8LAllocateHistogramSet(histogram_image_xysize, 0);
523 int histogram_image_size = 0;
524 size_t bit_array_size = 0;
525 HuffmanTreeCode* huffman_codes = NULL;
526 VP8LBackwardRefs refs;
527 uint16_t* const histogram_symbols =
528 (uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
529 sizeof(*histogram_symbols));
530 assert(histogram_bits >= MIN_HUFFMAN_BITS);
531 assert(histogram_bits <= MAX_HUFFMAN_BITS);
532 if (histogram_image == NULL || histogram_symbols == NULL) goto Error;
533
534 // Calculate backward references from ARGB image.
535 if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits,
536 use_2d_locality, &refs)) {
537 goto Error;
538 }
539 // Build histogram image and symbols from backward references.
540 if (!VP8LGetHistoImageSymbols(width, height, &refs,
541 quality, histogram_bits, cache_bits,
542 histogram_image,
543 histogram_symbols)) {
544 goto Error;
545 }
546 // Create Huffman bit lengths and codes for each histogram image.
547 histogram_image_size = histogram_image->size;
548 bit_array_size = 5 * histogram_image_size;
549 huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
550 sizeof(*huffman_codes));
551 if (huffman_codes == NULL ||
552 !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
553 goto Error;
554 }
555
556 // Color Cache parameters.
557 VP8LWriteBits(bw, 1, use_color_cache);
558 if (use_color_cache) {
559 VP8LWriteBits(bw, 4, cache_bits);
560 }
561
562 // Huffman image + meta huffman.
563 {
564 const int write_histogram_image = (histogram_image_size > 1);
565 VP8LWriteBits(bw, 1, write_histogram_image);
566 if (write_histogram_image) {
567 uint32_t* const histogram_argb =
568 (uint32_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
569 sizeof(*histogram_argb));
570 int max_index = 0;
571 uint32_t i;
572 if (histogram_argb == NULL) goto Error;
573 for (i = 0; i < histogram_image_xysize; ++i) {
574 const int index = histogram_symbols[i] & 0xffff;
575 histogram_argb[i] = 0xff000000 | (index << 8);
576 if (index >= max_index) {
577 max_index = index + 1;
578 }
579 }
580 histogram_image_size = max_index;
581
582 VP8LWriteBits(bw, 3, histogram_bits - 2);
583 ok = EncodeImageNoHuffman(bw, histogram_argb,
584 VP8LSubSampleSize(width, histogram_bits),
585 VP8LSubSampleSize(height, histogram_bits),
586 quality);
587 free(histogram_argb);
588 if (!ok) goto Error;
589 }
590 }
591
592 // Store Huffman codes.
593 {
594 int i;
595 for (i = 0; i < 5 * histogram_image_size; ++i) {
596 HuffmanTreeCode* const codes = &huffman_codes[i];
597 if (!StoreHuffmanCode(bw, codes)) goto Error;
598 ClearHuffmanTreeIfOnlyOneSymbol(codes);
599 }
600 }
601 // Free combined histograms.
602 free(histogram_image);
603 histogram_image = NULL;
604
605 // Store actual literals.
606 StoreImageToBitMask(bw, width, histogram_bits, &refs,
607 histogram_symbols, huffman_codes);
608 ok = 1;
609
610 Error:
611 if (!ok) free(histogram_image);
612
613 VP8LClearBackwardRefs(&refs);
614 if (huffman_codes != NULL) {
615 free(huffman_codes->codes);
616 free(huffman_codes);
617 }
618 free(histogram_symbols);
619 return ok;
620 }
621
622 // -----------------------------------------------------------------------------
623 // Transforms
624
625 // Check if it would be a good idea to subtract green from red and blue. We
626 // only impact entropy in red/blue components, don't bother to look at others.
EvalAndApplySubtractGreen(VP8LEncoder * const enc,int width,int height,VP8LBitWriter * const bw)627 static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
628 int width, int height,
629 VP8LBitWriter* const bw) {
630 if (!enc->use_palette_) {
631 int i;
632 const uint32_t* const argb = enc->argb_;
633 double bit_cost_before, bit_cost_after;
634 VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo));
635 if (histo == NULL) return 0;
636
637 VP8LHistogramInit(histo, 1);
638 for (i = 0; i < width * height; ++i) {
639 const uint32_t c = argb[i];
640 ++histo->red_[(c >> 16) & 0xff];
641 ++histo->blue_[(c >> 0) & 0xff];
642 }
643 bit_cost_before = VP8LHistogramEstimateBits(histo);
644
645 VP8LHistogramInit(histo, 1);
646 for (i = 0; i < width * height; ++i) {
647 const uint32_t c = argb[i];
648 const int green = (c >> 8) & 0xff;
649 ++histo->red_[((c >> 16) - green) & 0xff];
650 ++histo->blue_[((c >> 0) - green) & 0xff];
651 }
652 bit_cost_after = VP8LHistogramEstimateBits(histo);
653 free(histo);
654
655 // Check if subtracting green yields low entropy.
656 enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
657 if (enc->use_subtract_green_) {
658 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
659 VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
660 VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
661 }
662 }
663 return 1;
664 }
665
ApplyPredictFilter(const VP8LEncoder * const enc,int width,int height,int quality,VP8LBitWriter * const bw)666 static int ApplyPredictFilter(const VP8LEncoder* const enc,
667 int width, int height, int quality,
668 VP8LBitWriter* const bw) {
669 const int pred_bits = enc->transform_bits_;
670 const int transform_width = VP8LSubSampleSize(width, pred_bits);
671 const int transform_height = VP8LSubSampleSize(height, pred_bits);
672
673 VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_,
674 enc->transform_data_);
675 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
676 VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
677 assert(pred_bits >= 2);
678 VP8LWriteBits(bw, 3, pred_bits - 2);
679 if (!EncodeImageNoHuffman(bw, enc->transform_data_,
680 transform_width, transform_height, quality)) {
681 return 0;
682 }
683 return 1;
684 }
685
ApplyCrossColorFilter(const VP8LEncoder * const enc,int width,int height,int quality,VP8LBitWriter * const bw)686 static int ApplyCrossColorFilter(const VP8LEncoder* const enc,
687 int width, int height, int quality,
688 VP8LBitWriter* const bw) {
689 const int ccolor_transform_bits = enc->transform_bits_;
690 const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
691 const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
692 const int step = (quality == 0) ? 32 : 8;
693
694 VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step,
695 enc->argb_, enc->transform_data_);
696 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
697 VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
698 assert(ccolor_transform_bits >= 2);
699 VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
700 if (!EncodeImageNoHuffman(bw, enc->transform_data_,
701 transform_width, transform_height, quality)) {
702 return 0;
703 }
704 return 1;
705 }
706
707 // -----------------------------------------------------------------------------
708
PutLE32(uint8_t * const data,uint32_t val)709 static void PutLE32(uint8_t* const data, uint32_t val) {
710 data[0] = (val >> 0) & 0xff;
711 data[1] = (val >> 8) & 0xff;
712 data[2] = (val >> 16) & 0xff;
713 data[3] = (val >> 24) & 0xff;
714 }
715
WriteRiffHeader(const WebPPicture * const pic,size_t riff_size,size_t vp8l_size)716 static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
717 size_t riff_size, size_t vp8l_size) {
718 uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
719 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
720 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
721 };
722 PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
723 PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
724 if (!pic->writer(riff, sizeof(riff), pic)) {
725 return VP8_ENC_ERROR_BAD_WRITE;
726 }
727 return VP8_ENC_OK;
728 }
729
WriteImageSize(const WebPPicture * const pic,VP8LBitWriter * const bw)730 static int WriteImageSize(const WebPPicture* const pic,
731 VP8LBitWriter* const bw) {
732 const int width = pic->width - 1;
733 const int height = pic->height - 1;
734 assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
735
736 VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, width);
737 VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, height);
738 return !bw->error_;
739 }
740
WriteRealAlphaAndVersion(VP8LBitWriter * const bw,int has_alpha)741 static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
742 VP8LWriteBits(bw, 1, has_alpha);
743 VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION);
744 return !bw->error_;
745 }
746
WriteImage(const WebPPicture * const pic,VP8LBitWriter * const bw,size_t * const coded_size)747 static WebPEncodingError WriteImage(const WebPPicture* const pic,
748 VP8LBitWriter* const bw,
749 size_t* const coded_size) {
750 WebPEncodingError err = VP8_ENC_OK;
751 const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
752 const size_t webpll_size = VP8LBitWriterNumBytes(bw);
753 const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
754 const size_t pad = vp8l_size & 1;
755 const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
756
757 err = WriteRiffHeader(pic, riff_size, vp8l_size);
758 if (err != VP8_ENC_OK) goto Error;
759
760 if (!pic->writer(webpll_data, webpll_size, pic)) {
761 err = VP8_ENC_ERROR_BAD_WRITE;
762 goto Error;
763 }
764
765 if (pad) {
766 const uint8_t pad_byte[1] = { 0 };
767 if (!pic->writer(pad_byte, 1, pic)) {
768 err = VP8_ENC_ERROR_BAD_WRITE;
769 goto Error;
770 }
771 }
772 *coded_size = CHUNK_HEADER_SIZE + riff_size;
773 return VP8_ENC_OK;
774
775 Error:
776 return err;
777 }
778
779 // -----------------------------------------------------------------------------
780
781 // Allocates the memory for argb (W x H) buffer, 2 rows of context for
782 // prediction and transform data.
AllocateTransformBuffer(VP8LEncoder * const enc,int width,int height)783 static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
784 int width, int height) {
785 WebPEncodingError err = VP8_ENC_OK;
786 const int tile_size = 1 << enc->transform_bits_;
787 const uint64_t image_size = width * height;
788 const uint64_t argb_scratch_size = tile_size * width + width;
789 const uint64_t transform_data_size =
790 (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) *
791 (uint64_t)VP8LSubSampleSize(height, enc->transform_bits_);
792 const uint64_t total_size =
793 image_size + argb_scratch_size + transform_data_size;
794 uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem));
795 if (mem == NULL) {
796 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
797 goto Error;
798 }
799 enc->argb_ = mem;
800 mem += image_size;
801 enc->argb_scratch_ = mem;
802 mem += argb_scratch_size;
803 enc->transform_data_ = mem;
804 enc->current_width_ = width;
805
806 Error:
807 return err;
808 }
809
810 // Bundles multiple (2, 4 or 8) pixels into a single pixel.
811 // Returns the new xsize.
BundleColorMap(const WebPPicture * const pic,int xbits,uint32_t * bundled_argb,int xs)812 static void BundleColorMap(const WebPPicture* const pic,
813 int xbits, uint32_t* bundled_argb, int xs) {
814 int y;
815 const int bit_depth = 1 << (3 - xbits);
816 uint32_t code = 0;
817 const uint32_t* argb = pic->argb;
818 const int width = pic->width;
819 const int height = pic->height;
820
821 for (y = 0; y < height; ++y) {
822 int x;
823 for (x = 0; x < width; ++x) {
824 const int mask = (1 << xbits) - 1;
825 const int xsub = x & mask;
826 if (xsub == 0) {
827 code = 0;
828 }
829 // TODO(vikasa): simplify the bundling logic.
830 code |= (argb[x] & 0xff00) << (bit_depth * xsub);
831 bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code;
832 }
833 argb += pic->argb_stride;
834 }
835 }
836
837 // Note: Expects "enc->palette_" to be set properly.
838 // Also, "enc->palette_" will be modified after this call and should not be used
839 // later.
ApplyPalette(VP8LBitWriter * const bw,VP8LEncoder * const enc,int quality)840 static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw,
841 VP8LEncoder* const enc, int quality) {
842 WebPEncodingError err = VP8_ENC_OK;
843 int i, x, y;
844 const WebPPicture* const pic = enc->pic_;
845 uint32_t* argb = pic->argb;
846 const int width = pic->width;
847 const int height = pic->height;
848 uint32_t* const palette = enc->palette_;
849 const int palette_size = enc->palette_size_;
850
851 // Replace each input pixel by corresponding palette index.
852 for (y = 0; y < height; ++y) {
853 for (x = 0; x < width; ++x) {
854 const uint32_t pix = argb[x];
855 for (i = 0; i < palette_size; ++i) {
856 if (pix == palette[i]) {
857 argb[x] = 0xff000000u | (i << 8);
858 break;
859 }
860 }
861 }
862 argb += pic->argb_stride;
863 }
864
865 // Save palette to bitstream.
866 VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
867 VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM);
868 assert(palette_size >= 1);
869 VP8LWriteBits(bw, 8, palette_size - 1);
870 for (i = palette_size - 1; i >= 1; --i) {
871 palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
872 }
873 if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) {
874 err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
875 goto Error;
876 }
877
878 if (palette_size <= 16) {
879 // Image can be packed (multiple pixels per uint32_t).
880 int xbits = 1;
881 if (palette_size <= 2) {
882 xbits = 3;
883 } else if (palette_size <= 4) {
884 xbits = 2;
885 }
886 err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
887 if (err != VP8_ENC_OK) goto Error;
888 BundleColorMap(pic, xbits, enc->argb_, enc->current_width_);
889 }
890
891 Error:
892 return err;
893 }
894
895 // -----------------------------------------------------------------------------
896
GetHistoBits(const WebPConfig * const config,const WebPPicture * const pic)897 static int GetHistoBits(const WebPConfig* const config,
898 const WebPPicture* const pic) {
899 const int width = pic->width;
900 const int height = pic->height;
901 const size_t hist_size = sizeof(VP8LHistogram);
902 // Make tile size a function of encoding method (Range: 0 to 6).
903 int histo_bits = 7 - config->method;
904 while (1) {
905 const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) *
906 VP8LSubSampleSize(height, histo_bits) *
907 hist_size;
908 if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
909 ++histo_bits;
910 }
911 return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
912 (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
913 }
914
InitEncParams(VP8LEncoder * const enc)915 static void InitEncParams(VP8LEncoder* const enc) {
916 const WebPConfig* const config = enc->config_;
917 const WebPPicture* const picture = enc->pic_;
918 const int method = config->method;
919 const float quality = config->quality;
920 enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4;
921 enc->histo_bits_ = GetHistoBits(config, picture);
922 enc->cache_bits_ = (quality <= 25.f) ? 0 : 7;
923 }
924
925 // -----------------------------------------------------------------------------
926 // VP8LEncoder
927
VP8LEncoderNew(const WebPConfig * const config,const WebPPicture * const picture)928 static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
929 const WebPPicture* const picture) {
930 VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc));
931 if (enc == NULL) {
932 WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
933 return NULL;
934 }
935 enc->config_ = config;
936 enc->pic_ = picture;
937 return enc;
938 }
939
VP8LEncoderDelete(VP8LEncoder * enc)940 static void VP8LEncoderDelete(VP8LEncoder* enc) {
941 free(enc->argb_);
942 free(enc);
943 }
944
945 // -----------------------------------------------------------------------------
946 // Main call
947
VP8LEncodeStream(const WebPConfig * const config,const WebPPicture * const picture,VP8LBitWriter * const bw)948 WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
949 const WebPPicture* const picture,
950 VP8LBitWriter* const bw) {
951 WebPEncodingError err = VP8_ENC_OK;
952 const int quality = (int)config->quality;
953 const int width = picture->width;
954 const int height = picture->height;
955 VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
956 const size_t byte_position = VP8LBitWriterNumBytes(bw);
957
958 if (enc == NULL) {
959 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
960 goto Error;
961 }
962
963 InitEncParams(enc);
964
965 // ---------------------------------------------------------------------------
966 // Analyze image (entropy, num_palettes etc)
967
968 if (!VP8LEncAnalyze(enc, config->image_hint)) {
969 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
970 goto Error;
971 }
972
973 if (enc->use_palette_) {
974 err = ApplyPalette(bw, enc, quality);
975 if (err != VP8_ENC_OK) goto Error;
976 // Color cache is disabled for palette.
977 enc->cache_bits_ = 0;
978 }
979
980 // In case image is not packed.
981 if (enc->argb_ == NULL) {
982 int y;
983 err = AllocateTransformBuffer(enc, width, height);
984 if (err != VP8_ENC_OK) goto Error;
985 for (y = 0; y < height; ++y) {
986 memcpy(enc->argb_ + y * width,
987 picture->argb + y * picture->argb_stride,
988 width * sizeof(*enc->argb_));
989 }
990 enc->current_width_ = width;
991 }
992
993 // ---------------------------------------------------------------------------
994 // Apply transforms and write transform data.
995
996 if (!EvalAndApplySubtractGreen(enc, enc->current_width_, height, bw)) {
997 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
998 goto Error;
999 }
1000
1001 if (enc->use_predict_) {
1002 if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, bw)) {
1003 err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
1004 goto Error;
1005 }
1006 }
1007
1008 if (enc->use_cross_color_) {
1009 if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw)) {
1010 err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
1011 goto Error;
1012 }
1013 }
1014
1015 VP8LWriteBits(bw, 1, !TRANSFORM_PRESENT); // No more transforms.
1016
1017 // ---------------------------------------------------------------------------
1018 // Estimate the color cache size.
1019
1020 if (enc->cache_bits_ > 0) {
1021 if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
1022 height, &enc->cache_bits_)) {
1023 err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
1024 goto Error;
1025 }
1026 }
1027
1028 // ---------------------------------------------------------------------------
1029 // Encode and write the transformed image.
1030
1031 if (!EncodeImageInternal(bw, enc->argb_, enc->current_width_, height,
1032 quality, enc->cache_bits_, enc->histo_bits_)) {
1033 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1034 goto Error;
1035 }
1036
1037 if (picture->stats != NULL) {
1038 WebPAuxStats* const stats = picture->stats;
1039 stats->lossless_features = 0;
1040 if (enc->use_predict_) stats->lossless_features |= 1;
1041 if (enc->use_cross_color_) stats->lossless_features |= 2;
1042 if (enc->use_subtract_green_) stats->lossless_features |= 4;
1043 if (enc->use_palette_) stats->lossless_features |= 8;
1044 stats->histogram_bits = enc->histo_bits_;
1045 stats->transform_bits = enc->transform_bits_;
1046 stats->cache_bits = enc->cache_bits_;
1047 stats->palette_size = enc->palette_size_;
1048 stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position);
1049 }
1050
1051 Error:
1052 VP8LEncoderDelete(enc);
1053 return err;
1054 }
1055
VP8LEncodeImage(const WebPConfig * const config,const WebPPicture * const picture)1056 int VP8LEncodeImage(const WebPConfig* const config,
1057 const WebPPicture* const picture) {
1058 int width, height;
1059 int has_alpha;
1060 size_t coded_size;
1061 int percent = 0;
1062 WebPEncodingError err = VP8_ENC_OK;
1063 VP8LBitWriter bw;
1064
1065 if (picture == NULL) return 0;
1066
1067 if (config == NULL || picture->argb == NULL) {
1068 err = VP8_ENC_ERROR_NULL_PARAMETER;
1069 WebPEncodingSetError(picture, err);
1070 return 0;
1071 }
1072
1073 width = picture->width;
1074 height = picture->height;
1075 if (!VP8LBitWriterInit(&bw, (width * height) >> 1)) {
1076 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1077 goto Error;
1078 }
1079
1080 if (!WebPReportProgress(picture, 1, &percent)) {
1081 UserAbort:
1082 err = VP8_ENC_ERROR_USER_ABORT;
1083 goto Error;
1084 }
1085 // Reset stats (for pure lossless coding)
1086 if (picture->stats != NULL) {
1087 WebPAuxStats* const stats = picture->stats;
1088 memset(stats, 0, sizeof(*stats));
1089 stats->PSNR[0] = 99.f;
1090 stats->PSNR[1] = 99.f;
1091 stats->PSNR[2] = 99.f;
1092 stats->PSNR[3] = 99.f;
1093 stats->PSNR[4] = 99.f;
1094 }
1095
1096 // Write image size.
1097 if (!WriteImageSize(picture, &bw)) {
1098 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1099 goto Error;
1100 }
1101
1102 has_alpha = WebPPictureHasTransparency(picture);
1103 // Write the non-trivial Alpha flag and lossless version.
1104 if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
1105 err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1106 goto Error;
1107 }
1108
1109 if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
1110
1111 // Encode main image stream.
1112 err = VP8LEncodeStream(config, picture, &bw);
1113 if (err != VP8_ENC_OK) goto Error;
1114
1115 // TODO(skal): have a fine-grained progress report in VP8LEncodeStream().
1116 if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
1117
1118 // Finish the RIFF chunk.
1119 err = WriteImage(picture, &bw, &coded_size);
1120 if (err != VP8_ENC_OK) goto Error;
1121
1122 if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
1123
1124 // Save size.
1125 if (picture->stats != NULL) {
1126 picture->stats->coded_size += (int)coded_size;
1127 picture->stats->lossless_size = (int)coded_size;
1128 }
1129
1130 if (picture->extra_info != NULL) {
1131 const int mb_w = (width + 15) >> 4;
1132 const int mb_h = (height + 15) >> 4;
1133 memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
1134 }
1135
1136 Error:
1137 if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
1138 VP8LBitWriterDestroy(&bw);
1139 if (err != VP8_ENC_OK) {
1140 WebPEncodingSetError(picture, err);
1141 return 0;
1142 }
1143 return 1;
1144 }
1145
1146 //------------------------------------------------------------------------------
1147
1148 #if defined(__cplusplus) || defined(c_plusplus)
1149 } // extern "C"
1150 #endif
1151