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 // main entry for the lossless encoder. | 10 // main entry for the lossless encoder. |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 | 119 |
120 // ----------------------------------------------------------------------------- | 120 // ----------------------------------------------------------------------------- |
121 // Palette | 121 // Palette |
122 | 122 |
123 // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, | 123 // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, |
124 // creates a palette and returns true, else returns false. | 124 // creates a palette and returns true, else returns false. |
125 static int AnalyzeAndCreatePalette(const WebPPicture* const pic, | 125 static int AnalyzeAndCreatePalette(const WebPPicture* const pic, |
126 int low_effort, | 126 int low_effort, |
127 uint32_t palette[MAX_PALETTE_SIZE], | 127 uint32_t palette[MAX_PALETTE_SIZE], |
128 int* const palette_size) { | 128 int* const palette_size) { |
129 int i, x, y, key; | 129 const int num_colors = WebPGetColorPalette(pic, palette); |
130 int num_colors = 0; | 130 if (num_colors > MAX_PALETTE_SIZE) return 0; |
131 uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; | |
132 uint32_t colors[MAX_PALETTE_SIZE * 4]; | |
133 static const uint32_t kHashMul = 0x1e35a7bd; | |
134 const uint32_t* argb = pic->argb; | |
135 const int width = pic->width; | |
136 const int height = pic->height; | |
137 uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] | |
138 | |
139 for (y = 0; y < height; ++y) { | |
140 for (x = 0; x < width; ++x) { | |
141 if (argb[x] == last_pix) { | |
142 continue; | |
143 } | |
144 last_pix = argb[x]; | |
145 key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT; | |
146 while (1) { | |
147 if (!in_use[key]) { | |
148 colors[key] = last_pix; | |
149 in_use[key] = 1; | |
150 ++num_colors; | |
151 if (num_colors > MAX_PALETTE_SIZE) { | |
152 return 0; | |
153 } | |
154 break; | |
155 } else if (colors[key] == last_pix) { | |
156 // The color is already there. | |
157 break; | |
158 } else { | |
159 // Some other color sits there. | |
160 // Do linear conflict resolution. | |
161 ++key; | |
162 key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. | |
163 } | |
164 } | |
165 } | |
166 argb += pic->argb_stride; | |
167 } | |
168 | |
169 // TODO(skal): could we reuse in_use[] to speed up EncodePalette()? | |
170 num_colors = 0; | |
171 for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { | |
172 if (in_use[i]) { | |
173 palette[num_colors] = colors[i]; | |
174 ++num_colors; | |
175 } | |
176 } | |
177 *palette_size = num_colors; | 131 *palette_size = num_colors; |
178 qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); | 132 qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); |
179 if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { | 133 if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { |
180 GreedyMinimizeDeltas(palette, num_colors); | 134 GreedyMinimizeDeltas(palette, num_colors); |
181 } | 135 } |
182 return 1; | 136 return 1; |
183 } | 137 } |
184 | 138 |
185 // These five modes are evaluated and their respective entropy is computed. | 139 // These five modes are evaluated and their respective entropy is computed. |
186 typedef enum { | 140 typedef enum { |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 const uint32_t* const blue_histo = | 283 const uint32_t* const blue_histo = |
330 &histo[256 * kHistoPairs[*min_entropy_ix][1]]; | 284 &histo[256 * kHistoPairs[*min_entropy_ix][1]]; |
331 for (i = 1; i < 256; ++i) { | 285 for (i = 1; i < 256; ++i) { |
332 if ((red_histo[i] | blue_histo[i]) != 0) { | 286 if ((red_histo[i] | blue_histo[i]) != 0) { |
333 *red_and_blue_always_zero = 0; | 287 *red_and_blue_always_zero = 0; |
334 break; | 288 break; |
335 } | 289 } |
336 } | 290 } |
337 } | 291 } |
338 } | 292 } |
339 free(histo); | 293 WebPSafeFree(histo); |
340 return 1; | 294 return 1; |
341 } else { | 295 } else { |
342 return 0; | 296 return 0; |
343 } | 297 } |
344 } | 298 } |
345 | 299 |
346 static int GetHistoBits(int method, int use_palette, int width, int height) { | 300 static int GetHistoBits(int method, int use_palette, int width, int height) { |
347 // Make tile size a function of encoding method (Range: 0 to 6). | 301 // Make tile size a function of encoding method (Range: 0 to 6). |
348 int histo_bits = (use_palette ? 9 : 7) - method; | 302 int histo_bits = (use_palette ? 9 : 7) - method; |
349 while (1) { | 303 while (1) { |
(...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
754 int cache_bits = 0; | 708 int cache_bits = 0; |
755 VP8LHistogramSet* histogram_image = NULL; | 709 VP8LHistogramSet* histogram_image = NULL; |
756 HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( | 710 HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( |
757 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); | 711 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); |
758 if (huff_tree == NULL) { | 712 if (huff_tree == NULL) { |
759 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 713 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
760 goto Error; | 714 goto Error; |
761 } | 715 } |
762 | 716 |
763 // Calculate backward references from ARGB image. | 717 // Calculate backward references from ARGB image. |
| 718 if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { |
| 719 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
| 720 goto Error; |
| 721 } |
764 refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, | 722 refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, |
765 hash_chain, refs_array); | 723 hash_chain, refs_array); |
766 if (refs == NULL) { | 724 if (refs == NULL) { |
767 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 725 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
768 goto Error; | 726 goto Error; |
769 } | 727 } |
770 histogram_image = VP8LAllocateHistogramSet(1, cache_bits); | 728 histogram_image = VP8LAllocateHistogramSet(1, cache_bits); |
771 if (histogram_image == NULL) { | 729 if (histogram_image == NULL) { |
772 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 730 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
773 goto Error; | 731 goto Error; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
817 VP8LFreeHistogramSet(histogram_image); | 775 VP8LFreeHistogramSet(histogram_image); |
818 WebPSafeFree(huffman_codes[0].codes); | 776 WebPSafeFree(huffman_codes[0].codes); |
819 return err; | 777 return err; |
820 } | 778 } |
821 | 779 |
822 static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | 780 static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, |
823 const uint32_t* const argb, | 781 const uint32_t* const argb, |
824 VP8LHashChain* const hash_chain, | 782 VP8LHashChain* const hash_chain, |
825 VP8LBackwardRefs refs_array[2], | 783 VP8LBackwardRefs refs_array[2], |
826 int width, int height, int quality, | 784 int width, int height, int quality, |
827 int low_effort, int* cache_bits, | 785 int low_effort, |
| 786 int use_cache, int* cache_bits, |
828 int histogram_bits, | 787 int histogram_bits, |
829 size_t init_byte_position, | 788 size_t init_byte_position, |
830 int* const hdr_size, | 789 int* const hdr_size, |
831 int* const data_size) { | 790 int* const data_size) { |
832 WebPEncodingError err = VP8_ENC_OK; | 791 WebPEncodingError err = VP8_ENC_OK; |
833 const uint32_t histogram_image_xysize = | 792 const uint32_t histogram_image_xysize = |
834 VP8LSubSampleSize(width, histogram_bits) * | 793 VP8LSubSampleSize(width, histogram_bits) * |
835 VP8LSubSampleSize(height, histogram_bits); | 794 VP8LSubSampleSize(height, histogram_bits); |
836 VP8LHistogramSet* histogram_image = NULL; | 795 VP8LHistogramSet* histogram_image = NULL; |
837 VP8LHistogramSet* tmp_histos = NULL; | 796 VP8LHistogramSet* tmp_histos = NULL; |
(...skipping 11 matching lines...) Expand all Loading... |
849 assert(histogram_bits <= MAX_HUFFMAN_BITS); | 808 assert(histogram_bits <= MAX_HUFFMAN_BITS); |
850 assert(hdr_size != NULL); | 809 assert(hdr_size != NULL); |
851 assert(data_size != NULL); | 810 assert(data_size != NULL); |
852 | 811 |
853 VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); | 812 VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); |
854 if (histogram_symbols == NULL) { | 813 if (histogram_symbols == NULL) { |
855 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 814 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
856 goto Error; | 815 goto Error; |
857 } | 816 } |
858 | 817 |
859 *cache_bits = MAX_COLOR_CACHE_BITS; | 818 *cache_bits = use_cache ? MAX_COLOR_CACHE_BITS : 0; |
860 // 'best_refs' is the reference to the best backward refs and points to one | 819 // 'best_refs' is the reference to the best backward refs and points to one |
861 // of refs_array[0] or refs_array[1]. | 820 // of refs_array[0] or refs_array[1]. |
862 // Calculate backward references from ARGB image. | 821 // Calculate backward references from ARGB image. |
| 822 if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { |
| 823 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
| 824 goto Error; |
| 825 } |
863 best_refs = VP8LGetBackwardReferences(width, height, argb, quality, | 826 best_refs = VP8LGetBackwardReferences(width, height, argb, quality, |
864 low_effort, cache_bits, hash_chain, | 827 low_effort, cache_bits, hash_chain, |
865 refs_array); | 828 refs_array); |
866 if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { | 829 if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { |
867 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 830 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
868 goto Error; | 831 goto Error; |
869 } | 832 } |
870 histogram_image = | 833 histogram_image = |
871 VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits); | 834 VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits); |
872 tmp_histos = VP8LAllocateHistogramSet(2, *cache_bits); | 835 tmp_histos = VP8LAllocateHistogramSet(2, *cache_bits); |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1000 static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, | 963 static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, |
1001 VP8LBitWriter* const bw) { | 964 VP8LBitWriter* const bw) { |
1002 VP8LPutBits(bw, TRANSFORM_PRESENT, 1); | 965 VP8LPutBits(bw, TRANSFORM_PRESENT, 1); |
1003 VP8LPutBits(bw, SUBTRACT_GREEN, 2); | 966 VP8LPutBits(bw, SUBTRACT_GREEN, 2); |
1004 VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); | 967 VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); |
1005 } | 968 } |
1006 | 969 |
1007 static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc, | 970 static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc, |
1008 int width, int height, | 971 int width, int height, |
1009 int quality, int low_effort, | 972 int quality, int low_effort, |
| 973 int used_subtract_green, |
1010 VP8LBitWriter* const bw) { | 974 VP8LBitWriter* const bw) { |
1011 const int pred_bits = enc->transform_bits_; | 975 const int pred_bits = enc->transform_bits_; |
1012 const int transform_width = VP8LSubSampleSize(width, pred_bits); | 976 const int transform_width = VP8LSubSampleSize(width, pred_bits); |
1013 const int transform_height = VP8LSubSampleSize(height, pred_bits); | 977 const int transform_height = VP8LSubSampleSize(height, pred_bits); |
| 978 // we disable near-lossless quantization if palette is used. |
| 979 const int near_lossless_strength = enc->use_palette_ ? 100 |
| 980 : enc->config_->near_lossless; |
1014 | 981 |
1015 VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_, | 982 VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_, |
1016 enc->argb_scratch_, enc->transform_data_, | 983 enc->argb_scratch_, enc->transform_data_, |
1017 enc->config_->exact); | 984 near_lossless_strength, enc->config_->exact, |
| 985 used_subtract_green); |
1018 VP8LPutBits(bw, TRANSFORM_PRESENT, 1); | 986 VP8LPutBits(bw, TRANSFORM_PRESENT, 1); |
1019 VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); | 987 VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); |
1020 assert(pred_bits >= 2); | 988 assert(pred_bits >= 2); |
1021 VP8LPutBits(bw, pred_bits - 2, 3); | 989 VP8LPutBits(bw, pred_bits - 2, 3); |
1022 return EncodeImageNoHuffman(bw, enc->transform_data_, | 990 return EncodeImageNoHuffman(bw, enc->transform_data_, |
1023 (VP8LHashChain*)&enc->hash_chain_, | 991 (VP8LHashChain*)&enc->hash_chain_, |
1024 (VP8LBackwardRefs*)enc->refs_, // cast const away | 992 (VP8LBackwardRefs*)enc->refs_, // cast const away |
1025 transform_width, transform_height, | 993 transform_width, transform_height, |
1026 quality); | 994 quality); |
1027 } | 995 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1107 } | 1075 } |
1108 *coded_size = CHUNK_HEADER_SIZE + riff_size; | 1076 *coded_size = CHUNK_HEADER_SIZE + riff_size; |
1109 return VP8_ENC_OK; | 1077 return VP8_ENC_OK; |
1110 | 1078 |
1111 Error: | 1079 Error: |
1112 return err; | 1080 return err; |
1113 } | 1081 } |
1114 | 1082 |
1115 // ----------------------------------------------------------------------------- | 1083 // ----------------------------------------------------------------------------- |
1116 | 1084 |
| 1085 static void ClearTransformBuffer(VP8LEncoder* const enc) { |
| 1086 WebPSafeFree(enc->transform_mem_); |
| 1087 enc->transform_mem_ = NULL; |
| 1088 enc->transform_mem_size_ = 0; |
| 1089 } |
| 1090 |
1117 // Allocates the memory for argb (W x H) buffer, 2 rows of context for | 1091 // Allocates the memory for argb (W x H) buffer, 2 rows of context for |
1118 // prediction and transform data. | 1092 // prediction and transform data. |
1119 // Flags influencing the memory allocated: | 1093 // Flags influencing the memory allocated: |
1120 // enc->transform_bits_ | 1094 // enc->transform_bits_ |
1121 // enc->use_predict_, enc->use_cross_color_ | 1095 // enc->use_predict_, enc->use_cross_color_ |
1122 static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, | 1096 static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, |
1123 int width, int height) { | 1097 int width, int height) { |
1124 WebPEncodingError err = VP8_ENC_OK; | 1098 WebPEncodingError err = VP8_ENC_OK; |
1125 if (enc->argb_ == NULL) { | 1099 const uint64_t image_size = width * height; |
1126 const int tile_size = 1 << enc->transform_bits_; | 1100 // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra |
1127 const uint64_t image_size = width * height; | 1101 // pixel in each, plus 2 regular scanlines of bytes. |
1128 // Ensure enough size for tiles, as well as for two scanlines and two | 1102 // TODO(skal): Clean up by using arithmetic in bytes instead of words. |
1129 // extra pixels for CopyImageWithPrediction. | 1103 const uint64_t argb_scratch_size = |
1130 const uint64_t argb_scratch_size = | 1104 enc->use_predict_ |
1131 enc->use_predict_ ? tile_size * width + width + 2 : 0; | 1105 ? (width + 1) * 2 + |
1132 const int transform_data_size = | 1106 (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t) |
1133 (enc->use_predict_ || enc->use_cross_color_) | 1107 : 0; |
1134 ? VP8LSubSampleSize(width, enc->transform_bits_) * | 1108 const uint64_t transform_data_size = |
1135 VP8LSubSampleSize(height, enc->transform_bits_) | 1109 (enc->use_predict_ || enc->use_cross_color_) |
1136 : 0; | 1110 ? VP8LSubSampleSize(width, enc->transform_bits_) * |
1137 const uint64_t total_size = | 1111 VP8LSubSampleSize(height, enc->transform_bits_) |
1138 image_size + WEBP_ALIGN_CST + | 1112 : 0; |
1139 argb_scratch_size + WEBP_ALIGN_CST + | 1113 const uint64_t max_alignment_in_words = |
1140 (uint64_t)transform_data_size; | 1114 (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t); |
1141 uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem)); | 1115 const uint64_t mem_size = |
| 1116 image_size + max_alignment_in_words + |
| 1117 argb_scratch_size + max_alignment_in_words + |
| 1118 transform_data_size; |
| 1119 uint32_t* mem = enc->transform_mem_; |
| 1120 if (mem == NULL || mem_size > enc->transform_mem_size_) { |
| 1121 ClearTransformBuffer(enc); |
| 1122 mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem)); |
1142 if (mem == NULL) { | 1123 if (mem == NULL) { |
1143 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1124 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1144 goto Error; | 1125 goto Error; |
1145 } | 1126 } |
1146 enc->argb_ = mem; | 1127 enc->transform_mem_ = mem; |
1147 mem = (uint32_t*)WEBP_ALIGN(mem + image_size); | 1128 enc->transform_mem_size_ = (size_t)mem_size; |
1148 enc->argb_scratch_ = mem; | |
1149 mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); | |
1150 enc->transform_data_ = mem; | |
1151 enc->current_width_ = width; | |
1152 } | 1129 } |
| 1130 enc->argb_ = mem; |
| 1131 mem = (uint32_t*)WEBP_ALIGN(mem + image_size); |
| 1132 enc->argb_scratch_ = mem; |
| 1133 mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); |
| 1134 enc->transform_data_ = mem; |
| 1135 |
| 1136 enc->current_width_ = width; |
1153 Error: | 1137 Error: |
1154 return err; | 1138 return err; |
1155 } | 1139 } |
1156 | 1140 |
1157 static void ClearTransformBuffer(VP8LEncoder* const enc) { | |
1158 WebPSafeFree(enc->argb_); | |
1159 enc->argb_ = NULL; | |
1160 } | |
1161 | |
1162 static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { | 1141 static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { |
1163 WebPEncodingError err = VP8_ENC_OK; | 1142 WebPEncodingError err = VP8_ENC_OK; |
1164 const WebPPicture* const picture = enc->pic_; | 1143 const WebPPicture* const picture = enc->pic_; |
1165 const int width = picture->width; | 1144 const int width = picture->width; |
1166 const int height = picture->height; | 1145 const int height = picture->height; |
1167 int y; | 1146 int y; |
1168 err = AllocateTransformBuffer(enc, width, height); | 1147 err = AllocateTransformBuffer(enc, width, height); |
1169 if (err != VP8_ENC_OK) return err; | 1148 if (err != VP8_ENC_OK) return err; |
1170 for (y = 0; y < height; ++y) { | 1149 for (y = 0; y < height; ++y) { |
1171 memcpy(enc->argb_ + y * width, | 1150 memcpy(enc->argb_ + y * width, |
1172 picture->argb + y * picture->argb_stride, | 1151 picture->argb + y * picture->argb_stride, |
1173 width * sizeof(*enc->argb_)); | 1152 width * sizeof(*enc->argb_)); |
1174 } | 1153 } |
1175 assert(enc->current_width_ == width); | 1154 assert(enc->current_width_ == width); |
1176 return VP8_ENC_OK; | 1155 return VP8_ENC_OK; |
1177 } | 1156 } |
1178 | 1157 |
1179 // ----------------------------------------------------------------------------- | 1158 // ----------------------------------------------------------------------------- |
1180 | 1159 |
1181 static void MapToPalette(const uint32_t palette[], int num_colors, | 1160 static int SearchColor(const uint32_t sorted[], uint32_t color, int hi) { |
| 1161 int low = 0; |
| 1162 if (sorted[low] == color) return low; // loop invariant: sorted[low] != color |
| 1163 while (1) { |
| 1164 const int mid = (low + hi) >> 1; |
| 1165 if (sorted[mid] == color) { |
| 1166 return mid; |
| 1167 } else if (sorted[mid] < color) { |
| 1168 low = mid; |
| 1169 } else { |
| 1170 hi = mid; |
| 1171 } |
| 1172 } |
| 1173 } |
| 1174 |
| 1175 // Sort palette in increasing order and prepare an inverse mapping array. |
| 1176 static void PrepareMapToPalette(const uint32_t palette[], int num_colors, |
| 1177 uint32_t sorted[], int idx_map[]) { |
| 1178 int i; |
| 1179 memcpy(sorted, palette, num_colors * sizeof(*sorted)); |
| 1180 qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); |
| 1181 for (i = 0; i < num_colors; ++i) { |
| 1182 idx_map[SearchColor(sorted, palette[i], num_colors)] = i; |
| 1183 } |
| 1184 } |
| 1185 |
| 1186 static void MapToPalette(const uint32_t sorted_palette[], int num_colors, |
1182 uint32_t* const last_pix, int* const last_idx, | 1187 uint32_t* const last_pix, int* const last_idx, |
| 1188 const int idx_map[], |
1183 const uint32_t* src, uint8_t* dst, int width) { | 1189 const uint32_t* src, uint8_t* dst, int width) { |
1184 int x; | 1190 int x; |
1185 int prev_idx = *last_idx; | 1191 int prev_idx = *last_idx; |
1186 uint32_t prev_pix = *last_pix; | 1192 uint32_t prev_pix = *last_pix; |
1187 for (x = 0; x < width; ++x) { | 1193 for (x = 0; x < width; ++x) { |
1188 const uint32_t pix = src[x]; | 1194 const uint32_t pix = src[x]; |
1189 if (pix != prev_pix) { | 1195 if (pix != prev_pix) { |
1190 int i; | 1196 prev_idx = idx_map[SearchColor(sorted_palette, pix, num_colors)]; |
1191 for (i = 0; i < num_colors; ++i) { | 1197 prev_pix = pix; |
1192 if (pix == palette[i]) { | |
1193 prev_idx = i; | |
1194 prev_pix = pix; | |
1195 break; | |
1196 } | |
1197 } | |
1198 } | 1198 } |
1199 dst[x] = prev_idx; | 1199 dst[x] = prev_idx; |
1200 } | 1200 } |
1201 *last_idx = prev_idx; | 1201 *last_idx = prev_idx; |
1202 *last_pix = prev_pix; | 1202 *last_pix = prev_pix; |
1203 } | 1203 } |
1204 | 1204 |
1205 // Remap argb values in src[] to packed palettes entries in dst[] | 1205 // Remap argb values in src[] to packed palettes entries in dst[] |
1206 // using 'row' as a temporary buffer of size 'width'. | 1206 // using 'row' as a temporary buffer of size 'width'. |
1207 // We assume that all src[] values have a corresponding entry in the palette. | 1207 // We assume that all src[] values have a corresponding entry in the palette. |
(...skipping 26 matching lines...) Expand all Loading... |
1234 for (x = 0; x < width; ++x) { | 1234 for (x = 0; x < width; ++x) { |
1235 const int color = (src[x] >> 8) & 0xff; | 1235 const int color = (src[x] >> 8) & 0xff; |
1236 tmp_row[x] = inv_palette[color]; | 1236 tmp_row[x] = inv_palette[color]; |
1237 } | 1237 } |
1238 VP8LBundleColorMap(tmp_row, width, xbits, dst); | 1238 VP8LBundleColorMap(tmp_row, width, xbits, dst); |
1239 src += src_stride; | 1239 src += src_stride; |
1240 dst += dst_stride; | 1240 dst += dst_stride; |
1241 } | 1241 } |
1242 } else { | 1242 } else { |
1243 // Use 1 pixel cache for ARGB pixels. | 1243 // Use 1 pixel cache for ARGB pixels. |
1244 uint32_t last_pix = palette[0]; | 1244 uint32_t last_pix; |
1245 int last_idx = 0; | 1245 int last_idx; |
| 1246 uint32_t sorted[MAX_PALETTE_SIZE]; |
| 1247 int idx_map[MAX_PALETTE_SIZE]; |
| 1248 PrepareMapToPalette(palette, palette_size, sorted, idx_map); |
| 1249 last_pix = palette[0]; |
| 1250 last_idx = 0; |
1246 for (y = 0; y < height; ++y) { | 1251 for (y = 0; y < height; ++y) { |
1247 MapToPalette(palette, palette_size, &last_pix, &last_idx, | 1252 MapToPalette(sorted, palette_size, &last_pix, &last_idx, |
1248 src, tmp_row, width); | 1253 idx_map, src, tmp_row, width); |
1249 VP8LBundleColorMap(tmp_row, width, xbits, dst); | 1254 VP8LBundleColorMap(tmp_row, width, xbits, dst); |
1250 src += src_stride; | 1255 src += src_stride; |
1251 dst += dst_stride; | 1256 dst += dst_stride; |
1252 } | 1257 } |
1253 } | 1258 } |
1254 WebPSafeFree(tmp_row); | 1259 WebPSafeFree(tmp_row); |
1255 return VP8_ENC_OK; | 1260 return VP8_ENC_OK; |
1256 } | 1261 } |
1257 | 1262 |
1258 // Note: Expects "enc->palette_" to be set properly. | 1263 // Note: Expects "enc->palette_" to be set properly. |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1371 ClearTransformBuffer(enc); | 1376 ClearTransformBuffer(enc); |
1372 WebPSafeFree(enc); | 1377 WebPSafeFree(enc); |
1373 } | 1378 } |
1374 } | 1379 } |
1375 | 1380 |
1376 // ----------------------------------------------------------------------------- | 1381 // ----------------------------------------------------------------------------- |
1377 // Main call | 1382 // Main call |
1378 | 1383 |
1379 WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | 1384 WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, |
1380 const WebPPicture* const picture, | 1385 const WebPPicture* const picture, |
1381 VP8LBitWriter* const bw) { | 1386 VP8LBitWriter* const bw, int use_cache) { |
1382 WebPEncodingError err = VP8_ENC_OK; | 1387 WebPEncodingError err = VP8_ENC_OK; |
1383 const int quality = (int)config->quality; | 1388 const int quality = (int)config->quality; |
1384 const int low_effort = (config->method == 0); | 1389 const int low_effort = (config->method == 0); |
1385 const int width = picture->width; | 1390 const int width = picture->width; |
1386 const int height = picture->height; | 1391 const int height = picture->height; |
1387 VP8LEncoder* const enc = VP8LEncoderNew(config, picture); | 1392 VP8LEncoder* const enc = VP8LEncoderNew(config, picture); |
1388 const size_t byte_position = VP8LBitWriterNumBytes(bw); | 1393 const size_t byte_position = VP8LBitWriterNumBytes(bw); |
1389 int use_near_lossless = 0; | 1394 int use_near_lossless = 0; |
1390 int hdr_size = 0; | 1395 int hdr_size = 0; |
1391 int data_size = 0; | 1396 int data_size = 0; |
1392 int use_delta_palettization = 0; | 1397 int use_delta_palettization = 0; |
1393 | 1398 |
1394 if (enc == NULL) { | 1399 if (enc == NULL) { |
1395 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1400 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1396 goto Error; | 1401 goto Error; |
1397 } | 1402 } |
1398 | 1403 |
1399 // --------------------------------------------------------------------------- | 1404 // --------------------------------------------------------------------------- |
1400 // Analyze image (entropy, num_palettes etc) | 1405 // Analyze image (entropy, num_palettes etc) |
1401 | 1406 |
1402 if (!AnalyzeAndInit(enc)) { | 1407 if (!AnalyzeAndInit(enc)) { |
1403 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1408 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1404 goto Error; | 1409 goto Error; |
1405 } | 1410 } |
1406 | 1411 |
1407 // Apply near-lossless preprocessing. | 1412 // Apply near-lossless preprocessing. |
1408 use_near_lossless = !enc->use_palette_ && (config->near_lossless < 100); | 1413 use_near_lossless = |
| 1414 (config->near_lossless < 100) && !enc->use_palette_ && !enc->use_predict_; |
1409 if (use_near_lossless) { | 1415 if (use_near_lossless) { |
1410 if (!VP8ApplyNearLossless(width, height, picture->argb, | 1416 if (!VP8ApplyNearLossless(width, height, picture->argb, |
1411 config->near_lossless)) { | 1417 config->near_lossless)) { |
1412 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1418 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1413 goto Error; | 1419 goto Error; |
1414 } | 1420 } |
1415 } | 1421 } |
1416 | 1422 |
1417 #ifdef WEBP_EXPERIMENTAL_FEATURES | 1423 #ifdef WEBP_EXPERIMENTAL_FEATURES |
1418 if (config->delta_palettization) { | 1424 if (config->delta_palettization) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1450 | 1456 |
1451 // ------------------------------------------------------------------------- | 1457 // ------------------------------------------------------------------------- |
1452 // Apply transforms and write transform data. | 1458 // Apply transforms and write transform data. |
1453 | 1459 |
1454 if (enc->use_subtract_green_) { | 1460 if (enc->use_subtract_green_) { |
1455 ApplySubtractGreen(enc, enc->current_width_, height, bw); | 1461 ApplySubtractGreen(enc, enc->current_width_, height, bw); |
1456 } | 1462 } |
1457 | 1463 |
1458 if (enc->use_predict_) { | 1464 if (enc->use_predict_) { |
1459 err = ApplyPredictFilter(enc, enc->current_width_, height, quality, | 1465 err = ApplyPredictFilter(enc, enc->current_width_, height, quality, |
1460 low_effort, bw); | 1466 low_effort, enc->use_subtract_green_, bw); |
1461 if (err != VP8_ENC_OK) goto Error; | 1467 if (err != VP8_ENC_OK) goto Error; |
1462 } | 1468 } |
1463 | 1469 |
1464 if (enc->use_cross_color_) { | 1470 if (enc->use_cross_color_) { |
1465 err = ApplyCrossColorFilter(enc, enc->current_width_, | 1471 err = ApplyCrossColorFilter(enc, enc->current_width_, |
1466 height, quality, bw); | 1472 height, quality, bw); |
1467 if (err != VP8_ENC_OK) goto Error; | 1473 if (err != VP8_ENC_OK) goto Error; |
1468 } | 1474 } |
1469 } | 1475 } |
1470 | 1476 |
1471 VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. | 1477 VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. |
1472 | 1478 |
1473 // --------------------------------------------------------------------------- | 1479 // --------------------------------------------------------------------------- |
1474 // Encode and write the transformed image. | 1480 // Encode and write the transformed image. |
1475 err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, | 1481 err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, |
1476 enc->current_width_, height, quality, low_effort, | 1482 enc->current_width_, height, quality, low_effort, |
1477 &enc->cache_bits_, enc->histo_bits_, byte_position, | 1483 use_cache, &enc->cache_bits_, enc->histo_bits_, |
1478 &hdr_size, &data_size); | 1484 byte_position, &hdr_size, &data_size); |
1479 if (err != VP8_ENC_OK) goto Error; | 1485 if (err != VP8_ENC_OK) goto Error; |
1480 | 1486 |
1481 if (picture->stats != NULL) { | 1487 if (picture->stats != NULL) { |
1482 WebPAuxStats* const stats = picture->stats; | 1488 WebPAuxStats* const stats = picture->stats; |
1483 stats->lossless_features = 0; | 1489 stats->lossless_features = 0; |
1484 if (enc->use_predict_) stats->lossless_features |= 1; | 1490 if (enc->use_predict_) stats->lossless_features |= 1; |
1485 if (enc->use_cross_color_) stats->lossless_features |= 2; | 1491 if (enc->use_cross_color_) stats->lossless_features |= 2; |
1486 if (enc->use_subtract_green_) stats->lossless_features |= 4; | 1492 if (enc->use_subtract_green_) stats->lossless_features |= 4; |
1487 if (enc->use_palette_) stats->lossless_features |= 8; | 1493 if (enc->use_palette_) stats->lossless_features |= 8; |
1488 stats->histogram_bits = enc->histo_bits_; | 1494 stats->histogram_bits = enc->histo_bits_; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1553 has_alpha = WebPPictureHasTransparency(picture); | 1559 has_alpha = WebPPictureHasTransparency(picture); |
1554 // Write the non-trivial Alpha flag and lossless version. | 1560 // Write the non-trivial Alpha flag and lossless version. |
1555 if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { | 1561 if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { |
1556 err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1562 err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1557 goto Error; | 1563 goto Error; |
1558 } | 1564 } |
1559 | 1565 |
1560 if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; | 1566 if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; |
1561 | 1567 |
1562 // Encode main image stream. | 1568 // Encode main image stream. |
1563 err = VP8LEncodeStream(config, picture, &bw); | 1569 err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/); |
1564 if (err != VP8_ENC_OK) goto Error; | 1570 if (err != VP8_ENC_OK) goto Error; |
1565 | 1571 |
1566 // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). | 1572 // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). |
1567 if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort; | 1573 if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort; |
1568 | 1574 |
1569 // Finish the RIFF chunk. | 1575 // Finish the RIFF chunk. |
1570 err = WriteImage(picture, &bw, &coded_size); | 1576 err = WriteImage(picture, &bw, &coded_size); |
1571 if (err != VP8_ENC_OK) goto Error; | 1577 if (err != VP8_ENC_OK) goto Error; |
1572 | 1578 |
1573 if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort; | 1579 if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort; |
(...skipping 14 matching lines...) Expand all Loading... |
1588 if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY; | 1594 if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY; |
1589 VP8LBitWriterWipeOut(&bw); | 1595 VP8LBitWriterWipeOut(&bw); |
1590 if (err != VP8_ENC_OK) { | 1596 if (err != VP8_ENC_OK) { |
1591 WebPEncodingSetError(picture, err); | 1597 WebPEncodingSetError(picture, err); |
1592 return 0; | 1598 return 0; |
1593 } | 1599 } |
1594 return 1; | 1600 return 1; |
1595 } | 1601 } |
1596 | 1602 |
1597 //------------------------------------------------------------------------------ | 1603 //------------------------------------------------------------------------------ |
OLD | NEW |