go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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 //------------------------------------------------------------------------------