1 // Copyright 2012 Google Inc. All Rights Reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
10 // Author: Jyrki Alakuijala (jyrki@google.com)
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.
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;
61 static WEBP_INLINE
int FindMatchLength(const uint32_t* const array1
,
62 const uint32_t* const array2
,
63 const int max_limit
) {
65 while (match_len
< max_limit
&& array1
[match_len
] == array2
[match_len
]) {
71 // -----------------------------------------------------------------------------
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
) {
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
;
91 void VP8LBackwardRefsClear(VP8LBackwardRefs
* const refs
) {
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_
;
106 (block_size
< MIN_BLOCK_SIZE
) ? MIN_BLOCK_SIZE
: block_size
;
109 VP8LRefsCursor
VP8LRefsCursorInit(const VP8LBackwardRefs
* const refs
) {
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_
;
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_
;
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
);
140 b
->start_
= (PixOrCopy
*)((uint8_t*)b
+ sizeof(*b
)); // not always aligned
141 } else { // recycle from free-list
142 refs
->free_blocks_
= b
->next_
;
145 refs
->tail_
= &b
->next_
;
146 refs
->last_block_
= b
;
152 static WEBP_INLINE
void BackwardRefsCursorAdd(VP8LBackwardRefs
* const refs
,
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_
);
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_
;
177 // -----------------------------------------------------------------------------
180 // initialize as empty
181 static void HashChainInit(VP8LHashChain
* const p
) {
184 for (i
= 0; i
< p
->size_
; ++i
) {
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
);
196 p
->chain_
= (int*)WebPSafeMalloc(size
, sizeof(*p
->chain_
));
197 if (p
->chain_
== NULL
) return 0;
203 void VP8LHashChainClear(VP8LHashChain
* const p
) {
205 WebPSafeFree(p
->chain_
);
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
);
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)
236 *window_size
= (max_window_size
> WINDOW_SIZE
) ? WINDOW_SIZE
238 *iter_pos
= 8 + (quality
>> 3);
239 // For lower entropy images, the rigorous search loop in HashChainFindCopy
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
;
256 (base_position
> window_size
) ? base_position
- window_size
: 0;
259 if (max_len
> MAX_LENGTH
) {
260 max_len
= MAX_LENGTH
;
262 for (pos
= p
->hash_to_first_index_
[GetPixPairHash64(argb_start
)];
264 pos
= p
->chain_
[pos
]) {
266 uint32_t curr_length
;
268 const uint32_t* const ptr1
= (argb
+ pos
+ best_length
- 1);
269 const uint32_t* const ptr2
= (argb_start
+ best_length
- 1);
272 if (iter_pos
< iter_limit
|| best_val
>= 0xff0000) {
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)) {
295 val
+= 9 * 9 + 9 * 9;
296 val
-= y
* y
+ x
* x
;
299 if (best_val
< val
) {
301 best_length
= curr_length
;
302 best_distance
= distance
;
303 if (curr_length
>= (uint32_t)max_len
) {
306 if ((best_distance
== 1 || distance
== xsize
) &&
307 best_length
>= 128) {
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
;
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
;
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]) {
340 PushBackCopy(refs
, match_len
);
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
) {
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
;
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.
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
,
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.
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
,
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.
399 if (use_color_cache
&& VP8LColorCacheContains(&hashers
, pixel
)) {
400 const int ix
= VP8LColorCacheGetIndex(&hashers
, pixel
);
401 v
= PixOrCopyCreateCacheIdx(ix
);
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.
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
);
430 const uint32_t pixel
= argb
[i
];
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
);
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
);
449 if (cc_init
) VP8LColorCacheClear(&hashers
);
453 // -----------------------------------------------------------------------------
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
];
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
[]) {
474 for (i
= 0; i
< num_symbols
; ++i
) {
475 sum
+= population_counts
[i
];
476 if (population_counts
[i
] > 0) {
481 memset(output
, 0, num_symbols
* sizeof(*output
));
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
) {
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
,
506 if (!BackwardReferencesHashChain(xsize
, ysize
, argb
, cache_bits
, quality
,
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_
);
530 VP8LFreeHistogram(histo
);
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] +
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
,
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
,
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
) {
567 const int pix_count
= xsize
* ysize
;
568 const int use_color_cache
= (cache_bits
> 0);
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
;
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
)) {
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.
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;
604 prev_cost
= cost
[i
- 1];
606 for (shortmax
= 0; shortmax
< 2; ++shortmax
) {
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
,
615 if (len
>= MIN_LENGTH
) {
616 const int code
= DistanceToPlaneCode(xsize
, offset
);
617 const double distance_cost
=
618 prev_cost
+ GetDistanceCost(cost_model
, code
);
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
642 for (k
= i
; k
< last
; ++k
) {
643 HashChainInsert(hash_chain
, &argb
[k
], k
);
647 i
+= len
- 1; // for loop does ++i, thus -1 here.
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
;
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.
672 // Last pixel still to do, it can only be a single step if not reached
673 // through cheaper means already.
676 if (cc_init
) VP8LColorCacheClear(&hashers
);
677 WebPSafeFree(cost_model
);
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
,
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
) {
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);
715 int window_size
= WINDOW_SIZE
;
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
) {
732 int max_len
= chosen_path
[ix
];
734 HashChainFindCopy(hash_chain
, i
, xsize
, argb
, max_len
,
735 window_size
, iter_pos
, iter_limit
,
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
);
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
);
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
);
770 if (cc_init
) VP8LColorCacheClear(&hashers
);
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
) {
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
,
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
,
803 WebPSafeFree(dist_array
);
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]) {
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
)) {
834 if (!BackwardReferencesRle(width
, height
, argb
, refs_rle
)) {
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
,
874 if (use_2d_locality
) BackwardReferences2DLocality(width
, 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
,
886 const int use_color_cache
= (cache_bits
> 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
);
910 VP8LHistogramAddSinglePixOrCopy(histo
, v
);
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
;
929 if (cc_init
) VP8LColorCacheClear(&hashers
);
930 VP8LFreeHistogram(histo
);
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
) {
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
,
952 // Do a binary search to find the optimal entropy for cache_bits.
953 while (cache_bits_high
- cache_bits_low
> 1) {
956 ComputeCacheEntropy(argb
, xsize
, ysize
, refs
, cache_bits_low
);
961 ComputeCacheEntropy(argb
, xsize
, ysize
, refs
, cache_bits_high
);
964 if (entropy_high
< entropy_low
) {
965 *best_cache_bits
= cache_bits_high
;
966 cache_bits_low
= (cache_bits_low
+ cache_bits_high
) / 2;
969 *best_cache_bits
= cache_bits_low
;
970 cache_bits_high
= (cache_bits_low
+ cache_bits_high
) / 2;