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