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

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

Issue 2651883004: libwebp-0.6.0-rc1 (Closed)
Patch Set: Created 3 years, 11 months 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
« no previous file with comments | « third_party/libwebp/enc/backward_references_enc.h ('k') | third_party/libwebp/enc/config.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_enc.h"
17 #include "./histogram.h" 17 #include "./histogram_enc.h"
18 #include "../dsp/lossless.h" 18 #include "../dsp/lossless.h"
19 #include "../dsp/lossless_common.h"
19 #include "../dsp/dsp.h" 20 #include "../dsp/dsp.h"
20 #include "../utils/color_cache.h" 21 #include "../utils/color_cache_utils.h"
21 #include "../utils/utils.h" 22 #include "../utils/utils.h"
22 23
23 #define VALUES_IN_BYTE 256 24 #define VALUES_IN_BYTE 256
24 25
25 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references 26 #define MIN_BLOCK_SIZE 256 // minimum block size for backward references
26 27
27 #define MAX_ENTROPY (1e30f) 28 #define MAX_ENTROPY (1e30f)
28 29
29 // 1M window (4M bytes) minus 120 special codes for short distances. 30 // 1M window (4M bytes) minus 120 special codes for short distances.
30 #define WINDOW_SIZE_BITS 20 31 #define WINDOW_SIZE_BITS 20
31 #define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120) 32 #define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120)
32 33
33 // Bounds for the match length. 34 // Minimum number of pixels for which it is cheaper to encode a
34 #define MIN_LENGTH 2 35 // distance + length instead of each pixel as a literal.
36 #define MIN_LENGTH 4
35 // If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it 37 // If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it
36 // is used in VP8LHashChain. 38 // is used in VP8LHashChain.
37 #define MAX_LENGTH_BITS 12 39 #define MAX_LENGTH_BITS 12
38 // We want the max value to be attainable and stored in MAX_LENGTH_BITS bits. 40 // We want the max value to be attainable and stored in MAX_LENGTH_BITS bits.
39 #define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1) 41 #define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1)
40 #if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32 42 #if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32
41 #error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32" 43 #error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32"
42 #endif 44 #endif
43 45
44 // ----------------------------------------------------------------------------- 46 // -----------------------------------------------------------------------------
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
204 void VP8LHashChainClear(VP8LHashChain* const p) { 206 void VP8LHashChainClear(VP8LHashChain* const p) {
205 assert(p != NULL); 207 assert(p != NULL);
206 WebPSafeFree(p->offset_length_); 208 WebPSafeFree(p->offset_length_);
207 209
208 p->size_ = 0; 210 p->size_ = 0;
209 p->offset_length_ = NULL; 211 p->offset_length_ = NULL;
210 } 212 }
211 213
212 // ----------------------------------------------------------------------------- 214 // -----------------------------------------------------------------------------
213 215
214 #define HASH_MULTIPLIER_HI (0xc6a4a793U) 216 #define HASH_MULTIPLIER_HI (0xc6a4a793ULL)
215 #define HASH_MULTIPLIER_LO (0x5bd1e996U) 217 #define HASH_MULTIPLIER_LO (0x5bd1e996ULL)
216 218
217 static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) { 219 static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
218 uint32_t key; 220 uint32_t key;
219 key = argb[1] * HASH_MULTIPLIER_HI; 221 key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu;
220 key += argb[0] * HASH_MULTIPLIER_LO; 222 key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu;
221 key = key >> (32 - HASH_BITS); 223 key = key >> (32 - HASH_BITS);
222 return key; 224 return key;
223 } 225 }
224 226
225 // Returns the maximum number of hash chain lookups to do for a 227 // Returns the maximum number of hash chain lookups to do for a
226 // given compression quality. Return value in range [8, 86]. 228 // given compression quality. Return value in range [8, 86].
227 static int GetMaxItersForQuality(int quality) { 229 static int GetMaxItersForQuality(int quality) {
228 return 8 + (quality * quality) / 128; 230 return 8 + (quality * quality) / 128;
229 } 231 }
230 232
231 static int GetWindowSizeForHashChain(int quality, int xsize) { 233 static int GetWindowSizeForHashChain(int quality, int xsize) {
232 const int max_window_size = (quality > 75) ? WINDOW_SIZE 234 const int max_window_size = (quality > 75) ? WINDOW_SIZE
233 : (quality > 50) ? (xsize << 8) 235 : (quality > 50) ? (xsize << 8)
234 : (quality > 25) ? (xsize << 6) 236 : (quality > 25) ? (xsize << 6)
235 : (xsize << 4); 237 : (xsize << 4);
236 assert(xsize > 0); 238 assert(xsize > 0);
237 return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size; 239 return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;
238 } 240 }
239 241
240 static WEBP_INLINE int MaxFindCopyLength(int len) { 242 static WEBP_INLINE int MaxFindCopyLength(int len) {
241 return (len < MAX_LENGTH) ? len : MAX_LENGTH; 243 return (len < MAX_LENGTH) ? len : MAX_LENGTH;
242 } 244 }
243 245
244 int VP8LHashChainFill(VP8LHashChain* const p, int quality, 246 int VP8LHashChainFill(VP8LHashChain* const p, int quality,
245 const uint32_t* const argb, int xsize, int ysize) { 247 const uint32_t* const argb, int xsize, int ysize,
248 int low_effort) {
246 const int size = xsize * ysize; 249 const int size = xsize * ysize;
247 const int iter_max = GetMaxItersForQuality(quality); 250 const int iter_max = GetMaxItersForQuality(quality);
248 const int iter_min = iter_max - quality / 10;
249 const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize); 251 const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
250 int pos; 252 int pos;
253 int argb_comp;
251 uint32_t base_position; 254 uint32_t base_position;
252 int32_t* hash_to_first_index; 255 int32_t* hash_to_first_index;
253 // Temporarily use the p->offset_length_ as a hash chain. 256 // Temporarily use the p->offset_length_ as a hash chain.
254 int32_t* chain = (int32_t*)p->offset_length_; 257 int32_t* chain = (int32_t*)p->offset_length_;
258 assert(size > 0);
255 assert(p->size_ != 0); 259 assert(p->size_ != 0);
256 assert(p->offset_length_ != NULL); 260 assert(p->offset_length_ != NULL);
257 261
262 if (size <= 2) {
263 p->offset_length_[0] = p->offset_length_[size - 1] = 0;
264 return 1;
265 }
266
258 hash_to_first_index = 267 hash_to_first_index =
259 (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); 268 (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
260 if (hash_to_first_index == NULL) return 0; 269 if (hash_to_first_index == NULL) return 0;
261 270
262 // Set the int32_t array to -1. 271 // Set the int32_t array to -1.
263 memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index)); 272 memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
264 // Fill the chain linking pixels with the same hash. 273 // Fill the chain linking pixels with the same hash.
265 for (pos = 0; pos < size - 1; ++pos) { 274 argb_comp = (argb[0] == argb[1]);
266 const uint32_t hash_code = GetPixPairHash64(argb + pos); 275 for (pos = 0; pos < size - 2;) {
267 chain[pos] = hash_to_first_index[hash_code]; 276 uint32_t hash_code;
268 hash_to_first_index[hash_code] = pos; 277 const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);
278 if (argb_comp && argb_comp_next) {
279 // Consecutive pixels with the same color will share the same hash.
280 // We therefore use a different hash: the color and its repetition
281 // length.
282 uint32_t tmp[2];
283 uint32_t len = 1;
284 tmp[0] = argb[pos];
285 // Figure out how far the pixels are the same.
286 // The last pixel has a different 64 bit hash, as its next pixel does
287 // not have the same color, so we just need to get to the last pixel equal
288 // to its follower.
289 while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {
290 ++len;
291 }
292 if (len > MAX_LENGTH) {
293 // Skip the pixels that match for distance=1 and length>MAX_LENGTH
294 // because they are linked to their predecessor and we automatically
295 // check that in the main for loop below. Skipping means setting no
296 // predecessor in the chain, hence -1.
297 memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));
298 pos += len - MAX_LENGTH;
299 len = MAX_LENGTH;
300 }
301 // Process the rest of the hash chain.
302 while (len) {
303 tmp[1] = len--;
304 hash_code = GetPixPairHash64(tmp);
305 chain[pos] = hash_to_first_index[hash_code];
306 hash_to_first_index[hash_code] = pos++;
307 }
308 argb_comp = 0;
309 } else {
310 // Just move one pixel forward.
311 hash_code = GetPixPairHash64(argb + pos);
312 chain[pos] = hash_to_first_index[hash_code];
313 hash_to_first_index[hash_code] = pos++;
314 argb_comp = argb_comp_next;
315 }
269 } 316 }
317 // Process the penultimate pixel.
318 chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
319
270 WebPSafeFree(hash_to_first_index); 320 WebPSafeFree(hash_to_first_index);
271 321
272 // Find the best match interval at each pixel, defined by an offset to the 322 // Find the best match interval at each pixel, defined by an offset to the
273 // pixel and a length. The right-most pixel cannot match anything to the right 323 // pixel and a length. The right-most pixel cannot match anything to the right
274 // (hence a best length of 0) and the left-most pixel nothing to the left 324 // (hence a best length of 0) and the left-most pixel nothing to the left
275 // (hence an offset of 0). 325 // (hence an offset of 0).
326 assert(size > 2);
276 p->offset_length_[0] = p->offset_length_[size - 1] = 0; 327 p->offset_length_[0] = p->offset_length_[size - 1] = 0;
277 for (base_position = size - 2 < 0 ? 0 : size - 2; base_position > 0;) { 328 for (base_position = size - 2; base_position > 0;) {
278 const int max_len = MaxFindCopyLength(size - 1 - base_position); 329 const int max_len = MaxFindCopyLength(size - 1 - base_position);
279 const uint32_t* const argb_start = argb + base_position; 330 const uint32_t* const argb_start = argb + base_position;
280 int iter = iter_max; 331 int iter = iter_max;
281 int best_length = 0; 332 int best_length = 0;
282 uint32_t best_distance = 0; 333 uint32_t best_distance = 0;
334 uint32_t best_argb;
283 const int min_pos = 335 const int min_pos =
284 (base_position > window_size) ? base_position - window_size : 0; 336 (base_position > window_size) ? base_position - window_size : 0;
285 const int length_max = (max_len < 256) ? max_len : 256; 337 const int length_max = (max_len < 256) ? max_len : 256;
286 uint32_t max_base_position; 338 uint32_t max_base_position;
287 339
288 for (pos = chain[base_position]; pos >= min_pos; pos = chain[pos]) { 340 pos = chain[base_position];
341 if (!low_effort) {
289 int curr_length; 342 int curr_length;
290 if (--iter < 0) { 343 // Heuristic: use the comparison with the above line as an initialization.
291 break; 344 if (base_position >= (uint32_t)xsize) {
345 curr_length = FindMatchLength(argb_start - xsize, argb_start,
346 best_length, max_len);
347 if (curr_length > best_length) {
348 best_length = curr_length;
349 best_distance = xsize;
350 }
351 --iter;
292 } 352 }
353 // Heuristic: compare to the previous pixel.
354 curr_length =
355 FindMatchLength(argb_start - 1, argb_start, best_length, max_len);
356 if (curr_length > best_length) {
357 best_length = curr_length;
358 best_distance = 1;
359 }
360 --iter;
361 // Skip the for loop if we already have the maximum.
362 if (best_length == MAX_LENGTH) pos = min_pos - 1;
363 }
364 best_argb = argb_start[best_length];
365
366 for (; pos >= min_pos && --iter; pos = chain[pos]) {
367 int curr_length;
293 assert(base_position > (uint32_t)pos); 368 assert(base_position > (uint32_t)pos);
294 369
295 curr_length = 370 if (argb[pos + best_length] != best_argb) continue;
296 FindMatchLength(argb + pos, argb_start, best_length, max_len); 371
372 curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);
297 if (best_length < curr_length) { 373 if (best_length < curr_length) {
298 best_length = curr_length; 374 best_length = curr_length;
299 best_distance = base_position - pos; 375 best_distance = base_position - pos;
300 // Stop if we have reached the maximum length. Otherwise, make sure 376 best_argb = argb_start[best_length];
301 // we have executed a minimum number of iterations depending on the 377 // Stop if we have reached a good enough length.
302 // quality. 378 if (best_length >= length_max) break;
303 if ((best_length == MAX_LENGTH) ||
304 (curr_length >= length_max && iter < iter_min)) {
305 break;
306 }
307 } 379 }
308 } 380 }
309 // We have the best match but in case the two intervals continue matching 381 // We have the best match but in case the two intervals continue matching
310 // to the left, we have the best matches for the left-extended pixels. 382 // to the left, we have the best matches for the left-extended pixels.
311 max_base_position = base_position; 383 max_base_position = base_position;
312 while (1) { 384 while (1) {
313 assert(best_length <= MAX_LENGTH); 385 assert(best_length <= MAX_LENGTH);
314 assert(best_distance <= WINDOW_SIZE); 386 assert(best_distance <= WINDOW_SIZE);
315 p->offset_length_[base_position] = 387 p->offset_length_[base_position] =
316 (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length; 388 (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 457
386 if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) { 458 if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {
387 return 0; 459 return 0;
388 } 460 }
389 ClearBackwardRefs(refs); 461 ClearBackwardRefs(refs);
390 // Add first pixel as literal. 462 // Add first pixel as literal.
391 AddSingleLiteral(argb[0], use_color_cache, &hashers, refs); 463 AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);
392 i = 1; 464 i = 1;
393 while (i < pix_count) { 465 while (i < pix_count) {
394 const int max_len = MaxFindCopyLength(pix_count - i); 466 const int max_len = MaxFindCopyLength(pix_count - i);
395 const int kMinLength = 4;
396 const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len); 467 const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);
397 const int prev_row_len = (i < xsize) ? 0 : 468 const int prev_row_len = (i < xsize) ? 0 :
398 FindMatchLength(argb + i, argb + i - xsize, 0, max_len); 469 FindMatchLength(argb + i, argb + i - xsize, 0, max_len);
399 if (rle_len >= prev_row_len && rle_len >= kMinLength) { 470 if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {
400 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len)); 471 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));
401 // We don't need to update the color cache here since it is always the 472 // We don't need to update the color cache here since it is always the
402 // same pixel being copied, and that does not change the color cache 473 // same pixel being copied, and that does not change the color cache
403 // state. 474 // state.
404 i += rle_len; 475 i += rle_len;
405 } else if (prev_row_len >= kMinLength) { 476 } else if (prev_row_len >= MIN_LENGTH) {
406 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len)); 477 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));
407 if (use_color_cache) { 478 if (use_color_cache) {
408 for (k = 0; k < prev_row_len; ++k) { 479 for (k = 0; k < prev_row_len; ++k) {
409 VP8LColorCacheInsert(&hashers, argb[i + k]); 480 VP8LColorCacheInsert(&hashers, argb[i + k]);
410 } 481 }
411 } 482 }
412 i += prev_row_len; 483 i += prev_row_len;
413 } else { 484 } else {
414 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); 485 AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
415 i++; 486 i++;
(...skipping 19 matching lines...) Expand all
435 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 506 cc_init = VP8LColorCacheInit(&hashers, cache_bits);
436 if (!cc_init) goto Error; 507 if (!cc_init) goto Error;
437 } 508 }
438 ClearBackwardRefs(refs); 509 ClearBackwardRefs(refs);
439 for (i = 0; i < pix_count;) { 510 for (i = 0; i < pix_count;) {
440 // Alternative#1: Code the pixels starting at 'i' using backward reference. 511 // Alternative#1: Code the pixels starting at 'i' using backward reference.
441 int offset = 0; 512 int offset = 0;
442 int len = 0; 513 int len = 0;
443 int j; 514 int j;
444 HashChainFindCopy(hash_chain, i, &offset, &len); 515 HashChainFindCopy(hash_chain, i, &offset, &len);
445 if (len > MIN_LENGTH + 1) { 516 if (len >= MIN_LENGTH) {
446 const int len_ini = len; 517 const int len_ini = len;
447 int max_reach = 0; 518 int max_reach = 0;
448 assert(i + len < pix_count); 519 assert(i + len < pix_count);
449 // Only start from what we have not checked already. 520 // Only start from what we have not checked already.
450 i_last_check = (i > i_last_check) ? i : i_last_check; 521 i_last_check = (i > i_last_check) ? i : i_last_check;
451 // We know the best match for the current pixel but we try to find the 522 // We know the best match for the current pixel but we try to find the
452 // best matches for the current pixel AND the next one combined. 523 // best matches for the current pixel AND the next one combined.
453 // The naive method would use the intervals: 524 // The naive method would use the intervals:
454 // [i,i+len) + [i+len, length of best match at i+len) 525 // [i,i+len) + [i+len, length of best match at i+len)
455 // while we check if we can use: 526 // while we check if we can use:
456 // [i,j) (where j<=i+len) + [j, length of best match at j) 527 // [i,j) (where j<=i+len) + [j, length of best match at j)
457 for (j = i_last_check + 1; j <= i + len_ini; ++j) { 528 for (j = i_last_check + 1; j <= i + len_ini; ++j) {
458 const int len_j = HashChainFindLength(hash_chain, j); 529 const int len_j = HashChainFindLength(hash_chain, j);
459 const int reach = 530 const int reach =
460 j + (len_j > MIN_LENGTH + 1 ? len_j : 1); // 1 for single literal. 531 j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal.
461 if (reach > max_reach) { 532 if (reach > max_reach) {
462 len = j - i; 533 len = j - i;
463 max_reach = reach; 534 max_reach = reach;
464 } 535 }
465 } 536 }
466 } else { 537 } else {
467 len = 1; 538 len = 1;
468 } 539 }
469 // Go with literal or backward reference. 540 // Go with literal or backward reference.
470 assert(len > 0); 541 assert(len > 0);
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
574 } 645 }
575 646
576 static void AddSingleLiteralWithCostModel(const uint32_t* const argb, 647 static void AddSingleLiteralWithCostModel(const uint32_t* const argb,
577 VP8LColorCache* const hashers, 648 VP8LColorCache* const hashers,
578 const CostModel* const cost_model, 649 const CostModel* const cost_model,
579 int idx, int use_color_cache, 650 int idx, int use_color_cache,
580 double prev_cost, float* const cost, 651 double prev_cost, float* const cost,
581 uint16_t* const dist_array) { 652 uint16_t* const dist_array) {
582 double cost_val = prev_cost; 653 double cost_val = prev_cost;
583 const uint32_t color = argb[0]; 654 const uint32_t color = argb[0];
584 if (use_color_cache && VP8LColorCacheContains(hashers, color)) { 655 const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;
656 if (ix >= 0) {
657 // use_color_cache is true and hashers contains color
585 const double mul0 = 0.68; 658 const double mul0 = 0.68;
586 const int ix = VP8LColorCacheGetIndex(hashers, color);
587 cost_val += GetCacheCost(cost_model, ix) * mul0; 659 cost_val += GetCacheCost(cost_model, ix) * mul0;
588 } else { 660 } else {
589 const double mul1 = 0.82; 661 const double mul1 = 0.82;
590 if (use_color_cache) VP8LColorCacheInsert(hashers, color); 662 if (use_color_cache) VP8LColorCacheInsert(hashers, color);
591 cost_val += GetLiteralCost(cost_model, color) * mul1; 663 cost_val += GetLiteralCost(cost_model, color) * mul1;
592 } 664 }
593 if (cost[idx] > cost_val) { 665 if (cost[idx] > cost_val) {
594 cost[idx] = (float)cost_val; 666 cost[idx] = (float)cost_val;
595 dist_array[idx] = 1; // only one is inserted. 667 dist_array[idx] = 1; // only one is inserted.
596 } 668 }
(...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after
1208 dist_array[0] = 0; 1280 dist_array[0] = 0;
1209 // Add first pixel as literal. 1281 // Add first pixel as literal.
1210 AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0, 1282 AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0,
1211 use_color_cache, 0.0, cost_manager->costs_, 1283 use_color_cache, 0.0, cost_manager->costs_,
1212 dist_array); 1284 dist_array);
1213 1285
1214 for (i = 1; i < pix_count - 1; ++i) { 1286 for (i = 1; i < pix_count - 1; ++i) {
1215 int offset = 0, len = 0; 1287 int offset = 0, len = 0;
1216 double prev_cost = cost_manager->costs_[i - 1]; 1288 double prev_cost = cost_manager->costs_[i - 1];
1217 HashChainFindCopy(hash_chain, i, &offset, &len); 1289 HashChainFindCopy(hash_chain, i, &offset, &len);
1218 if (len >= MIN_LENGTH) { 1290 if (len >= 2) {
1291 // If we are dealing with a non-literal.
1219 const int code = DistanceToPlaneCode(xsize, offset); 1292 const int code = DistanceToPlaneCode(xsize, offset);
1220 const double offset_cost = GetDistanceCost(cost_model, code); 1293 const double offset_cost = GetDistanceCost(cost_model, code);
1221 const int first_i = i; 1294 const int first_i = i;
1222 int j_max = 0, interval_ends_index = 0; 1295 int j_max = 0, interval_ends_index = 0;
1223 const int is_offset_zero = (offset_cost == 0.); 1296 const int is_offset_zero = (offset_cost == 0.);
1224 1297
1225 if (!is_offset_zero) { 1298 if (!is_offset_zero) {
1226 j_max = (int)ceil( 1299 j_max = (int)ceil(
1227 (cost_manager->max_cost_cache_ - cost_manager->min_cost_cache_) / 1300 (cost_manager->max_cost_cache_ - cost_manager->min_cost_cache_) /
1228 offset_cost); 1301 offset_cost);
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1297 } 1370 }
1298 } 1371 }
1299 // 2) jump. 1372 // 2) jump.
1300 { 1373 {
1301 const int i_next = i + len - 1; // for loop does ++i, thus -1 here. 1374 const int i_next = i + len - 1; // for loop does ++i, thus -1 here.
1302 for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1); 1375 for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1);
1303 i = i_next; 1376 i = i_next;
1304 } 1377 }
1305 goto next_symbol; 1378 goto next_symbol;
1306 } 1379 }
1307 if (len > MIN_LENGTH) { 1380 if (len > 2) {
1308 int code_min_length; 1381 // Also try the smallest interval possible (size 2).
1309 double cost_total; 1382 double cost_total =
1310 offset = HashChainFindOffset(hash_chain, i); 1383 prev_cost + offset_cost + GetLengthCost(cost_model, 1);
1311 code_min_length = DistanceToPlaneCode(xsize, offset);
1312 cost_total = prev_cost +
1313 GetDistanceCost(cost_model, code_min_length) +
1314 GetLengthCost(cost_model, 1);
1315 if (cost_manager->costs_[i + 1] > cost_total) { 1384 if (cost_manager->costs_[i + 1] > cost_total) {
1316 cost_manager->costs_[i + 1] = (float)cost_total; 1385 cost_manager->costs_[i + 1] = (float)cost_total;
1317 dist_array[i + 1] = 2; 1386 dist_array[i + 1] = 2;
1318 } 1387 }
1319 } 1388 }
1320 } else { // len < MIN_LENGTH 1389 } else {
1390 // The pixel is added as a single literal so just update the costs.
1321 UpdateCostPerIndex(cost_manager, i + 1); 1391 UpdateCostPerIndex(cost_manager, i + 1);
1322 } 1392 }
1323 1393
1324 AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, 1394 AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i,
1325 use_color_cache, prev_cost, 1395 use_color_cache, prev_cost,
1326 cost_manager->costs_, dist_array); 1396 cost_manager->costs_, dist_array);
1327 1397
1328 next_symbol: ; 1398 next_symbol: ;
1329 } 1399 }
1330 // Handle the last pixel. 1400 // Handle the last pixel.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1386 const int offset = HashChainFindOffset(hash_chain, i); 1456 const int offset = HashChainFindOffset(hash_chain, i);
1387 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); 1457 BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
1388 if (use_color_cache) { 1458 if (use_color_cache) {
1389 for (k = 0; k < len; ++k) { 1459 for (k = 0; k < len; ++k) {
1390 VP8LColorCacheInsert(&hashers, argb[i + k]); 1460 VP8LColorCacheInsert(&hashers, argb[i + k]);
1391 } 1461 }
1392 } 1462 }
1393 i += len; 1463 i += len;
1394 } else { 1464 } else {
1395 PixOrCopy v; 1465 PixOrCopy v;
1396 if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { 1466 const int idx =
1467 use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;
1468 if (idx >= 0) {
1469 // use_color_cache is true and hashers contains argb[i]
1397 // push pixel as a color cache index 1470 // push pixel as a color cache index
1398 const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
1399 v = PixOrCopyCreateCacheIdx(idx); 1471 v = PixOrCopyCreateCacheIdx(idx);
1400 } else { 1472 } else {
1401 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); 1473 if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
1402 v = PixOrCopyCreateLiteral(argb[i]); 1474 v = PixOrCopyCreateLiteral(argb[i]);
1403 } 1475 }
1404 BackwardRefsCursorAdd(refs, v); 1476 BackwardRefsCursorAdd(refs, v);
1405 ++i; 1477 ++i;
1406 } 1478 }
1407 } 1479 }
1408 ok = !refs->error_; 1480 ok = !refs->error_;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1447 while (VP8LRefsCursorOk(&c)) { 1519 while (VP8LRefsCursorOk(&c)) {
1448 if (PixOrCopyIsCopy(c.cur_pos)) { 1520 if (PixOrCopyIsCopy(c.cur_pos)) {
1449 const int dist = c.cur_pos->argb_or_distance; 1521 const int dist = c.cur_pos->argb_or_distance;
1450 const int transformed_dist = DistanceToPlaneCode(xsize, dist); 1522 const int transformed_dist = DistanceToPlaneCode(xsize, dist);
1451 c.cur_pos->argb_or_distance = transformed_dist; 1523 c.cur_pos->argb_or_distance = transformed_dist;
1452 } 1524 }
1453 VP8LRefsCursorNext(&c); 1525 VP8LRefsCursorNext(&c);
1454 } 1526 }
1455 } 1527 }
1456 1528
1457 // Returns entropy for the given cache bits. 1529 // Computes the entropies for a color cache size (in bits) between 0 (unused)
1458 static double ComputeCacheEntropy(const uint32_t* argb, 1530 // and cache_bits_max (inclusive).
1459 const VP8LBackwardRefs* const refs, 1531 // Returns 1 on success, 0 in case of allocation error.
1460 int cache_bits) { 1532 static int ComputeCacheEntropies(const uint32_t* argb,
1461 const int use_color_cache = (cache_bits > 0); 1533 const VP8LBackwardRefs* const refs,
1462 int cc_init = 0; 1534 int cache_bits_max, double entropies[]) {
1463 double entropy = MAX_ENTROPY; 1535 int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };
1464 const double kSmallPenaltyForLargeCache = 4.0; 1536 VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];
1465 VP8LColorCache hashers;
1466 VP8LRefsCursor c = VP8LRefsCursorInit(refs); 1537 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
1467 VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits); 1538 VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };
1468 if (histo == NULL) goto Error; 1539 int ok = 0;
1540 int i;
1469 1541
1470 if (use_color_cache) { 1542 for (i = 0; i <= cache_bits_max; ++i) {
1471 cc_init = VP8LColorCacheInit(&hashers, cache_bits); 1543 histos[i] = VP8LAllocateHistogram(i);
1472 if (!cc_init) goto Error; 1544 if (histos[i] == NULL) goto Error;
1545 if (i == 0) continue;
1546 cc_init[i] = VP8LColorCacheInit(&hashers[i], i);
1547 if (!cc_init[i]) goto Error;
1473 } 1548 }
1474 if (!use_color_cache) { 1549
1475 while (VP8LRefsCursorOk(&c)) { 1550 assert(cache_bits_max >= 0);
1476 VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos); 1551 // Do not use the color cache for cache_bits=0.
1477 VP8LRefsCursorNext(&c); 1552 while (VP8LRefsCursorOk(&c)) {
1478 } 1553 VP8LHistogramAddSinglePixOrCopy(histos[0], c.cur_pos);
1479 } else { 1554 VP8LRefsCursorNext(&c);
1555 }
1556 if (cache_bits_max > 0) {
1557 c = VP8LRefsCursorInit(refs);
1480 while (VP8LRefsCursorOk(&c)) { 1558 while (VP8LRefsCursorOk(&c)) {
1481 const PixOrCopy* const v = c.cur_pos; 1559 const PixOrCopy* const v = c.cur_pos;
1482 if (PixOrCopyIsLiteral(v)) { 1560 if (PixOrCopyIsLiteral(v)) {
1483 const uint32_t pix = *argb++; 1561 const uint32_t pix = *argb++;
1484 const uint32_t key = VP8LColorCacheGetIndex(&hashers, pix); 1562 // The keys of the caches can be derived from the longest one.
1485 if (VP8LColorCacheLookup(&hashers, key) == pix) { 1563 int key = HashPix(pix, 32 - cache_bits_max);
1486 ++histo->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key]; 1564 for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
1487 } else { 1565 if (VP8LColorCacheLookup(&hashers[i], key) == pix) {
1488 VP8LColorCacheSet(&hashers, key, pix); 1566 ++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];
1489 ++histo->blue_[pix & 0xff]; 1567 } else {
1490 ++histo->literal_[(pix >> 8) & 0xff]; 1568 VP8LColorCacheSet(&hashers[i], key, pix);
1491 ++histo->red_[(pix >> 16) & 0xff]; 1569 ++histos[i]->blue_[pix & 0xff];
1492 ++histo->alpha_[pix >> 24]; 1570 ++histos[i]->literal_[(pix >> 8) & 0xff];
1571 ++histos[i]->red_[(pix >> 16) & 0xff];
1572 ++histos[i]->alpha_[pix >> 24];
1573 }
1493 } 1574 }
1494 } else { 1575 } else {
1576 // Update the histograms for distance/length.
1495 int len = PixOrCopyLength(v); 1577 int len = PixOrCopyLength(v);
1496 int code, extra_bits; 1578 int code_dist, code_len, extra_bits;
1497 VP8LPrefixEncodeBits(len, &code, &extra_bits); 1579 uint32_t argb_prev = *argb ^ 0xffffffffu;
1498 ++histo->literal_[NUM_LITERAL_CODES + code]; 1580 VP8LPrefixEncodeBits(len, &code_len, &extra_bits);
1499 VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); 1581 VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code_dist, &extra_bits);
1500 ++histo->distance_[code]; 1582 for (i = 1; i <= cache_bits_max; ++i) {
1583 ++histos[i]->literal_[NUM_LITERAL_CODES + code_len];
1584 ++histos[i]->distance_[code_dist];
1585 }
1586 // Update the colors caches.
1501 do { 1587 do {
1502 VP8LColorCacheInsert(&hashers, *argb++); 1588 if (*argb != argb_prev) {
1503 } while(--len != 0); 1589 // Efficiency: insert only if the color changes.
1590 int key = HashPix(*argb, 32 - cache_bits_max);
1591 for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
1592 hashers[i].colors_[key] = *argb;
1593 }
1594 argb_prev = *argb;
1595 }
1596 argb++;
1597 } while (--len != 0);
1504 } 1598 }
1505 VP8LRefsCursorNext(&c); 1599 VP8LRefsCursorNext(&c);
1506 } 1600 }
1507 } 1601 }
1508 entropy = VP8LHistogramEstimateBits(histo) + 1602 for (i = 0; i <= cache_bits_max; ++i) {
1509 kSmallPenaltyForLargeCache * cache_bits; 1603 entropies[i] = VP8LHistogramEstimateBits(histos[i]);
1510 Error: 1604 }
1511 if (cc_init) VP8LColorCacheClear(&hashers); 1605 ok = 1;
1512 VP8LFreeHistogram(histo); 1606 Error:
1513 return entropy; 1607 for (i = 0; i <= cache_bits_max; ++i) {
1608 if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);
1609 VP8LFreeHistogram(histos[i]);
1610 }
1611 return ok;
1514 } 1612 }
1515 1613
1516 // Evaluate optimal cache bits for the local color cache. 1614 // Evaluate optimal cache bits for the local color cache.
1517 // The input *best_cache_bits sets the maximum cache bits to use (passing 0 1615 // The input *best_cache_bits sets the maximum cache bits to use (passing 0
1518 // implies disabling the local color cache). The local color cache is also 1616 // implies disabling the local color cache). The local color cache is also
1519 // disabled for the lower (<= 25) quality. 1617 // disabled for the lower (<= 25) quality.
1520 // Returns 0 in case of memory error. 1618 // Returns 0 in case of memory error.
1521 static int CalculateBestCacheSize(const uint32_t* const argb, 1619 static int CalculateBestCacheSize(const uint32_t* const argb,
1522 int xsize, int ysize, int quality, 1620 int xsize, int ysize, int quality,
1523 const VP8LHashChain* const hash_chain, 1621 const VP8LHashChain* const hash_chain,
1524 VP8LBackwardRefs* const refs, 1622 VP8LBackwardRefs* const refs,
1525 int* const lz77_computed, 1623 int* const lz77_computed,
1526 int* const best_cache_bits) { 1624 int* const best_cache_bits) {
1527 int eval_low = 1; 1625 int i;
1528 int eval_high = 1;
1529 double entropy_low = MAX_ENTROPY;
1530 double entropy_high = MAX_ENTROPY;
1531 const double cost_mul = 5e-4;
1532 int cache_bits_low = 0;
1533 int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits; 1626 int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits;
1627 double entropy_min = MAX_ENTROPY;
1628 double entropies[MAX_COLOR_CACHE_BITS + 1];
1534 1629
1535 assert(cache_bits_high <= MAX_COLOR_CACHE_BITS); 1630 assert(cache_bits_high <= MAX_COLOR_CACHE_BITS);
1536 1631
1537 *lz77_computed = 0; 1632 *lz77_computed = 0;
1538 if (cache_bits_high == 0) { 1633 if (cache_bits_high == 0) {
1539 *best_cache_bits = 0; 1634 *best_cache_bits = 0;
1540 // Local color cache is disabled. 1635 // Local color cache is disabled.
1541 return 1; 1636 return 1;
1542 } 1637 }
1543 if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, hash_chain, 1638 // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color cache
1544 refs)) { 1639 // is not that different in practice.
1640 if (!BackwardReferencesLz77(xsize, ysize, argb, 0, hash_chain, refs)) {
1545 return 0; 1641 return 0;
1546 } 1642 }
1547 // Do a binary search to find the optimal entropy for cache_bits. 1643 // Find the cache_bits giving the lowest entropy. The search is done in a
1548 while (eval_low || eval_high) { 1644 // brute-force way as the function (entropy w.r.t cache_bits) can be
1549 if (eval_low) { 1645 // anything in practice.
1550 entropy_low = ComputeCacheEntropy(argb, refs, cache_bits_low); 1646 if (!ComputeCacheEntropies(argb, refs, cache_bits_high, entropies)) {
1551 entropy_low += entropy_low * cache_bits_low * cost_mul; 1647 return 0;
1552 eval_low = 0; 1648 }
1553 } 1649 for (i = 0; i <= cache_bits_high; ++i) {
1554 if (eval_high) { 1650 if (i == 0 || entropies[i] < entropy_min) {
1555 entropy_high = ComputeCacheEntropy(argb, refs, cache_bits_high); 1651 entropy_min = entropies[i];
1556 entropy_high += entropy_high * cache_bits_high * cost_mul; 1652 *best_cache_bits = i;
1557 eval_high = 0;
1558 }
1559 if (entropy_high < entropy_low) {
1560 const int prev_cache_bits_low = cache_bits_low;
1561 *best_cache_bits = cache_bits_high;
1562 cache_bits_low = (cache_bits_low + cache_bits_high) / 2;
1563 if (cache_bits_low != prev_cache_bits_low) eval_low = 1;
1564 } else {
1565 *best_cache_bits = cache_bits_low;
1566 cache_bits_high = (cache_bits_low + cache_bits_high) / 2;
1567 if (cache_bits_high != cache_bits_low) eval_high = 1;
1568 } 1653 }
1569 } 1654 }
1570 *lz77_computed = 1;
1571 return 1; 1655 return 1;
1572 } 1656 }
1573 1657
1574 // Update (in-place) backward references for specified cache_bits. 1658 // Update (in-place) backward references for specified cache_bits.
1575 static int BackwardRefsWithLocalCache(const uint32_t* const argb, 1659 static int BackwardRefsWithLocalCache(const uint32_t* const argb,
1576 int cache_bits, 1660 int cache_bits,
1577 VP8LBackwardRefs* const refs) { 1661 VP8LBackwardRefs* const refs) {
1578 int pixel_index = 0; 1662 int pixel_index = 0;
1579 VP8LColorCache hashers; 1663 VP8LColorCache hashers;
1580 VP8LRefsCursor c = VP8LRefsCursorInit(refs); 1664 VP8LRefsCursor c = VP8LRefsCursorInit(refs);
1581 if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0; 1665 if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;
1582 1666
1583 while (VP8LRefsCursorOk(&c)) { 1667 while (VP8LRefsCursorOk(&c)) {
1584 PixOrCopy* const v = c.cur_pos; 1668 PixOrCopy* const v = c.cur_pos;
1585 if (PixOrCopyIsLiteral(v)) { 1669 if (PixOrCopyIsLiteral(v)) {
1586 const uint32_t argb_literal = v->argb_or_distance; 1670 const uint32_t argb_literal = v->argb_or_distance;
1587 if (VP8LColorCacheContains(&hashers, argb_literal)) { 1671 const int ix = VP8LColorCacheContains(&hashers, argb_literal);
1588 const int ix = VP8LColorCacheGetIndex(&hashers, argb_literal); 1672 if (ix >= 0) {
1673 // hashers contains argb_literal
1589 *v = PixOrCopyCreateCacheIdx(ix); 1674 *v = PixOrCopyCreateCacheIdx(ix);
1590 } else { 1675 } else {
1591 VP8LColorCacheInsert(&hashers, argb_literal); 1676 VP8LColorCacheInsert(&hashers, argb_literal);
1592 } 1677 }
1593 ++pixel_index; 1678 ++pixel_index;
1594 } else { 1679 } else {
1595 // refs was created without local cache, so it can not have cache indexes. 1680 // refs was created without local cache, so it can not have cache indexes.
1596 int k; 1681 int k;
1597 assert(PixOrCopyIsCopy(v)); 1682 assert(PixOrCopyIsCopy(v));
1598 for (k = 0; k < v->len; ++k) { 1683 for (k = 0; k < v->len; ++k) {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
1706 int low_effort, int* const cache_bits, 1791 int low_effort, int* const cache_bits,
1707 const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { 1792 const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) {
1708 if (low_effort) { 1793 if (low_effort) {
1709 return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, 1794 return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
1710 hash_chain, refs_array); 1795 hash_chain, refs_array);
1711 } else { 1796 } else {
1712 return GetBackwardReferences(width, height, argb, quality, cache_bits, 1797 return GetBackwardReferences(width, height, argb, quality, cache_bits,
1713 hash_chain, refs_array); 1798 hash_chain, refs_array);
1714 } 1799 }
1715 } 1800 }
OLDNEW
« no previous file with comments | « third_party/libwebp/enc/backward_references_enc.h ('k') | third_party/libwebp/enc/config.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698