• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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