/work/libwebp/sharpyuv/sharpyuv.c
Line | Count | Source |
1 | | // Copyright 2022 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 | | // Sharp RGB to YUV conversion. |
11 | | // |
12 | | // Author: Skal (pascal.massimino@gmail.com) |
13 | | |
14 | | #include "./sharpyuv.h" |
15 | | |
16 | | #include <assert.h> |
17 | | #include <limits.h> |
18 | | #include <stddef.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include "./sharpyuv_cpu.h" |
23 | | #include "./sharpyuv_dsp.h" |
24 | | #include "./sharpyuv_gamma.h" |
25 | | #include "webp/types.h" |
26 | | |
27 | | //------------------------------------------------------------------------------ |
28 | | |
29 | 0 | int SharpYuvGetVersion(void) { return SHARPYUV_VERSION; } |
30 | | |
31 | | //------------------------------------------------------------------------------ |
32 | | // Sharp RGB->YUV conversion |
33 | | |
34 | | static const int kNumIterations = 4; |
35 | | |
36 | 43.8k | #define YUV_FIX 16 // fixed-point precision for RGB->YUV |
37 | | static const int kYuvHalf = 1 << (YUV_FIX - 1); |
38 | | |
39 | | // Max bit depth so that intermediate calculations fit in 16 bits. |
40 | | static const int kMaxBitDepth = 14; |
41 | | |
42 | | // Returns the precision shift to use based on the input rgb_bit_depth. |
43 | 734 | static int GetPrecisionShift(int rgb_bit_depth) { |
44 | | // Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove |
45 | | // bits if needed. |
46 | 734 | return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2 |
47 | 734 | : (kMaxBitDepth - rgb_bit_depth); |
48 | 734 | } |
49 | | |
50 | | typedef int16_t fixed_t; // signed type with extra precision for UV |
51 | | typedef uint16_t fixed_y_t; // unsigned type with extra precision for W |
52 | | |
53 | | //------------------------------------------------------------------------------ |
54 | | |
55 | 3.59k | static uint8_t clip_8b(fixed_t v) { |
56 | 3.59k | return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u; |
57 | 3.59k | } |
58 | | |
59 | 4.22k | static uint16_t clip(fixed_t v, int max) { |
60 | 4.22k | return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v; |
61 | 4.22k | } |
62 | | |
63 | 9.82k | static fixed_y_t clip_bit_depth(int y, int bit_depth) { |
64 | 9.82k | const int max = (1 << bit_depth) - 1; |
65 | 9.82k | return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max; |
66 | 9.82k | } |
67 | | |
68 | | //------------------------------------------------------------------------------ |
69 | | |
70 | 28.2k | static int RGBToGray(int64_t r, int64_t g, int64_t b) { |
71 | | // r/g/b can reach ~71501 (Smpte428's EOTF), not just the usual <= 65536. |
72 | 28.2k | const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf; |
73 | 28.2k | return (int)(luma >> YUV_FIX); |
74 | 28.2k | } |
75 | | |
76 | | static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d, |
77 | | int bit_depth, |
78 | 13.5k | SharpYuvTransferFunctionType transfer_type) { |
79 | 13.5k | const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type); |
80 | 13.5k | const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type); |
81 | 13.5k | const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type); |
82 | 13.5k | const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type); |
83 | 13.5k | return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth, |
84 | 13.5k | transfer_type); |
85 | 13.5k | } |
86 | | |
87 | | static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w, |
88 | | int bit_depth, |
89 | 2.16k | SharpYuvTransferFunctionType transfer_type) { |
90 | 2.16k | int i = 0; |
91 | 18.0k | do { |
92 | 18.0k | const uint32_t R = |
93 | 18.0k | SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type); |
94 | 18.0k | const uint32_t G = |
95 | 18.0k | SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type); |
96 | 18.0k | const uint32_t B = |
97 | 18.0k | SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type); |
98 | 18.0k | const uint32_t Y = RGBToGray(R, G, B); |
99 | 18.0k | dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type); |
100 | 18.0k | } while (++i < w); |
101 | 2.16k | } |
102 | | |
103 | | static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2, |
104 | | fixed_t* dst, int uv_w, int bit_depth, |
105 | 1.08k | SharpYuvTransferFunctionType transfer_type) { |
106 | 1.08k | int i = 0; |
107 | 4.51k | do { |
108 | 4.51k | const int r = |
109 | 4.51k | ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0], |
110 | 4.51k | src2[0 * uv_w + 1], bit_depth, transfer_type); |
111 | 4.51k | const int g = |
112 | 4.51k | ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0], |
113 | 4.51k | src2[2 * uv_w + 1], bit_depth, transfer_type); |
114 | 4.51k | const int b = |
115 | 4.51k | ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0], |
116 | 4.51k | src2[4 * uv_w + 1], bit_depth, transfer_type); |
117 | 4.51k | const int W = RGBToGray(r, g, b); |
118 | 4.51k | dst[0 * uv_w] = (fixed_t)(r - W); |
119 | 4.51k | dst[1 * uv_w] = (fixed_t)(g - W); |
120 | 4.51k | dst[2 * uv_w] = (fixed_t)(b - W); |
121 | 4.51k | dst += 1; |
122 | 4.51k | src1 += 2; |
123 | 4.51k | src2 += 2; |
124 | 4.51k | } while (++i < uv_w); |
125 | 1.08k | } |
126 | | |
127 | 528 | static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) { |
128 | 528 | int i = 0; |
129 | 528 | assert(w > 0); |
130 | 5.63k | do { |
131 | 5.63k | y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]); |
132 | 5.63k | } while (++i < w); |
133 | 528 | } |
134 | | |
135 | | //------------------------------------------------------------------------------ |
136 | | |
137 | 9.82k | static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) { |
138 | 9.82k | const int v0 = (A * 3 + B + 2) >> 2; |
139 | 9.82k | return clip_bit_depth(v0 + W0, bit_depth); |
140 | 9.82k | } |
141 | | |
142 | | //------------------------------------------------------------------------------ |
143 | | |
144 | 15.2k | static WEBP_INLINE int Shift(int v, int shift) { |
145 | 15.2k | return (shift >= 0) ? (v << shift) : (v >> -shift); |
146 | 15.2k | } |
147 | | |
148 | | static void ImportOneRow(const uint8_t* const r_ptr, const uint8_t* const g_ptr, |
149 | | const uint8_t* const b_ptr, int rgb_step, |
150 | | int rgb_bit_depth, int pic_width, |
151 | 500 | fixed_y_t* const dst) { |
152 | | // Convert the rgb_step from a number of bytes to a number of uint8_t or |
153 | | // uint16_t values depending the bit depth. |
154 | 500 | const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step; |
155 | 500 | const int w = (pic_width + 1) & ~1; |
156 | 500 | const int shift = GetPrecisionShift(rgb_bit_depth); |
157 | 500 | const int max_val = (1 << rgb_bit_depth) - 1; |
158 | 500 | int i = 0; |
159 | | |
160 | 500 | if (rgb_bit_depth == 8) { |
161 | 2.30k | do { |
162 | 2.30k | const int off = i * step; |
163 | 2.30k | dst[i + 0 * w] = Shift(r_ptr[off], shift); |
164 | 2.30k | dst[i + 1 * w] = Shift(g_ptr[off], shift); |
165 | 2.30k | dst[i + 2 * w] = Shift(b_ptr[off], shift); |
166 | 2.30k | } while (++i < pic_width); |
167 | 309 | } else if (rgb_bit_depth < 16) { |
168 | 2.69k | do { |
169 | 2.69k | const int off = i * step; |
170 | 2.69k | int r = ((const uint16_t*)r_ptr)[off]; |
171 | 2.69k | int g = ((const uint16_t*)g_ptr)[off]; |
172 | 2.69k | int b = ((const uint16_t*)b_ptr)[off]; |
173 | 2.69k | dst[i + 0 * w] = Shift(r > max_val ? max_val : r, shift); |
174 | 2.69k | dst[i + 1 * w] = Shift(g > max_val ? max_val : g, shift); |
175 | 2.69k | dst[i + 2 * w] = Shift(b > max_val ? max_val : b, shift); |
176 | 2.69k | } while (++i < pic_width); |
177 | 309 | } else { // rgb_bit_depth == 16 |
178 | 0 | do { |
179 | 0 | const int off = i * step; |
180 | 0 | int r = ((const uint16_t*)r_ptr)[off]; |
181 | 0 | int g = ((const uint16_t*)g_ptr)[off]; |
182 | 0 | int b = ((const uint16_t*)b_ptr)[off]; |
183 | 0 | dst[i + 0 * w] = Shift(r, shift); |
184 | 0 | dst[i + 1 * w] = Shift(g, shift); |
185 | 0 | dst[i + 2 * w] = Shift(b, shift); |
186 | 0 | } while (++i < pic_width); |
187 | 0 | } |
188 | | |
189 | 500 | if (pic_width & 1) { // replicate rightmost pixel |
190 | 458 | dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1]; |
191 | 458 | dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1]; |
192 | 458 | dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1]; |
193 | 458 | } |
194 | 500 | } |
195 | | |
196 | | static void InterpolateTwoRows(const fixed_y_t* const best_y, |
197 | | const fixed_t* prev_uv, const fixed_t* cur_uv, |
198 | | const fixed_t* next_uv, int w, fixed_y_t* out1, |
199 | 819 | fixed_y_t* out2, int bit_depth) { |
200 | 819 | const int uv_w = w >> 1; |
201 | 819 | const int len = (w - 1) >> 1; // length to filter |
202 | 819 | int k = 3; |
203 | 3.27k | while (k-- > 0) { // process each R/G/B segments in turn |
204 | | // special boundary case for i==0 |
205 | 2.45k | out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth); |
206 | 2.45k | out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth); |
207 | | |
208 | 2.45k | SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1, |
209 | 2.45k | bit_depth); |
210 | 2.45k | SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1, |
211 | 2.45k | bit_depth); |
212 | | |
213 | | // special boundary case for i == w - 1 when w is even |
214 | 2.45k | if (!(w & 1)) { |
215 | 2.45k | out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1], |
216 | 2.45k | best_y[w - 1 + 0], bit_depth); |
217 | 2.45k | out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1], |
218 | 2.45k | best_y[w - 1 + w], bit_depth); |
219 | 2.45k | } |
220 | 2.45k | out1 += w; |
221 | 2.45k | out2 += w; |
222 | 2.45k | prev_uv += uv_w; |
223 | 2.45k | cur_uv += uv_w; |
224 | 2.45k | next_uv += uv_w; |
225 | 2.45k | } |
226 | 819 | } |
227 | | |
228 | | static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b, |
229 | 7.81k | const int coeffs[4], int sfix) { |
230 | 7.81k | const int64_t srounder = 1LL << (YUV_FIX + sfix - 1); |
231 | 7.81k | const int64_t luma = (int64_t)coeffs[0] * r + (int64_t)coeffs[1] * g + |
232 | 7.81k | (int64_t)coeffs[2] * b + coeffs[3] + srounder; |
233 | 7.81k | return (int)(luma >> (YUV_FIX + sfix)); |
234 | 7.81k | } |
235 | | |
236 | | static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, |
237 | | uint8_t* y_ptr, int y_stride, uint8_t* u_ptr, |
238 | | int u_stride, uint8_t* v_ptr, int v_stride, |
239 | | int rgb_bit_depth, int yuv_bit_depth, int width, |
240 | | int height, |
241 | 78 | const SharpYuvConversionMatrix* yuv_matrix) { |
242 | 78 | int i, j; |
243 | 78 | const fixed_t* const best_uv_base = best_uv; |
244 | 78 | const int w = (width + 1) & ~1; |
245 | 78 | const int h = (height + 1) & ~1; |
246 | 78 | const int uv_w = w >> 1; |
247 | 78 | const int uv_h = h >> 1; |
248 | 78 | const int sfix = GetPrecisionShift(rgb_bit_depth); |
249 | 78 | const int yuv_max = (1 << yuv_bit_depth) - 1; |
250 | | |
251 | 78 | best_uv = best_uv_base; |
252 | 78 | j = 0; |
253 | 500 | do { |
254 | 500 | i = 0; |
255 | 4.99k | do { |
256 | 4.99k | const int off = (i >> 1); |
257 | 4.99k | const int W = best_y[i]; |
258 | 4.99k | const int r = best_uv[off + 0 * uv_w] + W; |
259 | 4.99k | const int g = best_uv[off + 1 * uv_w] + W; |
260 | 4.99k | const int b = best_uv[off + 2 * uv_w] + W; |
261 | 4.99k | const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix); |
262 | 4.99k | if (yuv_bit_depth <= 8) { |
263 | 2.30k | y_ptr[i] = clip_8b(y); |
264 | 2.69k | } else { |
265 | 2.69k | ((uint16_t*)y_ptr)[i] = clip(y, yuv_max); |
266 | 2.69k | } |
267 | 4.99k | } while (++i < width); |
268 | 500 | best_y += w; |
269 | 500 | best_uv += (j & 1) * 3 * uv_w; |
270 | 500 | y_ptr += y_stride; |
271 | 500 | } while (++j < height); |
272 | | |
273 | 78 | best_uv = best_uv_base; |
274 | 78 | j = 0; |
275 | 264 | do { |
276 | 264 | i = 0; |
277 | 1.40k | do { |
278 | | // Note r, g and b values here are off by W, but a constant offset on all |
279 | | // 3 components doesn't change the value of u and v with a YCbCr matrix. |
280 | 1.40k | const int r = best_uv[i + 0 * uv_w]; |
281 | 1.40k | const int g = best_uv[i + 1 * uv_w]; |
282 | 1.40k | const int b = best_uv[i + 2 * uv_w]; |
283 | 1.40k | const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix); |
284 | 1.40k | const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix); |
285 | 1.40k | if (yuv_bit_depth <= 8) { |
286 | 642 | u_ptr[i] = clip_8b(u); |
287 | 642 | v_ptr[i] = clip_8b(v); |
288 | 767 | } else { |
289 | 767 | ((uint16_t*)u_ptr)[i] = clip(u, yuv_max); |
290 | 767 | ((uint16_t*)v_ptr)[i] = clip(v, yuv_max); |
291 | 767 | } |
292 | 1.40k | } while (++i < uv_w); |
293 | 264 | best_uv += 3 * uv_w; |
294 | 264 | u_ptr += u_stride; |
295 | 264 | v_ptr += v_stride; |
296 | 264 | } while (++j < uv_h); |
297 | 78 | return 1; |
298 | 78 | } |
299 | | |
300 | | //------------------------------------------------------------------------------ |
301 | | // Main function |
302 | | |
303 | 78 | static void* SafeMalloc(uint64_t nmemb, size_t size) { |
304 | 78 | const uint64_t total_size = nmemb * (uint64_t)size; |
305 | 78 | if (total_size != (size_t)total_size) return NULL; |
306 | 78 | return malloc((size_t)total_size); |
307 | 78 | } |
308 | | |
309 | | static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, |
310 | | const uint8_t* b_ptr, int rgb_step, int rgb_stride, |
311 | | int rgb_bit_depth, uint8_t* y_ptr, int y_stride, |
312 | | uint8_t* u_ptr, int u_stride, uint8_t* v_ptr, |
313 | | int v_stride, int yuv_bit_depth, int width, |
314 | | int height, |
315 | | const SharpYuvConversionMatrix* yuv_matrix, |
316 | 78 | SharpYuvTransferFunctionType transfer_type) { |
317 | | // we expand the right/bottom border if needed |
318 | 78 | const int w = (width + 1) & ~1; |
319 | 78 | const int h = (height + 1) & ~1; |
320 | 78 | const int uv_w = w >> 1; |
321 | 78 | const int uv_h = h >> 1; |
322 | 78 | const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth); |
323 | 78 | uint64_t prev_diff_y_sum = ~0; |
324 | 78 | int j, iter; |
325 | | |
326 | 78 | const uint64_t tmp_buffer_size = (uint64_t)w * 3 * 2; |
327 | 78 | const uint64_t best_y_base_size = (uint64_t)w * h; |
328 | 78 | const uint64_t target_y_base_size = (uint64_t)w * h; |
329 | 78 | const uint64_t best_rgb_y_size = (uint64_t)w * 2; |
330 | 78 | const uint64_t best_uv_base_size = (uint64_t)uv_w * 3 * uv_h; |
331 | 78 | const uint64_t target_uv_base_size = (uint64_t)uv_w * 3 * uv_h; |
332 | 78 | const uint64_t best_rgb_uv_size = (uint64_t)uv_w * 3; |
333 | 78 | fixed_y_t* const tmp_buffer = (fixed_y_t*)SafeMalloc( |
334 | 78 | (tmp_buffer_size + best_y_base_size + target_y_base_size + |
335 | 78 | best_rgb_y_size) + |
336 | 78 | (best_uv_base_size + target_uv_base_size + best_rgb_uv_size), |
337 | 78 | sizeof(*tmp_buffer)); |
338 | 78 | fixed_y_t *best_y_base, *target_y_base, *best_rgb_y; |
339 | 78 | fixed_t *best_uv_base, *target_uv_base, *best_rgb_uv; |
340 | 78 | fixed_y_t *best_y, *target_y; |
341 | 78 | fixed_t *best_uv, *target_uv; |
342 | 78 | const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h); |
343 | 78 | int ok; |
344 | 78 | assert(w > 0); |
345 | 78 | assert(h > 0); |
346 | 78 | assert(sizeof(fixed_y_t) == sizeof(fixed_t)); |
347 | | |
348 | 78 | if (tmp_buffer == NULL) { |
349 | 0 | ok = 0; |
350 | 0 | goto End; |
351 | 0 | } |
352 | 78 | best_y_base = tmp_buffer + tmp_buffer_size; |
353 | 78 | target_y_base = best_y_base + best_y_base_size; |
354 | 78 | best_rgb_y = target_y_base + target_y_base_size; |
355 | 78 | best_uv_base = (fixed_t*)(best_rgb_y + best_rgb_y_size); |
356 | 78 | target_uv_base = best_uv_base + best_uv_base_size; |
357 | 78 | best_rgb_uv = target_uv_base + target_uv_base_size; |
358 | 78 | best_y = best_y_base; |
359 | 78 | target_y = target_y_base; |
360 | 78 | best_uv = best_uv_base; |
361 | 78 | target_uv = target_uv_base; |
362 | | |
363 | | // Import RGB samples to W/RGB representation. |
364 | 342 | for (j = 0; j < height; j += 2) { |
365 | 264 | const int is_last_row = (j == height - 1); |
366 | 264 | fixed_y_t* const src1 = tmp_buffer + 0 * w; |
367 | 264 | fixed_y_t* const src2 = tmp_buffer + 3 * w; |
368 | | |
369 | | // prepare two rows of input |
370 | 264 | ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width, src1); |
371 | 264 | if (!is_last_row) { |
372 | 236 | ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride, |
373 | 236 | rgb_step, rgb_bit_depth, width, src2); |
374 | 236 | } else { |
375 | 28 | memcpy(src2, src1, 3 * w * sizeof(*src2)); |
376 | 28 | } |
377 | 264 | StoreGray(src1, best_y + 0, w); |
378 | 264 | StoreGray(src2, best_y + w, w); |
379 | | |
380 | 264 | UpdateW(src1, target_y, w, y_bit_depth, transfer_type); |
381 | 264 | UpdateW(src2, target_y + w, w, y_bit_depth, transfer_type); |
382 | 264 | UpdateChroma(src1, src2, target_uv, uv_w, y_bit_depth, transfer_type); |
383 | 264 | memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv)); |
384 | 264 | best_y += 2 * w; |
385 | 264 | best_uv += 3 * uv_w; |
386 | 264 | target_y += 2 * w; |
387 | 264 | target_uv += 3 * uv_w; |
388 | 264 | r_ptr += 2 * rgb_stride; |
389 | 264 | g_ptr += 2 * rgb_stride; |
390 | 264 | b_ptr += 2 * rgb_stride; |
391 | 264 | } |
392 | | |
393 | | // Iterate and resolve clipping conflicts. |
394 | 262 | for (iter = 0; iter < kNumIterations; ++iter) { |
395 | 232 | const fixed_t* cur_uv = best_uv_base; |
396 | 232 | const fixed_t* prev_uv = best_uv_base; |
397 | 232 | uint64_t diff_y_sum = 0; |
398 | | |
399 | 232 | best_y = best_y_base; |
400 | 232 | best_uv = best_uv_base; |
401 | 232 | target_y = target_y_base; |
402 | 232 | target_uv = target_uv_base; |
403 | 232 | j = 0; |
404 | 819 | do { |
405 | 819 | fixed_y_t* const src1 = tmp_buffer + 0 * w; |
406 | 819 | fixed_y_t* const src2 = tmp_buffer + 3 * w; |
407 | 819 | { |
408 | 819 | const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0); |
409 | 819 | InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2, |
410 | 819 | y_bit_depth); |
411 | 819 | prev_uv = cur_uv; |
412 | 819 | cur_uv = next_uv; |
413 | 819 | } |
414 | | |
415 | 819 | UpdateW(src1, best_rgb_y + 0 * w, w, y_bit_depth, transfer_type); |
416 | 819 | UpdateW(src2, best_rgb_y + 1 * w, w, y_bit_depth, transfer_type); |
417 | 819 | UpdateChroma(src1, src2, best_rgb_uv, uv_w, y_bit_depth, transfer_type); |
418 | | |
419 | | // update two rows of Y and one row of RGB |
420 | 819 | diff_y_sum += |
421 | 819 | SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth); |
422 | 819 | SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w); |
423 | | |
424 | 819 | best_y += 2 * w; |
425 | 819 | best_uv += 3 * uv_w; |
426 | 819 | target_y += 2 * w; |
427 | 819 | target_uv += 3 * uv_w; |
428 | 819 | j += 2; |
429 | 819 | } while (j < h); |
430 | | // test exit condition |
431 | 232 | if (iter > 0) { |
432 | 154 | if (diff_y_sum < diff_y_threshold) break; |
433 | 117 | if (diff_y_sum > prev_diff_y_sum) break; |
434 | 117 | } |
435 | 184 | prev_diff_y_sum = diff_y_sum; |
436 | 184 | } |
437 | | |
438 | | // final reconstruction |
439 | 78 | ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr, |
440 | 78 | u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth, |
441 | 78 | width, height, yuv_matrix); |
442 | | |
443 | 78 | End: |
444 | 78 | free(tmp_buffer); |
445 | 78 | return ok; |
446 | 78 | } |
447 | | |
448 | | #if defined(WEBP_USE_THREAD) && !defined(_WIN32) |
449 | | #include <pthread.h> // NOLINT |
450 | | |
451 | | #define LOCK_ACCESS \ |
452 | 78 | static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \ |
453 | 78 | if (pthread_mutex_lock(&sharpyuv_lock)) return |
454 | | #define UNLOCK_ACCESS_AND_RETURN \ |
455 | 78 | do { \ |
456 | 78 | (void)pthread_mutex_unlock(&sharpyuv_lock); \ |
457 | 78 | return; \ |
458 | 78 | } while (0) |
459 | | #else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32)) |
460 | | #define LOCK_ACCESS \ |
461 | | do { \ |
462 | | } while (0) |
463 | | #define UNLOCK_ACCESS_AND_RETURN return |
464 | | #endif // defined(WEBP_USE_THREAD) && !defined(_WIN32) |
465 | | |
466 | | // Hidden exported init function. |
467 | | // By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed, |
468 | | // users can declare it as extern and call it with an alternate VP8CPUInfo |
469 | | // function. |
470 | | extern VP8CPUInfo SharpYuvGetCPUInfo; |
471 | | SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func); |
472 | 78 | void SharpYuvInit(VP8CPUInfo cpu_info_func) { |
473 | 78 | static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used = |
474 | 78 | (VP8CPUInfo)&sharpyuv_last_cpuinfo_used; |
475 | 78 | LOCK_ACCESS; |
476 | | // Only update SharpYuvGetCPUInfo when called from external code to avoid a |
477 | | // race on reading the value in SharpYuvConvert(). |
478 | 78 | if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) { |
479 | 0 | SharpYuvGetCPUInfo = cpu_info_func; |
480 | 0 | } |
481 | 78 | if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) { |
482 | 77 | UNLOCK_ACCESS_AND_RETURN; |
483 | 77 | } |
484 | | |
485 | 1 | SharpYuvInitDsp(); |
486 | 1 | SharpYuvInitGammaTables(); |
487 | | |
488 | 1 | sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo; |
489 | 1 | UNLOCK_ACCESS_AND_RETURN; |
490 | 1 | } |
491 | | |
492 | | int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr, |
493 | | int rgb_step, int rgb_stride, int rgb_bit_depth, |
494 | | void* y_ptr, int y_stride, void* u_ptr, int u_stride, |
495 | | void* v_ptr, int v_stride, int yuv_bit_depth, int width, |
496 | 78 | int height, const SharpYuvConversionMatrix* yuv_matrix) { |
497 | 78 | SharpYuvOptions options; |
498 | 78 | options.yuv_matrix = yuv_matrix; |
499 | 78 | options.transfer_type = kSharpYuvTransferFunctionSrgb; |
500 | 78 | return SharpYuvConvertWithOptions( |
501 | 78 | r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride, |
502 | 78 | u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options); |
503 | 78 | } |
504 | | |
505 | | int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix, |
506 | 0 | SharpYuvOptions* options, int version) { |
507 | 0 | const int major = (version >> 24); |
508 | 0 | const int minor = (version >> 16) & 0xff; |
509 | 0 | if (options == NULL || yuv_matrix == NULL || |
510 | 0 | (major == SHARPYUV_VERSION_MAJOR && major == 0 && |
511 | 0 | minor != SHARPYUV_VERSION_MINOR) || |
512 | 0 | (major != SHARPYUV_VERSION_MAJOR)) { |
513 | 0 | return 0; |
514 | 0 | } |
515 | 0 | options->yuv_matrix = yuv_matrix; |
516 | 0 | options->transfer_type = kSharpYuvTransferFunctionSrgb; |
517 | 0 | return 1; |
518 | 0 | } |
519 | | |
520 | | int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr, |
521 | | const void* b_ptr, int rgb_step, int rgb_stride, |
522 | | int rgb_bit_depth, void* y_ptr, int y_stride, |
523 | | void* u_ptr, int u_stride, void* v_ptr, |
524 | | int v_stride, int yuv_bit_depth, int width, |
525 | 78 | int height, const SharpYuvOptions* options) { |
526 | 78 | const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix; |
527 | 78 | SharpYuvTransferFunctionType transfer_type = options->transfer_type; |
528 | 78 | SharpYuvConversionMatrix scaled_matrix; |
529 | 78 | const int rgb_max = (1 << rgb_bit_depth) - 1; |
530 | 78 | const int rgb_round = 1 << (rgb_bit_depth - 1); |
531 | 78 | const int yuv_max = (1 << yuv_bit_depth) - 1; |
532 | 78 | const int sfix = GetPrecisionShift(rgb_bit_depth); |
533 | | |
534 | 78 | if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX || |
535 | 78 | r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL || |
536 | 78 | u_ptr == NULL || v_ptr == NULL) { |
537 | 0 | return 0; |
538 | 0 | } |
539 | 78 | if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 && |
540 | 0 | rgb_bit_depth != 16) { |
541 | 0 | return 0; |
542 | 0 | } |
543 | 78 | if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) { |
544 | 0 | return 0; |
545 | 0 | } |
546 | 78 | if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) { |
547 | | // Step/stride should be even for uint16_t buffers. |
548 | 0 | return 0; |
549 | 0 | } |
550 | 78 | { |
551 | 78 | const uint64_t yuv_bytes = (yuv_bit_depth > 8) ? 2 : 1; |
552 | 78 | const uint64_t uv_width = (width + 1) / 2; |
553 | 78 | const uint64_t abs_step = |
554 | 78 | (uint64_t)((rgb_step < 0) ? -(int64_t)rgb_step : (int64_t)rgb_step); |
555 | 78 | const uint64_t abs_stride = |
556 | 78 | (uint64_t)((rgb_stride < 0) ? -(int64_t)rgb_stride |
557 | 78 | : (int64_t)rgb_stride); |
558 | 78 | const uint64_t total_rgb_size = (uint64_t)height * abs_stride; |
559 | 78 | const uint64_t uv_height = (height + 1) / 2; |
560 | 78 | const uint64_t total_y_size = (uint64_t)height * y_stride; |
561 | 78 | const uint64_t total_u_size = uv_height * u_stride; |
562 | 78 | const uint64_t total_v_size = uv_height * v_stride; |
563 | | |
564 | 78 | if (y_stride < 0 || (uint64_t)y_stride < (uint64_t)width * yuv_bytes || |
565 | 78 | u_stride < 0 || (uint64_t)u_stride < uv_width * yuv_bytes || |
566 | 78 | v_stride < 0 || (uint64_t)v_stride < uv_width * yuv_bytes) { |
567 | 0 | return 0; |
568 | 0 | } |
569 | 78 | if (abs_step == 0 || abs_stride < (uint64_t)width * abs_step) { |
570 | 0 | return 0; |
571 | 0 | } |
572 | 78 | if (total_rgb_size != (size_t)total_rgb_size || |
573 | 78 | total_y_size != (size_t)total_y_size || |
574 | 78 | total_u_size != (size_t)total_u_size || |
575 | 78 | total_v_size != (size_t)total_v_size) { |
576 | 0 | return 0; |
577 | 0 | } |
578 | 78 | } |
579 | 78 | if (yuv_bit_depth > 8 && |
580 | 49 | (y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) { |
581 | | // Stride should be even for uint16_t buffers. |
582 | 0 | return 0; |
583 | 0 | } |
584 | | // The address of the function pointer is used to avoid a read race. |
585 | 78 | SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo); |
586 | | |
587 | | // Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the |
588 | | // rgb->yuv conversion matrix. |
589 | 78 | if (rgb_bit_depth == yuv_bit_depth) { |
590 | 78 | memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix)); |
591 | 78 | } else { |
592 | 0 | int i; |
593 | 0 | for (i = 0; i < 3; ++i) { |
594 | 0 | scaled_matrix.rgb_to_y[i] = |
595 | 0 | (yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max; |
596 | 0 | scaled_matrix.rgb_to_u[i] = |
597 | 0 | (yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max; |
598 | 0 | scaled_matrix.rgb_to_v[i] = |
599 | 0 | (yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max; |
600 | 0 | } |
601 | 0 | } |
602 | | // Also incorporate precision change scaling. |
603 | 78 | scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix); |
604 | 78 | scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix); |
605 | 78 | scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix); |
606 | | |
607 | 78 | return DoSharpArgbToYuv( |
608 | 78 | (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr, |
609 | 78 | rgb_step, rgb_stride, rgb_bit_depth, (uint8_t*)y_ptr, y_stride, |
610 | 78 | (uint8_t*)u_ptr, u_stride, (uint8_t*)v_ptr, v_stride, yuv_bit_depth, |
611 | 78 | width, height, &scaled_matrix, transfer_type); |
612 | 78 | } |
613 | | |
614 | | //------------------------------------------------------------------------------ |