Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: third_party/libwebp/enc/backward_references.c

Issue 1546003002: libwebp: update to 0.5.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase around clang-cl fix Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2012 Google Inc. All Rights Reserved. 1 // Copyright 2012 Google Inc. All Rights Reserved.
2 // 2 //
3 // Use of this source code is governed by a BSD-style license 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 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 5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may 6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree. 7 // be found in the AUTHORS file in the root of the source tree.
8 // ----------------------------------------------------------------------------- 8 // -----------------------------------------------------------------------------
9 // 9 //
10 // Author: Jyrki Alakuijala (jyrki@google.com) 10 // Author: Jyrki Alakuijala (jyrki@google.com)
11 // 11 //
12 12
13 #include <assert.h> 13 #include <assert.h>
14 #include <math.h> 14 #include <math.h>
15 15
16 #include "./backward_references.h" 16 #include "./backward_references.h"
17 #include "./histogram.h" 17 #include "./histogram.h"
18 #include "../dsp/lossless.h" 18 #include "../dsp/lossless.h"
19 #include "../dsp/dsp.h"
19 #include "../utils/color_cache.h" 20 #include "../utils/color_cache.h"
20 #include "../utils/utils.h" 21 #include "../utils/utils.h"
21 22
22 #define VALUES_IN_BYTE 256 23 #define VALUES_IN_BYTE 256
23 24
24 #define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
25
26 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references 25 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references
27 26
28 #define MAX_ENTROPY (1e30f) 27 #define MAX_ENTROPY (1e30f)
29 28
30 // 1M window (4M bytes) minus 120 special codes for short distances. 29 // 1M window (4M bytes) minus 120 special codes for short distances.
31 #define WINDOW_SIZE ((1 << 20) - 120) 30 #define WINDOW_SIZE ((1 << 20) - 120)
32 31
33 // Bounds for the match length. 32 // Bounds for the match length.
34 #define MIN_LENGTH 2 33 #define MIN_LENGTH 2
35 #define MAX_LENGTH 4096 34 #define MAX_LENGTH 4096
(...skipping 15 matching lines...) Expand all
51 const int yoffset = dist / xsize; 50 const int yoffset = dist / xsize;
52 const int xoffset = dist - yoffset * xsize; 51 const int xoffset = dist - yoffset * xsize;
53 if (xoffset <= 8 && yoffset < 8) { 52 if (xoffset <= 8 && yoffset < 8) {
54 return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; 53 return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
55 } else if (xoffset > xsize - 8 && yoffset < 7) { 54 } else if (xoffset > xsize - 8 && yoffset < 7) {
56 return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1; 55 return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
57 } 56 }
58 return dist + 120; 57 return dist + 120;
59 } 58 }
60 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.
61 static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, 63 static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
62 const uint32_t* const array2, 64 const uint32_t* const array2,
63 const int max_limit) { 65 int best_len_match,
64 int match_len = 0; 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
65 while (match_len < max_limit && array1[match_len] == array2[match_len]) { 82 while (match_len < max_limit && array1[match_len] == array2[match_len]) {
66 ++match_len; 83 ++match_len;
67 } 84 }
68 return match_len; 85 return match_len;
69 } 86 }
70 87
71 // ----------------------------------------------------------------------------- 88 // -----------------------------------------------------------------------------
72 // VP8LBackwardRefs 89 // VP8LBackwardRefs
73 90
74 struct PixOrCopyBlock { 91 struct PixOrCopyBlock {
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 new_b->size_ = b->size_; 188 new_b->size_ = b->size_;
172 b = b->next_; 189 b = b->next_;
173 } 190 }
174 return 1; 191 return 1;
175 } 192 }
176 193
177 // ----------------------------------------------------------------------------- 194 // -----------------------------------------------------------------------------
178 // Hash chains 195 // Hash chains
179 196
180 // initialize as empty 197 // initialize as empty
181 static void HashChainInit(VP8LHashChain* const p) { 198 static void HashChainReset(VP8LHashChain* const p) {
182 int i;
183 assert(p != NULL); 199 assert(p != NULL);
184 for (i = 0; i < p->size_; ++i) { 200 // Set the int32_t arrays to -1.
185 p->chain_[i] = -1; 201 memset(p->chain_, 0xff, p->size_ * sizeof(*p->chain_));
186 } 202 memset(p->hash_to_first_index_, 0xff,
187 for (i = 0; i < HASH_SIZE; ++i) { 203 HASH_SIZE * sizeof(*p->hash_to_first_index_));
188 p->hash_to_first_index_[i] = -1;
189 }
190 } 204 }
191 205
192 int VP8LHashChainInit(VP8LHashChain* const p, int size) { 206 int VP8LHashChainInit(VP8LHashChain* const p, int size) {
193 assert(p->size_ == 0); 207 assert(p->size_ == 0);
194 assert(p->chain_ == NULL); 208 assert(p->chain_ == NULL);
195 assert(size > 0); 209 assert(size > 0);
196 p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_)); 210 p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_));
197 if (p->chain_ == NULL) return 0; 211 if (p->chain_ == NULL) return 0;
198 p->size_ = size; 212 p->size_ = size;
199 HashChainInit(p); 213 HashChainReset(p);
200 return 1; 214 return 1;
201 } 215 }
202 216
203 void VP8LHashChainClear(VP8LHashChain* const p) { 217 void VP8LHashChainClear(VP8LHashChain* const p) {
204 assert(p != NULL); 218 assert(p != NULL);
205 WebPSafeFree(p->chain_); 219 WebPSafeFree(p->chain_);
206 p->size_ = 0; 220 p->size_ = 0;
207 p->chain_ = NULL; 221 p->chain_ = NULL;
208 } 222 }
209 223
210 // ----------------------------------------------------------------------------- 224 // -----------------------------------------------------------------------------
211 225
212 static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) { 226 #define HASH_MULTIPLIER_HI (0xc6a4a793U)
213 uint64_t key = ((uint64_t)argb[1] << 32) | argb[0]; 227 #define HASH_MULTIPLIER_LO (0x5bd1e996U)
214 key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS); 228
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);
215 return key; 234 return key;
216 } 235 }
217 236
218 // Insertion of two pixels at a time. 237 // Insertion of two pixels at a time.
219 static void HashChainInsert(VP8LHashChain* const p, 238 static void HashChainInsert(VP8LHashChain* const p,
220 const uint32_t* const argb, int pos) { 239 const uint32_t* const argb, int pos) {
221 const uint64_t hash_code = GetPixPairHash64(argb); 240 const uint32_t hash_code = GetPixPairHash64(argb);
222 p->chain_[pos] = p->hash_to_first_index_[hash_code]; 241 p->chain_[pos] = p->hash_to_first_index_[hash_code];
223 p->hash_to_first_index_[hash_code] = pos; 242 p->hash_to_first_index_[hash_code] = pos;
224 } 243 }
225 244
226 static void GetParamsForHashChainFindCopy(int quality, int xsize, 245 // Returns the maximum number of hash chain lookups to do for a
227 int cache_bits, int* window_size, 246 // given compression quality. Return value in range [6, 86].
228 int* iter_pos, int* iter_limit) { 247 static int GetMaxItersForQuality(int quality, int low_effort) {
229 const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4); 248 return (low_effort ? 6 : 8) + (quality * quality) / 128;
230 const int iter_neg = -iter_mult * (quality >> 1); 249 }
231 // Limit the backward-ref window size for lower qualities. 250
232 const int max_window_size = (quality > 50) ? WINDOW_SIZE 251 static int GetWindowSizeForHashChain(int quality, int xsize) {
233 : (quality > 25) ? (xsize << 8) 252 const int max_window_size = (quality > 75) ? WINDOW_SIZE
253 : (quality > 50) ? (xsize << 8)
254 : (quality > 25) ? (xsize << 6)
234 : (xsize << 4); 255 : (xsize << 4);
235 assert(xsize > 0); 256 assert(xsize > 0);
236 *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE 257 return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;
237 : max_window_size; 258 }
238 *iter_pos = 8 + (quality >> 3); 259
239 // For lower entropy images, the rigorous search loop in HashChainFindCopy 260 static WEBP_INLINE int MaxFindCopyLength(int len) {
240 // can be relaxed. 261 return (len < MAX_LENGTH) ? len : MAX_LENGTH;
241 *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2; 262 }
263
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;
242 } 280 }
243 281
244 static int HashChainFindCopy(const VP8LHashChain* const p, 282 static int HashChainFindCopy(const VP8LHashChain* const p,
245 int base_position, int xsize_signed, 283 int base_position,
246 const uint32_t* const argb, int max_len, 284 const uint32_t* const argb, int max_len,
247 int window_size, int iter_pos, int iter_limit, 285 int window_size, int iter_max,
248 int* const distance_ptr, 286 int* const distance_ptr,
249 int* const length_ptr) { 287 int* const length_ptr) {
250 const uint32_t* const argb_start = argb + base_position; 288 const uint32_t* const argb_start = argb + base_position;
251 uint64_t best_val = 0; 289 int iter = iter_max;
252 uint32_t best_length = 1; 290 int best_length = 0;
253 uint32_t best_distance = 0; 291 int best_distance = 0;
254 const uint32_t xsize = (uint32_t)xsize_signed;
255 const int min_pos = 292 const int min_pos =
256 (base_position > window_size) ? base_position - window_size : 0; 293 (base_position > window_size) ? base_position - window_size : 0;
257 int pos; 294 int pos;
258 assert(xsize > 0); 295 int length_max = 256;
259 if (max_len > MAX_LENGTH) { 296 if (max_len < length_max) {
260 max_len = MAX_LENGTH; 297 length_max = max_len;
261 } 298 }
262 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)]; 299 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
263 pos >= min_pos; 300 pos >= min_pos;
264 pos = p->chain_[pos]) { 301 pos = p->chain_[pos]) {
265 uint64_t val; 302 int curr_length;
266 uint32_t curr_length; 303 int distance;
267 uint32_t distance; 304 if (--iter < 0) {
268 const uint32_t* const ptr1 = (argb + pos + best_length - 1); 305 break;
269 const uint32_t* const ptr2 = (argb_start + best_length - 1); 306 }
270 307
271 if (iter_pos < 0) { 308 curr_length = FindMatchLength(argb + pos, argb_start, best_length, max_len);
272 if (iter_pos < iter_limit || best_val >= 0xff0000) { 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) {
273 break; 314 break;
274 } 315 }
275 } 316 }
276 --iter_pos; 317 }
277 318 *distance_ptr = best_distance;
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;
281
282 curr_length = FindMatchLength(argb + pos, argb_start, max_len);
283 if (curr_length < best_length) continue;
284
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;
293 }
294 if (x <= 7) {
295 val += 9 * 9 + 9 * 9;
296 val -= y * y + x * x;
297 }
298 }
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;
305 }
306 if ((best_distance == 1 || distance == xsize) &&
307 best_length >= 128) {
308 break;
309 }
310 }
311 }
312 *distance_ptr = (int)best_distance;
313 *length_ptr = best_length; 319 *length_ptr = best_length;
314 return (best_length >= MIN_LENGTH); 320 return (best_length >= MIN_LENGTH);
315 } 321 }
316 322
317 static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) { 323 static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
318 while (length >= MAX_LENGTH) { 324 VP8LColorCache* const hashers,
319 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, MAX_LENGTH)); 325 VP8LBackwardRefs* const refs) {
320 length -= MAX_LENGTH; 326 PixOrCopy v;
321 } 327 if (use_color_cache) {
322 if (length > 0) { 328 const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);
323 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, length)); 329 if (VP8LColorCacheLookup(hashers, key) == pixel) {
324 } 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);
325 } 339 }
326 340
327 static int BackwardReferencesRle(int xsize, int ysize, 341 static int BackwardReferencesRle(int xsize, int ysize,
328 const uint32_t* const argb, 342 const uint32_t* const argb,
329 VP8LBackwardRefs* const refs) { 343 int cache_bits, VP8LBackwardRefs* const refs) {
330 const int pix_count = xsize * ysize; 344 const int pix_count = xsize * ysize;
331 int match_len = 0; 345 int i, k;
332 int i; 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 }
333 ClearBackwardRefs(refs); 352 ClearBackwardRefs(refs);
334 PushBackCopy(refs, match_len); // i=0 case 353 // Add first pixel as literal.
335 BackwardRefsCursorAdd(refs, PixOrCopyCreateLiteral(argb[0])); 354 AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);
336 for (i = 1; i < pix_count; ++i) { 355 i = 1;
337 if (argb[i] == argb[i - 1]) { 356 while (i < pix_count) {
338 ++match_len; 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;
339 } else { 376 } else {
340 PushBackCopy(refs, match_len); 377 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
341 match_len = 0; 378 i++;
342 BackwardRefsCursorAdd(refs, PixOrCopyCreateLiteral(argb[i])); 379 }
343 } 380 }
344 } 381 if (use_color_cache) VP8LColorCacheClear(&hashers);
345 PushBackCopy(refs, match_len);
346 return !refs->error_; 382 return !refs->error_;
347 } 383 }
348 384
349 static int BackwardReferencesHashChain(int xsize, int ysize, 385 static int BackwardReferencesLz77(int xsize, int ysize,
350 const uint32_t* const argb, 386 const uint32_t* const argb, int cache_bits,
351 int cache_bits, int quality, 387 int quality, int low_effort,
352 VP8LHashChain* const hash_chain, 388 VP8LHashChain* const hash_chain,
353 VP8LBackwardRefs* const refs) { 389 VP8LBackwardRefs* const refs) {
354 int i; 390 int i;
355 int ok = 0; 391 int ok = 0;
356 int cc_init = 0; 392 int cc_init = 0;
357 const int use_color_cache = (cache_bits > 0); 393 const int use_color_cache = (cache_bits > 0);
358 const int pix_count = xsize * ysize; 394 const int pix_count = xsize * ysize;
359 VP8LColorCache hashers; 395 VP8LColorCache hashers;
360 int window_size = WINDOW_SIZE; 396 int iter_max = GetMaxItersForQuality(quality, low_effort);
361 int iter_pos = 1; 397 const int window_size = GetWindowSizeForHashChain(quality, xsize);
362 int iter_limit = -1; 398 int min_matches = 32;
363 399
364 if (use_color_cache) { 400 if (use_color_cache) {
365 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 401 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
366 if (!cc_init) goto Error; 402 if (!cc_init) goto Error;
367 } 403 }
368
369 ClearBackwardRefs(refs); 404 ClearBackwardRefs(refs);
370 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, 405 HashChainReset(hash_chain);
371 &window_size, &iter_pos, &iter_limit); 406 for (i = 0; i < pix_count - 2; ) {
372 HashChainInit(hash_chain);
373 for (i = 0; i < pix_count; ) {
374 // Alternative#1: Code the pixels starting at 'i' using backward reference. 407 // Alternative#1: Code the pixels starting at 'i' using backward reference.
375 int offset = 0; 408 int offset = 0;
376 int len = 0; 409 int len = 0;
377 if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. 410 const int max_len = MaxFindCopyLength(pix_count - i);
378 int max_len = pix_count - i; 411 HashChainFindCopy(hash_chain, i, argb, max_len, window_size,
379 HashChainFindCopy(hash_chain, i, xsize, argb, max_len, 412 iter_max, &offset, &len);
380 window_size, iter_pos, iter_limit, 413 if (len > MIN_LENGTH || (len == MIN_LENGTH && offset <= 512)) {
381 &offset, &len);
382 }
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; 414 int offset2 = 0;
387 int len2 = 0; 415 int len2 = 0;
388 int k; 416 int k;
417 min_matches = 8;
389 HashChainInsert(hash_chain, &argb[i], i); 418 HashChainInsert(hash_chain, &argb[i], i);
390 if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2]. 419 if ((len < (max_len >> 2)) && !low_effort) {
391 int max_len = pix_count - (i + 1); 420 // Evaluate Alternative#2: Insert the pixel at 'i' as literal, and code
392 HashChainFindCopy(hash_chain, i + 1, xsize, argb, max_len, 421 // the pixels starting at 'i + 1' using backward reference.
393 window_size, iter_pos, iter_limit, 422 HashChainFindCopy(hash_chain, i + 1, argb, max_len - 1,
394 &offset2, &len2); 423 window_size, iter_max, &offset2,
424 &len2);
395 if (len2 > len + 1) { 425 if (len2 > len + 1) {
396 const uint32_t pixel = argb[i]; 426 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
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);
405 }
406 BackwardRefsCursorAdd(refs, v);
407 i++; // Backward reference to be done for next pixel. 427 i++; // Backward reference to be done for next pixel.
408 len = len2; 428 len = len2;
409 offset = offset2; 429 offset = offset2;
410 } 430 }
411 } 431 }
412 if (len >= MAX_LENGTH) {
413 len = MAX_LENGTH - 1;
414 }
415 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); 432 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
416 if (use_color_cache) { 433 if (use_color_cache) {
417 for (k = 0; k < len; ++k) { 434 for (k = 0; k < len; ++k) {
418 VP8LColorCacheInsert(&hashers, argb[i + k]); 435 VP8LColorCacheInsert(&hashers, argb[i + k]);
419 } 436 }
420 } 437 }
421 // Add to the hash_chain (but cannot add the last pixel). 438 // Add to the hash_chain (but cannot add the last pixel).
422 { 439 if (offset >= 3 && offset != xsize) {
423 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; 440 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
424 for (k = 1; k < last; ++k) { 441 for (k = 2; k < last - 8; k += 2) {
442 HashChainInsert(hash_chain, &argb[i + k], i + k);
443 }
444 for (; k < last; ++k) {
425 HashChainInsert(hash_chain, &argb[i + k], i + k); 445 HashChainInsert(hash_chain, &argb[i + k], i + k);
426 } 446 }
427 } 447 }
428 i += len; 448 i += len;
429 } else { 449 } else {
430 const uint32_t pixel = argb[i]; 450 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
431 PixOrCopy v; 451 HashChainInsert(hash_chain, &argb[i], i);
432 if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) { 452 ++i;
433 // push pixel as a PixOrCopyCreateCacheIdx pixel 453 --min_matches;
434 const int ix = VP8LColorCacheGetIndex(&hashers, pixel); 454 if (min_matches <= 0) {
435 v = PixOrCopyCreateCacheIdx(ix); 455 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
436 } else { 456 HashChainInsert(hash_chain, &argb[i], i);
437 if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); 457 ++i;
438 v = PixOrCopyCreateLiteral(pixel);
439 } 458 }
440 BackwardRefsCursorAdd(refs, v);
441 if (i + 1 < pix_count) {
442 HashChainInsert(hash_chain, &argb[i], i);
443 }
444 ++i;
445 } 459 }
446 } 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
447 ok = !refs->error_; 467 ok = !refs->error_;
448 Error: 468 Error:
449 if (cc_init) VP8LColorCacheClear(&hashers); 469 if (cc_init) VP8LColorCacheClear(&hashers);
450 return ok; 470 return ok;
451 } 471 }
452 472
453 // ----------------------------------------------------------------------------- 473 // -----------------------------------------------------------------------------
454 474
455 typedef struct { 475 typedef struct {
456 double alpha_[VALUES_IN_BYTE]; 476 double alpha_[VALUES_IN_BYTE];
457 double red_[VALUES_IN_BYTE]; 477 double red_[VALUES_IN_BYTE];
458 double literal_[PIX_OR_COPY_CODES_MAX];
459 double blue_[VALUES_IN_BYTE]; 478 double blue_[VALUES_IN_BYTE];
460 double distance_[NUM_DISTANCE_CODES]; 479 double distance_[NUM_DISTANCE_CODES];
480 double* literal_;
461 } CostModel; 481 } CostModel;
462 482
463 static int BackwardReferencesTraceBackwards( 483 static int BackwardReferencesTraceBackwards(
464 int xsize, int ysize, int recursive_cost_model, 484 int xsize, int ysize, const uint32_t* const argb, int quality,
465 const uint32_t* const argb, int quality, int cache_bits, 485 int cache_bits, VP8LHashChain* const hash_chain,
466 VP8LHashChain* const hash_chain,
467 VP8LBackwardRefs* const refs); 486 VP8LBackwardRefs* const refs);
468 487
469 static void ConvertPopulationCountTableToBitEstimates( 488 static void ConvertPopulationCountTableToBitEstimates(
470 int num_symbols, const uint32_t population_counts[], double output[]) { 489 int num_symbols, const uint32_t population_counts[], double output[]) {
471 uint32_t sum = 0; 490 uint32_t sum = 0;
472 int nonzeros = 0; 491 int nonzeros = 0;
473 int i; 492 int i;
474 for (i = 0; i < num_symbols; ++i) { 493 for (i = 0; i < num_symbols; ++i) {
475 sum += population_counts[i]; 494 sum += population_counts[i];
476 if (population_counts[i] > 0) { 495 if (population_counts[i] > 0) {
477 ++nonzeros; 496 ++nonzeros;
478 } 497 }
479 } 498 }
480 if (nonzeros <= 1) { 499 if (nonzeros <= 1) {
481 memset(output, 0, num_symbols * sizeof(*output)); 500 memset(output, 0, num_symbols * sizeof(*output));
482 } else { 501 } else {
483 const double logsum = VP8LFastLog2(sum); 502 const double logsum = VP8LFastLog2(sum);
484 for (i = 0; i < num_symbols; ++i) { 503 for (i = 0; i < num_symbols; ++i) {
485 output[i] = logsum - VP8LFastLog2(population_counts[i]); 504 output[i] = logsum - VP8LFastLog2(population_counts[i]);
486 } 505 }
487 } 506 }
488 } 507 }
489 508
490 static int CostModelBuild(CostModel* const m, int xsize, int ysize, 509 static int CostModelBuild(CostModel* const m, int cache_bits,
491 int recursion_level, const uint32_t* const argb,
492 int quality, int cache_bits,
493 VP8LHashChain* const hash_chain,
494 VP8LBackwardRefs* const refs) { 510 VP8LBackwardRefs* const refs) {
495 int ok = 0; 511 int ok = 0;
496 VP8LHistogram* histo = NULL; 512 VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
497
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;
504 }
505 } else {
506 if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
507 hash_chain, refs)) {
508 goto Error;
509 }
510 }
511 histo = VP8LAllocateHistogram(cache_bits);
512 if (histo == NULL) goto Error; 513 if (histo == NULL) goto Error;
513 514
514 VP8LHistogramCreate(histo, refs, cache_bits); 515 VP8LHistogramCreate(histo, refs, cache_bits);
515 516
516 ConvertPopulationCountTableToBitEstimates( 517 ConvertPopulationCountTableToBitEstimates(
517 VP8LHistogramNumCodes(histo->palette_code_bits_), 518 VP8LHistogramNumCodes(histo->palette_code_bits_),
518 histo->literal_, m->literal_); 519 histo->literal_, m->literal_);
519 ConvertPopulationCountTableToBitEstimates( 520 ConvertPopulationCountTableToBitEstimates(
520 VALUES_IN_BYTE, histo->red_, m->red_); 521 VALUES_IN_BYTE, histo->red_, m->red_);
521 ConvertPopulationCountTableToBitEstimates( 522 ConvertPopulationCountTableToBitEstimates(
(...skipping 28 matching lines...) Expand all
550 return m->literal_[VALUES_IN_BYTE + code] + extra_bits; 551 return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
551 } 552 }
552 553
553 static WEBP_INLINE double GetDistanceCost(const CostModel* const m, 554 static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
554 uint32_t distance) { 555 uint32_t distance) {
555 int code, extra_bits; 556 int code, extra_bits;
556 VP8LPrefixEncodeBits(distance, &code, &extra_bits); 557 VP8LPrefixEncodeBits(distance, &code, &extra_bits);
557 return m->distance_[code] + extra_bits; 558 return m->distance_[code] + extra_bits;
558 } 559 }
559 560
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
560 static int BackwardReferencesHashChainDistanceOnly( 586 static int BackwardReferencesHashChainDistanceOnly(
561 int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb, 587 int xsize, int ysize, const uint32_t* const argb,
562 int quality, int cache_bits, VP8LHashChain* const hash_chain, 588 int quality, int cache_bits, VP8LHashChain* const hash_chain,
563 VP8LBackwardRefs* const refs, uint32_t* const dist_array) { 589 VP8LBackwardRefs* const refs, uint16_t* const dist_array) {
564 int i; 590 int i;
565 int ok = 0; 591 int ok = 0;
566 int cc_init = 0; 592 int cc_init = 0;
567 const int pix_count = xsize * ysize; 593 const int pix_count = xsize * ysize;
568 const int use_color_cache = (cache_bits > 0); 594 const int use_color_cache = (cache_bits > 0);
569 float* const cost = 595 float* const cost =
570 (float*)WebPSafeMalloc(pix_count, sizeof(*cost)); 596 (float*)WebPSafeMalloc(pix_count, sizeof(*cost));
571 CostModel* cost_model = (CostModel*)WebPSafeMalloc(1ULL, sizeof(*cost_model)); 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);
572 VP8LColorCache hashers; 603 VP8LColorCache hashers;
573 const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68; 604 const int skip_length = 32 + quality;
574 const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82; 605 const int skip_min_distance_code = 2;
575 const int min_distance_code = 2; // TODO(vikasa): tune as function of quality 606 int iter_max = GetMaxItersForQuality(quality, 0);
576 int window_size = WINDOW_SIZE; 607 const int window_size = GetWindowSizeForHashChain(quality, xsize);
577 int iter_pos = 1;
578 int iter_limit = -1;
579 608
580 if (cost == NULL || cost_model == NULL) goto Error; 609 if (cost == NULL || cost_model == NULL) goto Error;
581 610
611 cost_model->literal_ = (double*)(cost_model + 1);
582 if (use_color_cache) { 612 if (use_color_cache) {
583 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 613 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
584 if (!cc_init) goto Error; 614 if (!cc_init) goto Error;
585 } 615 }
586 616
587 if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb, 617 if (!CostModelBuild(cost_model, cache_bits, refs)) {
588 quality, cache_bits, hash_chain, refs)) {
589 goto Error; 618 goto Error;
590 } 619 }
591 620
592 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f; 621 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f;
593 622
594 // We loop one pixel at a time, but store all currently best points to 623 // We loop one pixel at a time, but store all currently best points to
595 // non-processed locations from this point. 624 // non-processed locations from this point.
596 dist_array[0] = 0; 625 dist_array[0] = 0;
597 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, 626 HashChainReset(hash_chain);
598 &window_size, &iter_pos, &iter_limit); 627 // Add first pixel as literal.
599 HashChainInit(hash_chain); 628 AddSingleLiteralWithCostModel(argb + 0, hash_chain, &hashers, cost_model, 0,
600 for (i = 0; i < pix_count; ++i) { 629 0, use_color_cache, 0.0, cost, dist_array);
601 double prev_cost = 0.0; 630 for (i = 1; i < pix_count - 1; ++i) {
602 int shortmax; 631 int offset = 0;
603 if (i > 0) { 632 int len = 0;
604 prev_cost = cost[i - 1]; 633 double prev_cost = cost[i - 1];
605 } 634 const int max_len = MaxFindCopyLength(pix_count - i);
606 for (shortmax = 0; shortmax < 2; ++shortmax) { 635 HashChainFindCopy(hash_chain, i, argb, max_len, window_size,
607 int offset = 0; 636 iter_max, &offset, &len);
608 int len = 0; 637 if (len >= MIN_LENGTH) {
609 if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. 638 const int code = DistanceToPlaneCode(xsize, offset);
610 int max_len = shortmax ? 2 : pix_count - i; 639 const double distance_cost =
611 HashChainFindCopy(hash_chain, i, xsize, argb, max_len, 640 prev_cost + GetDistanceCost(cost_model, code);
612 window_size, iter_pos, iter_limit, 641 int k;
613 &offset, &len); 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 }
614 } 648 }
615 if (len >= MIN_LENGTH) { 649 // This if is for speedup only. It roughly doubles the speed, and
616 const int code = DistanceToPlaneCode(xsize, offset); 650 // makes compression worse by .1 %.
617 const double distance_cost = 651 if (len >= skip_length && code <= skip_min_distance_code) {
618 prev_cost + GetDistanceCost(cost_model, code); 652 // Long copy for short distances, let's skip the middle
619 int k; 653 // lookups for better copies.
620 for (k = 1; k < len; ++k) { 654 // 1) insert the hashes.
621 const double cost_val = distance_cost + GetLengthCost(cost_model, k); 655 if (use_color_cache) {
622 if (cost[i + k] > cost_val) { 656 for (k = 0; k < len; ++k) {
623 cost[i + k] = (float)cost_val; 657 VP8LColorCacheInsert(&hashers, argb[i + k]);
624 dist_array[i + k] = k + 1;
625 } 658 }
626 } 659 }
627 // This if is for speedup only. It roughly doubles the speed, and 660 // 2) Add to the hash_chain (but cannot add the last pixel)
628 // makes compression worse by .1 %. 661 {
629 if (len >= 128 && code <= min_distance_code) { 662 const int last = (len + i < pix_count - 1) ? len + i
630 // Long copy for short distances, let's skip the middle 663 : pix_count - 1;
631 // lookups for better copies. 664 for (k = i; k < last; ++k) {
632 // 1) insert the hashes. 665 HashChainInsert(hash_chain, &argb[k], k);
633 if (use_color_cache) {
634 for (k = 0; k < len; ++k) {
635 VP8LColorCacheInsert(&hashers, argb[i + k]);
636 }
637 } 666 }
638 // 2) Add to the hash_chain (but cannot add the last pixel) 667 }
639 { 668 // 3) jump.
640 const int last = (len + i < pix_count - 1) ? len + i 669 i += len - 1; // for loop does ++i, thus -1 here.
641 : pix_count - 1; 670 goto next_symbol;
642 for (k = i; k < last; ++k) { 671 }
643 HashChainInsert(hash_chain, &argb[k], k); 672 if (len != MIN_LENGTH) {
644 } 673 int code_min_length;
645 } 674 double cost_total;
646 // 3) jump. 675 HashChainFindOffset(hash_chain, i, argb, MIN_LENGTH, window_size,
647 i += len - 1; // for loop does ++i, thus -1 here. 676 &offset);
648 goto next_symbol; 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;
649 } 684 }
650 } 685 }
651 } 686 }
652 if (i < pix_count - 1) { 687 AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i,
653 HashChainInsert(hash_chain, &argb[i], i); 688 0, use_color_cache, prev_cost, cost,
654 } 689 dist_array);
655 {
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;
664 }
665 if (cost[i] > cost_val) {
666 cost[i] = (float)cost_val;
667 dist_array[i] = 1; // only one is inserted.
668 }
669 }
670 next_symbol: ; 690 next_symbol: ;
671 } 691 }
672 // Last pixel still to do, it can only be a single step if not reached 692 // Handle the last pixel.
673 // through cheaper means already. 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 }
674 ok = !refs->error_; 698 ok = !refs->error_;
675 Error: 699 Error:
676 if (cc_init) VP8LColorCacheClear(&hashers); 700 if (cc_init) VP8LColorCacheClear(&hashers);
677 WebPSafeFree(cost_model); 701 WebPSafeFree(cost_model);
678 WebPSafeFree(cost); 702 WebPSafeFree(cost);
679 return ok; 703 return ok;
680 } 704 }
681 705
682 // We pack the path at the end of *dist_array and return 706 // We pack the path at the end of *dist_array and return
683 // a pointer to this part of the array. Example: 707 // a pointer to this part of the array. Example:
684 // dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232] 708 // dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
685 static void TraceBackwards(uint32_t* const dist_array, 709 static void TraceBackwards(uint16_t* const dist_array,
686 int dist_array_size, 710 int dist_array_size,
687 uint32_t** const chosen_path, 711 uint16_t** const chosen_path,
688 int* const chosen_path_size) { 712 int* const chosen_path_size) {
689 uint32_t* path = dist_array + dist_array_size; 713 uint16_t* path = dist_array + dist_array_size;
690 uint32_t* cur = dist_array + dist_array_size - 1; 714 uint16_t* cur = dist_array + dist_array_size - 1;
691 while (cur >= dist_array) { 715 while (cur >= dist_array) {
692 const int k = *cur; 716 const int k = *cur;
693 --path; 717 --path;
694 *path = k; 718 *path = k;
695 cur -= k; 719 cur -= k;
696 } 720 }
697 *chosen_path = path; 721 *chosen_path = path;
698 *chosen_path_size = (int)(dist_array + dist_array_size - path); 722 *chosen_path_size = (int)(dist_array + dist_array_size - path);
699 } 723 }
700 724
701 static int BackwardReferencesHashChainFollowChosenPath( 725 static int BackwardReferencesHashChainFollowChosenPath(
702 int xsize, int ysize, const uint32_t* const argb, 726 int xsize, int ysize, const uint32_t* const argb,
703 int quality, int cache_bits, 727 int quality, int cache_bits,
704 const uint32_t* const chosen_path, int chosen_path_size, 728 const uint16_t* const chosen_path, int chosen_path_size,
705 VP8LHashChain* const hash_chain, 729 VP8LHashChain* const hash_chain,
706 VP8LBackwardRefs* const refs) { 730 VP8LBackwardRefs* const refs) {
707 const int pix_count = xsize * ysize; 731 const int pix_count = xsize * ysize;
708 const int use_color_cache = (cache_bits > 0); 732 const int use_color_cache = (cache_bits > 0);
709 int size = 0; 733 int ix;
710 int i = 0; 734 int i = 0;
711 int k;
712 int ix;
713 int ok = 0; 735 int ok = 0;
714 int cc_init = 0; 736 int cc_init = 0;
715 int window_size = WINDOW_SIZE; 737 const int window_size = GetWindowSizeForHashChain(quality, xsize);
716 int iter_pos = 1;
717 int iter_limit = -1;
718 VP8LColorCache hashers; 738 VP8LColorCache hashers;
719 739
720 if (use_color_cache) { 740 if (use_color_cache) {
721 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 741 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
722 if (!cc_init) goto Error; 742 if (!cc_init) goto Error;
723 } 743 }
724 744
725 ClearBackwardRefs(refs); 745 ClearBackwardRefs(refs);
726 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, 746 HashChainReset(hash_chain);
727 &window_size, &iter_pos, &iter_limit); 747 for (ix = 0; ix < chosen_path_size; ++ix) {
728 HashChainInit(hash_chain);
729 for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
730 int offset = 0; 748 int offset = 0;
731 int len = 0; 749 const int len = chosen_path[ix];
732 int max_len = chosen_path[ix]; 750 if (len != 1) {
733 if (max_len != 1) { 751 int k;
734 HashChainFindCopy(hash_chain, i, xsize, argb, max_len, 752 HashChainFindOffset(hash_chain, i, argb, len, window_size, &offset);
735 window_size, iter_pos, iter_limit,
736 &offset, &len);
737 assert(len == max_len);
738 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); 753 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
739 if (use_color_cache) { 754 if (use_color_cache) {
740 for (k = 0; k < len; ++k) { 755 for (k = 0; k < len; ++k) {
741 VP8LColorCacheInsert(&hashers, argb[i + k]); 756 VP8LColorCacheInsert(&hashers, argb[i + k]);
742 } 757 }
743 } 758 }
744 { 759 {
745 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; 760 const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
746 for (k = 0; k < last; ++k) { 761 for (k = 0; k < last; ++k) {
747 HashChainInsert(hash_chain, &argb[i + k], i + k); 762 HashChainInsert(hash_chain, &argb[i + k], i + k);
(...skipping 11 matching lines...) Expand all
759 v = PixOrCopyCreateLiteral(argb[i]); 774 v = PixOrCopyCreateLiteral(argb[i]);
760 } 775 }
761 BackwardRefsCursorAdd(refs, v); 776 BackwardRefsCursorAdd(refs, v);
762 if (i + 1 < pix_count) { 777 if (i + 1 < pix_count) {
763 HashChainInsert(hash_chain, &argb[i], i); 778 HashChainInsert(hash_chain, &argb[i], i);
764 } 779 }
765 ++i; 780 ++i;
766 } 781 }
767 } 782 }
768 ok = !refs->error_; 783 ok = !refs->error_;
769 Error: 784 Error:
770 if (cc_init) VP8LColorCacheClear(&hashers); 785 if (cc_init) VP8LColorCacheClear(&hashers);
771 return ok; 786 return ok;
772 } 787 }
773 788
774 // Returns 1 on success. 789 // Returns 1 on success.
775 static int BackwardReferencesTraceBackwards(int xsize, int ysize, 790 static int BackwardReferencesTraceBackwards(int xsize, int ysize,
776 int recursive_cost_model,
777 const uint32_t* const argb, 791 const uint32_t* const argb,
778 int quality, int cache_bits, 792 int quality, int cache_bits,
779 VP8LHashChain* const hash_chain, 793 VP8LHashChain* const hash_chain,
780 VP8LBackwardRefs* const refs) { 794 VP8LBackwardRefs* const refs) {
781 int ok = 0; 795 int ok = 0;
782 const int dist_array_size = xsize * ysize; 796 const int dist_array_size = xsize * ysize;
783 uint32_t* chosen_path = NULL; 797 uint16_t* chosen_path = NULL;
784 int chosen_path_size = 0; 798 int chosen_path_size = 0;
785 uint32_t* dist_array = 799 uint16_t* dist_array =
786 (uint32_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array)); 800 (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));
787 801
788 if (dist_array == NULL) goto Error; 802 if (dist_array == NULL) goto Error;
789 803
790 if (!BackwardReferencesHashChainDistanceOnly( 804 if (!BackwardReferencesHashChainDistanceOnly(
791 xsize, ysize, recursive_cost_model, argb, quality, cache_bits, hash_chain, 805 xsize, ysize, argb, quality, cache_bits, hash_chain,
792 refs, dist_array)) { 806 refs, dist_array)) {
793 goto Error; 807 goto Error;
794 } 808 }
795 TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); 809 TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
796 if (!BackwardReferencesHashChainFollowChosenPath( 810 if (!BackwardReferencesHashChainFollowChosenPath(
797 xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size, 811 xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
798 hash_chain, refs)) { 812 hash_chain, refs)) {
799 goto Error; 813 goto Error;
800 } 814 }
801 ok = 1; 815 ok = 1;
802 Error: 816 Error:
803 WebPSafeFree(dist_array); 817 WebPSafeFree(dist_array);
804 return ok; 818 return ok;
805 } 819 }
806 820
807 static void BackwardReferences2DLocality(int xsize, 821 static void BackwardReferences2DLocality(int xsize,
808 const VP8LBackwardRefs* const refs) { 822 const VP8LBackwardRefs* const refs) {
809 VP8LRefsCursor c = VP8LRefsCursorInit(refs); 823 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
810 while (VP8LRefsCursorOk(&c)) { 824 while (VP8LRefsCursorOk(&c)) {
811 if (PixOrCopyIsCopy(c.cur_pos)) { 825 if (PixOrCopyIsCopy(c.cur_pos)) {
812 const int dist = c.cur_pos->argb_or_distance; 826 const int dist = c.cur_pos->argb_or_distance;
813 const int transformed_dist = DistanceToPlaneCode(xsize, dist); 827 const int transformed_dist = DistanceToPlaneCode(xsize, dist);
814 c.cur_pos->argb_or_distance = transformed_dist; 828 c.cur_pos->argb_or_distance = transformed_dist;
815 } 829 }
816 VP8LRefsCursorNext(&c); 830 VP8LRefsCursorNext(&c);
817 } 831 }
818 } 832 }
819 833
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];
829
830 if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
831 hash_chain, refs_lz77)) {
832 return NULL;
833 }
834 if (!BackwardReferencesRle(width, height, argb, refs_rle)) {
835 return NULL;
836 }
837
838 {
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);
851 }
852
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;
868 }
869 }
870 } else {
871 best = refs_rle;
872 }
873
874 if (use_2d_locality) BackwardReferences2DLocality(width, best);
875
876 return best;
877 }
878
879 // Returns entropy for the given cache bits. 834 // Returns entropy for the given cache bits.
880 static double ComputeCacheEntropy(const uint32_t* const argb, 835 static double ComputeCacheEntropy(const uint32_t* argb,
881 int xsize, int ysize,
882 const VP8LBackwardRefs* const refs, 836 const VP8LBackwardRefs* const refs,
883 int cache_bits) { 837 int cache_bits) {
884 int pixel_index = 0;
885 uint32_t k;
886 const int use_color_cache = (cache_bits > 0); 838 const int use_color_cache = (cache_bits > 0);
887 int cc_init = 0; 839 int cc_init = 0;
888 double entropy = MAX_ENTROPY; 840 double entropy = MAX_ENTROPY;
889 const double kSmallPenaltyForLargeCache = 4.0; 841 const double kSmallPenaltyForLargeCache = 4.0;
890 VP8LColorCache hashers; 842 VP8LColorCache hashers;
891 VP8LRefsCursor c = VP8LRefsCursorInit(refs); 843 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
892 VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits); 844 VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits);
893 if (histo == NULL) goto Error; 845 if (histo == NULL) goto Error;
894 846
895 if (use_color_cache) { 847 if (use_color_cache) {
896 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 848 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
897 if (!cc_init) goto Error; 849 if (!cc_init) goto Error;
898 } 850 }
899 851 if (!use_color_cache) {
900 while (VP8LRefsCursorOk(&c)) { 852 while (VP8LRefsCursorOk(&c)) {
901 const PixOrCopy* const v = c.cur_pos; 853 VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos);
902 if (PixOrCopyIsLiteral(v)) { 854 VP8LRefsCursorNext(&c);
903 if (use_color_cache && 855 }
904 VP8LColorCacheContains(&hashers, argb[pixel_index])) { 856 } else {
905 // push pixel as a cache index 857 while (VP8LRefsCursorOk(&c)) {
906 const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); 858 const PixOrCopy* const v = c.cur_pos;
907 const PixOrCopy token = PixOrCopyCreateCacheIdx(ix); 859 if (PixOrCopyIsLiteral(v)) {
908 VP8LHistogramAddSinglePixOrCopy(histo, &token); 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 }
909 } else { 871 } else {
910 VP8LHistogramAddSinglePixOrCopy(histo, v); 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);
911 } 881 }
912 } else { 882 VP8LRefsCursorNext(&c);
913 VP8LHistogramAddSinglePixOrCopy(histo, v);
914 } 883 }
915 if (use_color_cache) {
916 for (k = 0; k < PixOrCopyLength(v); ++k) {
917 VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
918 }
919 }
920 pixel_index += PixOrCopyLength(v);
921 VP8LRefsCursorNext(&c);
922 } 884 }
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) + 885 entropy = VP8LHistogramEstimateBits(histo) +
927 kSmallPenaltyForLargeCache * cache_bits; 886 kSmallPenaltyForLargeCache * cache_bits;
928 Error: 887 Error:
929 if (cc_init) VP8LColorCacheClear(&hashers); 888 if (cc_init) VP8LColorCacheClear(&hashers);
930 VP8LFreeHistogram(histo); 889 VP8LFreeHistogram(histo);
931 return entropy; 890 return entropy;
932 } 891 }
933 892
934 // *best_cache_bits will contain how many bits are to be used for a color cache. 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.
935 // Returns 0 in case of memory error. 897 // Returns 0 in case of memory error.
936 int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, 898 static int CalculateBestCacheSize(const uint32_t* const argb,
937 int xsize, int ysize, int quality, 899 int xsize, int ysize, int quality,
938 VP8LHashChain* const hash_chain, 900 VP8LHashChain* const hash_chain,
939 VP8LBackwardRefs* const refs, 901 VP8LBackwardRefs* const refs,
940 int* const best_cache_bits) { 902 int* const lz77_computed,
903 int* const best_cache_bits) {
941 int eval_low = 1; 904 int eval_low = 1;
942 int eval_high = 1; 905 int eval_high = 1;
943 double entropy_low = MAX_ENTROPY; 906 double entropy_low = MAX_ENTROPY;
944 double entropy_high = MAX_ENTROPY; 907 double entropy_high = MAX_ENTROPY;
908 const double cost_mul = 5e-4;
945 int cache_bits_low = 0; 909 int cache_bits_low = 0;
946 int cache_bits_high = MAX_COLOR_CACHE_BITS; 910 int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits;
947 911
948 if (!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, hash_chain, 912 assert(cache_bits_high <= MAX_COLOR_CACHE_BITS);
949 refs)) { 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)) {
950 return 0; 922 return 0;
951 } 923 }
952 // Do a binary search to find the optimal entropy for cache_bits. 924 // Do a binary search to find the optimal entropy for cache_bits.
953 while (cache_bits_high - cache_bits_low > 1) { 925 while (eval_low || eval_high) {
954 if (eval_low) { 926 if (eval_low) {
955 entropy_low = 927 entropy_low = ComputeCacheEntropy(argb, refs, cache_bits_low);
956 ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_low); 928 entropy_low += entropy_low * cache_bits_low * cost_mul;
957 eval_low = 0; 929 eval_low = 0;
958 } 930 }
959 if (eval_high) { 931 if (eval_high) {
960 entropy_high = 932 entropy_high = ComputeCacheEntropy(argb, refs, cache_bits_high);
961 ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_high); 933 entropy_high += entropy_high * cache_bits_high * cost_mul;
962 eval_high = 0; 934 eval_high = 0;
963 } 935 }
964 if (entropy_high < entropy_low) { 936 if (entropy_high < entropy_low) {
937 const int prev_cache_bits_low = cache_bits_low;
965 *best_cache_bits = cache_bits_high; 938 *best_cache_bits = cache_bits_high;
966 cache_bits_low = (cache_bits_low + cache_bits_high) / 2; 939 cache_bits_low = (cache_bits_low + cache_bits_high) / 2;
967 eval_low = 1; 940 if (cache_bits_low != prev_cache_bits_low) eval_low = 1;
968 } else { 941 } else {
969 *best_cache_bits = cache_bits_low; 942 *best_cache_bits = cache_bits_low;
970 cache_bits_high = (cache_bits_low + cache_bits_high) / 2; 943 cache_bits_high = (cache_bits_low + cache_bits_high) / 2;
971 eval_high = 1; 944 if (cache_bits_high != cache_bits_low) eval_high = 1;
972 } 945 }
973 } 946 }
947 *lz77_computed = 1;
974 return 1; 948 return 1;
975 } 949 }
950
951 // Update (in-place) backward references for specified cache_bits.
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698