| OLD | NEW |
| 1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // This code is licensed under the same terms as WebM: | 3 // Use of this source code is governed by a BSD-style license |
| 4 // Software License Agreement: http://www.webmproject.org/license/software/ | 4 // that can be found in the COPYING file in the root of the source |
| 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. |
| 6 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 7 // | 9 // |
| 8 // Author: Jyrki Alakuijala (jyrki@google.com) | 10 // Author: Jyrki Alakuijala (jyrki@google.com) |
| 9 // | 11 // |
| 10 | 12 |
| 11 #include <assert.h> | 13 #include <assert.h> |
| 12 #include <math.h> | 14 #include <math.h> |
| 13 #include <stdio.h> | 15 #include <stdio.h> |
| 14 | 16 |
| 15 #include "./backward_references.h" | 17 #include "./backward_references.h" |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 | 137 |
| 136 // Insertion of two pixels at a time. | 138 // Insertion of two pixels at a time. |
| 137 static void HashChainInsert(HashChain* const p, | 139 static void HashChainInsert(HashChain* const p, |
| 138 const uint32_t* const argb, int pos) { | 140 const uint32_t* const argb, int pos) { |
| 139 const uint64_t hash_code = GetPixPairHash64(argb); | 141 const uint64_t hash_code = GetPixPairHash64(argb); |
| 140 p->chain_[pos] = p->hash_to_first_index_[hash_code]; | 142 p->chain_[pos] = p->hash_to_first_index_[hash_code]; |
| 141 p->hash_to_first_index_[hash_code] = pos; | 143 p->hash_to_first_index_[hash_code] = pos; |
| 142 } | 144 } |
| 143 | 145 |
| 144 static void GetParamsForHashChainFindCopy(int quality, int xsize, | 146 static void GetParamsForHashChainFindCopy(int quality, int xsize, |
| 145 int* window_size, int* iter_pos, | 147 int cache_bits, int* window_size, |
| 146 int* iter_limit) { | 148 int* iter_pos, int* iter_limit) { |
| 147 const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4); | 149 const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4); |
| 150 const int iter_neg = -iter_mult * (quality >> 1); |
| 148 // Limit the backward-ref window size for lower qualities. | 151 // Limit the backward-ref window size for lower qualities. |
| 149 const int max_window_size = (quality > 50) ? WINDOW_SIZE | 152 const int max_window_size = (quality > 50) ? WINDOW_SIZE |
| 150 : (quality > 25) ? (xsize << 8) | 153 : (quality > 25) ? (xsize << 8) |
| 151 : (xsize << 4); | 154 : (xsize << 4); |
| 152 assert(xsize > 0); | 155 assert(xsize > 0); |
| 153 *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE | 156 *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE |
| 154 : max_window_size; | 157 : max_window_size; |
| 155 *iter_pos = 5 + (quality >> 3); | 158 *iter_pos = 8 + (quality >> 3); |
| 156 *iter_limit = -quality * iter_mult; | 159 // For lower entropy images, the rigourous search loop in HashChainFindCopy |
| 160 // can be relaxed. |
| 161 *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2; |
| 157 } | 162 } |
| 158 | 163 |
| 159 static int HashChainFindCopy(const HashChain* const p, | 164 static int HashChainFindCopy(const HashChain* const p, |
| 160 int base_position, int xsize, | 165 int base_position, int xsize_signed, |
| 161 const uint32_t* const argb, int maxlen, | 166 const uint32_t* const argb, int maxlen, |
| 162 int window_size, int iter_pos, int iter_limit, | 167 int window_size, int iter_pos, int iter_limit, |
| 163 int* const distance_ptr, | 168 int* const distance_ptr, |
| 164 int* const length_ptr) { | 169 int* const length_ptr) { |
| 165 const uint64_t hash_code = GetPixPairHash64(&argb[base_position]); | |
| 166 int prev_length = 0; | |
| 167 int64_t best_val = 0; | |
| 168 int best_length = 0; | |
| 169 int best_distance = 0; | |
| 170 const uint32_t* const argb_start = argb + base_position; | 170 const uint32_t* const argb_start = argb + base_position; |
| 171 uint64_t best_val = 0; |
| 172 uint32_t best_length = 1; |
| 173 uint32_t best_distance = 0; |
| 174 const uint32_t xsize = (uint32_t)xsize_signed; |
| 171 const int min_pos = | 175 const int min_pos = |
| 172 (base_position > window_size) ? base_position - window_size : 0; | 176 (base_position > window_size) ? base_position - window_size : 0; |
| 173 int pos; | 177 int pos; |
| 174 | |
| 175 assert(xsize > 0); | 178 assert(xsize > 0); |
| 176 for (pos = p->hash_to_first_index_[hash_code]; | 179 for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)]; |
| 177 pos >= min_pos; | 180 pos >= min_pos; |
| 178 pos = p->chain_[pos]) { | 181 pos = p->chain_[pos]) { |
| 179 int64_t val; | 182 uint64_t val; |
| 180 int curr_length; | 183 uint32_t curr_length; |
| 184 uint32_t distance; |
| 181 if (iter_pos < 0) { | 185 if (iter_pos < 0) { |
| 182 if (iter_pos < iter_limit || best_val >= 0xff0000) { | 186 if (iter_pos < iter_limit || best_val >= 0xff0000) { |
| 183 break; | 187 break; |
| 184 } | 188 } |
| 185 } | 189 } |
| 186 --iter_pos; | 190 --iter_pos; |
| 187 if (best_length != 0 && | 191 if (argb[pos + best_length - 1] != argb_start[best_length - 1]) { |
| 188 argb[pos + best_length - 1] != argb_start[best_length - 1]) { | |
| 189 continue; | 192 continue; |
| 190 } | 193 } |
| 191 curr_length = FindMatchLength(argb + pos, argb_start, maxlen); | 194 curr_length = FindMatchLength(argb + pos, argb_start, maxlen); |
| 192 if (curr_length < prev_length) { | 195 if (curr_length < best_length) { |
| 193 continue; | 196 continue; |
| 194 } | 197 } |
| 195 val = 65536 * curr_length; | 198 distance = (uint32_t)(base_position - pos); |
| 199 val = curr_length << 16; |
| 196 // Favoring 2d locality here gives savings for certain images. | 200 // Favoring 2d locality here gives savings for certain images. |
| 197 if (base_position - pos < 9 * xsize) { | 201 if (distance < 9 * xsize) { |
| 198 const int y = (base_position - pos) / xsize; | 202 const uint32_t y = distance / xsize; |
| 199 int x = (base_position - pos) % xsize; | 203 uint32_t x = distance % xsize; |
| 200 if (x > xsize / 2) { | 204 if (x > (xsize >> 1)) { |
| 201 x = xsize - x; | 205 x = xsize - x; |
| 202 } | 206 } |
| 203 if (x <= 7 && x >= -8) { | 207 if (x <= 7) { |
| 208 val += 9 * 9 + 9 * 9; |
| 204 val -= y * y + x * x; | 209 val -= y * y + x * x; |
| 205 } else { | |
| 206 val -= 9 * 9 + 9 * 9; | |
| 207 } | 210 } |
| 208 } else { | |
| 209 val -= 9 * 9 + 9 * 9; | |
| 210 } | 211 } |
| 211 if (best_val < val) { | 212 if (best_val < val) { |
| 212 prev_length = curr_length; | |
| 213 best_val = val; | 213 best_val = val; |
| 214 best_length = curr_length; | 214 best_length = curr_length; |
| 215 best_distance = base_position - pos; | 215 best_distance = distance; |
| 216 if (curr_length >= MAX_LENGTH) { | 216 if (curr_length >= MAX_LENGTH) { |
| 217 break; | 217 break; |
| 218 } | 218 } |
| 219 if ((best_distance == 1 || best_distance == xsize) && | 219 if ((best_distance == 1 || distance == xsize) && |
| 220 best_length >= 128) { | 220 best_length >= 128) { |
| 221 break; | 221 break; |
| 222 } | 222 } |
| 223 } | 223 } |
| 224 } | 224 } |
| 225 *distance_ptr = best_distance; | 225 *distance_ptr = (int)best_distance; |
| 226 *length_ptr = best_length; | 226 *length_ptr = best_length; |
| 227 return (best_length >= MIN_LENGTH); | 227 return (best_length >= MIN_LENGTH); |
| 228 } | 228 } |
| 229 | 229 |
| 230 static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) { | 230 static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) { |
| 231 int size = refs->size; | 231 int size = refs->size; |
| 232 while (length >= MAX_LENGTH) { | 232 while (length >= MAX_LENGTH) { |
| 233 refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH); | 233 refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH); |
| 234 length -= MAX_LENGTH; | 234 length -= MAX_LENGTH; |
| 235 } | 235 } |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 277 | 277 |
| 278 if (hash_chain == NULL) return 0; | 278 if (hash_chain == NULL) return 0; |
| 279 if (use_color_cache) { | 279 if (use_color_cache) { |
| 280 cc_init = VP8LColorCacheInit(&hashers, cache_bits); | 280 cc_init = VP8LColorCacheInit(&hashers, cache_bits); |
| 281 if (!cc_init) goto Error; | 281 if (!cc_init) goto Error; |
| 282 } | 282 } |
| 283 | 283 |
| 284 if (!HashChainInit(hash_chain, pix_count)) goto Error; | 284 if (!HashChainInit(hash_chain, pix_count)) goto Error; |
| 285 | 285 |
| 286 refs->size = 0; | 286 refs->size = 0; |
| 287 GetParamsForHashChainFindCopy(quality, xsize, &window_size, &iter_pos, | 287 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, |
| 288 &iter_limit); | 288 &window_size, &iter_pos, &iter_limit); |
| 289 for (i = 0; i < pix_count; ) { | 289 for (i = 0; i < pix_count; ) { |
| 290 // Alternative#1: Code the pixels starting at 'i' using backward reference. | 290 // Alternative#1: Code the pixels starting at 'i' using backward reference. |
| 291 int offset = 0; | 291 int offset = 0; |
| 292 int len = 0; | 292 int len = 0; |
| 293 if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. | 293 if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. |
| 294 int maxlen = pix_count - i; | 294 int maxlen = pix_count - i; |
| 295 if (maxlen > MAX_LENGTH) { | 295 if (maxlen > MAX_LENGTH) { |
| 296 maxlen = MAX_LENGTH; | 296 maxlen = MAX_LENGTH; |
| 297 } | 297 } |
| 298 HashChainFindCopy(hash_chain, i, xsize, argb, maxlen, | 298 HashChainFindCopy(hash_chain, i, xsize, argb, maxlen, |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb, | 503 if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb, |
| 504 quality, cache_bits)) { | 504 quality, cache_bits)) { |
| 505 goto Error; | 505 goto Error; |
| 506 } | 506 } |
| 507 | 507 |
| 508 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f; | 508 for (i = 0; i < pix_count; ++i) cost[i] = 1e38f; |
| 509 | 509 |
| 510 // We loop one pixel at a time, but store all currently best points to | 510 // We loop one pixel at a time, but store all currently best points to |
| 511 // non-processed locations from this point. | 511 // non-processed locations from this point. |
| 512 dist_array[0] = 0; | 512 dist_array[0] = 0; |
| 513 GetParamsForHashChainFindCopy(quality, xsize, &window_size, &iter_pos, | 513 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, |
| 514 &iter_limit); | 514 &window_size, &iter_pos, &iter_limit); |
| 515 for (i = 0; i < pix_count; ++i) { | 515 for (i = 0; i < pix_count; ++i) { |
| 516 double prev_cost = 0.0; | 516 double prev_cost = 0.0; |
| 517 int shortmax; | 517 int shortmax; |
| 518 if (i > 0) { | 518 if (i > 0) { |
| 519 prev_cost = cost[i - 1]; | 519 prev_cost = cost[i - 1]; |
| 520 } | 520 } |
| 521 for (shortmax = 0; shortmax < 2; ++shortmax) { | 521 for (shortmax = 0; shortmax < 2; ++shortmax) { |
| 522 int offset = 0; | 522 int offset = 0; |
| 523 int len = 0; | 523 int len = 0; |
| 524 if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. | 524 if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 638 | 638 |
| 639 if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) { | 639 if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) { |
| 640 goto Error; | 640 goto Error; |
| 641 } | 641 } |
| 642 if (use_color_cache) { | 642 if (use_color_cache) { |
| 643 cc_init = VP8LColorCacheInit(&hashers, cache_bits); | 643 cc_init = VP8LColorCacheInit(&hashers, cache_bits); |
| 644 if (!cc_init) goto Error; | 644 if (!cc_init) goto Error; |
| 645 } | 645 } |
| 646 | 646 |
| 647 refs->size = 0; | 647 refs->size = 0; |
| 648 GetParamsForHashChainFindCopy(quality, xsize, &window_size, &iter_pos, | 648 GetParamsForHashChainFindCopy(quality, xsize, cache_bits, |
| 649 &iter_limit); | 649 &window_size, &iter_pos, &iter_limit); |
| 650 for (ix = 0; ix < chosen_path_size; ++ix, ++size) { | 650 for (ix = 0; ix < chosen_path_size; ++ix, ++size) { |
| 651 int offset = 0; | 651 int offset = 0; |
| 652 int len = 0; | 652 int len = 0; |
| 653 int maxlen = chosen_path[ix]; | 653 int maxlen = chosen_path[ix]; |
| 654 if (maxlen != 1) { | 654 if (maxlen != 1) { |
| 655 HashChainFindCopy(hash_chain, i, xsize, argb, maxlen, | 655 HashChainFindCopy(hash_chain, i, xsize, argb, maxlen, |
| 656 window_size, iter_pos, iter_limit, | 656 window_size, iter_pos, iter_limit, |
| 657 &offset, &len); | 657 &offset, &len); |
| 658 assert(len == maxlen); | 658 assert(len == maxlen); |
| 659 refs->refs[size] = PixOrCopyCreateCopy(offset, len); | 659 refs->refs[size] = PixOrCopyCreateCopy(offset, len); |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 778 free(histo); | 778 free(histo); |
| 779 } | 779 } |
| 780 | 780 |
| 781 // Choose appropriate backward reference. | 781 // Choose appropriate backward reference. |
| 782 if (lz77_is_useful) { | 782 if (lz77_is_useful) { |
| 783 // TraceBackwards is costly. Don't execute it at lower quality (q <= 10). | 783 // TraceBackwards is costly. Don't execute it at lower quality (q <= 10). |
| 784 const int try_lz77_trace_backwards = (quality > 10); | 784 const int try_lz77_trace_backwards = (quality > 10); |
| 785 *best = refs_lz77; // default guess: lz77 is better | 785 *best = refs_lz77; // default guess: lz77 is better |
| 786 VP8LClearBackwardRefs(&refs_rle); | 786 VP8LClearBackwardRefs(&refs_rle); |
| 787 if (try_lz77_trace_backwards) { | 787 if (try_lz77_trace_backwards) { |
| 788 const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; | 788 // Set recursion level for large images using a color cache. |
| 789 const int recursion_level = |
| 790 (num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0; |
| 789 VP8LBackwardRefs refs_trace; | 791 VP8LBackwardRefs refs_trace; |
| 790 if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) { | 792 if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) { |
| 791 goto End; | 793 goto End; |
| 792 } | 794 } |
| 793 if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb, | 795 if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb, |
| 794 quality, cache_bits, &refs_trace)) { | 796 quality, cache_bits, &refs_trace)) { |
| 795 VP8LClearBackwardRefs(&refs_lz77); | 797 VP8LClearBackwardRefs(&refs_lz77); |
| 796 *best = refs_trace; | 798 *best = refs_trace; |
| 797 } | 799 } |
| 798 } | 800 } |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 883 if (cache_bits == 0 || cur_entropy < lowest_entropy) { | 885 if (cache_bits == 0 || cur_entropy < lowest_entropy) { |
| 884 *best_cache_bits = cache_bits; | 886 *best_cache_bits = cache_bits; |
| 885 lowest_entropy = cur_entropy; | 887 lowest_entropy = cur_entropy; |
| 886 } | 888 } |
| 887 } | 889 } |
| 888 ok = 1; | 890 ok = 1; |
| 889 Error: | 891 Error: |
| 890 VP8LClearBackwardRefs(&refs); | 892 VP8LClearBackwardRefs(&refs); |
| 891 return ok; | 893 return ok; |
| 892 } | 894 } |
| OLD | NEW |