| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |