/src/libwebp/src/enc/predictor_enc.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | | // |
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 |
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. |
8 | | // ----------------------------------------------------------------------------- |
9 | | // |
10 | | // Image transform methods for lossless encoder. |
11 | | // |
12 | | // Authors: Vikas Arora (vikaas.arora@gmail.com) |
13 | | // Jyrki Alakuijala (jyrki@google.com) |
14 | | // Urvang Joshi (urvang@google.com) |
15 | | // Vincent Rabaud (vrabaud@google.com) |
16 | | |
17 | | #include <assert.h> |
18 | | #include <stdlib.h> |
19 | | #include <string.h> |
20 | | |
21 | | #include "src/dsp/lossless.h" |
22 | | #include "src/dsp/lossless_common.h" |
23 | | #include "src/enc/vp8i_enc.h" |
24 | | #include "src/enc/vp8li_enc.h" |
25 | | #include "src/utils/utils.h" |
26 | | #include "src/webp/encode.h" |
27 | | #include "src/webp/format_constants.h" |
28 | | #include "src/webp/types.h" |
29 | | |
30 | 0 | #define HISTO_SIZE (4 * 256) |
31 | | static const int64_t kSpatialPredictorBias = 15ll << LOG_2_PRECISION_BITS; |
32 | | static const int kPredLowEffort = 11; |
33 | | static const uint32_t kMaskAlpha = 0xff000000; |
34 | | static const int kNumPredModes = 14; |
35 | | |
36 | | // Mostly used to reduce code size + readability |
37 | 0 | static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; } |
38 | 0 | static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } |
39 | | |
40 | | //------------------------------------------------------------------------------ |
41 | | // Methods to calculate Entropy (Shannon). |
42 | | |
43 | | // Compute a bias for prediction entropy using a global heuristic to favor |
44 | | // values closer to 0. Hence the final negative sign. |
45 | | // 'exp_val' has a scaling factor of 1/100. |
46 | | static int64_t PredictionCostBias(const uint32_t counts[256], uint64_t weight_0, |
47 | 0 | uint64_t exp_val) { |
48 | 0 | const int significant_symbols = 256 >> 4; |
49 | 0 | const uint64_t exp_decay_factor = 6; // has a scaling factor of 1/10 |
50 | 0 | uint64_t bits = (weight_0 * counts[0]) << LOG_2_PRECISION_BITS; |
51 | 0 | int i; |
52 | 0 | exp_val <<= LOG_2_PRECISION_BITS; |
53 | 0 | for (i = 1; i < significant_symbols; ++i) { |
54 | 0 | bits += DivRound(exp_val * (counts[i] + counts[256 - i]), 100); |
55 | 0 | exp_val = DivRound(exp_decay_factor * exp_val, 10); |
56 | 0 | } |
57 | 0 | return -DivRound((int64_t)bits, 10); |
58 | 0 | } |
59 | | |
60 | | static int64_t PredictionCostSpatialHistogram( |
61 | | const uint32_t accumulated[HISTO_SIZE], const uint32_t tile[HISTO_SIZE], |
62 | 0 | int mode, int left_mode, int above_mode) { |
63 | 0 | int i; |
64 | 0 | int64_t retval = 0; |
65 | 0 | for (i = 0; i < 4; ++i) { |
66 | 0 | const uint64_t kExpValue = 94; |
67 | 0 | retval += PredictionCostBias(&tile[i * 256], 1, kExpValue); |
68 | | // Compute the new cost if 'tile' is added to 'accumulate' but also add the |
69 | | // cost of the current histogram to guide the spatial predictor selection. |
70 | | // Basically, favor low entropy, locally and globally. |
71 | 0 | retval += (int64_t)VP8LCombinedShannonEntropy(&tile[i * 256], |
72 | 0 | &accumulated[i * 256]); |
73 | 0 | } |
74 | | // Favor keeping the areas locally similar. |
75 | 0 | if (mode == left_mode) retval -= kSpatialPredictorBias; |
76 | 0 | if (mode == above_mode) retval -= kSpatialPredictorBias; |
77 | 0 | return retval; |
78 | 0 | } |
79 | | |
80 | | static WEBP_INLINE void UpdateHisto(uint32_t histo_argb[HISTO_SIZE], |
81 | 0 | uint32_t argb) { |
82 | 0 | ++histo_argb[0 * 256 + (argb >> 24)]; |
83 | 0 | ++histo_argb[1 * 256 + ((argb >> 16) & 0xff)]; |
84 | 0 | ++histo_argb[2 * 256 + ((argb >> 8) & 0xff)]; |
85 | 0 | ++histo_argb[3 * 256 + (argb & 0xff)]; |
86 | 0 | } |
87 | | |
88 | | //------------------------------------------------------------------------------ |
89 | | // Spatial transform functions. |
90 | | |
91 | | static WEBP_INLINE void PredictBatch(int mode, int x_start, int y, |
92 | | int num_pixels, const uint32_t* current, |
93 | 0 | const uint32_t* upper, uint32_t* out) { |
94 | 0 | if (x_start == 0) { |
95 | 0 | if (y == 0) { |
96 | | // ARGB_BLACK. |
97 | 0 | VP8LPredictorsSub[0](current, NULL, 1, out); |
98 | 0 | } else { |
99 | | // Top one. |
100 | 0 | VP8LPredictorsSub[2](current, upper, 1, out); |
101 | 0 | } |
102 | 0 | ++x_start; |
103 | 0 | ++out; |
104 | 0 | --num_pixels; |
105 | 0 | } |
106 | 0 | if (y == 0) { |
107 | | // Left one. |
108 | 0 | VP8LPredictorsSub[1](current + x_start, NULL, num_pixels, out); |
109 | 0 | } else { |
110 | 0 | VP8LPredictorsSub[mode](current + x_start, upper + x_start, num_pixels, |
111 | 0 | out); |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | #if (WEBP_NEAR_LOSSLESS == 1) |
116 | 0 | static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) { |
117 | 0 | const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24)); |
118 | 0 | const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff)); |
119 | 0 | const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff)); |
120 | 0 | const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff)); |
121 | 0 | return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b)); |
122 | 0 | } |
123 | | |
124 | | static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down, |
125 | 0 | uint32_t left, uint32_t right) { |
126 | 0 | const int diff_up = MaxDiffBetweenPixels(current, up); |
127 | 0 | const int diff_down = MaxDiffBetweenPixels(current, down); |
128 | 0 | const int diff_left = MaxDiffBetweenPixels(current, left); |
129 | 0 | const int diff_right = MaxDiffBetweenPixels(current, right); |
130 | 0 | return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right)); |
131 | 0 | } |
132 | | |
133 | 0 | static uint32_t AddGreenToBlueAndRed(uint32_t argb) { |
134 | 0 | const uint32_t green = (argb >> 8) & 0xff; |
135 | 0 | uint32_t red_blue = argb & 0x00ff00ffu; |
136 | 0 | red_blue += (green << 16) | green; |
137 | 0 | red_blue &= 0x00ff00ffu; |
138 | 0 | return (argb & 0xff00ff00u) | red_blue; |
139 | 0 | } |
140 | | |
141 | | static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb, |
142 | 0 | uint8_t* const max_diffs, int used_subtract_green) { |
143 | 0 | uint32_t current, up, down, left, right; |
144 | 0 | int x; |
145 | 0 | if (width <= 2) return; |
146 | 0 | current = argb[0]; |
147 | 0 | right = argb[1]; |
148 | 0 | if (used_subtract_green) { |
149 | 0 | current = AddGreenToBlueAndRed(current); |
150 | 0 | right = AddGreenToBlueAndRed(right); |
151 | 0 | } |
152 | | // max_diffs[0] and max_diffs[width - 1] are never used. |
153 | 0 | for (x = 1; x < width - 1; ++x) { |
154 | 0 | up = argb[-stride + x]; |
155 | 0 | down = argb[stride + x]; |
156 | 0 | left = current; |
157 | 0 | current = right; |
158 | 0 | right = argb[x + 1]; |
159 | 0 | if (used_subtract_green) { |
160 | 0 | up = AddGreenToBlueAndRed(up); |
161 | 0 | down = AddGreenToBlueAndRed(down); |
162 | 0 | right = AddGreenToBlueAndRed(right); |
163 | 0 | } |
164 | 0 | max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right); |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | // Quantize the difference between the actual component value and its prediction |
169 | | // to a multiple of quantization, working modulo 256, taking care not to cross |
170 | | // a boundary (inclusive upper limit). |
171 | | static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict, |
172 | 0 | uint8_t boundary, int quantization) { |
173 | 0 | const int residual = (value - predict) & 0xff; |
174 | 0 | const int boundary_residual = (boundary - predict) & 0xff; |
175 | 0 | const int lower = residual & ~(quantization - 1); |
176 | 0 | const int upper = lower + quantization; |
177 | | // Resolve ties towards a value closer to the prediction (i.e. towards lower |
178 | | // if value comes after prediction and towards upper otherwise). |
179 | 0 | const int bias = ((boundary - value) & 0xff) < boundary_residual; |
180 | 0 | if (residual - lower < upper - residual + bias) { |
181 | | // lower is closer to residual than upper. |
182 | 0 | if (residual > boundary_residual && lower <= boundary_residual) { |
183 | | // Halve quantization step to avoid crossing boundary. This midpoint is |
184 | | // on the same side of boundary as residual because midpoint >= residual |
185 | | // (since lower is closer than upper) and residual is above the boundary. |
186 | 0 | return lower + (quantization >> 1); |
187 | 0 | } |
188 | 0 | return lower; |
189 | 0 | } else { |
190 | | // upper is closer to residual than lower. |
191 | 0 | if (residual <= boundary_residual && upper > boundary_residual) { |
192 | | // Halve quantization step to avoid crossing boundary. This midpoint is |
193 | | // on the same side of boundary as residual because midpoint <= residual |
194 | | // (since upper is closer than lower) and residual is below the boundary. |
195 | 0 | return lower + (quantization >> 1); |
196 | 0 | } |
197 | 0 | return upper & 0xff; |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | 0 | static WEBP_INLINE uint8_t NearLosslessDiff(uint8_t a, uint8_t b) { |
202 | 0 | return (uint8_t)((((int)(a) - (int)(b))) & 0xff); |
203 | 0 | } |
204 | | |
205 | | // Quantize every component of the difference between the actual pixel value and |
206 | | // its prediction to a multiple of a quantization (a power of 2, not larger than |
207 | | // max_quantization which is a power of 2, smaller than max_diff). Take care if |
208 | | // value and predict have undergone subtract green, which means that red and |
209 | | // blue are represented as offsets from green. |
210 | | static uint32_t NearLossless(uint32_t value, uint32_t predict, |
211 | | int max_quantization, int max_diff, |
212 | 0 | int used_subtract_green) { |
213 | 0 | int quantization; |
214 | 0 | uint8_t new_green = 0; |
215 | 0 | uint8_t green_diff = 0; |
216 | 0 | uint8_t a, r, g, b; |
217 | 0 | if (max_diff <= 2) { |
218 | 0 | return VP8LSubPixels(value, predict); |
219 | 0 | } |
220 | 0 | quantization = max_quantization; |
221 | 0 | while (quantization >= max_diff) { |
222 | 0 | quantization >>= 1; |
223 | 0 | } |
224 | 0 | if ((value >> 24) == 0 || (value >> 24) == 0xff) { |
225 | | // Preserve transparency of fully transparent or fully opaque pixels. |
226 | 0 | a = NearLosslessDiff((value >> 24) & 0xff, (predict >> 24) & 0xff); |
227 | 0 | } else { |
228 | 0 | a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization); |
229 | 0 | } |
230 | 0 | g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff, |
231 | 0 | quantization); |
232 | 0 | if (used_subtract_green) { |
233 | | // The green offset will be added to red and blue components during decoding |
234 | | // to obtain the actual red and blue values. |
235 | 0 | new_green = ((predict >> 8) + g) & 0xff; |
236 | | // The amount by which green has been adjusted during quantization. It is |
237 | | // subtracted from red and blue for compensation, to avoid accumulating two |
238 | | // quantization errors in them. |
239 | 0 | green_diff = NearLosslessDiff(new_green, (value >> 8) & 0xff); |
240 | 0 | } |
241 | 0 | r = NearLosslessComponent(NearLosslessDiff((value >> 16) & 0xff, green_diff), |
242 | 0 | (predict >> 16) & 0xff, 0xff - new_green, |
243 | 0 | quantization); |
244 | 0 | b = NearLosslessComponent(NearLosslessDiff(value & 0xff, green_diff), |
245 | 0 | predict & 0xff, 0xff - new_green, quantization); |
246 | 0 | return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; |
247 | 0 | } |
248 | | #endif // (WEBP_NEAR_LOSSLESS == 1) |
249 | | |
250 | | // Stores the difference between the pixel and its prediction in "out". |
251 | | // In case of a lossy encoding, updates the source image to avoid propagating |
252 | | // the deviation further to pixels which depend on the current pixel for their |
253 | | // predictions. |
254 | | static WEBP_INLINE void GetResidual( |
255 | | int width, int height, uint32_t* const upper_row, |
256 | | uint32_t* const current_row, const uint8_t* const max_diffs, int mode, |
257 | | int x_start, int x_end, int y, int max_quantization, int exact, |
258 | 0 | int used_subtract_green, uint32_t* const out) { |
259 | 0 | if (exact) { |
260 | 0 | PredictBatch(mode, x_start, y, x_end - x_start, current_row, upper_row, |
261 | 0 | out); |
262 | 0 | } else { |
263 | 0 | const VP8LPredictorFunc pred_func = VP8LPredictors[mode]; |
264 | 0 | int x; |
265 | 0 | for (x = x_start; x < x_end; ++x) { |
266 | 0 | uint32_t predict; |
267 | 0 | uint32_t residual; |
268 | 0 | if (y == 0) { |
269 | 0 | predict = (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left. |
270 | 0 | } else if (x == 0) { |
271 | 0 | predict = upper_row[x]; // Top. |
272 | 0 | } else { |
273 | 0 | predict = pred_func(¤t_row[x - 1], upper_row + x); |
274 | 0 | } |
275 | 0 | #if (WEBP_NEAR_LOSSLESS == 1) |
276 | 0 | if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 || |
277 | 0 | x == 0 || x == width - 1) { |
278 | 0 | residual = VP8LSubPixels(current_row[x], predict); |
279 | 0 | } else { |
280 | 0 | residual = NearLossless(current_row[x], predict, max_quantization, |
281 | 0 | max_diffs[x], used_subtract_green); |
282 | | // Update the source image. |
283 | 0 | current_row[x] = VP8LAddPixels(predict, residual); |
284 | | // x is never 0 here so we do not need to update upper_row like below. |
285 | 0 | } |
286 | | #else |
287 | | (void)max_diffs; |
288 | | (void)height; |
289 | | (void)max_quantization; |
290 | | (void)used_subtract_green; |
291 | | residual = VP8LSubPixels(current_row[x], predict); |
292 | | #endif |
293 | 0 | if ((current_row[x] & kMaskAlpha) == 0) { |
294 | | // If alpha is 0, cleanup RGB. We can choose the RGB values of the |
295 | | // residual for best compression. The prediction of alpha itself can be |
296 | | // non-zero and must be kept though. We choose RGB of the residual to be |
297 | | // 0. |
298 | 0 | residual &= kMaskAlpha; |
299 | | // Update the source image. |
300 | 0 | current_row[x] = predict & ~kMaskAlpha; |
301 | | // The prediction for the rightmost pixel in a row uses the leftmost |
302 | | // pixel |
303 | | // in that row as its top-right context pixel. Hence if we change the |
304 | | // leftmost pixel of current_row, the corresponding change must be |
305 | | // applied |
306 | | // to upper_row as well where top-right context is being read from. |
307 | 0 | if (x == 0 && y != 0) upper_row[width] = current_row[0]; |
308 | 0 | } |
309 | 0 | out[x - x_start] = residual; |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | // Accessors to residual histograms. |
315 | | static WEBP_INLINE uint32_t* GetHistoArgb(uint32_t* const all_histos, |
316 | 0 | int subsampling_index, int mode) { |
317 | 0 | return &all_histos[(subsampling_index * kNumPredModes + mode) * HISTO_SIZE]; |
318 | 0 | } |
319 | | |
320 | | static WEBP_INLINE const uint32_t* GetHistoArgbConst( |
321 | 0 | const uint32_t* const all_histos, int subsampling_index, int mode) { |
322 | 0 | return &all_histos[subsampling_index * kNumPredModes * HISTO_SIZE + |
323 | 0 | mode * HISTO_SIZE]; |
324 | 0 | } |
325 | | |
326 | | // Accessors to accumulated residual histogram. |
327 | | static WEBP_INLINE uint32_t* GetAccumulatedHisto(uint32_t* all_accumulated, |
328 | 0 | int subsampling_index) { |
329 | 0 | return &all_accumulated[subsampling_index * HISTO_SIZE]; |
330 | 0 | } |
331 | | |
332 | | // Find and store the best predictor for a tile at subsampling |
333 | | // 'subsampling_index'. |
334 | | static void GetBestPredictorForTile(const uint32_t* const all_argb, |
335 | | int subsampling_index, int tile_x, |
336 | | int tile_y, int tiles_per_row, |
337 | | uint32_t* all_accumulated_argb, |
338 | | uint32_t** const all_modes, |
339 | 0 | uint32_t* const all_pred_histos) { |
340 | 0 | uint32_t* const accumulated_argb = |
341 | 0 | GetAccumulatedHisto(all_accumulated_argb, subsampling_index); |
342 | 0 | uint32_t* const modes = all_modes[subsampling_index]; |
343 | 0 | uint32_t* const pred_histos = |
344 | 0 | &all_pred_histos[subsampling_index * kNumPredModes]; |
345 | | // Prediction modes of the left and above neighbor tiles. |
346 | 0 | const int left_mode = |
347 | 0 | (tile_x > 0) ? (modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff |
348 | 0 | : 0xff; |
349 | 0 | const int above_mode = |
350 | 0 | (tile_y > 0) ? (modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff |
351 | 0 | : 0xff; |
352 | 0 | int mode; |
353 | 0 | int64_t best_diff = WEBP_INT64_MAX; |
354 | 0 | uint32_t best_mode = 0; |
355 | 0 | const uint32_t* best_histo = |
356 | 0 | GetHistoArgbConst(all_argb, /*subsampling_index=*/0, best_mode); |
357 | 0 | for (mode = 0; mode < kNumPredModes; ++mode) { |
358 | 0 | const uint32_t* const histo_argb = |
359 | 0 | GetHistoArgbConst(all_argb, subsampling_index, mode); |
360 | 0 | const int64_t cur_diff = PredictionCostSpatialHistogram( |
361 | 0 | accumulated_argb, histo_argb, mode, left_mode, above_mode); |
362 | |
|
363 | 0 | if (cur_diff < best_diff) { |
364 | 0 | best_histo = histo_argb; |
365 | 0 | best_diff = cur_diff; |
366 | 0 | best_mode = mode; |
367 | 0 | } |
368 | 0 | } |
369 | | // Update the accumulated histogram. |
370 | 0 | VP8LAddVectorEq(best_histo, accumulated_argb, HISTO_SIZE); |
371 | 0 | modes[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (best_mode << 8); |
372 | 0 | ++pred_histos[best_mode]; |
373 | 0 | } |
374 | | |
375 | | // Computes the residuals for the different predictors. |
376 | | // If max_quantization > 1, assumes that near lossless processing will be |
377 | | // applied, quantizing residuals to multiples of quantization levels up to |
378 | | // max_quantization (the actual quantization level depends on smoothness near |
379 | | // the given pixel). |
380 | | static void ComputeResidualsForTile( |
381 | | int width, int height, int tile_x, int tile_y, int min_bits, |
382 | | uint32_t update_up_to_index, uint32_t* const all_argb, |
383 | | uint32_t* const argb_scratch, const uint32_t* const argb, |
384 | 0 | int max_quantization, int exact, int used_subtract_green) { |
385 | 0 | const int start_x = tile_x << min_bits; |
386 | 0 | const int start_y = tile_y << min_bits; |
387 | 0 | const int tile_size = 1 << min_bits; |
388 | 0 | const int max_y = GetMin(tile_size, height - start_y); |
389 | 0 | const int max_x = GetMin(tile_size, width - start_x); |
390 | | // Whether there exist columns just outside the tile. |
391 | 0 | const int have_left = (start_x > 0); |
392 | | // Position and size of the strip covering the tile and adjacent columns if |
393 | | // they exist. |
394 | 0 | const int context_start_x = start_x - have_left; |
395 | 0 | #if (WEBP_NEAR_LOSSLESS == 1) |
396 | 0 | const int context_width = max_x + have_left + (max_x < width - start_x); |
397 | 0 | #endif |
398 | | // The width of upper_row and current_row is one pixel larger than image width |
399 | | // to allow the top right pixel to point to the leftmost pixel of the next row |
400 | | // when at the right edge. |
401 | 0 | uint32_t* upper_row = argb_scratch; |
402 | 0 | uint32_t* current_row = upper_row + width + 1; |
403 | 0 | uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1); |
404 | 0 | int mode; |
405 | | // Need pointers to be able to swap arrays. |
406 | 0 | uint32_t residuals[1 << MAX_TRANSFORM_BITS]; |
407 | 0 | assert(max_x <= (1 << MAX_TRANSFORM_BITS)); |
408 | 0 | for (mode = 0; mode < kNumPredModes; ++mode) { |
409 | 0 | int relative_y; |
410 | 0 | uint32_t* const histo_argb = |
411 | 0 | GetHistoArgb(all_argb, /*subsampling_index=*/0, mode); |
412 | 0 | if (start_y > 0) { |
413 | | // Read the row above the tile which will become the first upper_row. |
414 | | // Include a pixel to the left if it exists; include a pixel to the right |
415 | | // in all cases (wrapping to the leftmost pixel of the next row if it does |
416 | | // not exist). |
417 | 0 | memcpy(current_row + context_start_x, |
418 | 0 | argb + (start_y - 1) * width + context_start_x, |
419 | 0 | sizeof(*argb) * (max_x + have_left + 1)); |
420 | 0 | } |
421 | 0 | for (relative_y = 0; relative_y < max_y; ++relative_y) { |
422 | 0 | const int y = start_y + relative_y; |
423 | 0 | int relative_x; |
424 | 0 | uint32_t* tmp = upper_row; |
425 | 0 | upper_row = current_row; |
426 | 0 | current_row = tmp; |
427 | | // Read current_row. Include a pixel to the left if it exists; include a |
428 | | // pixel to the right in all cases except at the bottom right corner of |
429 | | // the image (wrapping to the leftmost pixel of the next row if it does |
430 | | // not exist in the current row). |
431 | 0 | memcpy(current_row + context_start_x, |
432 | 0 | argb + y * width + context_start_x, |
433 | 0 | sizeof(*argb) * (max_x + have_left + (y + 1 < height))); |
434 | 0 | #if (WEBP_NEAR_LOSSLESS == 1) |
435 | 0 | if (max_quantization > 1 && y >= 1 && y + 1 < height) { |
436 | 0 | MaxDiffsForRow(context_width, width, argb + y * width + context_start_x, |
437 | 0 | max_diffs + context_start_x, used_subtract_green); |
438 | 0 | } |
439 | 0 | #endif |
440 | |
|
441 | 0 | GetResidual(width, height, upper_row, current_row, max_diffs, mode, |
442 | 0 | start_x, start_x + max_x, y, max_quantization, exact, |
443 | 0 | used_subtract_green, residuals); |
444 | 0 | for (relative_x = 0; relative_x < max_x; ++relative_x) { |
445 | 0 | UpdateHisto(histo_argb, residuals[relative_x]); |
446 | 0 | } |
447 | 0 | if (update_up_to_index > 0) { |
448 | 0 | uint32_t subsampling_index; |
449 | 0 | for (subsampling_index = 1; subsampling_index <= update_up_to_index; |
450 | 0 | ++subsampling_index) { |
451 | 0 | uint32_t* const super_histo = |
452 | 0 | GetHistoArgb(all_argb, subsampling_index, mode); |
453 | 0 | for (relative_x = 0; relative_x < max_x; ++relative_x) { |
454 | 0 | UpdateHisto(super_histo, residuals[relative_x]); |
455 | 0 | } |
456 | 0 | } |
457 | 0 | } |
458 | 0 | } |
459 | 0 | } |
460 | 0 | } |
461 | | |
462 | | // Converts pixels of the image to residuals with respect to predictions. |
463 | | // If max_quantization > 1, applies near lossless processing, quantizing |
464 | | // residuals to multiples of quantization levels up to max_quantization |
465 | | // (the actual quantization level depends on smoothness near the given pixel). |
466 | | static void CopyImageWithPrediction(int width, int height, int bits, |
467 | | const uint32_t* const modes, |
468 | | uint32_t* const argb_scratch, |
469 | | uint32_t* const argb, int low_effort, |
470 | | int max_quantization, int exact, |
471 | 0 | int used_subtract_green) { |
472 | 0 | const int tiles_per_row = VP8LSubSampleSize(width, bits); |
473 | | // The width of upper_row and current_row is one pixel larger than image width |
474 | | // to allow the top right pixel to point to the leftmost pixel of the next row |
475 | | // when at the right edge. |
476 | 0 | uint32_t* upper_row = argb_scratch; |
477 | 0 | uint32_t* current_row = upper_row + width + 1; |
478 | 0 | uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1); |
479 | 0 | #if (WEBP_NEAR_LOSSLESS == 1) |
480 | 0 | uint8_t* lower_max_diffs = current_max_diffs + width; |
481 | 0 | #endif |
482 | 0 | int y; |
483 | |
|
484 | 0 | for (y = 0; y < height; ++y) { |
485 | 0 | int x; |
486 | 0 | uint32_t* const tmp32 = upper_row; |
487 | 0 | upper_row = current_row; |
488 | 0 | current_row = tmp32; |
489 | 0 | memcpy(current_row, argb + y * width, |
490 | 0 | sizeof(*argb) * (width + (y + 1 < height))); |
491 | |
|
492 | 0 | if (low_effort) { |
493 | 0 | PredictBatch(kPredLowEffort, 0, y, width, current_row, upper_row, |
494 | 0 | argb + y * width); |
495 | 0 | } else { |
496 | 0 | #if (WEBP_NEAR_LOSSLESS == 1) |
497 | 0 | if (max_quantization > 1) { |
498 | | // Compute max_diffs for the lower row now, because that needs the |
499 | | // contents of argb for the current row, which we will overwrite with |
500 | | // residuals before proceeding with the next row. |
501 | 0 | uint8_t* const tmp8 = current_max_diffs; |
502 | 0 | current_max_diffs = lower_max_diffs; |
503 | 0 | lower_max_diffs = tmp8; |
504 | 0 | if (y + 2 < height) { |
505 | 0 | MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs, |
506 | 0 | used_subtract_green); |
507 | 0 | } |
508 | 0 | } |
509 | 0 | #endif |
510 | 0 | for (x = 0; x < width;) { |
511 | 0 | const int mode = |
512 | 0 | (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff; |
513 | 0 | int x_end = x + (1 << bits); |
514 | 0 | if (x_end > width) x_end = width; |
515 | 0 | GetResidual(width, height, upper_row, current_row, current_max_diffs, |
516 | 0 | mode, x, x_end, y, max_quantization, exact, |
517 | 0 | used_subtract_green, argb + y * width + x); |
518 | 0 | x = x_end; |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | | // Checks whether 'image' can be subsampled by finding the biggest power of 2 |
525 | | // squares (defined by 'best_bits') of uniform value it is made out of. |
526 | | void VP8LOptimizeSampling(uint32_t* const image, int full_width, |
527 | | int full_height, int bits, int max_bits, |
528 | 0 | int* best_bits_out) { |
529 | 0 | int width = VP8LSubSampleSize(full_width, bits); |
530 | 0 | int height = VP8LSubSampleSize(full_height, bits); |
531 | 0 | int old_width, x, y, square_size; |
532 | 0 | int best_bits = bits; |
533 | 0 | *best_bits_out = bits; |
534 | | // Check rows first. |
535 | 0 | while (best_bits < max_bits) { |
536 | 0 | const int new_square_size = 1 << (best_bits + 1 - bits); |
537 | 0 | int is_good = 1; |
538 | 0 | square_size = 1 << (best_bits - bits); |
539 | 0 | for (y = 0; y + square_size < height; y += new_square_size) { |
540 | | // Check the first lines of consecutive line groups. |
541 | 0 | if (memcmp(&image[y * width], &image[(y + square_size) * width], |
542 | 0 | width * sizeof(*image)) != 0) { |
543 | 0 | is_good = 0; |
544 | 0 | break; |
545 | 0 | } |
546 | 0 | } |
547 | 0 | if (is_good) { |
548 | 0 | ++best_bits; |
549 | 0 | } else { |
550 | 0 | break; |
551 | 0 | } |
552 | 0 | } |
553 | 0 | if (best_bits == bits) return; |
554 | | |
555 | | // Check columns. |
556 | 0 | while (best_bits > bits) { |
557 | 0 | int is_good = 1; |
558 | 0 | square_size = 1 << (best_bits - bits); |
559 | 0 | for (y = 0; is_good && y < height; ++y) { |
560 | 0 | for (x = 0; is_good && x < width; x += square_size) { |
561 | 0 | int i; |
562 | 0 | for (i = x + 1; i < GetMin(x + square_size, width); ++i) { |
563 | 0 | if (image[y * width + i] != image[y * width + x]) { |
564 | 0 | is_good = 0; |
565 | 0 | break; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } |
570 | 0 | if (is_good) { |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | --best_bits; |
574 | 0 | } |
575 | 0 | if (best_bits == bits) return; |
576 | | |
577 | | // Subsample the image. |
578 | 0 | old_width = width; |
579 | 0 | square_size = 1 << (best_bits - bits); |
580 | 0 | width = VP8LSubSampleSize(full_width, best_bits); |
581 | 0 | height = VP8LSubSampleSize(full_height, best_bits); |
582 | 0 | for (y = 0; y < height; ++y) { |
583 | 0 | for (x = 0; x < width; ++x) { |
584 | 0 | image[y * width + x] = image[square_size * (y * old_width + x)]; |
585 | 0 | } |
586 | 0 | } |
587 | 0 | *best_bits_out = best_bits; |
588 | 0 | } |
589 | | |
590 | | // Computes the best predictor image. |
591 | | // Finds the best predictors per tile. Once done, finds the best predictor image |
592 | | // sampling. |
593 | | // best_bits is set to 0 in case of error. |
594 | | // The following requires some glossary: |
595 | | // - a tile is a square of side 2^min_bits pixels. |
596 | | // - a super-tile of a tile is a square of side 2^bits pixels with bits in |
597 | | // [min_bits+1, max_bits]. |
598 | | // - the max-tile of a tile is the square of 2^max_bits pixels containing it. |
599 | | // If this max-tile crosses the border of an image, it is cropped. |
600 | | // - tile, super-tiles and max_tile are aligned on powers of 2 in the original |
601 | | // image. |
602 | | // - coordinates for tile, super-tile, max-tile are respectively named |
603 | | // tile_x, super_tile_x, max_tile_x at their bit scale. |
604 | | // - in the max-tile, a tile has local coordinates (local_tile_x, local_tile_y). |
605 | | // The tiles are processed in the following zigzag order to complete the |
606 | | // super-tiles as soon as possible: |
607 | | // 1 2| 5 6 |
608 | | // 3 4| 7 8 |
609 | | // -------------- |
610 | | // 9 10| 13 14 |
611 | | // 11 12| 15 16 |
612 | | // When computing the residuals for a tile, the histogram of the above |
613 | | // super-tile is updated. If this super-tile is finished, its histogram is used |
614 | | // to update the histogram of the next super-tile and so on up to the max-tile. |
615 | | static void GetBestPredictorsAndSubSampling( |
616 | | int width, int height, const int min_bits, const int max_bits, |
617 | | uint32_t* const argb_scratch, const uint32_t* const argb, |
618 | | int max_quantization, int exact, int used_subtract_green, |
619 | | const WebPPicture* const pic, int percent_range, int* const percent, |
620 | 0 | uint32_t** const all_modes, int* best_bits, uint32_t** best_mode) { |
621 | 0 | const uint32_t tiles_per_row = VP8LSubSampleSize(width, min_bits); |
622 | 0 | const uint32_t tiles_per_col = VP8LSubSampleSize(height, min_bits); |
623 | 0 | int64_t best_cost; |
624 | 0 | uint32_t subsampling_index; |
625 | 0 | const uint32_t max_subsampling_index = max_bits - min_bits; |
626 | | // Compute the needed memory size for residual histograms, accumulated |
627 | | // residual histograms and predictor histograms. |
628 | 0 | const int num_argb = (max_subsampling_index + 1) * kNumPredModes * HISTO_SIZE; |
629 | 0 | const int num_accumulated_rgb = (max_subsampling_index + 1) * HISTO_SIZE; |
630 | 0 | const int num_predictors = (max_subsampling_index + 1) * kNumPredModes; |
631 | 0 | uint32_t* const raw_data = (uint32_t*)WebPSafeCalloc( |
632 | 0 | num_argb + num_accumulated_rgb + num_predictors, sizeof(uint32_t)); |
633 | 0 | uint32_t* const all_argb = raw_data; |
634 | 0 | uint32_t* const all_accumulated_argb = all_argb + num_argb; |
635 | 0 | uint32_t* const all_pred_histos = all_accumulated_argb + num_accumulated_rgb; |
636 | 0 | const int max_tile_size = 1 << max_subsampling_index; // in tile size |
637 | 0 | int percent_start = *percent; |
638 | | // When using the residuals of a tile for its super-tiles, you can either: |
639 | | // - use each residual to update the histogram of the super-tile, with a cost |
640 | | // of 4 * (1<<n)^2 increment operations (4 for the number of channels, and |
641 | | // (1<<n)^2 for the number of pixels in the tile) |
642 | | // - use the histogram of the tile to update the histogram of the super-tile, |
643 | | // with a cost of HISTO_SIZE (1024) |
644 | | // The first method is therefore faster until n==4. 'update_up_to_index' |
645 | | // defines the maximum subsampling_index for which the residuals should be |
646 | | // individually added to the super-tile histogram. |
647 | 0 | const uint32_t update_up_to_index = |
648 | 0 | GetMax(GetMin(4, max_bits), min_bits) - min_bits; |
649 | | // Coordinates in the max-tile in tile units. |
650 | 0 | uint32_t local_tile_x = 0, local_tile_y = 0; |
651 | 0 | uint32_t max_tile_x = 0, max_tile_y = 0; |
652 | 0 | uint32_t tile_x = 0, tile_y = 0; |
653 | |
|
654 | 0 | *best_bits = 0; |
655 | 0 | *best_mode = NULL; |
656 | 0 | if (raw_data == NULL) return; |
657 | | |
658 | 0 | while (tile_y < tiles_per_col) { |
659 | 0 | ComputeResidualsForTile(width, height, tile_x, tile_y, min_bits, |
660 | 0 | update_up_to_index, all_argb, argb_scratch, argb, |
661 | 0 | max_quantization, exact, used_subtract_green); |
662 | | |
663 | | // Update all the super-tiles that are complete. |
664 | 0 | subsampling_index = 0; |
665 | 0 | while (1) { |
666 | 0 | const uint32_t super_tile_x = tile_x >> subsampling_index; |
667 | 0 | const uint32_t super_tile_y = tile_y >> subsampling_index; |
668 | 0 | const uint32_t super_tiles_per_row = |
669 | 0 | VP8LSubSampleSize(width, min_bits + subsampling_index); |
670 | 0 | GetBestPredictorForTile(all_argb, subsampling_index, super_tile_x, |
671 | 0 | super_tile_y, super_tiles_per_row, |
672 | 0 | all_accumulated_argb, all_modes, all_pred_histos); |
673 | 0 | if (subsampling_index == max_subsampling_index) break; |
674 | | |
675 | | // Update the following super-tile histogram if it has not been updated |
676 | | // yet. |
677 | 0 | ++subsampling_index; |
678 | 0 | if (subsampling_index > update_up_to_index && |
679 | 0 | subsampling_index <= max_subsampling_index) { |
680 | 0 | VP8LAddVectorEq( |
681 | 0 | GetHistoArgbConst(all_argb, subsampling_index - 1, /*mode=*/0), |
682 | 0 | GetHistoArgb(all_argb, subsampling_index, /*mode=*/0), |
683 | 0 | HISTO_SIZE * kNumPredModes); |
684 | 0 | } |
685 | | // Check whether the super-tile is not complete (if the smallest tile |
686 | | // is not at the end of a line/column or at the beginning of a super-tile |
687 | | // of size (1 << subsampling_index)). |
688 | 0 | if (!((tile_x == (tiles_per_row - 1) || |
689 | 0 | (local_tile_x + 1) % (1 << subsampling_index) == 0) && |
690 | 0 | (tile_y == (tiles_per_col - 1) || |
691 | 0 | (local_tile_y + 1) % (1 << subsampling_index) == 0))) { |
692 | 0 | --subsampling_index; |
693 | | // subsampling_index now is the index of the last finished super-tile. |
694 | 0 | break; |
695 | 0 | } |
696 | 0 | } |
697 | | // Reset all the histograms belonging to finished tiles. |
698 | 0 | memset(all_argb, 0, |
699 | 0 | HISTO_SIZE * kNumPredModes * (subsampling_index + 1) * |
700 | 0 | sizeof(*all_argb)); |
701 | |
|
702 | 0 | if (subsampling_index == max_subsampling_index) { |
703 | | // If a new max-tile is started. |
704 | 0 | if (tile_x == (tiles_per_row - 1)) { |
705 | 0 | max_tile_x = 0; |
706 | 0 | ++max_tile_y; |
707 | 0 | } else { |
708 | 0 | ++max_tile_x; |
709 | 0 | } |
710 | 0 | local_tile_x = 0; |
711 | 0 | local_tile_y = 0; |
712 | 0 | } else { |
713 | | // Proceed with the Z traversal. |
714 | 0 | uint32_t coord_x = local_tile_x >> subsampling_index; |
715 | 0 | uint32_t coord_y = local_tile_y >> subsampling_index; |
716 | 0 | if (tile_x == (tiles_per_row - 1) && coord_x % 2 == 0) { |
717 | 0 | ++coord_y; |
718 | 0 | } else { |
719 | 0 | if (coord_x % 2 == 0) { |
720 | 0 | ++coord_x; |
721 | 0 | } else { |
722 | | // Z traversal. |
723 | 0 | ++coord_y; |
724 | 0 | --coord_x; |
725 | 0 | } |
726 | 0 | } |
727 | 0 | local_tile_x = coord_x << subsampling_index; |
728 | 0 | local_tile_y = coord_y << subsampling_index; |
729 | 0 | } |
730 | 0 | tile_x = max_tile_x * max_tile_size + local_tile_x; |
731 | 0 | tile_y = max_tile_y * max_tile_size + local_tile_y; |
732 | |
|
733 | 0 | if (tile_x == 0 && |
734 | 0 | !WebPReportProgress( |
735 | 0 | pic, percent_start + percent_range * tile_y / tiles_per_col, |
736 | 0 | percent)) { |
737 | 0 | WebPSafeFree(raw_data); |
738 | 0 | return; |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | | // Figure out the best sampling. |
743 | 0 | best_cost = WEBP_INT64_MAX; |
744 | 0 | for (subsampling_index = 0; subsampling_index <= max_subsampling_index; |
745 | 0 | ++subsampling_index) { |
746 | 0 | int plane; |
747 | 0 | const uint32_t* const accumulated = |
748 | 0 | GetAccumulatedHisto(all_accumulated_argb, subsampling_index); |
749 | 0 | int64_t cost = VP8LShannonEntropy( |
750 | 0 | &all_pred_histos[subsampling_index * kNumPredModes], kNumPredModes); |
751 | 0 | for (plane = 0; plane < 4; ++plane) { |
752 | 0 | cost += VP8LShannonEntropy(&accumulated[plane * 256], 256); |
753 | 0 | } |
754 | 0 | if (cost < best_cost) { |
755 | 0 | best_cost = cost; |
756 | 0 | *best_bits = min_bits + subsampling_index; |
757 | 0 | *best_mode = all_modes[subsampling_index]; |
758 | 0 | } |
759 | 0 | } |
760 | |
|
761 | 0 | WebPSafeFree(raw_data); |
762 | |
|
763 | 0 | VP8LOptimizeSampling(*best_mode, width, height, *best_bits, |
764 | 0 | MAX_TRANSFORM_BITS, best_bits); |
765 | 0 | } |
766 | | |
767 | | // Finds the best predictor for each tile, and converts the image to residuals |
768 | | // with respect to predictions. If near_lossless_quality < 100, applies |
769 | | // near lossless processing, shaving off more bits of residuals for lower |
770 | | // qualities. |
771 | | int VP8LResidualImage(int width, int height, int min_bits, int max_bits, |
772 | | int low_effort, uint32_t* const argb, |
773 | | uint32_t* const argb_scratch, uint32_t* const image, |
774 | | int near_lossless_quality, int exact, |
775 | | int used_subtract_green, const WebPPicture* const pic, |
776 | | int percent_range, int* const percent, |
777 | 0 | int* const best_bits) { |
778 | 0 | int percent_start = *percent; |
779 | 0 | const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality); |
780 | 0 | if (low_effort) { |
781 | 0 | const int tiles_per_row = VP8LSubSampleSize(width, max_bits); |
782 | 0 | const int tiles_per_col = VP8LSubSampleSize(height, max_bits); |
783 | 0 | int i; |
784 | 0 | for (i = 0; i < tiles_per_row * tiles_per_col; ++i) { |
785 | 0 | image[i] = ARGB_BLACK | (kPredLowEffort << 8); |
786 | 0 | } |
787 | 0 | *best_bits = max_bits; |
788 | 0 | } else { |
789 | | // Allocate data to try all samplings from min_bits to max_bits. |
790 | 0 | int bits; |
791 | 0 | uint32_t sum_num_pixels = 0u; |
792 | 0 | uint32_t *modes_raw, *best_mode; |
793 | 0 | uint32_t* modes[MAX_TRANSFORM_BITS + 1]; |
794 | 0 | uint32_t num_pixels[MAX_TRANSFORM_BITS + 1]; |
795 | 0 | for (bits = min_bits; bits <= max_bits; ++bits) { |
796 | 0 | const int tiles_per_row = VP8LSubSampleSize(width, bits); |
797 | 0 | const int tiles_per_col = VP8LSubSampleSize(height, bits); |
798 | 0 | num_pixels[bits] = tiles_per_row * tiles_per_col; |
799 | 0 | sum_num_pixels += num_pixels[bits]; |
800 | 0 | } |
801 | 0 | modes_raw = (uint32_t*)WebPSafeMalloc(sum_num_pixels, sizeof(*modes_raw)); |
802 | 0 | if (modes_raw == NULL) return 0; |
803 | | // Have modes point to the right global memory modes_raw. |
804 | 0 | modes[min_bits] = modes_raw; |
805 | 0 | for (bits = min_bits + 1; bits <= max_bits; ++bits) { |
806 | 0 | modes[bits] = modes[bits - 1] + num_pixels[bits - 1]; |
807 | 0 | } |
808 | | // Find the best sampling. |
809 | 0 | GetBestPredictorsAndSubSampling( |
810 | 0 | width, height, min_bits, max_bits, argb_scratch, argb, max_quantization, |
811 | 0 | exact, used_subtract_green, pic, percent_range, percent, |
812 | 0 | &modes[min_bits], best_bits, &best_mode); |
813 | 0 | if (*best_bits == 0) { |
814 | 0 | WebPSafeFree(modes_raw); |
815 | 0 | return 0; |
816 | 0 | } |
817 | | // Keep the best predictor image. |
818 | 0 | memcpy(image, best_mode, |
819 | 0 | VP8LSubSampleSize(width, *best_bits) * |
820 | 0 | VP8LSubSampleSize(height, *best_bits) * sizeof(*image)); |
821 | 0 | WebPSafeFree(modes_raw); |
822 | 0 | } |
823 | | |
824 | 0 | CopyImageWithPrediction(width, height, *best_bits, image, argb_scratch, argb, |
825 | 0 | low_effort, max_quantization, exact, |
826 | 0 | used_subtract_green); |
827 | 0 | return WebPReportProgress(pic, percent_start + percent_range, percent); |
828 | 0 | } |
829 | | |
830 | | //------------------------------------------------------------------------------ |
831 | | // Color transform functions. |
832 | | |
833 | 0 | static WEBP_INLINE void MultipliersClear(VP8LMultipliers* const m) { |
834 | 0 | m->green_to_red = 0; |
835 | 0 | m->green_to_blue = 0; |
836 | 0 | m->red_to_blue = 0; |
837 | 0 | } |
838 | | |
839 | | static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, |
840 | 0 | VP8LMultipliers* const m) { |
841 | 0 | m->green_to_red = (color_code >> 0) & 0xff; |
842 | 0 | m->green_to_blue = (color_code >> 8) & 0xff; |
843 | 0 | m->red_to_blue = (color_code >> 16) & 0xff; |
844 | 0 | } |
845 | | |
846 | | static WEBP_INLINE uint32_t MultipliersToColorCode( |
847 | 0 | const VP8LMultipliers* const m) { |
848 | 0 | return 0xff000000u | |
849 | 0 | ((uint32_t)(m->red_to_blue) << 16) | |
850 | 0 | ((uint32_t)(m->green_to_blue) << 8) | |
851 | 0 | m->green_to_red; |
852 | 0 | } |
853 | | |
854 | | static int64_t PredictionCostCrossColor(const uint32_t accumulated[256], |
855 | 0 | const uint32_t counts[256]) { |
856 | | // Favor low entropy, locally and globally. |
857 | | // Favor small absolute values for PredictionCostSpatial |
858 | 0 | static const uint64_t kExpValue = 240; |
859 | 0 | return (int64_t)VP8LCombinedShannonEntropy(counts, accumulated) + |
860 | 0 | PredictionCostBias(counts, 3, kExpValue); |
861 | 0 | } |
862 | | |
863 | | static int64_t GetPredictionCostCrossColorRed( |
864 | | const uint32_t* argb, int stride, int tile_width, int tile_height, |
865 | | VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_red, |
866 | 0 | const uint32_t accumulated_red_histo[256]) { |
867 | 0 | uint32_t histo[256] = { 0 }; |
868 | 0 | int64_t cur_diff; |
869 | |
|
870 | 0 | VP8LCollectColorRedTransforms(argb, stride, tile_width, tile_height, |
871 | 0 | green_to_red, histo); |
872 | |
|
873 | 0 | cur_diff = PredictionCostCrossColor(accumulated_red_histo, histo); |
874 | 0 | if ((uint8_t)green_to_red == prev_x.green_to_red) { |
875 | | // favor keeping the areas locally similar |
876 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
877 | 0 | } |
878 | 0 | if ((uint8_t)green_to_red == prev_y.green_to_red) { |
879 | | // favor keeping the areas locally similar |
880 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
881 | 0 | } |
882 | 0 | if (green_to_red == 0) { |
883 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
884 | 0 | } |
885 | 0 | return cur_diff; |
886 | 0 | } |
887 | | |
888 | | static void GetBestGreenToRed(const uint32_t* argb, int stride, int tile_width, |
889 | | int tile_height, VP8LMultipliers prev_x, |
890 | | VP8LMultipliers prev_y, int quality, |
891 | | const uint32_t accumulated_red_histo[256], |
892 | 0 | VP8LMultipliers* const best_tx) { |
893 | 0 | const int kMaxIters = 4 + ((7 * quality) >> 8); // in range [4..6] |
894 | 0 | int green_to_red_best = 0; |
895 | 0 | int iter, offset; |
896 | 0 | int64_t best_diff = GetPredictionCostCrossColorRed( |
897 | 0 | argb, stride, tile_width, tile_height, prev_x, prev_y, green_to_red_best, |
898 | 0 | accumulated_red_histo); |
899 | 0 | for (iter = 0; iter < kMaxIters; ++iter) { |
900 | | // ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to |
901 | | // one in color computation. Having initial delta here as 1 is sufficient |
902 | | // to explore the range of (-2, 2). |
903 | 0 | const int delta = 32 >> iter; |
904 | | // Try a negative and a positive delta from the best known value. |
905 | 0 | for (offset = -delta; offset <= delta; offset += 2 * delta) { |
906 | 0 | const int green_to_red_cur = offset + green_to_red_best; |
907 | 0 | const int64_t cur_diff = GetPredictionCostCrossColorRed( |
908 | 0 | argb, stride, tile_width, tile_height, prev_x, prev_y, |
909 | 0 | green_to_red_cur, accumulated_red_histo); |
910 | 0 | if (cur_diff < best_diff) { |
911 | 0 | best_diff = cur_diff; |
912 | 0 | green_to_red_best = green_to_red_cur; |
913 | 0 | } |
914 | 0 | } |
915 | 0 | } |
916 | 0 | best_tx->green_to_red = (green_to_red_best & 0xff); |
917 | 0 | } |
918 | | |
919 | | static int64_t GetPredictionCostCrossColorBlue( |
920 | | const uint32_t* argb, int stride, int tile_width, int tile_height, |
921 | | VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_blue, |
922 | 0 | int red_to_blue, const uint32_t accumulated_blue_histo[256]) { |
923 | 0 | uint32_t histo[256] = { 0 }; |
924 | 0 | int64_t cur_diff; |
925 | |
|
926 | 0 | VP8LCollectColorBlueTransforms(argb, stride, tile_width, tile_height, |
927 | 0 | green_to_blue, red_to_blue, histo); |
928 | |
|
929 | 0 | cur_diff = PredictionCostCrossColor(accumulated_blue_histo, histo); |
930 | 0 | if ((uint8_t)green_to_blue == prev_x.green_to_blue) { |
931 | | // favor keeping the areas locally similar |
932 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
933 | 0 | } |
934 | 0 | if ((uint8_t)green_to_blue == prev_y.green_to_blue) { |
935 | | // favor keeping the areas locally similar |
936 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
937 | 0 | } |
938 | 0 | if ((uint8_t)red_to_blue == prev_x.red_to_blue) { |
939 | | // favor keeping the areas locally similar |
940 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
941 | 0 | } |
942 | 0 | if ((uint8_t)red_to_blue == prev_y.red_to_blue) { |
943 | | // favor keeping the areas locally similar |
944 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
945 | 0 | } |
946 | 0 | if (green_to_blue == 0) { |
947 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
948 | 0 | } |
949 | 0 | if (red_to_blue == 0) { |
950 | 0 | cur_diff -= 3ll << LOG_2_PRECISION_BITS; |
951 | 0 | } |
952 | 0 | return cur_diff; |
953 | 0 | } |
954 | | |
955 | 0 | #define kGreenRedToBlueNumAxis 8 |
956 | 0 | #define kGreenRedToBlueMaxIters 7 |
957 | | static void GetBestGreenRedToBlue(const uint32_t* argb, int stride, |
958 | | int tile_width, int tile_height, |
959 | | VP8LMultipliers prev_x, |
960 | | VP8LMultipliers prev_y, int quality, |
961 | | const uint32_t accumulated_blue_histo[256], |
962 | 0 | VP8LMultipliers* const best_tx) { |
963 | 0 | const int8_t offset[kGreenRedToBlueNumAxis][2] = |
964 | 0 | {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; |
965 | 0 | const int8_t delta_lut[kGreenRedToBlueMaxIters] = { 16, 16, 8, 4, 2, 2, 2 }; |
966 | 0 | const int iters = |
967 | 0 | (quality < 25) ? 1 : (quality > 50) ? kGreenRedToBlueMaxIters : 4; |
968 | 0 | int green_to_blue_best = 0; |
969 | 0 | int red_to_blue_best = 0; |
970 | 0 | int iter; |
971 | | // Initial value at origin: |
972 | 0 | int64_t best_diff = GetPredictionCostCrossColorBlue( |
973 | 0 | argb, stride, tile_width, tile_height, prev_x, prev_y, green_to_blue_best, |
974 | 0 | red_to_blue_best, accumulated_blue_histo); |
975 | 0 | for (iter = 0; iter < iters; ++iter) { |
976 | 0 | const int delta = delta_lut[iter]; |
977 | 0 | int axis; |
978 | 0 | for (axis = 0; axis < kGreenRedToBlueNumAxis; ++axis) { |
979 | 0 | const int green_to_blue_cur = |
980 | 0 | offset[axis][0] * delta + green_to_blue_best; |
981 | 0 | const int red_to_blue_cur = offset[axis][1] * delta + red_to_blue_best; |
982 | 0 | const int64_t cur_diff = GetPredictionCostCrossColorBlue( |
983 | 0 | argb, stride, tile_width, tile_height, prev_x, prev_y, |
984 | 0 | green_to_blue_cur, red_to_blue_cur, accumulated_blue_histo); |
985 | 0 | if (cur_diff < best_diff) { |
986 | 0 | best_diff = cur_diff; |
987 | 0 | green_to_blue_best = green_to_blue_cur; |
988 | 0 | red_to_blue_best = red_to_blue_cur; |
989 | 0 | } |
990 | 0 | if (quality < 25 && iter == 4) { |
991 | | // Only axis aligned diffs for lower quality. |
992 | 0 | break; // next iter. |
993 | 0 | } |
994 | 0 | } |
995 | 0 | if (delta == 2 && green_to_blue_best == 0 && red_to_blue_best == 0) { |
996 | | // Further iterations would not help. |
997 | 0 | break; // out of iter-loop. |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | best_tx->green_to_blue = green_to_blue_best & 0xff; |
1001 | 0 | best_tx->red_to_blue = red_to_blue_best & 0xff; |
1002 | 0 | } |
1003 | | #undef kGreenRedToBlueMaxIters |
1004 | | #undef kGreenRedToBlueNumAxis |
1005 | | |
1006 | | static VP8LMultipliers GetBestColorTransformForTile( |
1007 | | int tile_x, int tile_y, int bits, VP8LMultipliers prev_x, |
1008 | | VP8LMultipliers prev_y, int quality, int xsize, int ysize, |
1009 | | const uint32_t accumulated_red_histo[256], |
1010 | 0 | const uint32_t accumulated_blue_histo[256], const uint32_t* const argb) { |
1011 | 0 | const int max_tile_size = 1 << bits; |
1012 | 0 | const int tile_y_offset = tile_y * max_tile_size; |
1013 | 0 | const int tile_x_offset = tile_x * max_tile_size; |
1014 | 0 | const int all_x_max = GetMin(tile_x_offset + max_tile_size, xsize); |
1015 | 0 | const int all_y_max = GetMin(tile_y_offset + max_tile_size, ysize); |
1016 | 0 | const int tile_width = all_x_max - tile_x_offset; |
1017 | 0 | const int tile_height = all_y_max - tile_y_offset; |
1018 | 0 | const uint32_t* const tile_argb = argb + tile_y_offset * xsize |
1019 | 0 | + tile_x_offset; |
1020 | 0 | VP8LMultipliers best_tx; |
1021 | 0 | MultipliersClear(&best_tx); |
1022 | |
|
1023 | 0 | GetBestGreenToRed(tile_argb, xsize, tile_width, tile_height, |
1024 | 0 | prev_x, prev_y, quality, accumulated_red_histo, &best_tx); |
1025 | 0 | GetBestGreenRedToBlue(tile_argb, xsize, tile_width, tile_height, |
1026 | 0 | prev_x, prev_y, quality, accumulated_blue_histo, |
1027 | 0 | &best_tx); |
1028 | 0 | return best_tx; |
1029 | 0 | } |
1030 | | |
1031 | | static void CopyTileWithColorTransform(int xsize, int ysize, |
1032 | | int tile_x, int tile_y, |
1033 | | int max_tile_size, |
1034 | | VP8LMultipliers color_transform, |
1035 | 0 | uint32_t* argb) { |
1036 | 0 | const int xscan = GetMin(max_tile_size, xsize - tile_x); |
1037 | 0 | int yscan = GetMin(max_tile_size, ysize - tile_y); |
1038 | 0 | argb += tile_y * xsize + tile_x; |
1039 | 0 | while (yscan-- > 0) { |
1040 | 0 | VP8LTransformColor(&color_transform, argb, xscan); |
1041 | 0 | argb += xsize; |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | | int VP8LColorSpaceTransform(int width, int height, int bits, int quality, |
1046 | | uint32_t* const argb, uint32_t* image, |
1047 | | const WebPPicture* const pic, int percent_range, |
1048 | 0 | int* const percent, int* const best_bits) { |
1049 | 0 | const int max_tile_size = 1 << bits; |
1050 | 0 | const int tile_xsize = VP8LSubSampleSize(width, bits); |
1051 | 0 | const int tile_ysize = VP8LSubSampleSize(height, bits); |
1052 | 0 | int percent_start = *percent; |
1053 | 0 | uint32_t accumulated_red_histo[256] = { 0 }; |
1054 | 0 | uint32_t accumulated_blue_histo[256] = { 0 }; |
1055 | 0 | int tile_x, tile_y; |
1056 | 0 | VP8LMultipliers prev_x, prev_y; |
1057 | 0 | MultipliersClear(&prev_y); |
1058 | 0 | MultipliersClear(&prev_x); |
1059 | 0 | for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { |
1060 | 0 | for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { |
1061 | 0 | int y; |
1062 | 0 | const int tile_x_offset = tile_x * max_tile_size; |
1063 | 0 | const int tile_y_offset = tile_y * max_tile_size; |
1064 | 0 | const int all_x_max = GetMin(tile_x_offset + max_tile_size, width); |
1065 | 0 | const int all_y_max = GetMin(tile_y_offset + max_tile_size, height); |
1066 | 0 | const int offset = tile_y * tile_xsize + tile_x; |
1067 | 0 | if (tile_y != 0) { |
1068 | 0 | ColorCodeToMultipliers(image[offset - tile_xsize], &prev_y); |
1069 | 0 | } |
1070 | 0 | prev_x = GetBestColorTransformForTile(tile_x, tile_y, bits, |
1071 | 0 | prev_x, prev_y, |
1072 | 0 | quality, width, height, |
1073 | 0 | accumulated_red_histo, |
1074 | 0 | accumulated_blue_histo, |
1075 | 0 | argb); |
1076 | 0 | image[offset] = MultipliersToColorCode(&prev_x); |
1077 | 0 | CopyTileWithColorTransform(width, height, tile_x_offset, tile_y_offset, |
1078 | 0 | max_tile_size, prev_x, argb); |
1079 | | |
1080 | | // Gather accumulated histogram data. |
1081 | 0 | for (y = tile_y_offset; y < all_y_max; ++y) { |
1082 | 0 | int ix = y * width + tile_x_offset; |
1083 | 0 | const int ix_end = ix + all_x_max - tile_x_offset; |
1084 | 0 | for (; ix < ix_end; ++ix) { |
1085 | 0 | const uint32_t pix = argb[ix]; |
1086 | 0 | if (ix >= 2 && |
1087 | 0 | pix == argb[ix - 2] && |
1088 | 0 | pix == argb[ix - 1]) { |
1089 | 0 | continue; // repeated pixels are handled by backward references |
1090 | 0 | } |
1091 | 0 | if (ix >= width + 2 && |
1092 | 0 | argb[ix - 2] == argb[ix - width - 2] && |
1093 | 0 | argb[ix - 1] == argb[ix - width - 1] && |
1094 | 0 | pix == argb[ix - width]) { |
1095 | 0 | continue; // repeated pixels are handled by backward references |
1096 | 0 | } |
1097 | 0 | ++accumulated_red_histo[(pix >> 16) & 0xff]; |
1098 | 0 | ++accumulated_blue_histo[(pix >> 0) & 0xff]; |
1099 | 0 | } |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | if (!WebPReportProgress( |
1103 | 0 | pic, percent_start + percent_range * tile_y / tile_ysize, |
1104 | 0 | percent)) { |
1105 | 0 | return 0; |
1106 | 0 | } |
1107 | 0 | } |
1108 | 0 | VP8LOptimizeSampling(image, width, height, bits, MAX_TRANSFORM_BITS, |
1109 | 0 | best_bits); |
1110 | 0 | return 1; |
1111 | 0 | } |