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 // Author: Jyrki Alakuijala (jyrki@google.com)
9 //
10
11 #include <assert.h>
12 #include <math.h>
13 #include <stdio.h>
14
15 #include "./backward_references.h"
16 #include "./histogram.h"
17 #include "../dsp/lossless.h"
18 #include "../utils/color_cache.h"
19 #include "../utils/utils.h"
20
21 #define VALUES_IN_BYTE 256
22
23 #define HASH_BITS 18
24 #define HASH_SIZE (1 << HASH_BITS)
25 #define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
26
27 // 1M window (4M bytes) minus 120 special codes for short distances.
28 #define WINDOW_SIZE ((1 << 20) - 120)
29
30 // Bounds for the match length.
31 #define MIN_LENGTH 2
32 #define MAX_LENGTH 4096
33
34 typedef struct {
35 // Stores the most recently added position with the given hash value.
36 int32_t hash_to_first_index_[HASH_SIZE];
37 // chain_[pos] stores the previous position with the same hash value
38 // for every pixel in the image.
39 int32_t* chain_;
40 } HashChain;
41
42 // -----------------------------------------------------------------------------
43
44 static const uint8_t plane_to_code_lut[128] = {
45 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
46 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
47 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
48 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
49 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
50 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
51 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
52 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
53 };
54
DistanceToPlaneCode(int xsize,int dist)55 static int DistanceToPlaneCode(int xsize, int dist) {
56 const int yoffset = dist / xsize;
57 const int xoffset = dist - yoffset * xsize;
58 if (xoffset <= 8 && yoffset < 8) {
59 return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
60 } else if (xoffset > xsize - 8 && yoffset < 7) {
61 return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
62 }
63 return dist + 120;
64 }
65
FindMatchLength(const uint32_t * const array1,const uint32_t * const array2,const int max_limit)66 static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
67 const uint32_t* const array2,
68 const int max_limit) {
69 int match_len = 0;
70 while (match_len < max_limit && array1[match_len] == array2[match_len]) {
71 ++match_len;
72 }
73 return match_len;
74 }
75
76 // -----------------------------------------------------------------------------
77 // VP8LBackwardRefs
78
VP8LInitBackwardRefs(VP8LBackwardRefs * const refs)79 void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) {
80 if (refs != NULL) {
81 refs->refs = NULL;
82 refs->size = 0;
83 refs->max_size = 0;
84 }
85 }
86
VP8LClearBackwardRefs(VP8LBackwardRefs * const refs)87 void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
88 if (refs != NULL) {
89 free(refs->refs);
90 VP8LInitBackwardRefs(refs);
91 }
92 }
93
VP8LBackwardRefsAlloc(VP8LBackwardRefs * const refs,int max_size)94 int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
95 assert(refs != NULL);
96 refs->size = 0;
97 refs->max_size = 0;
98 refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
99 sizeof(*refs->refs));
100 if (refs->refs == NULL) return 0;
101 refs->max_size = max_size;
102 return 1;
103 }
104
105 // -----------------------------------------------------------------------------
106 // Hash chains
107
GetPixPairHash64(const uint32_t * const argb)108 static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
109 uint64_t key = ((uint64_t)(argb[1]) << 32) | argb[0];
110 key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS);
111 return key;
112 }
113
HashChainInit(HashChain * const p,int size)114 static int HashChainInit(HashChain* const p, int size) {
115 int i;
116 p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_));
117 if (p->chain_ == NULL) {
118 return 0;
119 }
120 for (i = 0; i < size; ++i) {
121 p->chain_[i] = -1;
122 }
123 for (i = 0; i < HASH_SIZE; ++i) {
124 p->hash_to_first_index_[i] = -1;
125 }
126 return 1;
127 }
128
HashChainDelete(HashChain * const p)129 static void HashChainDelete(HashChain* const p) {
130 if (p != NULL) {
131 free(p->chain_);
132 free(p);
133 }
134 }
135
136 // Insertion of two pixels at a time.
HashChainInsert(HashChain * const p,const uint32_t * const argb,int pos)137 static void HashChainInsert(HashChain* const p,
138 const uint32_t* const argb, int pos) {
139 const uint64_t hash_code = GetPixPairHash64(argb);
140 p->chain_[pos] = p->hash_to_first_index_[hash_code];
141 p->hash_to_first_index_[hash_code] = pos;
142 }
143
HashChainFindCopy(const HashChain * const p,int quality,int index,int xsize,const uint32_t * const argb,int maxlen,int * const distance_ptr,int * const length_ptr)144 static int HashChainFindCopy(const HashChain* const p,
145 int quality, int index, int xsize,
146 const uint32_t* const argb, int maxlen,
147 int* const distance_ptr,
148 int* const length_ptr) {
149 const uint64_t hash_code = GetPixPairHash64(&argb[index]);
150 int prev_length = 0;
151 int64_t best_val = 0;
152 int best_length = 0;
153 int best_distance = 0;
154 const uint32_t* const argb_start = argb + index;
155 const int iter_min_mult = (quality < 50) ? 2 : (quality < 75) ? 4 : 8;
156 const int iter_min = -quality * iter_min_mult;
157 int iter_cnt = 10 + (quality >> 1);
158 const int min_pos = (index > WINDOW_SIZE) ? index - WINDOW_SIZE : 0;
159 int pos;
160
161 assert(xsize > 0);
162 for (pos = p->hash_to_first_index_[hash_code];
163 pos >= min_pos;
164 pos = p->chain_[pos]) {
165 int64_t val;
166 int curr_length;
167 if (iter_cnt < 0) {
168 if (iter_cnt < iter_min || best_val >= 0xff0000) {
169 break;
170 }
171 }
172 --iter_cnt;
173 if (best_length != 0 &&
174 argb[pos + best_length - 1] != argb_start[best_length - 1]) {
175 continue;
176 }
177 curr_length = FindMatchLength(argb + pos, argb_start, maxlen);
178 if (curr_length < prev_length) {
179 continue;
180 }
181 val = 65536 * curr_length;
182 // Favoring 2d locality here gives savings for certain images.
183 if (index - pos < 9 * xsize) {
184 const int y = (index - pos) / xsize;
185 int x = (index - pos) % xsize;
186 if (x > xsize / 2) {
187 x = xsize - x;
188 }
189 if (x <= 7 && x >= -8) {
190 val -= y * y + x * x;
191 } else {
192 val -= 9 * 9 + 9 * 9;
193 }
194 } else {
195 val -= 9 * 9 + 9 * 9;
196 }
197 if (best_val < val) {
198 prev_length = curr_length;
199 best_val = val;
200 best_length = curr_length;
201 best_distance = index - pos;
202 if (curr_length >= MAX_LENGTH) {
203 break;
204 }
205 if ((best_distance == 1 || best_distance == xsize) &&
206 best_length >= 128) {
207 break;
208 }
209 }
210 }
211 *distance_ptr = best_distance;
212 *length_ptr = best_length;
213 return (best_length >= MIN_LENGTH);
214 }
215
PushBackCopy(VP8LBackwardRefs * const refs,int length)216 static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) {
217 int size = refs->size;
218 while (length >= MAX_LENGTH) {
219 refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH);
220 length -= MAX_LENGTH;
221 }
222 if (length > 0) {
223 refs->refs[size++] = PixOrCopyCreateCopy(1, length);
224 }
225 refs->size = size;
226 }
227
BackwardReferencesRle(int xsize,int ysize,const uint32_t * const argb,VP8LBackwardRefs * const refs)228 static void BackwardReferencesRle(int xsize, int ysize,
229 const uint32_t* const argb,
230 VP8LBackwardRefs* const refs) {
231 const int pix_count = xsize * ysize;
232 int match_len = 0;
233 int i;
234 refs->size = 0;
235 PushBackCopy(refs, match_len); // i=0 case
236 refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
237 for (i = 1; i < pix_count; ++i) {
238 if (argb[i] == argb[i - 1]) {
239 ++match_len;
240 } else {
241 PushBackCopy(refs, match_len);
242 match_len = 0;
243 refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]);
244 }
245 }
246 PushBackCopy(refs, match_len);
247 }
248
BackwardReferencesHashChain(int xsize,int ysize,const uint32_t * const argb,int cache_bits,int quality,VP8LBackwardRefs * const refs)249 static int BackwardReferencesHashChain(int xsize, int ysize,
250 const uint32_t* const argb,
251 int cache_bits, int quality,
252 VP8LBackwardRefs* const refs) {
253 int i;
254 int ok = 0;
255 int cc_init = 0;
256 const int use_color_cache = (cache_bits > 0);
257 const int pix_count = xsize * ysize;
258 HashChain* const hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
259 VP8LColorCache hashers;
260
261 if (hash_chain == NULL) return 0;
262 if (use_color_cache) {
263 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
264 if (!cc_init) goto Error;
265 }
266
267 if (!HashChainInit(hash_chain, pix_count)) goto Error;
268
269 refs->size = 0;
270 for (i = 0; i < pix_count; ) {
271 // Alternative#1: Code the pixels starting at 'i' using backward reference.
272 int offset = 0;
273 int len = 0;
274 if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1].
275 int maxlen = pix_count - i;
276 if (maxlen > MAX_LENGTH) {
277 maxlen = MAX_LENGTH;
278 }
279 HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen,
280 &offset, &len);
281 }
282 if (len >= MIN_LENGTH) {
283 // Alternative#2: Insert the pixel at 'i' as literal, and code the
284 // pixels starting at 'i + 1' using backward reference.
285 int offset2 = 0;
286 int len2 = 0;
287 int k;
288 HashChainInsert(hash_chain, &argb[i], i);
289 if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2].
290 int maxlen = pix_count - (i + 1);
291 if (maxlen > MAX_LENGTH) {
292 maxlen = MAX_LENGTH;
293 }
294 HashChainFindCopy(hash_chain, quality,
295 i + 1, xsize, argb, maxlen, &offset2, &len2);
296 if (len2 > len + 1) {
297 const uint32_t pixel = argb[i];
298 // Alternative#2 is a better match. So push pixel at 'i' as literal.
299 if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
300 const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
301 refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
302 } else {
303 refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
304 }
305 ++refs->size;
306 if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
307 i++; // Backward reference to be done for next pixel.
308 len = len2;
309 offset = offset2;
310 }
311 }
312 if (len >= MAX_LENGTH) {
313 len = MAX_LENGTH - 1;
314 }
315 refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
316 if (use_color_cache) {
317 for (k = 0; k < len; ++k) {
318 VP8LColorCacheInsert(&hashers, argb[i + k]);
319 }
320 }
321 // Add to the hash_chain (but cannot add the last pixel).
322 {
323 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
324 for (k = 1; k < last; ++k) {
325 HashChainInsert(hash_chain, &argb[i + k], i + k);
326 }
327 }
328 i += len;
329 } else {
330 const uint32_t pixel = argb[i];
331 if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
332 // push pixel as a PixOrCopyCreateCacheIdx pixel
333 const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
334 refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
335 } else {
336 refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
337 }
338 ++refs->size;
339 if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
340 if (i + 1 < pix_count) {
341 HashChainInsert(hash_chain, &argb[i], i);
342 }
343 ++i;
344 }
345 }
346 ok = 1;
347 Error:
348 if (cc_init) VP8LColorCacheClear(&hashers);
349 HashChainDelete(hash_chain);
350 return ok;
351 }
352
353 // -----------------------------------------------------------------------------
354
355 typedef struct {
356 double alpha_[VALUES_IN_BYTE];
357 double red_[VALUES_IN_BYTE];
358 double literal_[PIX_OR_COPY_CODES_MAX];
359 double blue_[VALUES_IN_BYTE];
360 double distance_[NUM_DISTANCE_CODES];
361 } CostModel;
362
363 static int BackwardReferencesTraceBackwards(
364 int xsize, int ysize, int recursive_cost_model,
365 const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs);
366
ConvertPopulationCountTableToBitEstimates(int num_symbols,const int population_counts[],double output[])367 static void ConvertPopulationCountTableToBitEstimates(
368 int num_symbols, const int population_counts[], double output[]) {
369 int sum = 0;
370 int nonzeros = 0;
371 int i;
372 for (i = 0; i < num_symbols; ++i) {
373 sum += population_counts[i];
374 if (population_counts[i] > 0) {
375 ++nonzeros;
376 }
377 }
378 if (nonzeros <= 1) {
379 memset(output, 0, num_symbols * sizeof(*output));
380 } else {
381 const double logsum = VP8LFastLog2(sum);
382 for (i = 0; i < num_symbols; ++i) {
383 output[i] = logsum - VP8LFastLog2(population_counts[i]);
384 }
385 }
386 }
387
CostModelBuild(CostModel * const m,int xsize,int ysize,int recursion_level,const uint32_t * const argb,int cache_bits)388 static int CostModelBuild(CostModel* const m, int xsize, int ysize,
389 int recursion_level, const uint32_t* const argb,
390 int cache_bits) {
391 int ok = 0;
392 VP8LHistogram histo;
393 VP8LBackwardRefs refs;
394 const int quality = 100;
395
396 if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
397
398 if (recursion_level > 0) {
399 if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
400 argb, cache_bits, &refs)) {
401 goto Error;
402 }
403 } else {
404 if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
405 &refs)) {
406 goto Error;
407 }
408 }
409 VP8LHistogramCreate(&histo, &refs, cache_bits);
410 ConvertPopulationCountTableToBitEstimates(
411 VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_);
412 ConvertPopulationCountTableToBitEstimates(
413 VALUES_IN_BYTE, histo.red_, m->red_);
414 ConvertPopulationCountTableToBitEstimates(
415 VALUES_IN_BYTE, histo.blue_, m->blue_);
416 ConvertPopulationCountTableToBitEstimates(
417 VALUES_IN_BYTE, histo.alpha_, m->alpha_);
418 ConvertPopulationCountTableToBitEstimates(
419 NUM_DISTANCE_CODES, histo.distance_, m->distance_);
420 ok = 1;
421
422 Error:
423 VP8LClearBackwardRefs(&refs);
424 return ok;
425 }
426
GetLiteralCost(const CostModel * const m,uint32_t v)427 static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
428 return m->alpha_[v >> 24] +
429 m->red_[(v >> 16) & 0xff] +
430 m->literal_[(v >> 8) & 0xff] +
431 m->blue_[v & 0xff];
432 }
433
GetCacheCost(const CostModel * const m,uint32_t idx)434 static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
435 const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
436 return m->literal_[literal_idx];
437 }
438
GetLengthCost(const CostModel * const m,uint32_t length)439 static WEBP_INLINE double GetLengthCost(const CostModel* const m,
440 uint32_t length) {
441 int code, extra_bits_count, extra_bits_value;
442 PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value);
443 return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count;
444 }
445
GetDistanceCost(const CostModel * const m,uint32_t distance)446 static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
447 uint32_t distance) {
448 int code, extra_bits_count, extra_bits_value;
449 PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value);
450 return m->distance_[code] + extra_bits_count;
451 }
452
BackwardReferencesHashChainDistanceOnly(int xsize,int ysize,int recursive_cost_model,const uint32_t * const argb,int cache_bits,uint32_t * const dist_array)453 static int BackwardReferencesHashChainDistanceOnly(
454 int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
455 int cache_bits, uint32_t* const dist_array) {
456 int i;
457 int ok = 0;
458 int cc_init = 0;
459 const int quality = 100;
460 const int pix_count = xsize * ysize;
461 const int use_color_cache = (cache_bits > 0);
462 double* const cost =
463 (double*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
464 CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model));
465 HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
466 VP8LColorCache hashers;
467 const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
468 const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
469
470 if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
471
472 if (!HashChainInit(hash_chain, pix_count)) goto Error;
473
474 if (use_color_cache) {
475 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
476 if (!cc_init) goto Error;
477 }
478
479 if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
480 cache_bits)) {
481 goto Error;
482 }
483
484 for (i = 0; i < pix_count; ++i) cost[i] = 1e100;
485
486 // We loop one pixel at a time, but store all currently best points to
487 // non-processed locations from this point.
488 dist_array[0] = 0;
489 for (i = 0; i < pix_count; ++i) {
490 double prev_cost = 0.0;
491 int shortmax;
492 if (i > 0) {
493 prev_cost = cost[i - 1];
494 }
495 for (shortmax = 0; shortmax < 2; ++shortmax) {
496 int offset = 0;
497 int len = 0;
498 if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1].
499 int maxlen = shortmax ? 2 : MAX_LENGTH;
500 if (maxlen > pix_count - i) {
501 maxlen = pix_count - i;
502 }
503 HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen,
504 &offset, &len);
505 }
506 if (len >= MIN_LENGTH) {
507 const int code = DistanceToPlaneCode(xsize, offset);
508 const double distance_cost =
509 prev_cost + GetDistanceCost(cost_model, code);
510 int k;
511 for (k = 1; k < len; ++k) {
512 const double cost_val =
513 distance_cost + GetLengthCost(cost_model, k);
514 if (cost[i + k] > cost_val) {
515 cost[i + k] = cost_val;
516 dist_array[i + k] = k + 1;
517 }
518 }
519 // This if is for speedup only. It roughly doubles the speed, and
520 // makes compression worse by .1 %.
521 if (len >= 128 && code < 2) {
522 // Long copy for short distances, let's skip the middle
523 // lookups for better copies.
524 // 1) insert the hashes.
525 if (use_color_cache) {
526 for (k = 0; k < len; ++k) {
527 VP8LColorCacheInsert(&hashers, argb[i + k]);
528 }
529 }
530 // 2) Add to the hash_chain (but cannot add the last pixel)
531 {
532 const int last = (len < pix_count - 1 - i) ? len
533 : pix_count - 1 - i;
534 for (k = 0; k < last; ++k) {
535 HashChainInsert(hash_chain, &argb[i + k], i + k);
536 }
537 }
538 // 3) jump.
539 i += len - 1; // for loop does ++i, thus -1 here.
540 goto next_symbol;
541 }
542 }
543 }
544 if (i < pix_count - 1) {
545 HashChainInsert(hash_chain, &argb[i], i);
546 }
547 {
548 // inserting a literal pixel
549 double cost_val = prev_cost;
550 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
551 const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
552 cost_val += GetCacheCost(cost_model, ix) * mul0;
553 } else {
554 cost_val += GetLiteralCost(cost_model, argb[i]) * mul1;
555 }
556 if (cost[i] > cost_val) {
557 cost[i] = cost_val;
558 dist_array[i] = 1; // only one is inserted.
559 }
560 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
561 }
562 next_symbol: ;
563 }
564 // Last pixel still to do, it can only be a single step if not reached
565 // through cheaper means already.
566 ok = 1;
567 Error:
568 if (cc_init) VP8LColorCacheClear(&hashers);
569 HashChainDelete(hash_chain);
570 free(cost_model);
571 free(cost);
572 return ok;
573 }
574
TraceBackwards(const uint32_t * const dist_array,int dist_array_size,uint32_t ** const chosen_path,int * const chosen_path_size)575 static int TraceBackwards(const uint32_t* const dist_array,
576 int dist_array_size,
577 uint32_t** const chosen_path,
578 int* const chosen_path_size) {
579 int i;
580 // Count how many.
581 int count = 0;
582 for (i = dist_array_size - 1; i >= 0; ) {
583 int k = dist_array[i];
584 assert(k >= 1);
585 ++count;
586 i -= k;
587 }
588 // Allocate.
589 *chosen_path_size = count;
590 *chosen_path =
591 (uint32_t*)WebPSafeMalloc((uint64_t)count, sizeof(**chosen_path));
592 if (*chosen_path == NULL) return 0;
593
594 // Write in reverse order.
595 for (i = dist_array_size - 1; i >= 0; ) {
596 int k = dist_array[i];
597 assert(k >= 1);
598 (*chosen_path)[--count] = k;
599 i -= k;
600 }
601 return 1;
602 }
603
BackwardReferencesHashChainFollowChosenPath(int xsize,int ysize,const uint32_t * const argb,int cache_bits,const uint32_t * const chosen_path,int chosen_path_size,VP8LBackwardRefs * const refs)604 static int BackwardReferencesHashChainFollowChosenPath(
605 int xsize, int ysize, const uint32_t* const argb, int cache_bits,
606 const uint32_t* const chosen_path, int chosen_path_size,
607 VP8LBackwardRefs* const refs) {
608 const int quality = 100;
609 const int pix_count = xsize * ysize;
610 const int use_color_cache = (cache_bits > 0);
611 int size = 0;
612 int i = 0;
613 int k;
614 int ix;
615 int ok = 0;
616 int cc_init = 0;
617 HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
618 VP8LColorCache hashers;
619
620 if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) {
621 goto Error;
622 }
623 if (use_color_cache) {
624 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
625 if (!cc_init) goto Error;
626 }
627
628 refs->size = 0;
629 for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
630 int offset = 0;
631 int len = 0;
632 int maxlen = chosen_path[ix];
633 if (maxlen != 1) {
634 HashChainFindCopy(hash_chain, quality,
635 i, xsize, argb, maxlen, &offset, &len);
636 assert(len == maxlen);
637 refs->refs[size] = PixOrCopyCreateCopy(offset, len);
638 if (use_color_cache) {
639 for (k = 0; k < len; ++k) {
640 VP8LColorCacheInsert(&hashers, argb[i + k]);
641 }
642 }
643 {
644 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
645 for (k = 0; k < last; ++k) {
646 HashChainInsert(hash_chain, &argb[i + k], i + k);
647 }
648 }
649 i += len;
650 } else {
651 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
652 // push pixel as a color cache index
653 const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
654 refs->refs[size] = PixOrCopyCreateCacheIdx(idx);
655 } else {
656 refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
657 }
658 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
659 if (i + 1 < pix_count) {
660 HashChainInsert(hash_chain, &argb[i], i);
661 }
662 ++i;
663 }
664 }
665 assert(size <= refs->max_size);
666 refs->size = size;
667 ok = 1;
668 Error:
669 if (cc_init) VP8LColorCacheClear(&hashers);
670 HashChainDelete(hash_chain);
671 return ok;
672 }
673
674 // Returns 1 on success.
BackwardReferencesTraceBackwards(int xsize,int ysize,int recursive_cost_model,const uint32_t * const argb,int cache_bits,VP8LBackwardRefs * const refs)675 static int BackwardReferencesTraceBackwards(int xsize, int ysize,
676 int recursive_cost_model,
677 const uint32_t* const argb,
678 int cache_bits,
679 VP8LBackwardRefs* const refs) {
680 int ok = 0;
681 const int dist_array_size = xsize * ysize;
682 uint32_t* chosen_path = NULL;
683 int chosen_path_size = 0;
684 uint32_t* dist_array =
685 (uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array));
686
687 if (dist_array == NULL) goto Error;
688
689 if (!BackwardReferencesHashChainDistanceOnly(
690 xsize, ysize, recursive_cost_model, argb, cache_bits, dist_array)) {
691 goto Error;
692 }
693 if (!TraceBackwards(dist_array, dist_array_size,
694 &chosen_path, &chosen_path_size)) {
695 goto Error;
696 }
697 free(dist_array); // no need to retain this memory any longer
698 dist_array = NULL;
699 if (!BackwardReferencesHashChainFollowChosenPath(
700 xsize, ysize, argb, cache_bits, chosen_path, chosen_path_size, refs)) {
701 goto Error;
702 }
703 ok = 1;
704 Error:
705 free(chosen_path);
706 free(dist_array);
707 return ok;
708 }
709
BackwardReferences2DLocality(int xsize,VP8LBackwardRefs * const refs)710 static void BackwardReferences2DLocality(int xsize,
711 VP8LBackwardRefs* const refs) {
712 int i;
713 for (i = 0; i < refs->size; ++i) {
714 if (PixOrCopyIsCopy(&refs->refs[i])) {
715 const int dist = refs->refs[i].argb_or_distance;
716 const int transformed_dist = DistanceToPlaneCode(xsize, dist);
717 refs->refs[i].argb_or_distance = transformed_dist;
718 }
719 }
720 }
721
VP8LGetBackwardReferences(int width,int height,const uint32_t * const argb,int quality,int cache_bits,int use_2d_locality,VP8LBackwardRefs * const best)722 int VP8LGetBackwardReferences(int width, int height,
723 const uint32_t* const argb,
724 int quality, int cache_bits, int use_2d_locality,
725 VP8LBackwardRefs* const best) {
726 int ok = 0;
727 int lz77_is_useful;
728 VP8LBackwardRefs refs_rle, refs_lz77;
729 const int num_pix = width * height;
730
731 VP8LBackwardRefsAlloc(&refs_rle, num_pix);
732 VP8LBackwardRefsAlloc(&refs_lz77, num_pix);
733 VP8LInitBackwardRefs(best);
734 if (refs_rle.refs == NULL || refs_lz77.refs == NULL) {
735 Error1:
736 VP8LClearBackwardRefs(&refs_rle);
737 VP8LClearBackwardRefs(&refs_lz77);
738 goto End;
739 }
740
741 if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
742 &refs_lz77)) {
743 goto End;
744 }
745 // Backward Reference using RLE only.
746 BackwardReferencesRle(width, height, argb, &refs_rle);
747
748 {
749 double bit_cost_lz77, bit_cost_rle;
750 VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo));
751 if (histo == NULL) goto Error1;
752 // Evaluate lz77 coding
753 VP8LHistogramCreate(histo, &refs_lz77, cache_bits);
754 bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
755 // Evaluate RLE coding
756 VP8LHistogramCreate(histo, &refs_rle, cache_bits);
757 bit_cost_rle = VP8LHistogramEstimateBits(histo);
758 // Decide if LZ77 is useful.
759 lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
760 free(histo);
761 }
762
763 // Choose appropriate backward reference.
764 if (lz77_is_useful) {
765 // TraceBackwards is costly. Run it for higher qualities.
766 const int try_lz77_trace_backwards = (quality >= 75);
767 *best = refs_lz77; // default guess: lz77 is better
768 VP8LClearBackwardRefs(&refs_rle);
769 if (try_lz77_trace_backwards) {
770 const int recursion_level = (num_pix < 320 * 200) ? 1 : 0;
771 VP8LBackwardRefs refs_trace;
772 if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) {
773 goto End;
774 }
775 if (BackwardReferencesTraceBackwards(
776 width, height, recursion_level, argb, cache_bits, &refs_trace)) {
777 VP8LClearBackwardRefs(&refs_lz77);
778 *best = refs_trace;
779 }
780 }
781 } else {
782 VP8LClearBackwardRefs(&refs_lz77);
783 *best = refs_rle;
784 }
785
786 if (use_2d_locality) BackwardReferences2DLocality(width, best);
787
788 ok = 1;
789
790 End:
791 if (!ok) {
792 VP8LClearBackwardRefs(best);
793 }
794 return ok;
795 }
796
797 // Returns 1 on success.
ComputeCacheHistogram(const uint32_t * const argb,int xsize,int ysize,const VP8LBackwardRefs * const refs,int cache_bits,VP8LHistogram * const histo)798 static int ComputeCacheHistogram(const uint32_t* const argb,
799 int xsize, int ysize,
800 const VP8LBackwardRefs* const refs,
801 int cache_bits,
802 VP8LHistogram* const histo) {
803 int pixel_index = 0;
804 int i;
805 uint32_t k;
806 VP8LColorCache hashers;
807 const int use_color_cache = (cache_bits > 0);
808 int cc_init = 0;
809
810 if (use_color_cache) {
811 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
812 if (!cc_init) return 0;
813 }
814
815 for (i = 0; i < refs->size; ++i) {
816 const PixOrCopy* const v = &refs->refs[i];
817 if (PixOrCopyIsLiteral(v)) {
818 if (use_color_cache &&
819 VP8LColorCacheContains(&hashers, argb[pixel_index])) {
820 // push pixel as a cache index
821 const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
822 const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
823 VP8LHistogramAddSinglePixOrCopy(histo, &token);
824 } else {
825 VP8LHistogramAddSinglePixOrCopy(histo, v);
826 }
827 } else {
828 VP8LHistogramAddSinglePixOrCopy(histo, v);
829 }
830 if (use_color_cache) {
831 for (k = 0; k < PixOrCopyLength(v); ++k) {
832 VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
833 }
834 }
835 pixel_index += PixOrCopyLength(v);
836 }
837 assert(pixel_index == xsize * ysize);
838 (void)xsize; // xsize is not used in non-debug compilations otherwise.
839 (void)ysize; // ysize is not used in non-debug compilations otherwise.
840 if (cc_init) VP8LColorCacheClear(&hashers);
841 return 1;
842 }
843
844 // Returns how many bits are to be used for a color cache.
VP8LCalculateEstimateForCacheSize(const uint32_t * const argb,int xsize,int ysize,int * const best_cache_bits)845 int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
846 int xsize, int ysize,
847 int* const best_cache_bits) {
848 int ok = 0;
849 int cache_bits;
850 double lowest_entropy = 1e99;
851 VP8LBackwardRefs refs;
852 static const double kSmallPenaltyForLargeCache = 4.0;
853 static const int quality = 30;
854 if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
855 !BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
856 goto Error;
857 }
858 for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) {
859 double cur_entropy;
860 VP8LHistogram histo;
861 VP8LHistogramInit(&histo, cache_bits);
862 ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo);
863 cur_entropy = VP8LHistogramEstimateBits(&histo) +
864 kSmallPenaltyForLargeCache * cache_bits;
865 if (cache_bits == 0 || cur_entropy < lowest_entropy) {
866 *best_cache_bits = cache_bits;
867 lowest_entropy = cur_entropy;
868 }
869 }
870 ok = 1;
871 Error:
872 VP8LClearBackwardRefs(&refs);
873 return ok;
874 }
875