1 // Copyright 2014 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 // WebPPicture utils for colorspace conversion
11 //
12 // Author: Skal (pascal.massimino@gmail.com)
13
14 #include <assert.h>
15 #include <stdlib.h>
16 #include <math.h>
17
18 #include "sharpyuv/sharpyuv.h"
19 #include "sharpyuv/sharpyuv_csp.h"
20 #include "src/enc/vp8i_enc.h"
21 #include "src/utils/random_utils.h"
22 #include "src/utils/utils.h"
23 #include "src/dsp/dsp.h"
24 #include "src/dsp/lossless.h"
25 #include "src/dsp/yuv.h"
26 #include "src/dsp/cpu.h"
27
28 #if defined(WEBP_USE_THREAD) && !defined(_WIN32)
29 #include <pthread.h>
30 #endif
31
32 // Uncomment to disable gamma-compression during RGB->U/V averaging
33 #define USE_GAMMA_COMPRESSION
34
35 // If defined, use table to compute x / alpha.
36 #define USE_INVERSE_ALPHA_TABLE
37
38 #ifdef WORDS_BIGENDIAN
39 // uint32_t 0xff000000 is 0xff,00,00,00 in memory
40 #define CHANNEL_OFFSET(i) (i)
41 #else
42 // uint32_t 0xff000000 is 0x00,00,00,ff in memory
43 #define CHANNEL_OFFSET(i) (3-(i))
44 #endif
45
46 #define ALPHA_OFFSET CHANNEL_OFFSET(0)
47
48 //------------------------------------------------------------------------------
49 // Detection of non-trivial transparency
50
51 // Returns true if alpha[] has non-0xff values.
CheckNonOpaque(const uint8_t * alpha,int width,int height,int x_step,int y_step)52 static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
53 int x_step, int y_step) {
54 if (alpha == NULL) return 0;
55 WebPInitAlphaProcessing();
56 if (x_step == 1) {
57 for (; height-- > 0; alpha += y_step) {
58 if (WebPHasAlpha8b(alpha, width)) return 1;
59 }
60 } else {
61 for (; height-- > 0; alpha += y_step) {
62 if (WebPHasAlpha32b(alpha, width)) return 1;
63 }
64 }
65 return 0;
66 }
67
68 // Checking for the presence of non-opaque alpha.
WebPPictureHasTransparency(const WebPPicture * picture)69 int WebPPictureHasTransparency(const WebPPicture* picture) {
70 if (picture == NULL) return 0;
71 if (picture->use_argb) {
72 if (picture->argb != NULL) {
73 return CheckNonOpaque((const uint8_t*)picture->argb + ALPHA_OFFSET,
74 picture->width, picture->height,
75 4, picture->argb_stride * sizeof(*picture->argb));
76 }
77 return 0;
78 }
79 return CheckNonOpaque(picture->a, picture->width, picture->height,
80 1, picture->a_stride);
81 }
82
83 //------------------------------------------------------------------------------
84 // Code for gamma correction
85
86 #if defined(USE_GAMMA_COMPRESSION)
87
88 // Gamma correction compensates loss of resolution during chroma subsampling.
89 #define GAMMA_FIX 12 // fixed-point precision for linear values
90 #define GAMMA_TAB_FIX 7 // fixed-point fractional bits precision
91 #define GAMMA_TAB_SIZE (1 << (GAMMA_FIX - GAMMA_TAB_FIX))
92 static const double kGamma = 0.80;
93 static const int kGammaScale = ((1 << GAMMA_FIX) - 1);
94 static const int kGammaTabScale = (1 << GAMMA_TAB_FIX);
95 static const int kGammaTabRounder = (1 << GAMMA_TAB_FIX >> 1);
96
97 static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1];
98 static uint16_t kGammaToLinearTab[256];
99 static volatile int kGammaTablesOk = 0;
100 static void InitGammaTables(void);
101
WEBP_DSP_INIT_FUNC(InitGammaTables)102 WEBP_DSP_INIT_FUNC(InitGammaTables) {
103 if (!kGammaTablesOk) {
104 int v;
105 const double scale = (double)(1 << GAMMA_TAB_FIX) / kGammaScale;
106 const double norm = 1. / 255.;
107 for (v = 0; v <= 255; ++v) {
108 kGammaToLinearTab[v] =
109 (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5);
110 }
111 for (v = 0; v <= GAMMA_TAB_SIZE; ++v) {
112 kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5);
113 }
114 kGammaTablesOk = 1;
115 }
116 }
117
GammaToLinear(uint8_t v)118 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
119 return kGammaToLinearTab[v];
120 }
121
Interpolate(int v)122 static WEBP_INLINE int Interpolate(int v) {
123 const int tab_pos = v >> (GAMMA_TAB_FIX + 2); // integer part
124 const int x = v & ((kGammaTabScale << 2) - 1); // fractional part
125 const int v0 = kLinearToGammaTab[tab_pos];
126 const int v1 = kLinearToGammaTab[tab_pos + 1];
127 const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate
128 assert(tab_pos + 1 < GAMMA_TAB_SIZE + 1);
129 return y;
130 }
131
132 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
133 // U/V value, suitable for RGBToU/V calls.
LinearToGamma(uint32_t base_value,int shift)134 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
135 const int y = Interpolate(base_value << shift); // final uplifted value
136 return (y + kGammaTabRounder) >> GAMMA_TAB_FIX; // descale
137 }
138
139 #else
140
InitGammaTables(void)141 static void InitGammaTables(void) {}
GammaToLinear(uint8_t v)142 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
LinearToGamma(uint32_t base_value,int shift)143 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
144 return (int)(base_value << shift);
145 }
146
147 #endif // USE_GAMMA_COMPRESSION
148
149 //------------------------------------------------------------------------------
150 // RGB -> YUV conversion
151
RGBToY(int r,int g,int b,VP8Random * const rg)152 static int RGBToY(int r, int g, int b, VP8Random* const rg) {
153 return (rg == NULL) ? VP8RGBToY(r, g, b, YUV_HALF)
154 : VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX));
155 }
156
RGBToU(int r,int g,int b,VP8Random * const rg)157 static int RGBToU(int r, int g, int b, VP8Random* const rg) {
158 return (rg == NULL) ? VP8RGBToU(r, g, b, YUV_HALF << 2)
159 : VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
160 }
161
RGBToV(int r,int g,int b,VP8Random * const rg)162 static int RGBToV(int r, int g, int b, VP8Random* const rg) {
163 return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2)
164 : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
165 }
166
167 //------------------------------------------------------------------------------
168 // Sharp RGB->YUV conversion
169
170 static const int kMinDimensionIterativeConversion = 4;
171
172 //------------------------------------------------------------------------------
173 // Main function
174
PreprocessARGB(const uint8_t * r_ptr,const uint8_t * g_ptr,const uint8_t * b_ptr,int step,int rgb_stride,WebPPicture * const picture)175 static int PreprocessARGB(const uint8_t* r_ptr,
176 const uint8_t* g_ptr,
177 const uint8_t* b_ptr,
178 int step, int rgb_stride,
179 WebPPicture* const picture) {
180 const int ok = SharpYuvConvert(
181 r_ptr, g_ptr, b_ptr, step, rgb_stride, /*rgb_bit_depth=*/8,
182 picture->y, picture->y_stride, picture->u, picture->uv_stride, picture->v,
183 picture->uv_stride, /*yuv_bit_depth=*/8, picture->width,
184 picture->height, SharpYuvGetConversionMatrix(kSharpYuvMatrixWebp));
185 if (!ok) {
186 return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
187 }
188 return ok;
189 }
190
191 //------------------------------------------------------------------------------
192 // "Fast" regular RGB->YUV
193
194 #define SUM4(ptr, step) LinearToGamma( \
195 GammaToLinear((ptr)[0]) + \
196 GammaToLinear((ptr)[(step)]) + \
197 GammaToLinear((ptr)[rgb_stride]) + \
198 GammaToLinear((ptr)[rgb_stride + (step)]), 0) \
199
200 #define SUM2(ptr) \
201 LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1)
202
203 #define SUM2ALPHA(ptr) ((ptr)[0] + (ptr)[rgb_stride])
204 #define SUM4ALPHA(ptr) (SUM2ALPHA(ptr) + SUM2ALPHA((ptr) + 4))
205
206 #if defined(USE_INVERSE_ALPHA_TABLE)
207
208 static const int kAlphaFix = 19;
209 // Following table is (1 << kAlphaFix) / a. The (v * kInvAlpha[a]) >> kAlphaFix
210 // formula is then equal to v / a in most (99.6%) cases. Note that this table
211 // and constant are adjusted very tightly to fit 32b arithmetic.
212 // In particular, they use the fact that the operands for 'v / a' are actually
213 // derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3
214 // with ai in [0..255] and pi in [0..1<<GAMMA_FIX). The constraint to avoid
215 // overflow is: GAMMA_FIX + kAlphaFix <= 31.
216 static const uint32_t kInvAlpha[4 * 0xff + 1] = {
217 0, /* alpha = 0 */
218 524288, 262144, 174762, 131072, 104857, 87381, 74898, 65536,
219 58254, 52428, 47662, 43690, 40329, 37449, 34952, 32768,
220 30840, 29127, 27594, 26214, 24966, 23831, 22795, 21845,
221 20971, 20164, 19418, 18724, 18078, 17476, 16912, 16384,
222 15887, 15420, 14979, 14563, 14169, 13797, 13443, 13107,
223 12787, 12483, 12192, 11915, 11650, 11397, 11155, 10922,
224 10699, 10485, 10280, 10082, 9892, 9709, 9532, 9362,
225 9198, 9039, 8886, 8738, 8594, 8456, 8322, 8192,
226 8065, 7943, 7825, 7710, 7598, 7489, 7384, 7281,
227 7182, 7084, 6990, 6898, 6808, 6721, 6636, 6553,
228 6472, 6393, 6316, 6241, 6168, 6096, 6026, 5957,
229 5890, 5825, 5761, 5698, 5637, 5577, 5518, 5461,
230 5405, 5349, 5295, 5242, 5190, 5140, 5090, 5041,
231 4993, 4946, 4899, 4854, 4809, 4766, 4723, 4681,
232 4639, 4599, 4559, 4519, 4481, 4443, 4405, 4369,
233 4332, 4297, 4262, 4228, 4194, 4161, 4128, 4096,
234 4064, 4032, 4002, 3971, 3942, 3912, 3883, 3855,
235 3826, 3799, 3771, 3744, 3718, 3692, 3666, 3640,
236 3615, 3591, 3566, 3542, 3518, 3495, 3472, 3449,
237 3426, 3404, 3382, 3360, 3339, 3318, 3297, 3276,
238 3256, 3236, 3216, 3196, 3177, 3158, 3139, 3120,
239 3102, 3084, 3066, 3048, 3030, 3013, 2995, 2978,
240 2962, 2945, 2928, 2912, 2896, 2880, 2864, 2849,
241 2833, 2818, 2803, 2788, 2774, 2759, 2744, 2730,
242 2716, 2702, 2688, 2674, 2661, 2647, 2634, 2621,
243 2608, 2595, 2582, 2570, 2557, 2545, 2532, 2520,
244 2508, 2496, 2484, 2473, 2461, 2449, 2438, 2427,
245 2416, 2404, 2394, 2383, 2372, 2361, 2351, 2340,
246 2330, 2319, 2309, 2299, 2289, 2279, 2269, 2259,
247 2250, 2240, 2231, 2221, 2212, 2202, 2193, 2184,
248 2175, 2166, 2157, 2148, 2139, 2131, 2122, 2114,
249 2105, 2097, 2088, 2080, 2072, 2064, 2056, 2048,
250 2040, 2032, 2024, 2016, 2008, 2001, 1993, 1985,
251 1978, 1971, 1963, 1956, 1949, 1941, 1934, 1927,
252 1920, 1913, 1906, 1899, 1892, 1885, 1879, 1872,
253 1865, 1859, 1852, 1846, 1839, 1833, 1826, 1820,
254 1814, 1807, 1801, 1795, 1789, 1783, 1777, 1771,
255 1765, 1759, 1753, 1747, 1741, 1736, 1730, 1724,
256 1718, 1713, 1707, 1702, 1696, 1691, 1685, 1680,
257 1675, 1669, 1664, 1659, 1653, 1648, 1643, 1638,
258 1633, 1628, 1623, 1618, 1613, 1608, 1603, 1598,
259 1593, 1588, 1583, 1579, 1574, 1569, 1565, 1560,
260 1555, 1551, 1546, 1542, 1537, 1533, 1528, 1524,
261 1519, 1515, 1510, 1506, 1502, 1497, 1493, 1489,
262 1485, 1481, 1476, 1472, 1468, 1464, 1460, 1456,
263 1452, 1448, 1444, 1440, 1436, 1432, 1428, 1424,
264 1420, 1416, 1413, 1409, 1405, 1401, 1398, 1394,
265 1390, 1387, 1383, 1379, 1376, 1372, 1368, 1365,
266 1361, 1358, 1354, 1351, 1347, 1344, 1340, 1337,
267 1334, 1330, 1327, 1323, 1320, 1317, 1314, 1310,
268 1307, 1304, 1300, 1297, 1294, 1291, 1288, 1285,
269 1281, 1278, 1275, 1272, 1269, 1266, 1263, 1260,
270 1257, 1254, 1251, 1248, 1245, 1242, 1239, 1236,
271 1233, 1230, 1227, 1224, 1222, 1219, 1216, 1213,
272 1210, 1208, 1205, 1202, 1199, 1197, 1194, 1191,
273 1188, 1186, 1183, 1180, 1178, 1175, 1172, 1170,
274 1167, 1165, 1162, 1159, 1157, 1154, 1152, 1149,
275 1147, 1144, 1142, 1139, 1137, 1134, 1132, 1129,
276 1127, 1125, 1122, 1120, 1117, 1115, 1113, 1110,
277 1108, 1106, 1103, 1101, 1099, 1096, 1094, 1092,
278 1089, 1087, 1085, 1083, 1081, 1078, 1076, 1074,
279 1072, 1069, 1067, 1065, 1063, 1061, 1059, 1057,
280 1054, 1052, 1050, 1048, 1046, 1044, 1042, 1040,
281 1038, 1036, 1034, 1032, 1030, 1028, 1026, 1024,
282 1022, 1020, 1018, 1016, 1014, 1012, 1010, 1008,
283 1006, 1004, 1002, 1000, 998, 996, 994, 992,
284 991, 989, 987, 985, 983, 981, 979, 978,
285 976, 974, 972, 970, 969, 967, 965, 963,
286 961, 960, 958, 956, 954, 953, 951, 949,
287 948, 946, 944, 942, 941, 939, 937, 936,
288 934, 932, 931, 929, 927, 926, 924, 923,
289 921, 919, 918, 916, 914, 913, 911, 910,
290 908, 907, 905, 903, 902, 900, 899, 897,
291 896, 894, 893, 891, 890, 888, 887, 885,
292 884, 882, 881, 879, 878, 876, 875, 873,
293 872, 870, 869, 868, 866, 865, 863, 862,
294 860, 859, 858, 856, 855, 853, 852, 851,
295 849, 848, 846, 845, 844, 842, 841, 840,
296 838, 837, 836, 834, 833, 832, 830, 829,
297 828, 826, 825, 824, 823, 821, 820, 819,
298 817, 816, 815, 814, 812, 811, 810, 809,
299 807, 806, 805, 804, 802, 801, 800, 799,
300 798, 796, 795, 794, 793, 791, 790, 789,
301 788, 787, 786, 784, 783, 782, 781, 780,
302 779, 777, 776, 775, 774, 773, 772, 771,
303 769, 768, 767, 766, 765, 764, 763, 762,
304 760, 759, 758, 757, 756, 755, 754, 753,
305 752, 751, 750, 748, 747, 746, 745, 744,
306 743, 742, 741, 740, 739, 738, 737, 736,
307 735, 734, 733, 732, 731, 730, 729, 728,
308 727, 726, 725, 724, 723, 722, 721, 720,
309 719, 718, 717, 716, 715, 714, 713, 712,
310 711, 710, 709, 708, 707, 706, 705, 704,
311 703, 702, 701, 700, 699, 699, 698, 697,
312 696, 695, 694, 693, 692, 691, 690, 689,
313 688, 688, 687, 686, 685, 684, 683, 682,
314 681, 680, 680, 679, 678, 677, 676, 675,
315 674, 673, 673, 672, 671, 670, 669, 668,
316 667, 667, 666, 665, 664, 663, 662, 661,
317 661, 660, 659, 658, 657, 657, 656, 655,
318 654, 653, 652, 652, 651, 650, 649, 648,
319 648, 647, 646, 645, 644, 644, 643, 642,
320 641, 640, 640, 639, 638, 637, 637, 636,
321 635, 634, 633, 633, 632, 631, 630, 630,
322 629, 628, 627, 627, 626, 625, 624, 624,
323 623, 622, 621, 621, 620, 619, 618, 618,
324 617, 616, 616, 615, 614, 613, 613, 612,
325 611, 611, 610, 609, 608, 608, 607, 606,
326 606, 605, 604, 604, 603, 602, 601, 601,
327 600, 599, 599, 598, 597, 597, 596, 595,
328 595, 594, 593, 593, 592, 591, 591, 590,
329 589, 589, 588, 587, 587, 586, 585, 585,
330 584, 583, 583, 582, 581, 581, 580, 579,
331 579, 578, 578, 577, 576, 576, 575, 574,
332 574, 573, 572, 572, 571, 571, 570, 569,
333 569, 568, 568, 567, 566, 566, 565, 564,
334 564, 563, 563, 562, 561, 561, 560, 560,
335 559, 558, 558, 557, 557, 556, 555, 555,
336 554, 554, 553, 553, 552, 551, 551, 550,
337 550, 549, 548, 548, 547, 547, 546, 546,
338 545, 544, 544, 543, 543, 542, 542, 541,
339 541, 540, 539, 539, 538, 538, 537, 537,
340 536, 536, 535, 534, 534, 533, 533, 532,
341 532, 531, 531, 530, 530, 529, 529, 528,
342 527, 527, 526, 526, 525, 525, 524, 524,
343 523, 523, 522, 522, 521, 521, 520, 520,
344 519, 519, 518, 518, 517, 517, 516, 516,
345 515, 515, 514, 514
346 };
347
348 // Note that LinearToGamma() expects the values to be premultiplied by 4,
349 // so we incorporate this factor 4 inside the DIVIDE_BY_ALPHA macro directly.
350 #define DIVIDE_BY_ALPHA(sum, a) (((sum) * kInvAlpha[(a)]) >> (kAlphaFix - 2))
351
352 #else
353
354 #define DIVIDE_BY_ALPHA(sum, a) (4 * (sum) / (a))
355
356 #endif // USE_INVERSE_ALPHA_TABLE
357
LinearToGammaWeighted(const uint8_t * src,const uint8_t * a_ptr,uint32_t total_a,int step,int rgb_stride)358 static WEBP_INLINE int LinearToGammaWeighted(const uint8_t* src,
359 const uint8_t* a_ptr,
360 uint32_t total_a, int step,
361 int rgb_stride) {
362 const uint32_t sum =
363 a_ptr[0] * GammaToLinear(src[0]) +
364 a_ptr[step] * GammaToLinear(src[step]) +
365 a_ptr[rgb_stride] * GammaToLinear(src[rgb_stride]) +
366 a_ptr[rgb_stride + step] * GammaToLinear(src[rgb_stride + step]);
367 assert(total_a > 0 && total_a <= 4 * 0xff);
368 #if defined(USE_INVERSE_ALPHA_TABLE)
369 assert((uint64_t)sum * kInvAlpha[total_a] < ((uint64_t)1 << 32));
370 #endif
371 return LinearToGamma(DIVIDE_BY_ALPHA(sum, total_a), 0);
372 }
373
ConvertRowToY(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,uint8_t * const dst_y,int width,VP8Random * const rg)374 static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr,
375 const uint8_t* const g_ptr,
376 const uint8_t* const b_ptr,
377 int step,
378 uint8_t* const dst_y,
379 int width,
380 VP8Random* const rg) {
381 int i, j;
382 for (i = 0, j = 0; i < width; i += 1, j += step) {
383 dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg);
384 }
385 }
386
AccumulateRGBA(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,const uint8_t * const a_ptr,int rgb_stride,uint16_t * dst,int width)387 static WEBP_INLINE void AccumulateRGBA(const uint8_t* const r_ptr,
388 const uint8_t* const g_ptr,
389 const uint8_t* const b_ptr,
390 const uint8_t* const a_ptr,
391 int rgb_stride,
392 uint16_t* dst, int width) {
393 int i, j;
394 // we loop over 2x2 blocks and produce one R/G/B/A value for each.
395 for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * 4, dst += 4) {
396 const uint32_t a = SUM4ALPHA(a_ptr + j);
397 int r, g, b;
398 if (a == 4 * 0xff || a == 0) {
399 r = SUM4(r_ptr + j, 4);
400 g = SUM4(g_ptr + j, 4);
401 b = SUM4(b_ptr + j, 4);
402 } else {
403 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride);
404 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride);
405 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride);
406 }
407 dst[0] = r;
408 dst[1] = g;
409 dst[2] = b;
410 dst[3] = a;
411 }
412 if (width & 1) {
413 const uint32_t a = 2u * SUM2ALPHA(a_ptr + j);
414 int r, g, b;
415 if (a == 4 * 0xff || a == 0) {
416 r = SUM2(r_ptr + j);
417 g = SUM2(g_ptr + j);
418 b = SUM2(b_ptr + j);
419 } else {
420 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride);
421 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride);
422 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride);
423 }
424 dst[0] = r;
425 dst[1] = g;
426 dst[2] = b;
427 dst[3] = a;
428 }
429 }
430
AccumulateRGB(const uint8_t * const r_ptr,const uint8_t * const g_ptr,const uint8_t * const b_ptr,int step,int rgb_stride,uint16_t * dst,int width)431 static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr,
432 const uint8_t* const g_ptr,
433 const uint8_t* const b_ptr,
434 int step, int rgb_stride,
435 uint16_t* dst, int width) {
436 int i, j;
437 for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * step, dst += 4) {
438 dst[0] = SUM4(r_ptr + j, step);
439 dst[1] = SUM4(g_ptr + j, step);
440 dst[2] = SUM4(b_ptr + j, step);
441 // MemorySanitizer may raise false positives with data that passes through
442 // RGBA32PackedToPlanar_16b_SSE41() due to incorrect modeling of shuffles.
443 // See https://crbug.com/webp/573.
444 #ifdef WEBP_MSAN
445 dst[3] = 0;
446 #endif
447 }
448 if (width & 1) {
449 dst[0] = SUM2(r_ptr + j);
450 dst[1] = SUM2(g_ptr + j);
451 dst[2] = SUM2(b_ptr + j);
452 #ifdef WEBP_MSAN
453 dst[3] = 0;
454 #endif
455 }
456 }
457
ConvertRowsToUV(const uint16_t * rgb,uint8_t * const dst_u,uint8_t * const dst_v,int width,VP8Random * const rg)458 static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb,
459 uint8_t* const dst_u,
460 uint8_t* const dst_v,
461 int width,
462 VP8Random* const rg) {
463 int i;
464 for (i = 0; i < width; i += 1, rgb += 4) {
465 const int r = rgb[0], g = rgb[1], b = rgb[2];
466 dst_u[i] = RGBToU(r, g, b, rg);
467 dst_v[i] = RGBToV(r, g, b, rg);
468 }
469 }
470
471 extern void SharpYuvInit(VP8CPUInfo cpu_info_func);
472
ImportYUVAFromRGBA(const uint8_t * r_ptr,const uint8_t * g_ptr,const uint8_t * b_ptr,const uint8_t * a_ptr,int step,int rgb_stride,float dithering,int use_iterative_conversion,WebPPicture * const picture)473 static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
474 const uint8_t* g_ptr,
475 const uint8_t* b_ptr,
476 const uint8_t* a_ptr,
477 int step, // bytes per pixel
478 int rgb_stride, // bytes per scanline
479 float dithering,
480 int use_iterative_conversion,
481 WebPPicture* const picture) {
482 int y;
483 const int width = picture->width;
484 const int height = picture->height;
485 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
486 const int is_rgb = (r_ptr < b_ptr); // otherwise it's bgr
487
488 picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
489 picture->use_argb = 0;
490
491 // disable smart conversion if source is too small (overkill).
492 if (width < kMinDimensionIterativeConversion ||
493 height < kMinDimensionIterativeConversion) {
494 use_iterative_conversion = 0;
495 }
496
497 if (!WebPPictureAllocYUVA(picture)) {
498 return 0;
499 }
500 if (has_alpha) {
501 assert(step == 4);
502 #if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE)
503 assert(kAlphaFix + GAMMA_FIX <= 31);
504 #endif
505 }
506
507 if (use_iterative_conversion) {
508 SharpYuvInit(VP8GetCPUInfo);
509 if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
510 return 0;
511 }
512 if (has_alpha) {
513 WebPExtractAlpha(a_ptr, rgb_stride, width, height,
514 picture->a, picture->a_stride);
515 }
516 } else {
517 const int uv_width = (width + 1) >> 1;
518 int use_dsp = (step == 3); // use special function in this case
519 // temporary storage for accumulated R/G/B values during conversion to U/V
520 uint16_t* const tmp_rgb =
521 (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb));
522 uint8_t* dst_y = picture->y;
523 uint8_t* dst_u = picture->u;
524 uint8_t* dst_v = picture->v;
525 uint8_t* dst_a = picture->a;
526
527 VP8Random base_rg;
528 VP8Random* rg = NULL;
529 if (dithering > 0.) {
530 VP8InitRandom(&base_rg, dithering);
531 rg = &base_rg;
532 use_dsp = 0; // can't use dsp in this case
533 }
534 WebPInitConvertARGBToYUV();
535 InitGammaTables();
536
537 if (tmp_rgb == NULL) return 0; // malloc error
538
539 // Downsample Y/U/V planes, two rows at a time
540 for (y = 0; y < (height >> 1); ++y) {
541 int rows_have_alpha = has_alpha;
542 if (use_dsp) {
543 if (is_rgb) {
544 WebPConvertRGB24ToY(r_ptr, dst_y, width);
545 WebPConvertRGB24ToY(r_ptr + rgb_stride,
546 dst_y + picture->y_stride, width);
547 } else {
548 WebPConvertBGR24ToY(b_ptr, dst_y, width);
549 WebPConvertBGR24ToY(b_ptr + rgb_stride,
550 dst_y + picture->y_stride, width);
551 }
552 } else {
553 ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
554 ConvertRowToY(r_ptr + rgb_stride,
555 g_ptr + rgb_stride,
556 b_ptr + rgb_stride, step,
557 dst_y + picture->y_stride, width, rg);
558 }
559 dst_y += 2 * picture->y_stride;
560 if (has_alpha) {
561 rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2,
562 dst_a, picture->a_stride);
563 dst_a += 2 * picture->a_stride;
564 }
565 // Collect averaged R/G/B(/A)
566 if (!rows_have_alpha) {
567 AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width);
568 } else {
569 AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width);
570 }
571 // Convert to U/V
572 if (rg == NULL) {
573 WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
574 } else {
575 ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
576 }
577 dst_u += picture->uv_stride;
578 dst_v += picture->uv_stride;
579 r_ptr += 2 * rgb_stride;
580 b_ptr += 2 * rgb_stride;
581 g_ptr += 2 * rgb_stride;
582 if (has_alpha) a_ptr += 2 * rgb_stride;
583 }
584 if (height & 1) { // extra last row
585 int row_has_alpha = has_alpha;
586 if (use_dsp) {
587 if (r_ptr < b_ptr) {
588 WebPConvertRGB24ToY(r_ptr, dst_y, width);
589 } else {
590 WebPConvertBGR24ToY(b_ptr, dst_y, width);
591 }
592 } else {
593 ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
594 }
595 if (row_has_alpha) {
596 row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0);
597 }
598 // Collect averaged R/G/B(/A)
599 if (!row_has_alpha) {
600 // Collect averaged R/G/B
601 AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0,
602 tmp_rgb, width);
603 } else {
604 AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0,
605 tmp_rgb, width);
606 }
607 if (rg == NULL) {
608 WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
609 } else {
610 ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
611 }
612 }
613 WebPSafeFree(tmp_rgb);
614 }
615 return 1;
616 }
617
618 #undef SUM4
619 #undef SUM2
620 #undef SUM4ALPHA
621 #undef SUM2ALPHA
622
623 //------------------------------------------------------------------------------
624 // call for ARGB->YUVA conversion
625
PictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace,float dithering,int use_iterative_conversion)626 static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace,
627 float dithering, int use_iterative_conversion) {
628 if (picture == NULL) return 0;
629 if (picture->argb == NULL) {
630 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
631 } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
632 return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
633 } else {
634 const uint8_t* const argb = (const uint8_t*)picture->argb;
635 const uint8_t* const a = argb + CHANNEL_OFFSET(0);
636 const uint8_t* const r = argb + CHANNEL_OFFSET(1);
637 const uint8_t* const g = argb + CHANNEL_OFFSET(2);
638 const uint8_t* const b = argb + CHANNEL_OFFSET(3);
639
640 picture->colorspace = WEBP_YUV420;
641 return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride,
642 dithering, use_iterative_conversion, picture);
643 }
644 }
645
WebPPictureARGBToYUVADithered(WebPPicture * picture,WebPEncCSP colorspace,float dithering)646 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
647 float dithering) {
648 return PictureARGBToYUVA(picture, colorspace, dithering, 0);
649 }
650
WebPPictureARGBToYUVA(WebPPicture * picture,WebPEncCSP colorspace)651 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
652 return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
653 }
654
WebPPictureSharpARGBToYUVA(WebPPicture * picture)655 int WebPPictureSharpARGBToYUVA(WebPPicture* picture) {
656 return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1);
657 }
658 // for backward compatibility
WebPPictureSmartARGBToYUVA(WebPPicture * picture)659 int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
660 return WebPPictureSharpARGBToYUVA(picture);
661 }
662
663 //------------------------------------------------------------------------------
664 // call for YUVA -> ARGB conversion
665
WebPPictureYUVAToARGB(WebPPicture * picture)666 int WebPPictureYUVAToARGB(WebPPicture* picture) {
667 if (picture == NULL) return 0;
668 if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
669 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
670 }
671 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
672 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
673 }
674 if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
675 return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
676 }
677 // Allocate a new argb buffer (discarding the previous one).
678 if (!WebPPictureAllocARGB(picture)) return 0;
679 picture->use_argb = 1;
680
681 // Convert
682 {
683 int y;
684 const int width = picture->width;
685 const int height = picture->height;
686 const int argb_stride = 4 * picture->argb_stride;
687 uint8_t* dst = (uint8_t*)picture->argb;
688 const uint8_t* cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
689 WebPUpsampleLinePairFunc upsample =
690 WebPGetLinePairConverter(ALPHA_OFFSET > 0);
691
692 // First row, with replicated top samples.
693 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
694 cur_y += picture->y_stride;
695 dst += argb_stride;
696 // Center rows.
697 for (y = 1; y + 1 < height; y += 2) {
698 const uint8_t* const top_u = cur_u;
699 const uint8_t* const top_v = cur_v;
700 cur_u += picture->uv_stride;
701 cur_v += picture->uv_stride;
702 upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
703 dst, dst + argb_stride, width);
704 cur_y += 2 * picture->y_stride;
705 dst += 2 * argb_stride;
706 }
707 // Last row (if needed), with replicated bottom samples.
708 if (height > 1 && !(height & 1)) {
709 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
710 }
711 // Insert alpha values if needed, in replacement for the default 0xff ones.
712 if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
713 for (y = 0; y < height; ++y) {
714 uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
715 const uint8_t* const src = picture->a + y * picture->a_stride;
716 int x;
717 for (x = 0; x < width; ++x) {
718 argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
719 }
720 }
721 }
722 }
723 return 1;
724 }
725
726 //------------------------------------------------------------------------------
727 // automatic import / conversion
728
Import(WebPPicture * const picture,const uint8_t * rgb,int rgb_stride,int step,int swap_rb,int import_alpha)729 static int Import(WebPPicture* const picture,
730 const uint8_t* rgb, int rgb_stride,
731 int step, int swap_rb, int import_alpha) {
732 int y;
733 // swap_rb -> b,g,r,a , !swap_rb -> r,g,b,a
734 const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0);
735 const uint8_t* g_ptr = rgb + 1;
736 const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2);
737 const int width = picture->width;
738 const int height = picture->height;
739
740 if (abs(rgb_stride) < (import_alpha ? 4 : 3) * width) return 0;
741
742 if (!picture->use_argb) {
743 const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
744 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
745 0.f /* no dithering */, 0, picture);
746 }
747 if (!WebPPictureAlloc(picture)) return 0;
748
749 VP8LDspInit();
750 WebPInitAlphaProcessing();
751
752 if (import_alpha) {
753 // dst[] byte order is {a,r,g,b} for big-endian, {b,g,r,a} for little endian
754 uint32_t* dst = picture->argb;
755 const int do_copy = (ALPHA_OFFSET == 3) && swap_rb;
756 assert(step == 4);
757 if (do_copy) {
758 for (y = 0; y < height; ++y) {
759 memcpy(dst, rgb, width * 4);
760 rgb += rgb_stride;
761 dst += picture->argb_stride;
762 }
763 } else {
764 for (y = 0; y < height; ++y) {
765 #ifdef WORDS_BIGENDIAN
766 // BGRA or RGBA input order.
767 const uint8_t* a_ptr = rgb + 3;
768 WebPPackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst);
769 r_ptr += rgb_stride;
770 g_ptr += rgb_stride;
771 b_ptr += rgb_stride;
772 #else
773 // RGBA input order. Need to swap R and B.
774 VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst);
775 #endif
776 rgb += rgb_stride;
777 dst += picture->argb_stride;
778 }
779 }
780 } else {
781 uint32_t* dst = picture->argb;
782 assert(step >= 3);
783 for (y = 0; y < height; ++y) {
784 WebPPackRGB(r_ptr, g_ptr, b_ptr, width, step, dst);
785 r_ptr += rgb_stride;
786 g_ptr += rgb_stride;
787 b_ptr += rgb_stride;
788 dst += picture->argb_stride;
789 }
790 }
791 return 1;
792 }
793
794 // Public API
795
796 #if !defined(WEBP_REDUCE_CSP)
797
WebPPictureImportBGR(WebPPicture * picture,const uint8_t * bgr,int bgr_stride)798 int WebPPictureImportBGR(WebPPicture* picture,
799 const uint8_t* bgr, int bgr_stride) {
800 return (picture != NULL && bgr != NULL)
801 ? Import(picture, bgr, bgr_stride, 3, 1, 0)
802 : 0;
803 }
804
WebPPictureImportBGRA(WebPPicture * picture,const uint8_t * bgra,int bgra_stride)805 int WebPPictureImportBGRA(WebPPicture* picture,
806 const uint8_t* bgra, int bgra_stride) {
807 return (picture != NULL && bgra != NULL)
808 ? Import(picture, bgra, bgra_stride, 4, 1, 1)
809 : 0;
810 }
811
812
WebPPictureImportBGRX(WebPPicture * picture,const uint8_t * bgrx,int bgrx_stride)813 int WebPPictureImportBGRX(WebPPicture* picture,
814 const uint8_t* bgrx, int bgrx_stride) {
815 return (picture != NULL && bgrx != NULL)
816 ? Import(picture, bgrx, bgrx_stride, 4, 1, 0)
817 : 0;
818 }
819
820 #endif // WEBP_REDUCE_CSP
821
WebPPictureImportRGB(WebPPicture * picture,const uint8_t * rgb,int rgb_stride)822 int WebPPictureImportRGB(WebPPicture* picture,
823 const uint8_t* rgb, int rgb_stride) {
824 return (picture != NULL && rgb != NULL)
825 ? Import(picture, rgb, rgb_stride, 3, 0, 0)
826 : 0;
827 }
828
WebPPictureImportRGBA(WebPPicture * picture,const uint8_t * rgba,int rgba_stride)829 int WebPPictureImportRGBA(WebPPicture* picture,
830 const uint8_t* rgba, int rgba_stride) {
831 return (picture != NULL && rgba != NULL)
832 ? Import(picture, rgba, rgba_stride, 4, 0, 1)
833 : 0;
834 }
835
WebPPictureImportRGBX(WebPPicture * picture,const uint8_t * rgbx,int rgbx_stride)836 int WebPPictureImportRGBX(WebPPicture* picture,
837 const uint8_t* rgbx, int rgbx_stride) {
838 return (picture != NULL && rgbx != NULL)
839 ? Import(picture, rgbx, rgbx_stride, 4, 0, 0)
840 : 0;
841 }
842
843 //------------------------------------------------------------------------------
844