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