/src/libwebp/src/utils/quant_levels_dec_utils.c
Line | Count | Source |
1 | | // Copyright 2013 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 | | // Implement gradient smoothing: we replace a current alpha value by its |
11 | | // surrounding average if it's close enough (that is: the change will be less |
12 | | // than the minimum distance between two quantized level). |
13 | | // We use sliding window for computing the 2d moving average. |
14 | | // |
15 | | // Author: Skal (pascal.massimino@gmail.com) |
16 | | |
17 | | #include "src/utils/quant_levels_dec_utils.h" |
18 | | |
19 | | #include <string.h> // for memset |
20 | | |
21 | | #include "src/utils/bounds_safety.h" |
22 | | #include "src/utils/utils.h" |
23 | | #include "src/webp/types.h" |
24 | | |
25 | | WEBP_ASSUME_UNSAFE_INDEXABLE_ABI |
26 | | |
27 | | // #define USE_DITHERING // uncomment to enable ordered dithering (not vital) |
28 | | |
29 | 0 | #define FIX 16 // fix-point precision for averaging |
30 | 0 | #define LFIX 2 // extra precision for look-up table |
31 | 0 | #define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size |
32 | 0 | #define CORRECTION_LUT_SIZE (1 + 2 * LUT_SIZE) |
33 | | |
34 | | #if defined(USE_DITHERING) |
35 | | |
36 | | #define DFIX 4 // extra precision for ordered dithering |
37 | | #define DSIZE 4 // dithering size (must be a power of two) |
38 | | // cf. https://en.wikipedia.org/wiki/Ordered_dithering |
39 | | static const uint8_t kOrderedDither[DSIZE][DSIZE] = { |
40 | | {0, 8, 2, 10}, // coefficients are in DFIX fixed-point precision |
41 | | {12, 4, 14, 6}, |
42 | | {3, 11, 1, 9}, |
43 | | {15, 7, 13, 5}}; |
44 | | |
45 | | #else |
46 | 0 | #define DFIX 0 |
47 | | #endif |
48 | | |
49 | | typedef struct { |
50 | | int width, height; // dimension |
51 | | int stride; // stride in bytes |
52 | | int row; // current input row being processed |
53 | | uint8_t* WEBP_INDEXABLE src; // input pointer |
54 | | uint8_t* WEBP_INDEXABLE dst; // output pointer |
55 | | |
56 | | int radius; // filter radius (=delay) |
57 | | int scale; // normalization factor, in FIX bits precision |
58 | | |
59 | | void* mem; // all memory |
60 | | |
61 | | // various scratch buffers |
62 | | uint16_t* WEBP_INDEXABLE start; |
63 | | uint16_t* WEBP_INDEXABLE cur; |
64 | | uint16_t* WEBP_BIDI_INDEXABLE end; |
65 | | uint16_t* WEBP_INDEXABLE top; |
66 | | uint16_t* WEBP_COUNTED_BY(width) average; |
67 | | |
68 | | // input levels distribution |
69 | | int num_levels; // number of quantized levels |
70 | | int min, max; // min and max level values |
71 | | int min_level_dist; // smallest distance between two consecutive levels |
72 | | |
73 | | // size = 1 + 2*LUT_SIZE -> ~4k memory |
74 | | int16_t* WEBP_COUNTED_BY_OR_NULL(CORRECTION_LUT_SIZE) correction; |
75 | | } SmoothParams; |
76 | | |
77 | | //------------------------------------------------------------------------------ |
78 | | |
79 | 0 | #define CLIP_8b_MASK (int)(~0U << (8 + DFIX)) |
80 | 0 | static WEBP_INLINE uint8_t clip_8b(int v) { |
81 | 0 | return (!(v & CLIP_8b_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u; |
82 | 0 | } |
83 | | #undef CLIP_8b_MASK |
84 | | |
85 | | // vertical accumulation |
86 | 0 | static void VFilter(SmoothParams* const p) { |
87 | 0 | const uint8_t* WEBP_INDEXABLE src = p->src; |
88 | 0 | const int w = p->width; |
89 | 0 | uint16_t* const WEBP_INDEXABLE cur = p->cur; |
90 | 0 | const uint16_t* const WEBP_INDEXABLE top = p->top; |
91 | 0 | uint16_t* const WEBP_INDEXABLE out = p->end; |
92 | 0 | uint16_t sum = 0; // all arithmetic is modulo 16bit |
93 | 0 | int x; |
94 | |
|
95 | 0 | for (x = 0; x < w; ++x) { |
96 | 0 | uint16_t new_value; |
97 | 0 | sum += src[x]; |
98 | 0 | new_value = top[x] + sum; |
99 | 0 | out[x] = new_value - cur[x]; // vertical sum of 'r' pixels. |
100 | 0 | cur[x] = new_value; |
101 | 0 | } |
102 | | // move input pointers one row down |
103 | 0 | p->top = p->cur; |
104 | 0 | p->cur += w; |
105 | 0 | if (p->cur == p->end) p->cur = p->start; // roll-over |
106 | | // We replicate edges, as it's somewhat easier as a boundary condition. |
107 | | // That's why we don't update the 'src' pointer on top/bottom area: |
108 | 0 | if (p->row >= 0 && p->row < p->height - 1) { |
109 | 0 | p->src += p->stride; |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | | // horizontal accumulation. We use mirror replication of missing pixels, as it's |
114 | | // a little easier to implement (surprisingly). |
115 | 0 | static void HFilter(SmoothParams* const p) { |
116 | 0 | const uint16_t* const WEBP_INDEXABLE in = p->end; |
117 | 0 | uint16_t* const WEBP_INDEXABLE out = p->average; |
118 | 0 | const uint32_t scale = p->scale; |
119 | 0 | const int w = p->width; |
120 | 0 | const int r = p->radius; |
121 | |
|
122 | 0 | int x; |
123 | 0 | for (x = 0; x <= r; ++x) { // left mirroring |
124 | 0 | const uint16_t delta = in[x + r - 1] + in[r - x]; |
125 | 0 | out[x] = (delta * scale) >> FIX; |
126 | 0 | } |
127 | 0 | for (; x < w - r; ++x) { // bulk middle run |
128 | 0 | const uint16_t delta = in[x + r] - in[x - r - 1]; |
129 | 0 | out[x] = (delta * scale) >> FIX; |
130 | 0 | } |
131 | 0 | for (; x < w; ++x) { // right mirroring |
132 | 0 | const uint16_t delta = |
133 | 0 | 2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1]; |
134 | 0 | out[x] = (delta * scale) >> FIX; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | // emit one filtered output row |
139 | 0 | static void ApplyFilter(SmoothParams* const p) { |
140 | 0 | const uint16_t* const WEBP_INDEXABLE average = p->average; |
141 | 0 | const int w = p->width; |
142 | | // correction is WEBP_COUNTED_BY, pointing to the start of the LUT. |
143 | | // We need the middle pointer for negative indexing. |
144 | 0 | const int16_t* const WEBP_BIDI_INDEXABLE correction = |
145 | 0 | p->correction + LUT_SIZE; |
146 | | #if defined(USE_DITHERING) |
147 | | const uint8_t* const dither = kOrderedDither[p->row % DSIZE]; |
148 | | #endif |
149 | 0 | uint8_t* const WEBP_INDEXABLE dst = p->dst; |
150 | 0 | int x; |
151 | 0 | for (x = 0; x < w; ++x) { |
152 | 0 | const int v = dst[x]; |
153 | 0 | if (v < p->max && v > p->min) { |
154 | 0 | const int c = (v << DFIX) + correction[average[x] - (v << LFIX)]; |
155 | | #if defined(USE_DITHERING) |
156 | | dst[x] = clip_8b(c + dither[x % DSIZE]); |
157 | | #else |
158 | 0 | dst[x] = clip_8b(c); |
159 | 0 | #endif |
160 | 0 | } |
161 | 0 | } |
162 | 0 | p->dst += p->stride; // advance output pointer |
163 | 0 | } |
164 | | |
165 | | //------------------------------------------------------------------------------ |
166 | | // Initialize correction table |
167 | | |
168 | | static void InitCorrectionLUT( |
169 | 0 | int16_t* const WEBP_COUNTED_BY(CORRECTION_LUT_SIZE) lut_ptr, int min_dist) { |
170 | | // The correction curve is: |
171 | | // f(x) = x for x <= threshold2 |
172 | | // f(x) = 0 for x >= threshold1 |
173 | | // and a linear interpolation for range x=[threshold2, threshold1] |
174 | | // (along with f(-x) = -f(x) symmetry). |
175 | | // Note that: threshold2 = 3/4 * threshold1 |
176 | 0 | const int threshold1 = min_dist << LFIX; |
177 | 0 | const int threshold2 = (3 * threshold1) >> 2; |
178 | 0 | const int max_threshold = threshold2 << DFIX; |
179 | 0 | const int delta = threshold1 - threshold2; |
180 | | // lut_ptr is WEBP_COUNTED_BY, pointing to the start of the LUT. |
181 | | // We need the middle pointer (lut) for negative indexing. |
182 | 0 | int16_t* const WEBP_BIDI_INDEXABLE lut = lut_ptr + LUT_SIZE; |
183 | 0 | int i; |
184 | 0 | for (i = 1; i <= LUT_SIZE; ++i) { |
185 | 0 | int c = (i <= threshold2) ? (i << DFIX) |
186 | 0 | : (i < threshold1) ? max_threshold * (threshold1 - i) / delta |
187 | 0 | : 0; |
188 | 0 | c >>= LFIX; |
189 | 0 | lut[+i] = +c; |
190 | 0 | lut[-i] = -c; |
191 | 0 | } |
192 | 0 | lut[0] = 0; |
193 | 0 | } |
194 | | |
195 | 0 | static void CountLevels(SmoothParams* const p) { |
196 | 0 | int i, j, last_level; |
197 | 0 | uint8_t used_levels[256] = {0}; |
198 | 0 | const uint8_t* WEBP_INDEXABLE data = p->src; |
199 | 0 | p->min = 255; |
200 | 0 | p->max = 0; |
201 | 0 | for (j = 0; j < p->height; ++j) { |
202 | 0 | for (i = 0; i < p->width; ++i) { |
203 | 0 | const int v = data[i]; |
204 | 0 | if (v < p->min) p->min = v; |
205 | 0 | if (v > p->max) p->max = v; |
206 | 0 | used_levels[v] = 1; |
207 | 0 | } |
208 | 0 | data += p->stride; |
209 | 0 | } |
210 | | // Compute the mininum distance between two non-zero levels. |
211 | 0 | p->min_level_dist = p->max - p->min; |
212 | 0 | last_level = -1; |
213 | 0 | for (i = 0; i < 256; ++i) { |
214 | 0 | if (used_levels[i]) { |
215 | 0 | ++p->num_levels; |
216 | 0 | if (last_level >= 0) { |
217 | 0 | const int level_dist = i - last_level; |
218 | 0 | if (level_dist < p->min_level_dist) { |
219 | 0 | p->min_level_dist = level_dist; |
220 | 0 | } |
221 | 0 | } |
222 | 0 | last_level = i; |
223 | 0 | } |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | // Initialize all params. |
228 | | static int InitParams(uint8_t* WEBP_SIZED_BY((size_t)stride* height) const data, |
229 | | int width, int height, int stride, int radius, |
230 | 0 | SmoothParams* const p) { |
231 | 0 | const int R = 2 * radius + 1; // total size of the kernel |
232 | |
|
233 | 0 | const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start); |
234 | 0 | const size_t size_m = width * sizeof(*p->average); |
235 | 0 | const size_t size_lut = CORRECTION_LUT_SIZE * sizeof(*p->correction); |
236 | 0 | const size_t total_size = size_scratch_m + size_m + size_lut; |
237 | 0 | uint8_t* WEBP_BIDI_INDEXABLE mem = (uint8_t*)WebPSafeMalloc(1U, total_size); |
238 | |
|
239 | 0 | if (mem == NULL) return 0; |
240 | 0 | p->mem = (void*)mem; |
241 | |
|
242 | 0 | p->start = (uint16_t*)mem; |
243 | 0 | p->cur = p->start; |
244 | 0 | p->end = p->start + R * width; |
245 | 0 | p->top = p->end - width; |
246 | 0 | WEBP_UNSAFE_MEMSET(p->top, 0, width * sizeof(*p->top)); |
247 | 0 | mem += size_scratch_m; |
248 | |
|
249 | 0 | p->width = width; |
250 | 0 | p->average = (uint16_t*)mem; |
251 | 0 | mem += size_m; |
252 | |
|
253 | 0 | p->height = height; |
254 | 0 | p->stride = stride; |
255 | 0 | p->src = data; |
256 | 0 | p->dst = data; |
257 | 0 | p->radius = radius; |
258 | 0 | p->scale = (1 << (FIX + LFIX)) / (R * R); // normalization constant |
259 | 0 | p->row = -radius; |
260 | | |
261 | | // analyze the input distribution so we can best-fit the threshold |
262 | 0 | CountLevels(p); |
263 | | |
264 | | // correction table. p->correction is WEBP_COUNTED_BY(CORRECTION_LUT_SIZE). |
265 | | // It points to the start of the buffer. |
266 | 0 | p->correction = ((int16_t*)mem); |
267 | 0 | InitCorrectionLUT(p->correction, p->min_level_dist); |
268 | |
|
269 | 0 | return 1; |
270 | 0 | } |
271 | | |
272 | 0 | static void CleanupParams(SmoothParams* const p) { WebPSafeFree(p->mem); } |
273 | | |
274 | | int WebPDequantizeLevels(uint8_t* WEBP_SIZED_BY((size_t)stride* height) |
275 | | const data, |
276 | 0 | int width, int height, int stride, int strength) { |
277 | 0 | int radius = 4 * strength / 100; |
278 | |
|
279 | 0 | if (strength < 0 || strength > 100) return 0; |
280 | 0 | if (data == NULL || width <= 0 || height <= 0) return 0; // bad params |
281 | | |
282 | | // limit the filter size to not exceed the image dimensions |
283 | 0 | if (2 * radius + 1 > width) radius = (width - 1) >> 1; |
284 | 0 | if (2 * radius + 1 > height) radius = (height - 1) >> 1; |
285 | |
|
286 | 0 | if (radius > 0) { |
287 | 0 | SmoothParams p; |
288 | 0 | WEBP_UNSAFE_MEMSET(&p, 0, sizeof(p)); |
289 | 0 | if (!InitParams(data, width, height, stride, radius, &p)) return 0; |
290 | 0 | if (p.num_levels > 2) { |
291 | 0 | for (; p.row < p.height; ++p.row) { |
292 | 0 | VFilter(&p); // accumulate average of input |
293 | | // Need to wait few rows in order to prime the filter, |
294 | | // before emitting some output. |
295 | 0 | if (p.row >= p.radius) { |
296 | 0 | HFilter(&p); |
297 | 0 | ApplyFilter(&p); |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | 0 | CleanupParams(&p); |
302 | 0 | } |
303 | 0 | return 1; |
304 | 0 | } |