Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / libwebp / enc / backward_references.c
bloba3c30aa0710e327eebe2234c25d3d6bb5ddba4a7
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // Author: Jyrki Alakuijala (jyrki@google.com)
13 #include <assert.h>
14 #include <math.h>
16 #include "./backward_references.h"
17 #include "./histogram.h"
18 #include "../dsp/lossless.h"
19 #include "../utils/color_cache.h"
20 #include "../utils/utils.h"
22 #define VALUES_IN_BYTE 256
24 #define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
26 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references
28 #define MAX_ENTROPY (1e30f)
30 // 1M window (4M bytes) minus 120 special codes for short distances.
31 #define WINDOW_SIZE ((1 << 20) - 120)
33 // Bounds for the match length.
34 #define MIN_LENGTH 2
35 #define MAX_LENGTH 4096
37 // -----------------------------------------------------------------------------
39 static const uint8_t plane_to_code_lut[128] = {
40 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
41 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
42 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
43 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
44 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
45 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
46 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
47 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
50 static int DistanceToPlaneCode(int xsize, int dist) {
51 const int yoffset = dist / xsize;
52 const int xoffset = dist - yoffset * xsize;
53 if (xoffset <= 8 && yoffset < 8) {
54 return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
55 } else if (xoffset > xsize - 8 && yoffset < 7) {
56 return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
58 return dist + 120;
61 static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
62 const uint32_t* const array2,
63 const int max_limit) {
64 int match_len = 0;
65 while (match_len < max_limit && array1[match_len] == array2[match_len]) {
66 ++match_len;
68 return match_len;
71 // -----------------------------------------------------------------------------
72 // VP8LBackwardRefs
74 struct PixOrCopyBlock {
75 PixOrCopyBlock* next_; // next block (or NULL)
76 PixOrCopy* start_; // data start
77 int size_; // currently used size
80 static void ClearBackwardRefs(VP8LBackwardRefs* const refs) {
81 assert(refs != NULL);
82 if (refs->tail_ != NULL) {
83 *refs->tail_ = refs->free_blocks_; // recycle all blocks at once
85 refs->free_blocks_ = refs->refs_;
86 refs->tail_ = &refs->refs_;
87 refs->last_block_ = NULL;
88 refs->refs_ = NULL;
91 void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
92 assert(refs != NULL);
93 ClearBackwardRefs(refs);
94 while (refs->free_blocks_ != NULL) {
95 PixOrCopyBlock* const next = refs->free_blocks_->next_;
96 WebPSafeFree(refs->free_blocks_);
97 refs->free_blocks_ = next;
101 void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
102 assert(refs != NULL);
103 memset(refs, 0, sizeof(*refs));
104 refs->tail_ = &refs->refs_;
105 refs->block_size_ =
106 (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;
109 VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {
110 VP8LRefsCursor c;
111 c.cur_block_ = refs->refs_;
112 if (refs->refs_ != NULL) {
113 c.cur_pos = c.cur_block_->start_;
114 c.last_pos_ = c.cur_pos + c.cur_block_->size_;
115 } else {
116 c.cur_pos = NULL;
117 c.last_pos_ = NULL;
119 return c;
122 void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {
123 PixOrCopyBlock* const b = c->cur_block_->next_;
124 c->cur_pos = (b == NULL) ? NULL : b->start_;
125 c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;
126 c->cur_block_ = b;
129 // Create a new block, either from the free list or allocated
130 static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
131 PixOrCopyBlock* b = refs->free_blocks_;
132 if (b == NULL) { // allocate new memory chunk
133 const size_t total_size =
134 sizeof(*b) + refs->block_size_ * sizeof(*b->start_);
135 b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);
136 if (b == NULL) {
137 refs->error_ |= 1;
138 return NULL;
140 b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned
141 } else { // recycle from free-list
142 refs->free_blocks_ = b->next_;
144 *refs->tail_ = b;
145 refs->tail_ = &b->next_;
146 refs->last_block_ = b;
147 b->next_ = NULL;
148 b->size_ = 0;
149 return b;
152 static WEBP_INLINE void BackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
153 const PixOrCopy v) {
154 PixOrCopyBlock* b = refs->last_block_;
155 if (b == NULL || b->size_ == refs->block_size_) {
156 b = BackwardRefsNewBlock(refs);
157 if (b == NULL) return; // refs->error_ is set
159 b->start_[b->size_++] = v;
162 int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
163 VP8LBackwardRefs* const dst) {
164 const PixOrCopyBlock* b = src->refs_;
165 ClearBackwardRefs(dst);
166 assert(src->block_size_ == dst->block_size_);
167 while (b != NULL) {
168 PixOrCopyBlock* const new_b = BackwardRefsNewBlock(dst);
169 if (new_b == NULL) return 0; // dst->error_ is set
170 memcpy(new_b->start_, b->start_, b->size_ * sizeof(*b->start_));
171 new_b->size_ = b->size_;
172 b = b->next_;
174 return 1;
177 // -----------------------------------------------------------------------------
178 // Hash chains
180 // initialize as empty
181 static void HashChainInit(VP8LHashChain* const p) {
182 int i;
183 assert(p != NULL);
184 for (i = 0; i < p->size_; ++i) {
185 p->chain_[i] = -1;
187 for (i = 0; i < HASH_SIZE; ++i) {
188 p->hash_to_first_index_[i] = -1;
192 int VP8LHashChainInit(VP8LHashChain* const p, int size) {
193 assert(p->size_ == 0);
194 assert(p->chain_ == NULL);
195 assert(size > 0);
196 p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_));
197 if (p->chain_ == NULL) return 0;
198 p->size_ = size;
199 HashChainInit(p);
200 return 1;
203 void VP8LHashChainClear(VP8LHashChain* const p) {
204 assert(p != NULL);
205 WebPSafeFree(p->chain_);
206 p->size_ = 0;
207 p->chain_ = NULL;
210 // -----------------------------------------------------------------------------
212 static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
213 uint64_t key = ((uint64_t)argb[1] << 32) | argb[0];
214 key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS);
215 return key;
218 // Insertion of two pixels at a time.
219 static void HashChainInsert(VP8LHashChain* const p,
220 const uint32_t* const argb, int pos) {
221 const uint64_t hash_code = GetPixPairHash64(argb);
222 p->chain_[pos] = p->hash_to_first_index_[hash_code];
223 p->hash_to_first_index_[hash_code] = pos;
226 static void GetParamsForHashChainFindCopy(int quality, int xsize,
227 int cache_bits, int* window_size,
228 int* iter_pos, int* iter_limit) {
229 const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4);
230 const int iter_neg = -iter_mult * (quality >> 1);
231 // Limit the backward-ref window size for lower qualities.
232 const int max_window_size = (quality > 50) ? WINDOW_SIZE
233 : (quality > 25) ? (xsize << 8)
234 : (xsize << 4);
235 assert(xsize > 0);
236 *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE
237 : max_window_size;
238 *iter_pos = 8 + (quality >> 3);
239 // For lower entropy images, the rigorous search loop in HashChainFindCopy
240 // can be relaxed.
241 *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2;
244 static int HashChainFindCopy(const VP8LHashChain* const p,
245 int base_position, int xsize_signed,
246 const uint32_t* const argb, int max_len,
247 int window_size, int iter_pos, int iter_limit,
248 int* const distance_ptr,
249 int* const length_ptr) {
250 const uint32_t* const argb_start = argb + base_position;
251 uint64_t best_val = 0;
252 uint32_t best_length = 1;
253 uint32_t best_distance = 0;
254 const uint32_t xsize = (uint32_t)xsize_signed;
255 const int min_pos =
256 (base_position > window_size) ? base_position - window_size : 0;
257 int pos;
258 assert(xsize > 0);
259 if (max_len > MAX_LENGTH) {
260 max_len = MAX_LENGTH;
262 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
263 pos >= min_pos;
264 pos = p->chain_[pos]) {
265 uint64_t val;
266 uint32_t curr_length;
267 uint32_t distance;
268 const uint32_t* const ptr1 = (argb + pos + best_length - 1);
269 const uint32_t* const ptr2 = (argb_start + best_length - 1);
271 if (iter_pos < 0) {
272 if (iter_pos < iter_limit || best_val >= 0xff0000) {
273 break;
276 --iter_pos;
278 // Before 'expensive' linear match, check if the two arrays match at the
279 // current best length index and also for the succeeding elements.
280 if (ptr1[0] != ptr2[0] || ptr1[1] != ptr2[1]) continue;
282 curr_length = FindMatchLength(argb + pos, argb_start, max_len);
283 if (curr_length < best_length) continue;
285 distance = (uint32_t)(base_position - pos);
286 val = curr_length << 16;
287 // Favoring 2d locality here gives savings for certain images.
288 if (distance < 9 * xsize) {
289 const uint32_t y = distance / xsize;
290 uint32_t x = distance % xsize;
291 if (x > (xsize >> 1)) {
292 x = xsize - x;
294 if (x <= 7) {
295 val += 9 * 9 + 9 * 9;
296 val -= y * y + x * x;
299 if (best_val < val) {
300 best_val = val;
301 best_length = curr_length;
302 best_distance = distance;
303 if (curr_length >= (uint32_t)max_len) {
304 break;
306 if ((best_distance == 1 || distance == xsize) &&
307 best_length >= 128) {
308 break;
312 *distance_ptr = (int)best_distance;
313 *length_ptr = best_length;
314 return (best_length >= MIN_LENGTH);
317 static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) {
318 while (length >= MAX_LENGTH) {
319 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, MAX_LENGTH));
320 length -= MAX_LENGTH;
322 if (length > 0) {
323 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, length));
327 static int BackwardReferencesRle(int xsize, int ysize,
328 const uint32_t* const argb,
329 VP8LBackwardRefs* const refs) {
330 const int pix_count = xsize * ysize;
331 int match_len = 0;
332 int i;
333 ClearBackwardRefs(refs);
334 PushBackCopy(refs, match_len); // i=0 case
335 BackwardRefsCursorAdd(refs, PixOrCopyCreateLiteral(argb[0]));
336 for (i = 1; i < pix_count; ++i) {
337 if (argb[i] == argb[i - 1]) {
338 ++match_len;
339 } else {
340 PushBackCopy(refs, match_len);
341 match_len = 0;
342 BackwardRefsCursorAdd(refs, PixOrCopyCreateLiteral(argb[i]));
345 PushBackCopy(refs, match_len);
346 return !refs->error_;
349 static int BackwardReferencesHashChain(int xsize, int ysize,
350 const uint32_t* const argb,
351 int cache_bits, int quality,
352 VP8LHashChain* const hash_chain,
353 VP8LBackwardRefs* const refs) {
354 int i;
355 int ok = 0;
356 int cc_init = 0;
357 const int use_color_cache = (cache_bits > 0);
358 const int pix_count = xsize * ysize;
359 VP8LColorCache hashers;
360 int window_size = WINDOW_SIZE;
361 int iter_pos = 1;
362 int iter_limit = -1;
364 if (use_color_cache) {
365 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
366 if (!cc_init) goto Error;
369 ClearBackwardRefs(refs);
370 GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
371 &window_size, &iter_pos, &iter_limit);
372 HashChainInit(hash_chain);
373 for (i = 0; i < pix_count; ) {
374 // Alternative#1: Code the pixels starting at 'i' using backward reference.
375 int offset = 0;
376 int len = 0;
377 if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1].
378 int max_len = pix_count - i;
379 HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
380 window_size, iter_pos, iter_limit,
381 &offset, &len);
383 if (len >= MIN_LENGTH) {
384 // Alternative#2: Insert the pixel at 'i' as literal, and code the
385 // pixels starting at 'i + 1' using backward reference.
386 int offset2 = 0;
387 int len2 = 0;
388 int k;
389 HashChainInsert(hash_chain, &argb[i], i);
390 if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2].
391 int max_len = pix_count - (i + 1);
392 HashChainFindCopy(hash_chain, i + 1, xsize, argb, max_len,
393 window_size, iter_pos, iter_limit,
394 &offset2, &len2);
395 if (len2 > len + 1) {
396 const uint32_t pixel = argb[i];
397 // Alternative#2 is a better match. So push pixel at 'i' as literal.
398 PixOrCopy v;
399 if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
400 const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
401 v = PixOrCopyCreateCacheIdx(ix);
402 } else {
403 if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
404 v = PixOrCopyCreateLiteral(pixel);
406 BackwardRefsCursorAdd(refs, v);
407 i++; // Backward reference to be done for next pixel.
408 len = len2;
409 offset = offset2;
412 if (len >= MAX_LENGTH) {
413 len = MAX_LENGTH - 1;
415 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
416 if (use_color_cache) {
417 for (k = 0; k < len; ++k) {
418 VP8LColorCacheInsert(&hashers, argb[i + k]);
421 // Add to the hash_chain (but cannot add the last pixel).
423 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
424 for (k = 1; k < last; ++k) {
425 HashChainInsert(hash_chain, &argb[i + k], i + k);
428 i += len;
429 } else {
430 const uint32_t pixel = argb[i];
431 PixOrCopy v;
432 if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
433 // push pixel as a PixOrCopyCreateCacheIdx pixel
434 const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
435 v = PixOrCopyCreateCacheIdx(ix);
436 } else {
437 if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
438 v = PixOrCopyCreateLiteral(pixel);
440 BackwardRefsCursorAdd(refs, v);
441 if (i + 1 < pix_count) {
442 HashChainInsert(hash_chain, &argb[i], i);
444 ++i;
447 ok = !refs->error_;
448 Error:
449 if (cc_init) VP8LColorCacheClear(&hashers);
450 return ok;
453 // -----------------------------------------------------------------------------
455 typedef struct {
456 double alpha_[VALUES_IN_BYTE];
457 double red_[VALUES_IN_BYTE];
458 double literal_[PIX_OR_COPY_CODES_MAX];
459 double blue_[VALUES_IN_BYTE];
460 double distance_[NUM_DISTANCE_CODES];
461 } CostModel;
463 static int BackwardReferencesTraceBackwards(
464 int xsize, int ysize, int recursive_cost_model,
465 const uint32_t* const argb, int quality, int cache_bits,
466 VP8LHashChain* const hash_chain,
467 VP8LBackwardRefs* const refs);
469 static void ConvertPopulationCountTableToBitEstimates(
470 int num_symbols, const uint32_t population_counts[], double output[]) {
471 uint32_t sum = 0;
472 int nonzeros = 0;
473 int i;
474 for (i = 0; i < num_symbols; ++i) {
475 sum += population_counts[i];
476 if (population_counts[i] > 0) {
477 ++nonzeros;
480 if (nonzeros <= 1) {
481 memset(output, 0, num_symbols * sizeof(*output));
482 } else {
483 const double logsum = VP8LFastLog2(sum);
484 for (i = 0; i < num_symbols; ++i) {
485 output[i] = logsum - VP8LFastLog2(population_counts[i]);
490 static int CostModelBuild(CostModel* const m, int xsize, int ysize,
491 int recursion_level, const uint32_t* const argb,
492 int quality, int cache_bits,
493 VP8LHashChain* const hash_chain,
494 VP8LBackwardRefs* const refs) {
495 int ok = 0;
496 VP8LHistogram* histo = NULL;
498 ClearBackwardRefs(refs);
499 if (recursion_level > 0) {
500 if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
501 argb, quality, cache_bits, hash_chain,
502 refs)) {
503 goto Error;
505 } else {
506 if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
507 hash_chain, refs)) {
508 goto Error;
511 histo = VP8LAllocateHistogram(cache_bits);
512 if (histo == NULL) goto Error;
514 VP8LHistogramCreate(histo, refs, cache_bits);
516 ConvertPopulationCountTableToBitEstimates(
517 VP8LHistogramNumCodes(histo->palette_code_bits_),
518 histo->literal_, m->literal_);
519 ConvertPopulationCountTableToBitEstimates(
520 VALUES_IN_BYTE, histo->red_, m->red_);
521 ConvertPopulationCountTableToBitEstimates(
522 VALUES_IN_BYTE, histo->blue_, m->blue_);
523 ConvertPopulationCountTableToBitEstimates(
524 VALUES_IN_BYTE, histo->alpha_, m->alpha_);
525 ConvertPopulationCountTableToBitEstimates(
526 NUM_DISTANCE_CODES, histo->distance_, m->distance_);
527 ok = 1;
529 Error:
530 VP8LFreeHistogram(histo);
531 return ok;
534 static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
535 return m->alpha_[v >> 24] +
536 m->red_[(v >> 16) & 0xff] +
537 m->literal_[(v >> 8) & 0xff] +
538 m->blue_[v & 0xff];
541 static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
542 const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
543 return m->literal_[literal_idx];
546 static WEBP_INLINE double GetLengthCost(const CostModel* const m,
547 uint32_t length) {
548 int code, extra_bits;
549 VP8LPrefixEncodeBits(length, &code, &extra_bits);
550 return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
553 static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
554 uint32_t distance) {
555 int code, extra_bits;
556 VP8LPrefixEncodeBits(distance, &code, &extra_bits);
557 return m->distance_[code] + extra_bits;
560 static int BackwardReferencesHashChainDistanceOnly(
561 int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
562 int quality, int cache_bits, VP8LHashChain* const hash_chain,
563 VP8LBackwardRefs* const refs, uint32_t* const dist_array) {
564 int i;
565 int ok = 0;
566 int cc_init = 0;
567 const int pix_count = xsize * ysize;
568 const int use_color_cache = (cache_bits > 0);
569 float* const cost =
570 (float*)WebPSafeMalloc(pix_count, sizeof(*cost));
571 CostModel* cost_model = (CostModel*)WebPSafeMalloc(1ULL, sizeof(*cost_model));
572 VP8LColorCache hashers;
573 const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
574 const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
575 const int min_distance_code = 2; // TODO(vikasa): tune as function of quality
576 int window_size = WINDOW_SIZE;
577 int iter_pos = 1;
578 int iter_limit = -1;
580 if (cost == NULL || cost_model == NULL) goto Error;
582 if (use_color_cache) {
583 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
584 if (!cc_init) goto Error;
587 if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
588 quality, cache_bits, hash_chain, refs)) {
589 goto Error;
592 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f;
594 // We loop one pixel at a time, but store all currently best points to
595 // non-processed locations from this point.
596 dist_array[0] = 0;
597 GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
598 &window_size, &iter_pos, &iter_limit);
599 HashChainInit(hash_chain);
600 for (i = 0; i < pix_count; ++i) {
601 double prev_cost = 0.0;
602 int shortmax;
603 if (i > 0) {
604 prev_cost = cost[i - 1];
606 for (shortmax = 0; shortmax < 2; ++shortmax) {
607 int offset = 0;
608 int len = 0;
609 if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1].
610 int max_len = shortmax ? 2 : pix_count - i;
611 HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
612 window_size, iter_pos, iter_limit,
613 &offset, &len);
615 if (len >= MIN_LENGTH) {
616 const int code = DistanceToPlaneCode(xsize, offset);
617 const double distance_cost =
618 prev_cost + GetDistanceCost(cost_model, code);
619 int k;
620 for (k = 1; k < len; ++k) {
621 const double cost_val = distance_cost + GetLengthCost(cost_model, k);
622 if (cost[i + k] > cost_val) {
623 cost[i + k] = (float)cost_val;
624 dist_array[i + k] = k + 1;
627 // This if is for speedup only. It roughly doubles the speed, and
628 // makes compression worse by .1 %.
629 if (len >= 128 && code <= min_distance_code) {
630 // Long copy for short distances, let's skip the middle
631 // lookups for better copies.
632 // 1) insert the hashes.
633 if (use_color_cache) {
634 for (k = 0; k < len; ++k) {
635 VP8LColorCacheInsert(&hashers, argb[i + k]);
638 // 2) Add to the hash_chain (but cannot add the last pixel)
640 const int last = (len + i < pix_count - 1) ? len + i
641 : pix_count - 1;
642 for (k = i; k < last; ++k) {
643 HashChainInsert(hash_chain, &argb[k], k);
646 // 3) jump.
647 i += len - 1; // for loop does ++i, thus -1 here.
648 goto next_symbol;
652 if (i < pix_count - 1) {
653 HashChainInsert(hash_chain, &argb[i], i);
656 // inserting a literal pixel
657 double cost_val = prev_cost;
658 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
659 const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
660 cost_val += GetCacheCost(cost_model, ix) * mul0;
661 } else {
662 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
663 cost_val += GetLiteralCost(cost_model, argb[i]) * mul1;
665 if (cost[i] > cost_val) {
666 cost[i] = (float)cost_val;
667 dist_array[i] = 1; // only one is inserted.
670 next_symbol: ;
672 // Last pixel still to do, it can only be a single step if not reached
673 // through cheaper means already.
674 ok = !refs->error_;
675 Error:
676 if (cc_init) VP8LColorCacheClear(&hashers);
677 WebPSafeFree(cost_model);
678 WebPSafeFree(cost);
679 return ok;
682 // We pack the path at the end of *dist_array and return
683 // a pointer to this part of the array. Example:
684 // dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
685 static void TraceBackwards(uint32_t* const dist_array,
686 int dist_array_size,
687 uint32_t** const chosen_path,
688 int* const chosen_path_size) {
689 uint32_t* path = dist_array + dist_array_size;
690 uint32_t* cur = dist_array + dist_array_size - 1;
691 while (cur >= dist_array) {
692 const int k = *cur;
693 --path;
694 *path = k;
695 cur -= k;
697 *chosen_path = path;
698 *chosen_path_size = (int)(dist_array + dist_array_size - path);
701 static int BackwardReferencesHashChainFollowChosenPath(
702 int xsize, int ysize, const uint32_t* const argb,
703 int quality, int cache_bits,
704 const uint32_t* const chosen_path, int chosen_path_size,
705 VP8LHashChain* const hash_chain,
706 VP8LBackwardRefs* const refs) {
707 const int pix_count = xsize * ysize;
708 const int use_color_cache = (cache_bits > 0);
709 int size = 0;
710 int i = 0;
711 int k;
712 int ix;
713 int ok = 0;
714 int cc_init = 0;
715 int window_size = WINDOW_SIZE;
716 int iter_pos = 1;
717 int iter_limit = -1;
718 VP8LColorCache hashers;
720 if (use_color_cache) {
721 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
722 if (!cc_init) goto Error;
725 ClearBackwardRefs(refs);
726 GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
727 &window_size, &iter_pos, &iter_limit);
728 HashChainInit(hash_chain);
729 for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
730 int offset = 0;
731 int len = 0;
732 int max_len = chosen_path[ix];
733 if (max_len != 1) {
734 HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
735 window_size, iter_pos, iter_limit,
736 &offset, &len);
737 assert(len == max_len);
738 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
739 if (use_color_cache) {
740 for (k = 0; k < len; ++k) {
741 VP8LColorCacheInsert(&hashers, argb[i + k]);
745 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
746 for (k = 0; k < last; ++k) {
747 HashChainInsert(hash_chain, &argb[i + k], i + k);
750 i += len;
751 } else {
752 PixOrCopy v;
753 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
754 // push pixel as a color cache index
755 const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
756 v = PixOrCopyCreateCacheIdx(idx);
757 } else {
758 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
759 v = PixOrCopyCreateLiteral(argb[i]);
761 BackwardRefsCursorAdd(refs, v);
762 if (i + 1 < pix_count) {
763 HashChainInsert(hash_chain, &argb[i], i);
765 ++i;
768 ok = !refs->error_;
769 Error:
770 if (cc_init) VP8LColorCacheClear(&hashers);
771 return ok;
774 // Returns 1 on success.
775 static int BackwardReferencesTraceBackwards(int xsize, int ysize,
776 int recursive_cost_model,
777 const uint32_t* const argb,
778 int quality, int cache_bits,
779 VP8LHashChain* const hash_chain,
780 VP8LBackwardRefs* const refs) {
781 int ok = 0;
782 const int dist_array_size = xsize * ysize;
783 uint32_t* chosen_path = NULL;
784 int chosen_path_size = 0;
785 uint32_t* dist_array =
786 (uint32_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));
788 if (dist_array == NULL) goto Error;
790 if (!BackwardReferencesHashChainDistanceOnly(
791 xsize, ysize, recursive_cost_model, argb, quality, cache_bits, hash_chain,
792 refs, dist_array)) {
793 goto Error;
795 TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
796 if (!BackwardReferencesHashChainFollowChosenPath(
797 xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
798 hash_chain, refs)) {
799 goto Error;
801 ok = 1;
802 Error:
803 WebPSafeFree(dist_array);
804 return ok;
807 static void BackwardReferences2DLocality(int xsize,
808 const VP8LBackwardRefs* const refs) {
809 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
810 while (VP8LRefsCursorOk(&c)) {
811 if (PixOrCopyIsCopy(c.cur_pos)) {
812 const int dist = c.cur_pos->argb_or_distance;
813 const int transformed_dist = DistanceToPlaneCode(xsize, dist);
814 c.cur_pos->argb_or_distance = transformed_dist;
816 VP8LRefsCursorNext(&c);
820 VP8LBackwardRefs* VP8LGetBackwardReferences(
821 int width, int height, const uint32_t* const argb, int quality,
822 int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain,
823 VP8LBackwardRefs refs_array[2]) {
824 int lz77_is_useful;
825 const int num_pix = width * height;
826 VP8LBackwardRefs* best = NULL;
827 VP8LBackwardRefs* const refs_lz77 = &refs_array[0];
828 VP8LBackwardRefs* const refs_rle = &refs_array[1];
830 if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
831 hash_chain, refs_lz77)) {
832 return NULL;
834 if (!BackwardReferencesRle(width, height, argb, refs_rle)) {
835 return NULL;
839 double bit_cost_lz77, bit_cost_rle;
840 VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
841 if (histo == NULL) return NULL;
842 // Evaluate LZ77 coding.
843 VP8LHistogramCreate(histo, refs_lz77, cache_bits);
844 bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
845 // Evaluate RLE coding.
846 VP8LHistogramCreate(histo, refs_rle, cache_bits);
847 bit_cost_rle = VP8LHistogramEstimateBits(histo);
848 // Decide if LZ77 is useful.
849 lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
850 VP8LFreeHistogram(histo);
853 // Choose appropriate backward reference.
854 if (lz77_is_useful) {
855 // TraceBackwards is costly. Don't execute it at lower quality.
856 const int try_lz77_trace_backwards = (quality >= 25);
857 best = refs_lz77; // default guess: lz77 is better
858 if (try_lz77_trace_backwards) {
859 // Set recursion level for large images using a color cache.
860 const int recursion_level =
861 (num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0;
862 VP8LBackwardRefs* const refs_trace = &refs_array[1];
863 ClearBackwardRefs(refs_trace);
864 if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb,
865 quality, cache_bits, hash_chain,
866 refs_trace)) {
867 best = refs_trace;
870 } else {
871 best = refs_rle;
874 if (use_2d_locality) BackwardReferences2DLocality(width, best);
876 return best;
879 // Returns entropy for the given cache bits.
880 static double ComputeCacheEntropy(const uint32_t* const argb,
881 int xsize, int ysize,
882 const VP8LBackwardRefs* const refs,
883 int cache_bits) {
884 int pixel_index = 0;
885 uint32_t k;
886 const int use_color_cache = (cache_bits > 0);
887 int cc_init = 0;
888 double entropy = MAX_ENTROPY;
889 const double kSmallPenaltyForLargeCache = 4.0;
890 VP8LColorCache hashers;
891 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
892 VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits);
893 if (histo == NULL) goto Error;
895 if (use_color_cache) {
896 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
897 if (!cc_init) goto Error;
900 while (VP8LRefsCursorOk(&c)) {
901 const PixOrCopy* const v = c.cur_pos;
902 if (PixOrCopyIsLiteral(v)) {
903 if (use_color_cache &&
904 VP8LColorCacheContains(&hashers, argb[pixel_index])) {
905 // push pixel as a cache index
906 const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
907 const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
908 VP8LHistogramAddSinglePixOrCopy(histo, &token);
909 } else {
910 VP8LHistogramAddSinglePixOrCopy(histo, v);
912 } else {
913 VP8LHistogramAddSinglePixOrCopy(histo, v);
915 if (use_color_cache) {
916 for (k = 0; k < PixOrCopyLength(v); ++k) {
917 VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
920 pixel_index += PixOrCopyLength(v);
921 VP8LRefsCursorNext(&c);
923 assert(pixel_index == xsize * ysize);
924 (void)xsize; // xsize is not used in non-debug compilations otherwise.
925 (void)ysize; // ysize is not used in non-debug compilations otherwise.
926 entropy = VP8LHistogramEstimateBits(histo) +
927 kSmallPenaltyForLargeCache * cache_bits;
928 Error:
929 if (cc_init) VP8LColorCacheClear(&hashers);
930 VP8LFreeHistogram(histo);
931 return entropy;
934 // *best_cache_bits will contain how many bits are to be used for a color cache.
935 // Returns 0 in case of memory error.
936 int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
937 int xsize, int ysize, int quality,
938 VP8LHashChain* const hash_chain,
939 VP8LBackwardRefs* const refs,
940 int* const best_cache_bits) {
941 int eval_low = 1;
942 int eval_high = 1;
943 double entropy_low = MAX_ENTROPY;
944 double entropy_high = MAX_ENTROPY;
945 int cache_bits_low = 0;
946 int cache_bits_high = MAX_COLOR_CACHE_BITS;
948 if (!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, hash_chain,
949 refs)) {
950 return 0;
952 // Do a binary search to find the optimal entropy for cache_bits.
953 while (cache_bits_high - cache_bits_low > 1) {
954 if (eval_low) {
955 entropy_low =
956 ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_low);
957 eval_low = 0;
959 if (eval_high) {
960 entropy_high =
961 ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_high);
962 eval_high = 0;
964 if (entropy_high < entropy_low) {
965 *best_cache_bits = cache_bits_high;
966 cache_bits_low = (cache_bits_low + cache_bits_high) / 2;
967 eval_low = 1;
968 } else {
969 *best_cache_bits = cache_bits_low;
970 cache_bits_high = (cache_bits_low + cache_bits_high) / 2;
971 eval_high = 1;
974 return 1;