Home | History | Annotate | Download | only in enc
      1 // Copyright 2011 Google Inc.
      2 //
      3 // This code is licensed under the same terms as WebM:
      4 //  Software License Agreement:  http://www.webmproject.org/license/software/
      5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
      6 // -----------------------------------------------------------------------------
      7 //
      8 // WebPPicture utils: colorspace conversion, crop, ...
      9 //
     10 // Author: Skal (pascal.massimino (at) gmail.com)
     11 
     12 #include <assert.h>
     13 #include <stdlib.h>
     14 #include "vp8enci.h"
     15 
     16 #if defined(__cplusplus) || defined(c_plusplus)
     17 extern "C" {
     18 #endif
     19 
     20 //-----------------------------------------------------------------------------
     21 // WebPPicture
     22 //-----------------------------------------------------------------------------
     23 
     24 int WebPPictureAlloc(WebPPicture* const picture) {
     25   if (picture) {
     26     const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
     27     const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
     28     const int width = picture->width;
     29     const int height = picture->height;
     30     const int y_stride = width;
     31     const int uv_width = (width + 1) / 2;
     32     const int uv_height = (height + 1) / 2;
     33     const int uv_stride = uv_width;
     34     int uv0_stride = 0;
     35     int a_width, a_stride;
     36     uint64_t y_size, uv_size, uv0_size, a_size, total_size;
     37     uint8_t* mem;
     38 
     39     // U/V
     40     switch (uv_csp) {
     41       case WEBP_YUV420:
     42         break;
     43 #ifdef WEBP_EXPERIMENTAL_FEATURES
     44       case WEBP_YUV400:    // for now, we'll just reset the U/V samples
     45         break;
     46       case WEBP_YUV422:
     47         uv0_stride = uv_width;
     48         break;
     49       case WEBP_YUV444:
     50         uv0_stride = width;
     51         break;
     52 #endif
     53       default:
     54         return 0;
     55     }
     56     uv0_size = height * uv0_stride;
     57 
     58     // alpha
     59     a_width = has_alpha ? width : 0;
     60     a_stride = a_width;
     61     y_size = (uint64_t)y_stride * height;
     62     uv_size = (uint64_t)uv_stride * uv_height;
     63     a_size =  (uint64_t)a_stride * height;
     64 
     65     total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
     66 
     67     // Security and validation checks
     68     if (width <= 0 || height <= 0 ||       // check for luma/alpha param error
     69         uv_width < 0 || uv_height < 0 ||   // check for u/v param error
     70         y_size >= (1ULL << 40) ||            // check for reasonable global size
     71         (size_t)total_size != total_size) {  // check for overflow on 32bit
     72       return 0;
     73     }
     74     picture->y_stride  = y_stride;
     75     picture->uv_stride = uv_stride;
     76     picture->a_stride  = a_stride;
     77     picture->uv0_stride  = uv0_stride;
     78     WebPPictureFree(picture);   // erase previous buffer
     79     mem = (uint8_t*)malloc((size_t)total_size);
     80     if (mem == NULL) return 0;
     81 
     82     picture->y = mem;
     83     mem += y_size;
     84 
     85     picture->u = mem;
     86     mem += uv_size;
     87     picture->v = mem;
     88     mem += uv_size;
     89 
     90     if (a_size) {
     91       picture->a = mem;
     92       mem += a_size;
     93     }
     94     if (uv0_size) {
     95       picture->u0 = mem;
     96       mem += uv0_size;
     97       picture->v0 = mem;
     98       mem += uv0_size;
     99     }
    100   }
    101   return 1;
    102 }
    103 
    104 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
    105 // into 'dst'. Mark 'dst' as not owning any memory. 'src' can be NULL.
    106 static void WebPPictureGrabSpecs(const WebPPicture* const src,
    107                                  WebPPicture* const dst) {
    108   if (src) *dst = *src;
    109   dst->y = dst->u = dst->v = NULL;
    110   dst->u0 = dst->v0 = NULL;
    111   dst->a = NULL;
    112 }
    113 
    114 // Release memory owned by 'picture'.
    115 void WebPPictureFree(WebPPicture* const picture) {
    116   if (picture) {
    117     free(picture->y);
    118     WebPPictureGrabSpecs(NULL, picture);
    119   }
    120 }
    121 
    122 //-----------------------------------------------------------------------------
    123 // Picture copying
    124 
    125 int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) {
    126   int y;
    127   if (src == NULL || dst == NULL) return 0;
    128   if (src == dst) return 1;
    129 
    130   WebPPictureGrabSpecs(src, dst);
    131   if (!WebPPictureAlloc(dst)) return 0;
    132 
    133   for (y = 0; y < dst->height; ++y) {
    134     memcpy(dst->y + y * dst->y_stride,
    135            src->y + y * src->y_stride, src->width);
    136   }
    137   for (y = 0; y < (dst->height + 1) / 2; ++y) {
    138     memcpy(dst->u + y * dst->uv_stride,
    139            src->u + y * src->uv_stride, (src->width + 1) / 2);
    140     memcpy(dst->v + y * dst->uv_stride,
    141            src->v + y * src->uv_stride, (src->width + 1) / 2);
    142   }
    143 #ifdef WEBP_EXPERIMENTAL_FEATURES
    144   if (dst->a != NULL)  {
    145     for (y = 0; y < dst->height; ++y) {
    146       memcpy(dst->a + y * dst->a_stride,
    147              src->a + y * src->a_stride, src->width);
    148     }
    149   }
    150   if (dst->u0 != NULL)  {
    151     int uv0_width = src->width;
    152     if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
    153       uv0_width = (uv0_width + 1) / 2;
    154     }
    155     for (y = 0; y < dst->height; ++y) {
    156       memcpy(dst->u0 + y * dst->uv0_stride,
    157              src->u0 + y * src->uv0_stride, uv0_width);
    158       memcpy(dst->v0 + y * dst->uv0_stride,
    159              src->v0 + y * src->uv0_stride, uv0_width);
    160     }
    161   }
    162 #endif
    163   return 1;
    164 }
    165 
    166 //-----------------------------------------------------------------------------
    167 // Picture cropping
    168 
    169 int WebPPictureCrop(WebPPicture* const pic,
    170                     int left, int top, int width, int height) {
    171   WebPPicture tmp;
    172   int y;
    173 
    174   if (pic == NULL) return 0;
    175   if (width <= 0 || height <= 0) return 0;
    176   if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0;
    177   if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0;
    178 
    179   WebPPictureGrabSpecs(pic, &tmp);
    180   tmp.width = width;
    181   tmp.height = height;
    182   if (!WebPPictureAlloc(&tmp)) return 0;
    183 
    184   for (y = 0; y < height; ++y) {
    185     memcpy(tmp.y + y * tmp.y_stride,
    186            pic->y + (top + y) * pic->y_stride + left, width);
    187   }
    188   for (y = 0; y < (height + 1) / 2; ++y) {
    189     const int offset = (y + top / 2) * pic->uv_stride + left / 2;
    190     memcpy(tmp.u + y * tmp.uv_stride, pic->u + offset, (width + 1) / 2);
    191     memcpy(tmp.v + y * tmp.uv_stride, pic->v + offset, (width + 1) / 2);
    192   }
    193 
    194 #ifdef WEBP_EXPERIMENTAL_FEATURES
    195   if (tmp.a) {
    196     for (y = 0; y < height; ++y) {
    197       memcpy(tmp.a + y * tmp.a_stride,
    198            pic->a + (top + y) * pic->a_stride + left, width);
    199     }
    200   }
    201   if (tmp.u0) {
    202     int w = width;
    203     int l = left;
    204     if (tmp.colorspace == WEBP_YUV422) {
    205       w = (w + 1) / 2;
    206       l = (l + 1) / 2;
    207     }
    208     for (y = 0; y < height; ++y) {
    209       memcpy(tmp.u0 + y * tmp.uv0_stride,
    210              pic->u0 + (top + y) * pic->uv0_stride + l, w);
    211       memcpy(tmp.v0 + y * tmp.uv0_stride,
    212              pic->v0 + (top + y) * pic->uv0_stride + l, w);
    213     }
    214   }
    215 #endif
    216 
    217   WebPPictureFree(pic);
    218   *pic = tmp;
    219   return 1;
    220 }
    221 
    222 //-----------------------------------------------------------------------------
    223 // Simple picture rescaler
    224 
    225 #define RFIX 30
    226 #define MULT(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
    227 static inline void ImportRow(const uint8_t* src, int src_width,
    228                              int32_t* frow, int32_t* irow, int dst_width) {
    229   const int x_expand = (src_width < dst_width);
    230   const int fx_scale = (1 << RFIX) / dst_width;
    231   int x_in = 0;
    232   int x_out;
    233   int x_accum = 0;
    234   if (!x_expand) {
    235     int sum = 0;
    236     for (x_out = 0; x_out < dst_width; ++x_out) {
    237       x_accum += src_width - dst_width;
    238       for (; x_accum > 0; x_accum -= dst_width) {
    239         sum += src[x_in++];
    240       }
    241       {        // Emit next horizontal pixel.
    242         const int32_t base = src[x_in++];
    243         const int32_t frac = base * (-x_accum);
    244         frow[x_out] = (sum + base) * dst_width - frac;
    245         sum = MULT(frac, fx_scale);    // fresh fractional start for next pixel
    246       }
    247     }
    248   } else {        // simple bilinear interpolation
    249     int left = src[0], right = src[0];
    250     for (x_out = 0; x_out < dst_width; ++x_out) {
    251       if (x_accum < 0) {
    252         left = right;
    253         right = src[++x_in];
    254         x_accum += dst_width - 1;
    255       }
    256       frow[x_out] = right * (dst_width - 1) + (left - right) * x_accum;
    257       x_accum -= src_width - 1;
    258     }
    259   }
    260   // Accumulate the new row's contribution
    261   for (x_out = 0; x_out < dst_width; ++x_out) {
    262     irow[x_out] += frow[x_out];
    263   }
    264 }
    265 
    266 static void ExportRow(int32_t* frow, int32_t* irow, uint8_t* dst, int dst_width,
    267                       const int yscale, const int64_t fxy_scale) {
    268   int x_out;
    269   for (x_out = 0; x_out < dst_width; ++x_out) {
    270     const int frac = MULT(frow[x_out], yscale);
    271     const int v = MULT(irow[x_out] - frac, fxy_scale);
    272     dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
    273     irow[x_out] = frac;   // new fractional start
    274   }
    275 }
    276 
    277 static void RescalePlane(const uint8_t* src,
    278                          int src_width, int src_height, int src_stride,
    279                          uint8_t* dst,
    280                          int dst_width, int dst_height, int dst_stride,
    281                          int32_t* const work) {
    282   const int x_expand = (src_width < dst_width);
    283   const int fy_scale = (1 << RFIX) / dst_height;
    284   const int64_t fxy_scale = x_expand ?
    285       ((int64_t)dst_height << RFIX) / (dst_width * src_height) :
    286       ((int64_t)dst_height << RFIX) / (src_width * src_height);
    287   int y_accum = src_height;
    288   int y;
    289   int32_t* irow = work;              // integral contribution
    290   int32_t* frow = work + dst_width;  // fractional contribution
    291 
    292   memset(work, 0, 2 * dst_width * sizeof(*work));
    293   for (y = 0; y < src_height; ++y) {
    294     // import new contribution of one source row.
    295     ImportRow(src, src_width, frow, irow, dst_width);
    296     src += src_stride;
    297     // emit output row(s)
    298     y_accum -= dst_height;
    299     for (; y_accum <= 0; y_accum += src_height) {
    300       const int yscale = fy_scale * (-y_accum);
    301       ExportRow(frow, irow, dst, dst_width, yscale, fxy_scale);
    302       dst += dst_stride;
    303     }
    304   }
    305 }
    306 #undef MULT
    307 #undef RFIX
    308 
    309 int WebPPictureRescale(WebPPicture* const pic, int width, int height) {
    310   WebPPicture tmp;
    311   int prev_width, prev_height;
    312   int32_t* work;
    313 
    314   if (pic == NULL) return 0;
    315   prev_width = pic->width;
    316   prev_height = pic->height;
    317   // if width is unspecified, scale original proportionally to height ratio.
    318   if (width == 0) {
    319     width = (prev_width * height + prev_height / 2) / prev_height;
    320   }
    321   // if height is unspecified, scale original proportionally to width ratio.
    322   if (height == 0) {
    323     height = (prev_height * width + prev_width / 2) / prev_width;
    324   }
    325   // Check if the overall dimensions still make sense.
    326   if (width <= 0 || height <= 0) return 0;
    327 
    328   WebPPictureGrabSpecs(pic, &tmp);
    329   tmp.width = width;
    330   tmp.height = height;
    331   if (!WebPPictureAlloc(&tmp)) return 0;
    332 
    333   work = malloc(2 * width * sizeof(int32_t));
    334   if (work == NULL) {
    335     WebPPictureFree(&tmp);
    336     return 0;
    337   }
    338 
    339   RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
    340                tmp.y, width, height, tmp.y_stride, work);
    341   RescalePlane(pic->u,
    342                (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride,
    343                tmp.u,
    344                (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work);
    345   RescalePlane(pic->v,
    346                (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride,
    347                tmp.v,
    348                (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work);
    349 
    350 #ifdef WEBP_EXPERIMENTAL_FEATURES
    351   if (tmp.a) {
    352     RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
    353                  tmp.a, width, height, tmp.a_stride, work);
    354   }
    355   if (tmp.u0) {
    356     int s = 1;
    357     if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
    358       s = 2;
    359     }
    360     RescalePlane(
    361         pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
    362         tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work);
    363     RescalePlane(
    364         pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
    365         tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work);
    366   }
    367 #endif
    368 
    369   WebPPictureFree(pic);
    370   free(work);
    371   *pic = tmp;
    372   return 1;
    373 }
    374 
    375 //-----------------------------------------------------------------------------
    376 // Write-to-memory
    377 
    378 typedef struct {
    379   uint8_t** mem;
    380   size_t    max_size;
    381   size_t*   size;
    382 } WebPMemoryWriter;
    383 
    384 static void InitMemoryWriter(WebPMemoryWriter* const writer) {
    385   *writer->mem = NULL;
    386   *writer->size = 0;
    387   writer->max_size = 0;
    388 }
    389 
    390 static int WebPMemoryWrite(const uint8_t* data, size_t data_size,
    391                            const WebPPicture* const picture) {
    392   WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
    393   size_t next_size;
    394   if (w == NULL) {
    395     return 1;
    396   }
    397   next_size = (*w->size) + data_size;
    398   if (next_size > w->max_size) {
    399     uint8_t* new_mem;
    400     size_t next_max_size = w->max_size * 2;
    401     if (next_max_size < next_size) next_max_size = next_size;
    402     if (next_max_size < 8192) next_max_size = 8192;
    403     new_mem = (uint8_t*)malloc(next_max_size);
    404     if (new_mem == NULL) {
    405       return 0;
    406     }
    407     if ((*w->size) > 0) {
    408       memcpy(new_mem, *w->mem, *w->size);
    409     }
    410     free(*w->mem);
    411     *w->mem = new_mem;
    412     w->max_size = next_max_size;
    413   }
    414   if (data_size) {
    415     memcpy((*w->mem) + (*w->size), data, data_size);
    416     *w->size += data_size;
    417   }
    418   return 1;
    419 }
    420 
    421 //-----------------------------------------------------------------------------
    422 // RGB -> YUV conversion
    423 // The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
    424 // More information at: http://en.wikipedia.org/wiki/YCbCr
    425 // Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
    426 // U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
    427 // V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
    428 // We use 16bit fixed point operations.
    429 
    430 enum { YUV_FRAC = 16 };
    431 
    432 static inline int clip_uv(int v) {
    433    v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2);
    434    return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
    435 }
    436 
    437 static inline int rgb_to_y(int r, int g, int b) {
    438   const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC);
    439   const int luma = 16839 * r + 33059 * g + 6420 * b;
    440   return (luma + kRound) >> YUV_FRAC;  // no need to clip
    441 }
    442 
    443 static inline int rgb_to_u(int r, int g, int b) {
    444   return clip_uv(-9719 * r - 19081 * g + 28800 * b);
    445 }
    446 
    447 static inline int rgb_to_v(int r, int g, int b) {
    448   return clip_uv(+28800 * r - 24116 * g - 4684 * b);
    449 }
    450 
    451 // TODO: we can do better than simply 2x2 averaging on U/V samples.
    452 #define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
    453                    (ptr)[rgb_stride] + (ptr)[rgb_stride + step])
    454 #define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step])
    455 #define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride])
    456 #define SUM1(ptr)  (4 * (ptr)[0])
    457 #define RGB_TO_UV(x, y, SUM) {                           \
    458   const int src = (2 * (step * (x) + (y) * rgb_stride)); \
    459   const int dst = (x) + (y) * picture->uv_stride;        \
    460   const int r = SUM(r_ptr + src);                        \
    461   const int g = SUM(g_ptr + src);                        \
    462   const int b = SUM(b_ptr + src);                        \
    463   picture->u[dst] = rgb_to_u(r, g, b);                   \
    464   picture->v[dst] = rgb_to_v(r, g, b);                   \
    465 }
    466 
    467 #define RGB_TO_UV0(x_in, x_out, y, SUM) {                \
    468   const int src = (step * (x_in) + (y) * rgb_stride);    \
    469   const int dst = (x_out) + (y) * picture->uv0_stride;   \
    470   const int r = SUM(r_ptr + src);                        \
    471   const int g = SUM(g_ptr + src);                        \
    472   const int b = SUM(b_ptr + src);                        \
    473   picture->u0[dst] = rgb_to_u(r, g, b);                  \
    474   picture->v0[dst] = rgb_to_v(r, g, b);                  \
    475 }
    476 
    477 static void MakeGray(WebPPicture* const picture) {
    478   int y;
    479   const int uv_width =  (picture->width + 1) >> 1;
    480   for (y = 0; y < ((picture->height + 1) >> 1); ++y) {
    481     memset(picture->u + y * picture->uv_stride, 128, uv_width);
    482     memset(picture->v + y * picture->uv_stride, 128, uv_width);
    483   }
    484 }
    485 
    486 static int Import(WebPPicture* const picture,
    487                   const uint8_t* const rgb, int rgb_stride,
    488                   int step, int swap_rb, int import_alpha) {
    489   const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
    490   int x, y;
    491   const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
    492   const uint8_t* const g_ptr = rgb + 1;
    493   const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
    494   const int width = picture->width;
    495   const int height = picture->height;
    496 
    497   // Import luma plane
    498   for (y = 0; y < height; ++y) {
    499     for (x = 0; x < width; ++x) {
    500       const int offset = step * x + y * rgb_stride;
    501       picture->y[x + y * picture->y_stride] =
    502         rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
    503     }
    504   }
    505 
    506   // Downsample U/V plane
    507   if (uv_csp != WEBP_YUV400) {
    508     for (y = 0; y < (height >> 1); ++y) {
    509       for (x = 0; x < (width >> 1); ++x) {
    510         RGB_TO_UV(x, y, SUM4);
    511       }
    512       if (picture->width & 1) {
    513         RGB_TO_UV(x, y, SUM2V);
    514       }
    515     }
    516     if (height & 1) {
    517       for (x = 0; x < (width >> 1); ++x) {
    518         RGB_TO_UV(x, y, SUM2H);
    519       }
    520       if (width & 1) {
    521         RGB_TO_UV(x, y, SUM1);
    522       }
    523     }
    524 
    525 #ifdef WEBP_EXPERIMENTAL_FEATURES
    526     // Store original U/V samples too
    527     if (uv_csp == WEBP_YUV422) {
    528       for (y = 0; y < height; ++y) {
    529         for (x = 0; x < (width >> 1); ++x) {
    530           RGB_TO_UV0(2 * x, x, y, SUM2H);
    531         }
    532         if (width & 1) {
    533           RGB_TO_UV0(2 * x, x, y, SUM1);
    534         }
    535       }
    536     } else if (uv_csp == WEBP_YUV444) {
    537       for (y = 0; y < height; ++y) {
    538         for (x = 0; x < width; ++x) {
    539           RGB_TO_UV0(x, x, y, SUM1);
    540         }
    541       }
    542     }
    543 #endif
    544   } else {
    545     MakeGray(picture);
    546   }
    547 
    548   if (import_alpha) {
    549 #ifdef WEBP_EXPERIMENTAL_FEATURES
    550     const uint8_t* const a_ptr = rgb + 3;
    551     assert(step >= 4);
    552     for (y = 0; y < height; ++y) {
    553       for (x = 0; x < width; ++x) {
    554         picture->a[x + y * picture->a_stride] =
    555           a_ptr[step * x + y * rgb_stride];
    556       }
    557     }
    558 #endif
    559   }
    560   return 1;
    561 }
    562 #undef SUM4
    563 #undef SUM2V
    564 #undef SUM2H
    565 #undef SUM1
    566 #undef RGB_TO_UV
    567 
    568 int WebPPictureImportRGB(WebPPicture* const picture,
    569                          const uint8_t* const rgb, int rgb_stride) {
    570   picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
    571   if (!WebPPictureAlloc(picture)) return 0;
    572   return Import(picture, rgb, rgb_stride, 3, 0, 0);
    573 }
    574 
    575 int WebPPictureImportBGR(WebPPicture* const picture,
    576                          const uint8_t* const rgb, int rgb_stride) {
    577   picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
    578   if (!WebPPictureAlloc(picture)) return 0;
    579   return Import(picture, rgb, rgb_stride, 3, 1, 0);
    580 }
    581 
    582 int WebPPictureImportRGBA(WebPPicture* const picture,
    583                           const uint8_t* const rgba, int rgba_stride) {
    584   picture->colorspace |= WEBP_CSP_ALPHA_BIT;
    585   if (!WebPPictureAlloc(picture)) return 0;
    586   return Import(picture, rgba, rgba_stride, 4, 0, 1);
    587 }
    588 
    589 int WebPPictureImportBGRA(WebPPicture* const picture,
    590                           const uint8_t* const rgba, int rgba_stride) {
    591   picture->colorspace |= WEBP_CSP_ALPHA_BIT;
    592   if (!WebPPictureAlloc(picture)) return 0;
    593   return Import(picture, rgba, rgba_stride, 4, 1, 1);
    594 }
    595 
    596 //-----------------------------------------------------------------------------
    597 // Simplest call:
    598 
    599 typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
    600 
    601 static size_t Encode(const uint8_t* rgba, int width, int height, int stride,
    602                      Importer import, float quality_factor, uint8_t** output) {
    603   size_t output_size = 0;
    604   WebPPicture pic;
    605   WebPConfig config;
    606   WebPMemoryWriter wrt;
    607   int ok;
    608 
    609   if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
    610       !WebPPictureInit(&pic)) {
    611     return 0;  // shouldn't happen, except if system installation is broken
    612   }
    613 
    614   pic.width = width;
    615   pic.height = height;
    616   pic.writer = WebPMemoryWrite;
    617   pic.custom_ptr = &wrt;
    618 
    619   wrt.mem = output;
    620   wrt.size = &output_size;
    621   InitMemoryWriter(&wrt);
    622 
    623   ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
    624   WebPPictureFree(&pic);
    625   if (!ok) {
    626     free(*output);
    627     *output = NULL;
    628     return 0;
    629   }
    630   return output_size;
    631 }
    632 
    633 #define ENCODE_FUNC(NAME, IMPORTER) \
    634 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
    635             uint8_t** out) { \
    636   return Encode(in, w, h, bps, IMPORTER, q, out);  \
    637 }
    638 
    639 ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB);
    640 ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR);
    641 ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA);
    642 ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA);
    643 
    644 #undef ENCODE_FUNC
    645 
    646 //-----------------------------------------------------------------------------
    647 
    648 #if defined(__cplusplus) || defined(c_plusplus)
    649 }    // extern "C"
    650 #endif
    651