picture.c revision 466727975bcc57c0c5597bcd0747a2fe4777b303
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@gmail.com)
11
12#include <assert.h>
13#include <stdlib.h>
14#include "vp8enci.h"
15
16#if defined(__cplusplus) || defined(c_plusplus)
17extern "C" {
18#endif
19
20//-----------------------------------------------------------------------------
21// WebPPicture
22//-----------------------------------------------------------------------------
23
24int 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.
106static 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'.
115void WebPPictureFree(WebPPicture* const picture) {
116  if (picture) {
117    free(picture->y);
118    WebPPictureGrabSpecs(NULL, picture);
119  }
120}
121
122//-----------------------------------------------------------------------------
123// Picture copying
124
125int 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
169int 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)
227static 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
266static 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
277static 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
309int 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
378typedef struct {
379  uint8_t** mem;
380  size_t    max_size;
381  size_t*   size;
382} WebPMemoryWriter;
383
384static void InitMemoryWriter(WebPMemoryWriter* const writer) {
385  *writer->mem = NULL;
386  *writer->size = 0;
387  writer->max_size = 0;
388}
389
390static 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
430enum { YUV_FRAC = 16 };
431
432static 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
437static 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
443static inline int rgb_to_u(int r, int g, int b) {
444  return clip_uv(-9719 * r - 19081 * g + 28800 * b);
445}
446
447static 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
477static 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
486static 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
568int 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
575int 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
582int 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
589int 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
599typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
600
601static 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) \
634size_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
639ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB);
640ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR);
641ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA);
642ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA);
643
644#undef ENCODE_FUNC
645
646//-----------------------------------------------------------------------------
647
648#if defined(__cplusplus) || defined(c_plusplus)
649}    // extern "C"
650#endif
651