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)
11 //
12
13 #include <assert.h>
14 #include <math.h>
15
16 #include "./backward_references.h"
17 #include "./histogram.h"
18 #include "../dsp/lossless.h"
19 #include "../dsp/dsp.h"
20 #include "../utils/color_cache.h"
21 #include "../utils/utils.h"
22
23 #define VALUES_IN_BYTE 256
24
25 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references
26
27 #define MAX_ENTROPY (1e30f)
28
29 // 1M window (4M bytes) minus 120 special codes for short distances.
30 #define WINDOW_SIZE ((1 << 20) - 120)
31
32 // Bounds for the match length.
33 #define MIN_LENGTH 2
34 #define MAX_LENGTH 4096
35
36 // -----------------------------------------------------------------------------
37
38 static const uint8_t plane_to_code_lut[128] = {
39 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
40 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
41 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
42 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
43 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
44 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
45 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
46 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
47 };
48
DistanceToPlaneCode(int xsize,int dist)49 static int DistanceToPlaneCode(int xsize, int dist) {
50 const int yoffset = dist / xsize;
51 const int xoffset = dist - yoffset * xsize;
52 if (xoffset <= 8 && yoffset < 8) {
53 return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
54 } else if (xoffset > xsize - 8 && yoffset < 7) {
55 return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
56 }
57 return dist + 120;
58 }
59
60 // Returns the exact index where array1 and array2 are different if this
61 // index is strictly superior to best_len_match. Otherwise, it returns 0.
62 // If no two elements are the same, it returns max_limit.
FindMatchLength(const uint32_t * const array1,const uint32_t * const array2,int best_len_match,int max_limit)63 static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
64 const uint32_t* const array2,
65 int best_len_match,
66 int max_limit) {
67 int match_len;
68
69 // Before 'expensive' linear match, check if the two arrays match at the
70 // current best length index.
71 if (array1[best_len_match] != array2[best_len_match]) return 0;
72
73 #if defined(WEBP_USE_SSE2)
74 // Check if anything is different up to best_len_match excluded.
75 // memcmp seems to be slower on ARM so it is disabled for now.
76 if (memcmp(array1, array2, best_len_match * sizeof(*array1))) return 0;
77 match_len = best_len_match + 1;
78 #else
79 match_len = 0;
80 #endif
81
82 while (match_len < max_limit && array1[match_len] == array2[match_len]) {
83 ++match_len;
84 }
85 return match_len;
86 }
87
88 // -----------------------------------------------------------------------------
89 // VP8LBackwardRefs
90
91 struct PixOrCopyBlock {
92 PixOrCopyBlock* next_; // next block (or NULL)
93 PixOrCopy* start_; // data start
94 int size_; // currently used size
95 };
96
ClearBackwardRefs(VP8LBackwardRefs * const refs)97 static void ClearBackwardRefs(VP8LBackwardRefs* const refs) {
98 assert(refs != NULL);
99 if (refs->tail_ != NULL) {
100 *refs->tail_ = refs->free_blocks_; // recycle all blocks at once
101 }
102 refs->free_blocks_ = refs->refs_;
103 refs->tail_ = &refs->refs_;
104 refs->last_block_ = NULL;
105 refs->refs_ = NULL;
106 }
107
VP8LBackwardRefsClear(VP8LBackwardRefs * const refs)108 void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
109 assert(refs != NULL);
110 ClearBackwardRefs(refs);
111 while (refs->free_blocks_ != NULL) {
112 PixOrCopyBlock* const next = refs->free_blocks_->next_;
113 WebPSafeFree(refs->free_blocks_);
114 refs->free_blocks_ = next;
115 }
116 }
117
VP8LBackwardRefsInit(VP8LBackwardRefs * const refs,int block_size)118 void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
119 assert(refs != NULL);
120 memset(refs, 0, sizeof(*refs));
121 refs->tail_ = &refs->refs_;
122 refs->block_size_ =
123 (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;
124 }
125
VP8LRefsCursorInit(const VP8LBackwardRefs * const refs)126 VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {
127 VP8LRefsCursor c;
128 c.cur_block_ = refs->refs_;
129 if (refs->refs_ != NULL) {
130 c.cur_pos = c.cur_block_->start_;
131 c.last_pos_ = c.cur_pos + c.cur_block_->size_;
132 } else {
133 c.cur_pos = NULL;
134 c.last_pos_ = NULL;
135 }
136 return c;
137 }
138
VP8LRefsCursorNextBlock(VP8LRefsCursor * const c)139 void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {
140 PixOrCopyBlock* const b = c->cur_block_->next_;
141 c->cur_pos = (b == NULL) ? NULL : b->start_;
142 c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;
143 c->cur_block_ = b;
144 }
145
146 // Create a new block, either from the free list or allocated
BackwardRefsNewBlock(VP8LBackwardRefs * const refs)147 static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
148 PixOrCopyBlock* b = refs->free_blocks_;
149 if (b == NULL) { // allocate new memory chunk
150 const size_t total_size =
151 sizeof(*b) + refs->block_size_ * sizeof(*b->start_);
152 b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);
153 if (b == NULL) {
154 refs->error_ |= 1;
155 return NULL;
156 }
157 b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned
158 } else { // recycle from free-list
159 refs->free_blocks_ = b->next_;
160 }
161 *refs->tail_ = b;
162 refs->tail_ = &b->next_;
163 refs->last_block_ = b;
164 b->next_ = NULL;
165 b->size_ = 0;
166 return b;
167 }
168
BackwardRefsCursorAdd(VP8LBackwardRefs * const refs,const PixOrCopy v)169 static WEBP_INLINE void BackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
170 const PixOrCopy v) {
171 PixOrCopyBlock* b = refs->last_block_;
172 if (b == NULL || b->size_ == refs->block_size_) {
173 b = BackwardRefsNewBlock(refs);
174 if (b == NULL) return; // refs->error_ is set
175 }
176 b->start_[b->size_++] = v;
177 }
178
VP8LBackwardRefsCopy(const VP8LBackwardRefs * const src,VP8LBackwardRefs * const dst)179 int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
180 VP8LBackwardRefs* const dst) {
181 const PixOrCopyBlock* b = src->refs_;
182 ClearBackwardRefs(dst);
183 assert(src->block_size_ == dst->block_size_);
184 while (b != NULL) {
185 PixOrCopyBlock* const new_b = BackwardRefsNewBlock(dst);
186 if (new_b == NULL) return 0; // dst->error_ is set
187 memcpy(new_b->start_, b->start_, b->size_ * sizeof(*b->start_));
188 new_b->size_ = b->size_;
189 b = b->next_;
190 }
191 return 1;
192 }
193
194 // -----------------------------------------------------------------------------
195 // Hash chains
196
197 // initialize as empty
HashChainReset(VP8LHashChain * const p)198 static void HashChainReset(VP8LHashChain* const p) {
199 assert(p != NULL);
200 // Set the int32_t arrays to -1.
201 memset(p->chain_, 0xff, p->size_ * sizeof(*p->chain_));
202 memset(p->hash_to_first_index_, 0xff,
203 HASH_SIZE * sizeof(*p->hash_to_first_index_));
204 }
205
VP8LHashChainInit(VP8LHashChain * const p,int size)206 int VP8LHashChainInit(VP8LHashChain* const p, int size) {
207 assert(p->size_ == 0);
208 assert(p->chain_ == NULL);
209 assert(size > 0);
210 p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_));
211 if (p->chain_ == NULL) return 0;
212 p->size_ = size;
213 HashChainReset(p);
214 return 1;
215 }
216
VP8LHashChainClear(VP8LHashChain * const p)217 void VP8LHashChainClear(VP8LHashChain* const p) {
218 assert(p != NULL);
219 WebPSafeFree(p->chain_);
220 p->size_ = 0;
221 p->chain_ = NULL;
222 }
223
224 // -----------------------------------------------------------------------------
225
226 #define HASH_MULTIPLIER_HI (0xc6a4a793U)
227 #define HASH_MULTIPLIER_LO (0x5bd1e996U)
228
GetPixPairHash64(const uint32_t * const argb)229 static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
230 uint32_t key;
231 key = argb[1] * HASH_MULTIPLIER_HI;
232 key += argb[0] * HASH_MULTIPLIER_LO;
233 key = key >> (32 - HASH_BITS);
234 return key;
235 }
236
237 // Insertion of two pixels at a time.
HashChainInsert(VP8LHashChain * const p,const uint32_t * const argb,int pos)238 static void HashChainInsert(VP8LHashChain* const p,
239 const uint32_t* const argb, int pos) {
240 const uint32_t hash_code = GetPixPairHash64(argb);
241 p->chain_[pos] = p->hash_to_first_index_[hash_code];
242 p->hash_to_first_index_[hash_code] = pos;
243 }
244
245 // Returns the maximum number of hash chain lookups to do for a
246 // given compression quality. Return value in range [6, 86].
GetMaxItersForQuality(int quality,int low_effort)247 static int GetMaxItersForQuality(int quality, int low_effort) {
248 return (low_effort ? 6 : 8) + (quality * quality) / 128;
249 }
250
GetWindowSizeForHashChain(int quality,int xsize)251 static int GetWindowSizeForHashChain(int quality, int xsize) {
252 const int max_window_size = (quality > 75) ? WINDOW_SIZE
253 : (quality > 50) ? (xsize << 8)
254 : (quality > 25) ? (xsize << 6)
255 : (xsize << 4);
256 assert(xsize > 0);
257 return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;
258 }
259
MaxFindCopyLength(int len)260 static WEBP_INLINE int MaxFindCopyLength(int len) {
261 return (len < MAX_LENGTH) ? len : MAX_LENGTH;
262 }
263
HashChainFindOffset(const VP8LHashChain * const p,int base_position,const uint32_t * const argb,int len,int window_size,int * const distance_ptr)264 static void HashChainFindOffset(const VP8LHashChain* const p, int base_position,
265 const uint32_t* const argb, int len,
266 int window_size, int* const distance_ptr) {
267 const uint32_t* const argb_start = argb + base_position;
268 const int min_pos =
269 (base_position > window_size) ? base_position - window_size : 0;
270 int pos;
271 assert(len <= MAX_LENGTH);
272 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
273 pos >= min_pos;
274 pos = p->chain_[pos]) {
275 const int curr_length =
276 FindMatchLength(argb + pos, argb_start, len - 1, len);
277 if (curr_length == len) break;
278 }
279 *distance_ptr = base_position - pos;
280 }
281
HashChainFindCopy(const VP8LHashChain * const p,int base_position,const uint32_t * const argb,int max_len,int window_size,int iter_max,int * const distance_ptr,int * const length_ptr)282 static int HashChainFindCopy(const VP8LHashChain* const p,
283 int base_position,
284 const uint32_t* const argb, int max_len,
285 int window_size, int iter_max,
286 int* const distance_ptr,
287 int* const length_ptr) {
288 const uint32_t* const argb_start = argb + base_position;
289 int iter = iter_max;
290 int best_length = 0;
291 int best_distance = 0;
292 const int min_pos =
293 (base_position > window_size) ? base_position - window_size : 0;
294 int pos;
295 int length_max = 256;
296 if (max_len < length_max) {
297 length_max = max_len;
298 }
299 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
300 pos >= min_pos;
301 pos = p->chain_[pos]) {
302 int curr_length;
303 int distance;
304 if (--iter < 0) {
305 break;
306 }
307
308 curr_length = FindMatchLength(argb + pos, argb_start, best_length, max_len);
309 if (best_length < curr_length) {
310 distance = base_position - pos;
311 best_length = curr_length;
312 best_distance = distance;
313 if (curr_length >= length_max) {
314 break;
315 }
316 }
317 }
318 *distance_ptr = best_distance;
319 *length_ptr = best_length;
320 return (best_length >= MIN_LENGTH);
321 }
322
AddSingleLiteral(uint32_t pixel,int use_color_cache,VP8LColorCache * const hashers,VP8LBackwardRefs * const refs)323 static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
324 VP8LColorCache* const hashers,
325 VP8LBackwardRefs* const refs) {
326 PixOrCopy v;
327 if (use_color_cache) {
328 const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);
329 if (VP8LColorCacheLookup(hashers, key) == pixel) {
330 v = PixOrCopyCreateCacheIdx(key);
331 } else {
332 v = PixOrCopyCreateLiteral(pixel);
333 VP8LColorCacheSet(hashers, key, pixel);
334 }
335 } else {
336 v = PixOrCopyCreateLiteral(pixel);
337 }
338 BackwardRefsCursorAdd(refs, v);
339 }
340
BackwardReferencesRle(int xsize,int ysize,const uint32_t * const argb,int cache_bits,VP8LBackwardRefs * const refs)341 static int BackwardReferencesRle(int xsize, int ysize,
342 const uint32_t* const argb,
343 int cache_bits, VP8LBackwardRefs* const refs) {
344 const int pix_count = xsize * ysize;
345 int i, k;
346 const int use_color_cache = (cache_bits > 0);
347 VP8LColorCache hashers;
348
349 if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {
350 return 0;
351 }
352 ClearBackwardRefs(refs);
353 // Add first pixel as literal.
354 AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);
355 i = 1;
356 while (i < pix_count) {
357 const int max_len = MaxFindCopyLength(pix_count - i);
358 const int kMinLength = 4;
359 const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);
360 const int prev_row_len = (i < xsize) ? 0 :
361 FindMatchLength(argb + i, argb + i - xsize, 0, max_len);
362 if (rle_len >= prev_row_len && rle_len >= kMinLength) {
363 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));
364 // We don't need to update the color cache here since it is always the
365 // same pixel being copied, and that does not change the color cache
366 // state.
367 i += rle_len;
368 } else if (prev_row_len >= kMinLength) {
369 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));
370 if (use_color_cache) {
371 for (k = 0; k < prev_row_len; ++k) {
372 VP8LColorCacheInsert(&hashers, argb[i + k]);
373 }
374 }
375 i += prev_row_len;
376 } else {
377 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
378 i++;
379 }
380 }
381 if (use_color_cache) VP8LColorCacheClear(&hashers);
382 return !refs->error_;
383 }
384
BackwardReferencesLz77(int xsize,int ysize,const uint32_t * const argb,int cache_bits,int quality,int low_effort,VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs)385 static int BackwardReferencesLz77(int xsize, int ysize,
386 const uint32_t* const argb, int cache_bits,
387 int quality, int low_effort,
388 VP8LHashChain* const hash_chain,
389 VP8LBackwardRefs* const refs) {
390 int i;
391 int ok = 0;
392 int cc_init = 0;
393 const int use_color_cache = (cache_bits > 0);
394 const int pix_count = xsize * ysize;
395 VP8LColorCache hashers;
396 int iter_max = GetMaxItersForQuality(quality, low_effort);
397 const int window_size = GetWindowSizeForHashChain(quality, xsize);
398 int min_matches = 32;
399
400 if (use_color_cache) {
401 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
402 if (!cc_init) goto Error;
403 }
404 ClearBackwardRefs(refs);
405 HashChainReset(hash_chain);
406 for (i = 0; i < pix_count - 2; ) {
407 // Alternative#1: Code the pixels starting at 'i' using backward reference.
408 int offset = 0;
409 int len = 0;
410 const int max_len = MaxFindCopyLength(pix_count - i);
411 HashChainFindCopy(hash_chain, i, argb, max_len, window_size,
412 iter_max, &offset, &len);
413 if (len > MIN_LENGTH || (len == MIN_LENGTH && offset <= 512)) {
414 int offset2 = 0;
415 int len2 = 0;
416 int k;
417 min_matches = 8;
418 HashChainInsert(hash_chain, &argb[i], i);
419 if ((len < (max_len >> 2)) && !low_effort) {
420 // Evaluate Alternative#2: Insert the pixel at 'i' as literal, and code
421 // the pixels starting at 'i + 1' using backward reference.
422 HashChainFindCopy(hash_chain, i + 1, argb, max_len - 1,
423 window_size, iter_max, &offset2,
424 &len2);
425 if (len2 > len + 1) {
426 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
427 i++; // Backward reference to be done for next pixel.
428 len = len2;
429 offset = offset2;
430 }
431 }
432 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
433 if (use_color_cache) {
434 for (k = 0; k < len; ++k) {
435 VP8LColorCacheInsert(&hashers, argb[i + k]);
436 }
437 }
438 // Add to the hash_chain (but cannot add the last pixel).
439 if (offset >= 3 && offset != xsize) {
440 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
441 for (k = 2; k < last - 8; k += 2) {
442 HashChainInsert(hash_chain, &argb[i + k], i + k);
443 }
444 for (; k < last; ++k) {
445 HashChainInsert(hash_chain, &argb[i + k], i + k);
446 }
447 }
448 i += len;
449 } else {
450 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
451 HashChainInsert(hash_chain, &argb[i], i);
452 ++i;
453 --min_matches;
454 if (min_matches <= 0) {
455 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
456 HashChainInsert(hash_chain, &argb[i], i);
457 ++i;
458 }
459 }
460 }
461 while (i < pix_count) {
462 // Handle the last pixel(s).
463 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
464 ++i;
465 }
466
467 ok = !refs->error_;
468 Error:
469 if (cc_init) VP8LColorCacheClear(&hashers);
470 return ok;
471 }
472
473 // -----------------------------------------------------------------------------
474
475 typedef struct {
476 double alpha_[VALUES_IN_BYTE];
477 double red_[VALUES_IN_BYTE];
478 double blue_[VALUES_IN_BYTE];
479 double distance_[NUM_DISTANCE_CODES];
480 double* literal_;
481 } CostModel;
482
483 static int BackwardReferencesTraceBackwards(
484 int xsize, int ysize, const uint32_t* const argb, int quality,
485 int cache_bits, VP8LHashChain* const hash_chain,
486 VP8LBackwardRefs* const refs);
487
ConvertPopulationCountTableToBitEstimates(int num_symbols,const uint32_t population_counts[],double output[])488 static void ConvertPopulationCountTableToBitEstimates(
489 int num_symbols, const uint32_t population_counts[], double output[]) {
490 uint32_t sum = 0;
491 int nonzeros = 0;
492 int i;
493 for (i = 0; i < num_symbols; ++i) {
494 sum += population_counts[i];
495 if (population_counts[i] > 0) {
496 ++nonzeros;
497 }
498 }
499 if (nonzeros <= 1) {
500 memset(output, 0, num_symbols * sizeof(*output));
501 } else {
502 const double logsum = VP8LFastLog2(sum);
503 for (i = 0; i < num_symbols; ++i) {
504 output[i] = logsum - VP8LFastLog2(population_counts[i]);
505 }
506 }
507 }
508
CostModelBuild(CostModel * const m,int cache_bits,VP8LBackwardRefs * const refs)509 static int CostModelBuild(CostModel* const m, int cache_bits,
510 VP8LBackwardRefs* const refs) {
511 int ok = 0;
512 VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
513 if (histo == NULL) goto Error;
514
515 VP8LHistogramCreate(histo, refs, cache_bits);
516
517 ConvertPopulationCountTableToBitEstimates(
518 VP8LHistogramNumCodes(histo->palette_code_bits_),
519 histo->literal_, m->literal_);
520 ConvertPopulationCountTableToBitEstimates(
521 VALUES_IN_BYTE, histo->red_, m->red_);
522 ConvertPopulationCountTableToBitEstimates(
523 VALUES_IN_BYTE, histo->blue_, m->blue_);
524 ConvertPopulationCountTableToBitEstimates(
525 VALUES_IN_BYTE, histo->alpha_, m->alpha_);
526 ConvertPopulationCountTableToBitEstimates(
527 NUM_DISTANCE_CODES, histo->distance_, m->distance_);
528 ok = 1;
529
530 Error:
531 VP8LFreeHistogram(histo);
532 return ok;
533 }
534
GetLiteralCost(const CostModel * const m,uint32_t v)535 static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
536 return m->alpha_[v >> 24] +
537 m->red_[(v >> 16) & 0xff] +
538 m->literal_[(v >> 8) & 0xff] +
539 m->blue_[v & 0xff];
540 }
541
GetCacheCost(const CostModel * const m,uint32_t idx)542 static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
543 const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
544 return m->literal_[literal_idx];
545 }
546
GetLengthCost(const CostModel * const m,uint32_t length)547 static WEBP_INLINE double GetLengthCost(const CostModel* const m,
548 uint32_t length) {
549 int code, extra_bits;
550 VP8LPrefixEncodeBits(length, &code, &extra_bits);
551 return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
552 }
553
GetDistanceCost(const CostModel * const m,uint32_t distance)554 static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
555 uint32_t distance) {
556 int code, extra_bits;
557 VP8LPrefixEncodeBits(distance, &code, &extra_bits);
558 return m->distance_[code] + extra_bits;
559 }
560
AddSingleLiteralWithCostModel(const uint32_t * const argb,VP8LHashChain * const hash_chain,VP8LColorCache * const hashers,const CostModel * const cost_model,int idx,int is_last,int use_color_cache,double prev_cost,float * const cost,uint16_t * const dist_array)561 static void AddSingleLiteralWithCostModel(
562 const uint32_t* const argb, VP8LHashChain* const hash_chain,
563 VP8LColorCache* const hashers, const CostModel* const cost_model, int idx,
564 int is_last, int use_color_cache, double prev_cost, float* const cost,
565 uint16_t* const dist_array) {
566 double cost_val = prev_cost;
567 const uint32_t color = argb[0];
568 if (!is_last) {
569 HashChainInsert(hash_chain, argb, idx);
570 }
571 if (use_color_cache && VP8LColorCacheContains(hashers, color)) {
572 const double mul0 = 0.68;
573 const int ix = VP8LColorCacheGetIndex(hashers, color);
574 cost_val += GetCacheCost(cost_model, ix) * mul0;
575 } else {
576 const double mul1 = 0.82;
577 if (use_color_cache) VP8LColorCacheInsert(hashers, color);
578 cost_val += GetLiteralCost(cost_model, color) * mul1;
579 }
580 if (cost[idx] > cost_val) {
581 cost[idx] = (float)cost_val;
582 dist_array[idx] = 1; // only one is inserted.
583 }
584 }
585
BackwardReferencesHashChainDistanceOnly(int xsize,int ysize,const uint32_t * const argb,int quality,int cache_bits,VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs,uint16_t * const dist_array)586 static int BackwardReferencesHashChainDistanceOnly(
587 int xsize, int ysize, const uint32_t* const argb,
588 int quality, int cache_bits, VP8LHashChain* const hash_chain,
589 VP8LBackwardRefs* const refs, uint16_t* const dist_array) {
590 int i;
591 int ok = 0;
592 int cc_init = 0;
593 const int pix_count = xsize * ysize;
594 const int use_color_cache = (cache_bits > 0);
595 float* const cost =
596 (float*)WebPSafeMalloc(pix_count, sizeof(*cost));
597 const size_t literal_array_size = sizeof(double) *
598 (NUM_LITERAL_CODES + NUM_LENGTH_CODES +
599 ((cache_bits > 0) ? (1 << cache_bits) : 0));
600 const size_t cost_model_size = sizeof(CostModel) + literal_array_size;
601 CostModel* const cost_model =
602 (CostModel*)WebPSafeMalloc(1ULL, cost_model_size);
603 VP8LColorCache hashers;
604 const int skip_length = 32 + quality;
605 const int skip_min_distance_code = 2;
606 int iter_max = GetMaxItersForQuality(quality, 0);
607 const int window_size = GetWindowSizeForHashChain(quality, xsize);
608
609 if (cost == NULL || cost_model == NULL) goto Error;
610
611 cost_model->literal_ = (double*)(cost_model + 1);
612 if (use_color_cache) {
613 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
614 if (!cc_init) goto Error;
615 }
616
617 if (!CostModelBuild(cost_model, cache_bits, refs)) {
618 goto Error;
619 }
620
621 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f;
622
623 // We loop one pixel at a time, but store all currently best points to
624 // non-processed locations from this point.
625 dist_array[0] = 0;
626 HashChainReset(hash_chain);
627 // Add first pixel as literal.
628 AddSingleLiteralWithCostModel(argb + 0, hash_chain, &hashers, cost_model, 0,
629 0, use_color_cache, 0.0, cost, dist_array);
630 for (i = 1; i < pix_count - 1; ++i) {
631 int offset = 0;
632 int len = 0;
633 double prev_cost = cost[i - 1];
634 const int max_len = MaxFindCopyLength(pix_count - i);
635 HashChainFindCopy(hash_chain, i, argb, max_len, window_size,
636 iter_max, &offset, &len);
637 if (len >= MIN_LENGTH) {
638 const int code = DistanceToPlaneCode(xsize, offset);
639 const double distance_cost =
640 prev_cost + GetDistanceCost(cost_model, code);
641 int k;
642 for (k = 1; k < len; ++k) {
643 const double cost_val = distance_cost + GetLengthCost(cost_model, k);
644 if (cost[i + k] > cost_val) {
645 cost[i + k] = (float)cost_val;
646 dist_array[i + k] = k + 1;
647 }
648 }
649 // This if is for speedup only. It roughly doubles the speed, and
650 // makes compression worse by .1 %.
651 if (len >= skip_length && code <= skip_min_distance_code) {
652 // Long copy for short distances, let's skip the middle
653 // lookups for better copies.
654 // 1) insert the hashes.
655 if (use_color_cache) {
656 for (k = 0; k < len; ++k) {
657 VP8LColorCacheInsert(&hashers, argb[i + k]);
658 }
659 }
660 // 2) Add to the hash_chain (but cannot add the last pixel)
661 {
662 const int last = (len + i < pix_count - 1) ? len + i
663 : pix_count - 1;
664 for (k = i; k < last; ++k) {
665 HashChainInsert(hash_chain, &argb[k], k);
666 }
667 }
668 // 3) jump.
669 i += len - 1; // for loop does ++i, thus -1 here.
670 goto next_symbol;
671 }
672 if (len != MIN_LENGTH) {
673 int code_min_length;
674 double cost_total;
675 HashChainFindOffset(hash_chain, i, argb, MIN_LENGTH, window_size,
676 &offset);
677 code_min_length = DistanceToPlaneCode(xsize, offset);
678 cost_total = prev_cost +
679 GetDistanceCost(cost_model, code_min_length) +
680 GetLengthCost(cost_model, 1);
681 if (cost[i + 1] > cost_total) {
682 cost[i + 1] = (float)cost_total;
683 dist_array[i + 1] = 2;
684 }
685 }
686 }
687 AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i,
688 0, use_color_cache, prev_cost, cost,
689 dist_array);
690 next_symbol: ;
691 }
692 // Handle the last pixel.
693 if (i == (pix_count - 1)) {
694 AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i,
695 1, use_color_cache, cost[pix_count - 2], cost,
696 dist_array);
697 }
698 ok = !refs->error_;
699 Error:
700 if (cc_init) VP8LColorCacheClear(&hashers);
701 WebPSafeFree(cost_model);
702 WebPSafeFree(cost);
703 return ok;
704 }
705
706 // We pack the path at the end of *dist_array and return
707 // a pointer to this part of the array. Example:
708 // dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
TraceBackwards(uint16_t * const dist_array,int dist_array_size,uint16_t ** const chosen_path,int * const chosen_path_size)709 static void TraceBackwards(uint16_t* const dist_array,
710 int dist_array_size,
711 uint16_t** const chosen_path,
712 int* const chosen_path_size) {
713 uint16_t* path = dist_array + dist_array_size;
714 uint16_t* cur = dist_array + dist_array_size - 1;
715 while (cur >= dist_array) {
716 const int k = *cur;
717 --path;
718 *path = k;
719 cur -= k;
720 }
721 *chosen_path = path;
722 *chosen_path_size = (int)(dist_array + dist_array_size - path);
723 }
724
BackwardReferencesHashChainFollowChosenPath(int xsize,int ysize,const uint32_t * const argb,int quality,int cache_bits,const uint16_t * const chosen_path,int chosen_path_size,VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs)725 static int BackwardReferencesHashChainFollowChosenPath(
726 int xsize, int ysize, const uint32_t* const argb,
727 int quality, int cache_bits,
728 const uint16_t* const chosen_path, int chosen_path_size,
729 VP8LHashChain* const hash_chain,
730 VP8LBackwardRefs* const refs) {
731 const int pix_count = xsize * ysize;
732 const int use_color_cache = (cache_bits > 0);
733 int ix;
734 int i = 0;
735 int ok = 0;
736 int cc_init = 0;
737 const int window_size = GetWindowSizeForHashChain(quality, xsize);
738 VP8LColorCache hashers;
739
740 if (use_color_cache) {
741 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
742 if (!cc_init) goto Error;
743 }
744
745 ClearBackwardRefs(refs);
746 HashChainReset(hash_chain);
747 for (ix = 0; ix < chosen_path_size; ++ix) {
748 int offset = 0;
749 const int len = chosen_path[ix];
750 if (len != 1) {
751 int k;
752 HashChainFindOffset(hash_chain, i, argb, len, window_size, &offset);
753 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
754 if (use_color_cache) {
755 for (k = 0; k < len; ++k) {
756 VP8LColorCacheInsert(&hashers, argb[i + k]);
757 }
758 }
759 {
760 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
761 for (k = 0; k < last; ++k) {
762 HashChainInsert(hash_chain, &argb[i + k], i + k);
763 }
764 }
765 i += len;
766 } else {
767 PixOrCopy v;
768 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
769 // push pixel as a color cache index
770 const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
771 v = PixOrCopyCreateCacheIdx(idx);
772 } else {
773 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
774 v = PixOrCopyCreateLiteral(argb[i]);
775 }
776 BackwardRefsCursorAdd(refs, v);
777 if (i + 1 < pix_count) {
778 HashChainInsert(hash_chain, &argb[i], i);
779 }
780 ++i;
781 }
782 }
783 ok = !refs->error_;
784 Error:
785 if (cc_init) VP8LColorCacheClear(&hashers);
786 return ok;
787 }
788
789 // Returns 1 on success.
BackwardReferencesTraceBackwards(int xsize,int ysize,const uint32_t * const argb,int quality,int cache_bits,VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs)790 static int BackwardReferencesTraceBackwards(int xsize, int ysize,
791 const uint32_t* const argb,
792 int quality, int cache_bits,
793 VP8LHashChain* const hash_chain,
794 VP8LBackwardRefs* const refs) {
795 int ok = 0;
796 const int dist_array_size = xsize * ysize;
797 uint16_t* chosen_path = NULL;
798 int chosen_path_size = 0;
799 uint16_t* dist_array =
800 (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));
801
802 if (dist_array == NULL) goto Error;
803
804 if (!BackwardReferencesHashChainDistanceOnly(
805 xsize, ysize, argb, quality, cache_bits, hash_chain,
806 refs, dist_array)) {
807 goto Error;
808 }
809 TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
810 if (!BackwardReferencesHashChainFollowChosenPath(
811 xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
812 hash_chain, refs)) {
813 goto Error;
814 }
815 ok = 1;
816 Error:
817 WebPSafeFree(dist_array);
818 return ok;
819 }
820
BackwardReferences2DLocality(int xsize,const VP8LBackwardRefs * const refs)821 static void BackwardReferences2DLocality(int xsize,
822 const VP8LBackwardRefs* const refs) {
823 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
824 while (VP8LRefsCursorOk(&c)) {
825 if (PixOrCopyIsCopy(c.cur_pos)) {
826 const int dist = c.cur_pos->argb_or_distance;
827 const int transformed_dist = DistanceToPlaneCode(xsize, dist);
828 c.cur_pos->argb_or_distance = transformed_dist;
829 }
830 VP8LRefsCursorNext(&c);
831 }
832 }
833
834 // Returns entropy for the given cache bits.
ComputeCacheEntropy(const uint32_t * argb,const VP8LBackwardRefs * const refs,int cache_bits)835 static double ComputeCacheEntropy(const uint32_t* argb,
836 const VP8LBackwardRefs* const refs,
837 int cache_bits) {
838 const int use_color_cache = (cache_bits > 0);
839 int cc_init = 0;
840 double entropy = MAX_ENTROPY;
841 const double kSmallPenaltyForLargeCache = 4.0;
842 VP8LColorCache hashers;
843 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
844 VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits);
845 if (histo == NULL) goto Error;
846
847 if (use_color_cache) {
848 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
849 if (!cc_init) goto Error;
850 }
851 if (!use_color_cache) {
852 while (VP8LRefsCursorOk(&c)) {
853 VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos);
854 VP8LRefsCursorNext(&c);
855 }
856 } else {
857 while (VP8LRefsCursorOk(&c)) {
858 const PixOrCopy* const v = c.cur_pos;
859 if (PixOrCopyIsLiteral(v)) {
860 const uint32_t pix = *argb++;
861 const uint32_t key = VP8LColorCacheGetIndex(&hashers, pix);
862 if (VP8LColorCacheLookup(&hashers, key) == pix) {
863 ++histo->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];
864 } else {
865 VP8LColorCacheSet(&hashers, key, pix);
866 ++histo->blue_[pix & 0xff];
867 ++histo->literal_[(pix >> 8) & 0xff];
868 ++histo->red_[(pix >> 16) & 0xff];
869 ++histo->alpha_[pix >> 24];
870 }
871 } else {
872 int len = PixOrCopyLength(v);
873 int code, extra_bits;
874 VP8LPrefixEncodeBits(len, &code, &extra_bits);
875 ++histo->literal_[NUM_LITERAL_CODES + code];
876 VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits);
877 ++histo->distance_[code];
878 do {
879 VP8LColorCacheInsert(&hashers, *argb++);
880 } while(--len != 0);
881 }
882 VP8LRefsCursorNext(&c);
883 }
884 }
885 entropy = VP8LHistogramEstimateBits(histo) +
886 kSmallPenaltyForLargeCache * cache_bits;
887 Error:
888 if (cc_init) VP8LColorCacheClear(&hashers);
889 VP8LFreeHistogram(histo);
890 return entropy;
891 }
892
893 // Evaluate optimal cache bits for the local color cache.
894 // The input *best_cache_bits sets the maximum cache bits to use (passing 0
895 // implies disabling the local color cache). The local color cache is also
896 // disabled for the lower (<= 25) quality.
897 // Returns 0 in case of memory error.
CalculateBestCacheSize(const uint32_t * const argb,int xsize,int ysize,int quality,VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs,int * const lz77_computed,int * const best_cache_bits)898 static int CalculateBestCacheSize(const uint32_t* const argb,
899 int xsize, int ysize, int quality,
900 VP8LHashChain* const hash_chain,
901 VP8LBackwardRefs* const refs,
902 int* const lz77_computed,
903 int* const best_cache_bits) {
904 int eval_low = 1;
905 int eval_high = 1;
906 double entropy_low = MAX_ENTROPY;
907 double entropy_high = MAX_ENTROPY;
908 const double cost_mul = 5e-4;
909 int cache_bits_low = 0;
910 int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits;
911
912 assert(cache_bits_high <= MAX_COLOR_CACHE_BITS);
913
914 *lz77_computed = 0;
915 if (cache_bits_high == 0) {
916 *best_cache_bits = 0;
917 // Local color cache is disabled.
918 return 1;
919 }
920 if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, quality, 0,
921 hash_chain, refs)) {
922 return 0;
923 }
924 // Do a binary search to find the optimal entropy for cache_bits.
925 while (eval_low || eval_high) {
926 if (eval_low) {
927 entropy_low = ComputeCacheEntropy(argb, refs, cache_bits_low);
928 entropy_low += entropy_low * cache_bits_low * cost_mul;
929 eval_low = 0;
930 }
931 if (eval_high) {
932 entropy_high = ComputeCacheEntropy(argb, refs, cache_bits_high);
933 entropy_high += entropy_high * cache_bits_high * cost_mul;
934 eval_high = 0;
935 }
936 if (entropy_high < entropy_low) {
937 const int prev_cache_bits_low = cache_bits_low;
938 *best_cache_bits = cache_bits_high;
939 cache_bits_low = (cache_bits_low + cache_bits_high) / 2;
940 if (cache_bits_low != prev_cache_bits_low) eval_low = 1;
941 } else {
942 *best_cache_bits = cache_bits_low;
943 cache_bits_high = (cache_bits_low + cache_bits_high) / 2;
944 if (cache_bits_high != cache_bits_low) eval_high = 1;
945 }
946 }
947 *lz77_computed = 1;
948 return 1;
949 }
950
951 // Update (in-place) backward references for specified cache_bits.
BackwardRefsWithLocalCache(const uint32_t * const argb,int cache_bits,VP8LBackwardRefs * const refs)952 static int BackwardRefsWithLocalCache(const uint32_t* const argb,
953 int cache_bits,
954 VP8LBackwardRefs* const refs) {
955 int pixel_index = 0;
956 VP8LColorCache hashers;
957 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
958 if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;
959
960 while (VP8LRefsCursorOk(&c)) {
961 PixOrCopy* const v = c.cur_pos;
962 if (PixOrCopyIsLiteral(v)) {
963 const uint32_t argb_literal = v->argb_or_distance;
964 if (VP8LColorCacheContains(&hashers, argb_literal)) {
965 const int ix = VP8LColorCacheGetIndex(&hashers, argb_literal);
966 *v = PixOrCopyCreateCacheIdx(ix);
967 } else {
968 VP8LColorCacheInsert(&hashers, argb_literal);
969 }
970 ++pixel_index;
971 } else {
972 // refs was created without local cache, so it can not have cache indexes.
973 int k;
974 assert(PixOrCopyIsCopy(v));
975 for (k = 0; k < v->len; ++k) {
976 VP8LColorCacheInsert(&hashers, argb[pixel_index++]);
977 }
978 }
979 VP8LRefsCursorNext(&c);
980 }
981 VP8LColorCacheClear(&hashers);
982 return 1;
983 }
984
GetBackwardReferencesLowEffort(int width,int height,const uint32_t * const argb,int quality,int * const cache_bits,VP8LHashChain * const hash_chain,VP8LBackwardRefs refs_array[2])985 static VP8LBackwardRefs* GetBackwardReferencesLowEffort(
986 int width, int height, const uint32_t* const argb, int quality,
987 int* const cache_bits, VP8LHashChain* const hash_chain,
988 VP8LBackwardRefs refs_array[2]) {
989 VP8LBackwardRefs* refs_lz77 = &refs_array[0];
990 *cache_bits = 0;
991 if (!BackwardReferencesLz77(width, height, argb, 0, quality,
992 1 /* Low effort. */, hash_chain, refs_lz77)) {
993 return NULL;
994 }
995 BackwardReferences2DLocality(width, refs_lz77);
996 return refs_lz77;
997 }
998
GetBackwardReferences(int width,int height,const uint32_t * const argb,int quality,int * const cache_bits,VP8LHashChain * const hash_chain,VP8LBackwardRefs refs_array[2])999 static VP8LBackwardRefs* GetBackwardReferences(
1000 int width, int height, const uint32_t* const argb, int quality,
1001 int* const cache_bits, VP8LHashChain* const hash_chain,
1002 VP8LBackwardRefs refs_array[2]) {
1003 int lz77_is_useful;
1004 int lz77_computed;
1005 double bit_cost_lz77, bit_cost_rle;
1006 VP8LBackwardRefs* best = NULL;
1007 VP8LBackwardRefs* refs_lz77 = &refs_array[0];
1008 VP8LBackwardRefs* refs_rle = &refs_array[1];
1009 VP8LHistogram* histo = NULL;
1010
1011 if (!CalculateBestCacheSize(argb, width, height, quality, hash_chain,
1012 refs_lz77, &lz77_computed, cache_bits)) {
1013 goto Error;
1014 }
1015
1016 if (lz77_computed) {
1017 // Transform refs_lz77 for the optimized cache_bits.
1018 if (*cache_bits > 0) {
1019 if (!BackwardRefsWithLocalCache(argb, *cache_bits, refs_lz77)) {
1020 goto Error;
1021 }
1022 }
1023 } else {
1024 if (!BackwardReferencesLz77(width, height, argb, *cache_bits, quality,
1025 0 /* Low effort. */, hash_chain, refs_lz77)) {
1026 goto Error;
1027 }
1028 }
1029
1030 if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) {
1031 goto Error;
1032 }
1033
1034 histo = VP8LAllocateHistogram(*cache_bits);
1035 if (histo == NULL) goto Error;
1036
1037 {
1038 // Evaluate LZ77 coding.
1039 VP8LHistogramCreate(histo, refs_lz77, *cache_bits);
1040 bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
1041 // Evaluate RLE coding.
1042 VP8LHistogramCreate(histo, refs_rle, *cache_bits);
1043 bit_cost_rle = VP8LHistogramEstimateBits(histo);
1044 // Decide if LZ77 is useful.
1045 lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
1046 }
1047
1048 // Choose appropriate backward reference.
1049 if (lz77_is_useful) {
1050 // TraceBackwards is costly. Don't execute it at lower quality.
1051 const int try_lz77_trace_backwards = (quality >= 25);
1052 best = refs_lz77; // default guess: lz77 is better
1053 if (try_lz77_trace_backwards) {
1054 VP8LBackwardRefs* const refs_trace = refs_rle;
1055 if (!VP8LBackwardRefsCopy(refs_lz77, refs_trace)) {
1056 best = NULL;
1057 goto Error;
1058 }
1059 if (BackwardReferencesTraceBackwards(width, height, argb, quality,
1060 *cache_bits, hash_chain,
1061 refs_trace)) {
1062 double bit_cost_trace;
1063 // Evaluate LZ77 coding.
1064 VP8LHistogramCreate(histo, refs_trace, *cache_bits);
1065 bit_cost_trace = VP8LHistogramEstimateBits(histo);
1066 if (bit_cost_trace < bit_cost_lz77) {
1067 best = refs_trace;
1068 }
1069 }
1070 }
1071 } else {
1072 best = refs_rle;
1073 }
1074
1075 BackwardReferences2DLocality(width, best);
1076
1077 Error:
1078 VP8LFreeHistogram(histo);
1079 return best;
1080 }
1081
VP8LGetBackwardReferences(int width,int height,const uint32_t * const argb,int quality,int low_effort,int * const cache_bits,VP8LHashChain * const hash_chain,VP8LBackwardRefs refs_array[2])1082 VP8LBackwardRefs* VP8LGetBackwardReferences(
1083 int width, int height, const uint32_t* const argb, int quality,
1084 int low_effort, int* const cache_bits, VP8LHashChain* const hash_chain,
1085 VP8LBackwardRefs refs_array[2]) {
1086 if (low_effort) {
1087 return GetBackwardReferencesLowEffort(width, height, argb, quality,
1088 cache_bits, hash_chain, refs_array);
1089 } else {
1090 return GetBackwardReferences(width, height, argb, quality, cache_bits,
1091 hash_chain, refs_array);
1092 }
1093 }
1094