OLD | NEW |
1 // Copyright 2011 Google Inc. All Rights Reserved. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Use of this source code is governed by a BSD-style license | 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 | 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 | 5 // tree. An additional intellectual property rights grant can be found |
6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
9 // | 9 // |
10 // WebPPicture utils: colorspace conversion, crop, ... | 10 // WebPPicture utils: colorspace conversion, crop, ... |
11 // | 11 // |
12 // Author: Skal (pascal.massimino@gmail.com) | 12 // Author: Skal (pascal.massimino@gmail.com) |
13 | 13 |
14 #include <assert.h> | 14 #include <assert.h> |
15 #include <stdlib.h> | 15 #include <stdlib.h> |
16 #include <math.h> | 16 #include <math.h> |
17 | 17 |
18 #include "./vp8enci.h" | 18 #include "./vp8enci.h" |
| 19 #include "../utils/alpha_processing.h" |
| 20 #include "../utils/random.h" |
19 #include "../utils/rescaler.h" | 21 #include "../utils/rescaler.h" |
20 #include "../utils/utils.h" | 22 #include "../utils/utils.h" |
21 #include "../dsp/dsp.h" | 23 #include "../dsp/dsp.h" |
22 #include "../dsp/yuv.h" | 24 #include "../dsp/yuv.h" |
23 | 25 |
24 #if defined(__cplusplus) || defined(c_plusplus) | 26 // Uncomment to disable gamma-compression during RGB->U/V averaging |
25 extern "C" { | 27 #define USE_GAMMA_COMPRESSION |
26 #endif | |
27 | 28 |
28 #define HALVE(x) (((x) + 1) >> 1) | 29 #define HALVE(x) (((x) + 1) >> 1) |
29 #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP)) | 30 #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP)) |
30 | 31 |
31 static const union { | 32 static const union { |
32 uint32_t argb; | 33 uint32_t argb; |
33 uint8_t bytes[4]; | 34 uint8_t bytes[4]; |
34 } test_endian = { 0xff000000u }; | 35 } test_endian = { 0xff000000u }; |
35 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) | 36 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) |
36 | 37 |
| 38 static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) { |
| 39 return (0xff000000u | (r << 16) | (g << 8) | b); |
| 40 } |
| 41 |
37 //------------------------------------------------------------------------------ | 42 //------------------------------------------------------------------------------ |
38 // WebPPicture | 43 // WebPPicture |
39 //------------------------------------------------------------------------------ | 44 //------------------------------------------------------------------------------ |
40 | 45 |
41 int WebPPictureAlloc(WebPPicture* picture) { | 46 int WebPPictureAlloc(WebPPicture* picture) { |
42 if (picture != NULL) { | 47 if (picture != NULL) { |
43 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; | 48 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; |
44 const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; | 49 const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; |
45 const int width = picture->width; | 50 const int width = picture->width; |
46 const int height = picture->height; | 51 const int height = picture->height; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 if (a_size) { | 116 if (a_size) { |
112 picture->a = mem; | 117 picture->a = mem; |
113 mem += a_size; | 118 mem += a_size; |
114 } | 119 } |
115 if (uv0_size) { | 120 if (uv0_size) { |
116 picture->u0 = mem; | 121 picture->u0 = mem; |
117 mem += uv0_size; | 122 mem += uv0_size; |
118 picture->v0 = mem; | 123 picture->v0 = mem; |
119 mem += uv0_size; | 124 mem += uv0_size; |
120 } | 125 } |
| 126 (void)mem; // makes the static analyzer happy |
121 } else { | 127 } else { |
122 void* memory; | 128 void* memory; |
123 const uint64_t argb_size = (uint64_t)width * height; | 129 const uint64_t argb_size = (uint64_t)width * height; |
124 if (width <= 0 || height <= 0) { | 130 if (width <= 0 || height <= 0) { |
125 return 0; | 131 return 0; |
126 } | 132 } |
127 // Clear previous buffer and allocate a new one. | 133 // Clear previous buffer and allocate a new one. |
128 WebPPictureFree(picture); // erase previous buffer | 134 WebPPictureFree(picture); // erase previous buffer |
129 memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb)); | 135 memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb)); |
130 if (memory == NULL) return 0; | 136 if (memory == NULL) return 0; |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
388 src_height, dst_height, | 394 src_height, dst_height, |
389 work); | 395 work); |
390 memset(work, 0, 2 * dst_width * num_channels * sizeof(*work)); | 396 memset(work, 0, 2 * dst_width * num_channels * sizeof(*work)); |
391 while (y < src_height) { | 397 while (y < src_height) { |
392 y += WebPRescalerImport(&rescaler, src_height - y, | 398 y += WebPRescalerImport(&rescaler, src_height - y, |
393 src + y * src_stride, src_stride); | 399 src + y * src_stride, src_stride); |
394 WebPRescalerExport(&rescaler); | 400 WebPRescalerExport(&rescaler); |
395 } | 401 } |
396 } | 402 } |
397 | 403 |
| 404 static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) { |
| 405 uint32_t* ptr = pic->argb; |
| 406 int y; |
| 407 for (y = 0; y < pic->height; ++y) { |
| 408 WebPMultARGBRow(ptr, pic->width, inverse); |
| 409 ptr += pic->argb_stride; |
| 410 } |
| 411 } |
| 412 |
| 413 static void AlphaMultiplyY(WebPPicture* const pic, int inverse) { |
| 414 const uint8_t* ptr_a = pic->a; |
| 415 if (ptr_a != NULL) { |
| 416 uint8_t* ptr_y = pic->y; |
| 417 int y; |
| 418 for (y = 0; y < pic->height; ++y) { |
| 419 WebPMultRow(ptr_y, ptr_a, pic->width, inverse); |
| 420 ptr_y += pic->y_stride; |
| 421 ptr_a += pic->a_stride; |
| 422 } |
| 423 } |
| 424 } |
| 425 |
398 int WebPPictureRescale(WebPPicture* pic, int width, int height) { | 426 int WebPPictureRescale(WebPPicture* pic, int width, int height) { |
399 WebPPicture tmp; | 427 WebPPicture tmp; |
400 int prev_width, prev_height; | 428 int prev_width, prev_height; |
401 int32_t* work; | 429 int32_t* work; |
402 | 430 |
403 if (pic == NULL) return 0; | 431 if (pic == NULL) return 0; |
404 prev_width = pic->width; | 432 prev_width = pic->width; |
405 prev_height = pic->height; | 433 prev_height = pic->height; |
406 // if width is unspecified, scale original proportionally to height ratio. | 434 // if width is unspecified, scale original proportionally to height ratio. |
407 if (width == 0) { | 435 if (width == 0) { |
(...skipping 10 matching lines...) Expand all Loading... |
418 tmp.width = width; | 446 tmp.width = width; |
419 tmp.height = height; | 447 tmp.height = height; |
420 if (!WebPPictureAlloc(&tmp)) return 0; | 448 if (!WebPPictureAlloc(&tmp)) return 0; |
421 | 449 |
422 if (!pic->use_argb) { | 450 if (!pic->use_argb) { |
423 work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); | 451 work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); |
424 if (work == NULL) { | 452 if (work == NULL) { |
425 WebPPictureFree(&tmp); | 453 WebPPictureFree(&tmp); |
426 return 0; | 454 return 0; |
427 } | 455 } |
| 456 // If present, we need to rescale alpha first (for AlphaMultiplyY). |
| 457 if (pic->a != NULL) { |
| 458 RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, |
| 459 tmp.a, width, height, tmp.a_stride, work, 1); |
| 460 } |
428 | 461 |
| 462 // We take transparency into account on the luma plane only. That's not |
| 463 // totally exact blending, but still is a good approximation. |
| 464 AlphaMultiplyY(pic, 0); |
429 RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, | 465 RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, |
430 tmp.y, width, height, tmp.y_stride, work, 1); | 466 tmp.y, width, height, tmp.y_stride, work, 1); |
| 467 AlphaMultiplyY(&tmp, 1); |
| 468 |
431 RescalePlane(pic->u, | 469 RescalePlane(pic->u, |
432 HALVE(prev_width), HALVE(prev_height), pic->uv_stride, | 470 HALVE(prev_width), HALVE(prev_height), pic->uv_stride, |
433 tmp.u, | 471 tmp.u, |
434 HALVE(width), HALVE(height), tmp.uv_stride, work, 1); | 472 HALVE(width), HALVE(height), tmp.uv_stride, work, 1); |
435 RescalePlane(pic->v, | 473 RescalePlane(pic->v, |
436 HALVE(prev_width), HALVE(prev_height), pic->uv_stride, | 474 HALVE(prev_width), HALVE(prev_height), pic->uv_stride, |
437 tmp.v, | 475 tmp.v, |
438 HALVE(width), HALVE(height), tmp.uv_stride, work, 1); | 476 HALVE(width), HALVE(height), tmp.uv_stride, work, 1); |
439 | 477 |
440 if (tmp.a != NULL) { | |
441 RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, | |
442 tmp.a, width, height, tmp.a_stride, work, 1); | |
443 } | |
444 #ifdef WEBP_EXPERIMENTAL_FEATURES | 478 #ifdef WEBP_EXPERIMENTAL_FEATURES |
445 if (tmp.u0 != NULL) { | 479 if (tmp.u0 != NULL) { |
446 const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; | 480 const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; |
447 RescalePlane( | 481 RescalePlane( |
448 pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, | 482 pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, |
449 tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); | 483 tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); |
450 RescalePlane( | 484 RescalePlane( |
451 pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, | 485 pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, |
452 tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); | 486 tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); |
453 } | 487 } |
454 #endif | 488 #endif |
455 } else { | 489 } else { |
456 work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); | 490 work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); |
457 if (work == NULL) { | 491 if (work == NULL) { |
458 WebPPictureFree(&tmp); | 492 WebPPictureFree(&tmp); |
459 return 0; | 493 return 0; |
460 } | 494 } |
461 | 495 // In order to correctly interpolate colors, we need to apply the alpha |
| 496 // weighting first (black-matting), scale the RGB values, and remove |
| 497 // the premultiplication afterward (while preserving the alpha channel). |
| 498 AlphaMultiplyARGB(pic, 0); |
462 RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, | 499 RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, |
463 pic->argb_stride * 4, | 500 pic->argb_stride * 4, |
464 (uint8_t*)tmp.argb, width, height, | 501 (uint8_t*)tmp.argb, width, height, |
465 tmp.argb_stride * 4, | 502 tmp.argb_stride * 4, |
466 work, 4); | 503 work, 4); |
| 504 AlphaMultiplyARGB(&tmp, 1); |
467 } | 505 } |
468 WebPPictureFree(pic); | 506 WebPPictureFree(pic); |
469 free(work); | 507 free(work); |
470 *pic = tmp; | 508 *pic = tmp; |
471 return 1; | 509 return 1; |
472 } | 510 } |
473 | 511 |
474 //------------------------------------------------------------------------------ | 512 //------------------------------------------------------------------------------ |
475 // WebPMemoryWriter: Write-to-memory | 513 // WebPMemoryWriter: Write-to-memory |
476 | 514 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
545 } | 583 } |
546 argb += picture->argb_stride; | 584 argb += picture->argb_stride; |
547 } | 585 } |
548 } | 586 } |
549 return 0; | 587 return 0; |
550 } | 588 } |
551 | 589 |
552 //------------------------------------------------------------------------------ | 590 //------------------------------------------------------------------------------ |
553 // RGB -> YUV conversion | 591 // RGB -> YUV conversion |
554 | 592 |
555 // TODO: we can do better than simply 2x2 averaging on U/V samples. | 593 static int RGBToY(int r, int g, int b, VP8Random* const rg) { |
556 #define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \ | 594 return VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX)); |
557 (ptr)[rgb_stride] + (ptr)[rgb_stride + step]) | 595 } |
558 #define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step]) | 596 |
559 #define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride]) | 597 static int RGBToU(int r, int g, int b, VP8Random* const rg) { |
560 #define SUM1(ptr) (4 * (ptr)[0]) | 598 return VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); |
| 599 } |
| 600 |
| 601 static int RGBToV(int r, int g, int b, VP8Random* const rg) { |
| 602 return VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); |
| 603 } |
| 604 |
| 605 //------------------------------------------------------------------------------ |
| 606 |
| 607 #if defined(USE_GAMMA_COMPRESSION) |
| 608 |
| 609 // gamma-compensates loss of resolution during chroma subsampling |
| 610 #define kGamma 0.80 |
| 611 #define kGammaFix 12 // fixed-point precision for linear values |
| 612 #define kGammaScale ((1 << kGammaFix) - 1) |
| 613 #define kGammaTabFix 7 // fixed-point fractional bits precision |
| 614 #define kGammaTabScale (1 << kGammaTabFix) |
| 615 #define kGammaTabRounder (kGammaTabScale >> 1) |
| 616 #define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) |
| 617 |
| 618 static int kLinearToGammaTab[kGammaTabSize + 1]; |
| 619 static uint16_t kGammaToLinearTab[256]; |
| 620 static int kGammaTablesOk = 0; |
| 621 |
| 622 static void InitGammaTables(void) { |
| 623 if (!kGammaTablesOk) { |
| 624 int v; |
| 625 const double scale = 1. / kGammaScale; |
| 626 for (v = 0; v <= 255; ++v) { |
| 627 kGammaToLinearTab[v] = |
| 628 (uint16_t)(pow(v / 255., kGamma) * kGammaScale + .5); |
| 629 } |
| 630 for (v = 0; v <= kGammaTabSize; ++v) { |
| 631 const double x = scale * (v << kGammaTabFix); |
| 632 kLinearToGammaTab[v] = (int)(pow(x, 1. / kGamma) * 255. + .5); |
| 633 } |
| 634 kGammaTablesOk = 1; |
| 635 } |
| 636 } |
| 637 |
| 638 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { |
| 639 return kGammaToLinearTab[v]; |
| 640 } |
| 641 |
| 642 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision |
| 643 // U/V value, suitable for RGBToU/V calls. |
| 644 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { |
| 645 const int v = base_value << shift; // final uplifted value |
| 646 const int tab_pos = v >> (kGammaTabFix + 2); // integer part |
| 647 const int x = v & ((kGammaTabScale << 2) - 1); // fractional part |
| 648 const int v0 = kLinearToGammaTab[tab_pos]; |
| 649 const int v1 = kLinearToGammaTab[tab_pos + 1]; |
| 650 const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate |
| 651 return (y + kGammaTabRounder) >> kGammaTabFix; // descale |
| 652 } |
| 653 |
| 654 #else |
| 655 |
| 656 static void InitGammaTables(void) {} |
| 657 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } |
| 658 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { |
| 659 (void)shift; |
| 660 return v; |
| 661 } |
| 662 |
| 663 #endif // USE_GAMMA_COMPRESSION |
| 664 |
| 665 //------------------------------------------------------------------------------ |
| 666 |
| 667 #define SUM4(ptr) LinearToGamma( \ |
| 668 GammaToLinear((ptr)[0]) + \ |
| 669 GammaToLinear((ptr)[step]) + \ |
| 670 GammaToLinear((ptr)[rgb_stride]) + \ |
| 671 GammaToLinear((ptr)[rgb_stride + step]), 0) \ |
| 672 |
| 673 #define SUM2H(ptr) \ |
| 674 LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[step]), 1) |
| 675 #define SUM2V(ptr) \ |
| 676 LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1) |
| 677 #define SUM1(ptr) \ |
| 678 LinearToGamma(GammaToLinear((ptr)[0]), 2) |
| 679 |
561 #define RGB_TO_UV(x, y, SUM) { \ | 680 #define RGB_TO_UV(x, y, SUM) { \ |
562 const int src = (2 * (step * (x) + (y) * rgb_stride)); \ | 681 const int src = (2 * (step * (x) + (y) * rgb_stride)); \ |
563 const int dst = (x) + (y) * picture->uv_stride; \ | 682 const int dst = (x) + (y) * picture->uv_stride; \ |
564 const int r = SUM(r_ptr + src); \ | 683 const int r = SUM(r_ptr + src); \ |
565 const int g = SUM(g_ptr + src); \ | 684 const int g = SUM(g_ptr + src); \ |
566 const int b = SUM(b_ptr + src); \ | 685 const int b = SUM(b_ptr + src); \ |
567 picture->u[dst] = VP8RGBToU(r, g, b); \ | 686 picture->u[dst] = RGBToU(r, g, b, &rg); \ |
568 picture->v[dst] = VP8RGBToV(r, g, b); \ | 687 picture->v[dst] = RGBToV(r, g, b, &rg); \ |
569 } | 688 } |
570 | 689 |
571 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \ | 690 #define RGB_TO_UV0(x_in, x_out, y, SUM) { \ |
572 const int src = (step * (x_in) + (y) * rgb_stride); \ | 691 const int src = (step * (x_in) + (y) * rgb_stride); \ |
573 const int dst = (x_out) + (y) * picture->uv0_stride; \ | 692 const int dst = (x_out) + (y) * picture->uv0_stride; \ |
574 const int r = SUM(r_ptr + src); \ | 693 const int r = SUM(r_ptr + src); \ |
575 const int g = SUM(g_ptr + src); \ | 694 const int g = SUM(g_ptr + src); \ |
576 const int b = SUM(b_ptr + src); \ | 695 const int b = SUM(b_ptr + src); \ |
577 picture->u0[dst] = VP8RGBToU(r, g, b); \ | 696 picture->u0[dst] = RGBToU(r, g, b, &rg); \ |
578 picture->v0[dst] = VP8RGBToV(r, g, b); \ | 697 picture->v0[dst] = RGBToV(r, g, b, &rg); \ |
579 } | 698 } |
580 | 699 |
581 static void MakeGray(WebPPicture* const picture) { | 700 static void MakeGray(WebPPicture* const picture) { |
582 int y; | 701 int y; |
583 const int uv_width = HALVE(picture->width); | 702 const int uv_width = HALVE(picture->width); |
584 const int uv_height = HALVE(picture->height); | 703 const int uv_height = HALVE(picture->height); |
585 for (y = 0; y < uv_height; ++y) { | 704 for (y = 0; y < uv_height; ++y) { |
586 memset(picture->u + y * picture->uv_stride, 128, uv_width); | 705 memset(picture->u + y * picture->uv_stride, 128, uv_width); |
587 memset(picture->v + y * picture->uv_stride, 128, uv_width); | 706 memset(picture->v + y * picture->uv_stride, 128, uv_width); |
588 } | 707 } |
589 } | 708 } |
590 | 709 |
591 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, | 710 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, |
592 const uint8_t* const g_ptr, | 711 const uint8_t* const g_ptr, |
593 const uint8_t* const b_ptr, | 712 const uint8_t* const b_ptr, |
594 const uint8_t* const a_ptr, | 713 const uint8_t* const a_ptr, |
595 int step, // bytes per pixel | 714 int step, // bytes per pixel |
596 int rgb_stride, // bytes per scanline | 715 int rgb_stride, // bytes per scanline |
| 716 float dithering, |
597 WebPPicture* const picture) { | 717 WebPPicture* const picture) { |
598 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; | 718 const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; |
599 int x, y; | 719 int x, y; |
600 const int width = picture->width; | 720 const int width = picture->width; |
601 const int height = picture->height; | 721 const int height = picture->height; |
602 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); | 722 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); |
| 723 VP8Random rg; |
603 | 724 |
604 picture->colorspace = uv_csp; | 725 picture->colorspace = uv_csp; |
605 picture->use_argb = 0; | 726 picture->use_argb = 0; |
606 if (has_alpha) { | 727 if (has_alpha) { |
607 picture->colorspace |= WEBP_CSP_ALPHA_BIT; | 728 picture->colorspace |= WEBP_CSP_ALPHA_BIT; |
608 } | 729 } |
609 if (!WebPPictureAlloc(picture)) return 0; | 730 if (!WebPPictureAlloc(picture)) return 0; |
610 | 731 |
| 732 VP8InitRandom(&rg, dithering); |
| 733 InitGammaTables(); |
| 734 |
611 // Import luma plane | 735 // Import luma plane |
612 for (y = 0; y < height; ++y) { | 736 for (y = 0; y < height; ++y) { |
613 for (x = 0; x < width; ++x) { | 737 for (x = 0; x < width; ++x) { |
614 const int offset = step * x + y * rgb_stride; | 738 const int offset = step * x + y * rgb_stride; |
615 picture->y[x + y * picture->y_stride] = | 739 picture->y[x + y * picture->y_stride] = |
616 VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]); | 740 RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset], &rg); |
617 } | 741 } |
618 } | 742 } |
619 | 743 |
620 // Downsample U/V plane | 744 // Downsample U/V plane |
621 if (uv_csp != WEBP_YUV400) { | 745 if (uv_csp != WEBP_YUV400) { |
622 for (y = 0; y < (height >> 1); ++y) { | 746 for (y = 0; y < (height >> 1); ++y) { |
623 for (x = 0; x < (width >> 1); ++x) { | 747 for (x = 0; x < (width >> 1); ++x) { |
624 RGB_TO_UV(x, y, SUM4); | 748 RGB_TO_UV(x, y, SUM4); |
625 } | 749 } |
626 if (width & 1) { | 750 if (width & 1) { |
(...skipping 27 matching lines...) Expand all Loading... |
654 } | 778 } |
655 } | 779 } |
656 } | 780 } |
657 #endif | 781 #endif |
658 } else { | 782 } else { |
659 MakeGray(picture); | 783 MakeGray(picture); |
660 } | 784 } |
661 | 785 |
662 if (has_alpha) { | 786 if (has_alpha) { |
663 assert(step >= 4); | 787 assert(step >= 4); |
| 788 assert(picture->a != NULL); |
664 for (y = 0; y < height; ++y) { | 789 for (y = 0; y < height; ++y) { |
665 for (x = 0; x < width; ++x) { | 790 for (x = 0; x < width; ++x) { |
666 picture->a[x + y * picture->a_stride] = | 791 picture->a[x + y * picture->a_stride] = |
667 a_ptr[step * x + y * rgb_stride]; | 792 a_ptr[step * x + y * rgb_stride]; |
668 } | 793 } |
669 } | 794 } |
670 } | 795 } |
671 return 1; | 796 return 1; |
672 } | 797 } |
673 | 798 |
674 static int Import(WebPPicture* const picture, | 799 static int Import(WebPPicture* const picture, |
675 const uint8_t* const rgb, int rgb_stride, | 800 const uint8_t* const rgb, int rgb_stride, |
676 int step, int swap_rb, int import_alpha) { | 801 int step, int swap_rb, int import_alpha) { |
677 const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0); | 802 const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0); |
678 const uint8_t* const g_ptr = rgb + 1; | 803 const uint8_t* const g_ptr = rgb + 1; |
679 const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2); | 804 const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2); |
680 const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL; | 805 const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL; |
681 const int width = picture->width; | 806 const int width = picture->width; |
682 const int height = picture->height; | 807 const int height = picture->height; |
683 | 808 |
684 if (!picture->use_argb) { | 809 if (!picture->use_argb) { |
685 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, | 810 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, |
686 picture); | 811 0.f /* no dithering */, picture); |
687 } | 812 } |
688 if (import_alpha) { | 813 if (import_alpha) { |
689 picture->colorspace |= WEBP_CSP_ALPHA_BIT; | 814 picture->colorspace |= WEBP_CSP_ALPHA_BIT; |
690 } else { | 815 } else { |
691 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; | 816 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; |
692 } | 817 } |
693 if (!WebPPictureAlloc(picture)) return 0; | 818 if (!WebPPictureAlloc(picture)) return 0; |
694 | 819 |
695 if (!import_alpha) { | 820 if (!import_alpha) { |
696 int x, y; | 821 int x, y; |
697 for (y = 0; y < height; ++y) { | 822 for (y = 0; y < height; ++y) { |
698 for (x = 0; x < width; ++x) { | 823 for (x = 0; x < width; ++x) { |
699 const int offset = step * x + y * rgb_stride; | 824 const int offset = step * x + y * rgb_stride; |
700 const uint32_t argb = | 825 const uint32_t argb = |
701 0xff000000u | | 826 MakeARGB32(r_ptr[offset], g_ptr[offset], b_ptr[offset]); |
702 (r_ptr[offset] << 16) | | |
703 (g_ptr[offset] << 8) | | |
704 (b_ptr[offset]); | |
705 picture->argb[x + y * picture->argb_stride] = argb; | 827 picture->argb[x + y * picture->argb_stride] = argb; |
706 } | 828 } |
707 } | 829 } |
708 } else { | 830 } else { |
709 int x, y; | 831 int x, y; |
710 assert(step >= 4); | 832 assert(step >= 4); |
711 for (y = 0; y < height; ++y) { | 833 for (y = 0; y < height; ++y) { |
712 for (x = 0; x < width; ++x) { | 834 for (x = 0; x < width; ++x) { |
713 const int offset = step * x + y * rgb_stride; | 835 const int offset = step * x + y * rgb_stride; |
714 const uint32_t argb = ((uint32_t)a_ptr[offset] << 24) | | 836 const uint32_t argb = ((uint32_t)a_ptr[offset] << 24) | |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
755 int WebPPictureImportBGRX(WebPPicture* picture, | 877 int WebPPictureImportBGRX(WebPPicture* picture, |
756 const uint8_t* rgba, int rgba_stride) { | 878 const uint8_t* rgba, int rgba_stride) { |
757 return Import(picture, rgba, rgba_stride, 4, 1, 0); | 879 return Import(picture, rgba, rgba_stride, 4, 1, 0); |
758 } | 880 } |
759 | 881 |
760 //------------------------------------------------------------------------------ | 882 //------------------------------------------------------------------------------ |
761 // Automatic YUV <-> ARGB conversions. | 883 // Automatic YUV <-> ARGB conversions. |
762 | 884 |
763 int WebPPictureYUVAToARGB(WebPPicture* picture) { | 885 int WebPPictureYUVAToARGB(WebPPicture* picture) { |
764 if (picture == NULL) return 0; | 886 if (picture == NULL) return 0; |
765 if (picture->memory_ == NULL || picture->y == NULL || | 887 if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { |
766 picture->u == NULL || picture->v == NULL) { | |
767 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); | 888 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); |
768 } | 889 } |
769 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { | 890 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { |
770 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); | 891 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); |
771 } | 892 } |
772 if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { | 893 if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { |
773 return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); | 894 return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); |
774 } | 895 } |
775 // Allocate a new argb buffer (discarding the previous one). | 896 // Allocate a new argb buffer (discarding the previous one). |
776 if (!PictureAllocARGB(picture)) return 0; | 897 if (!PictureAllocARGB(picture)) return 0; |
777 | 898 |
778 // Convert | 899 // Convert |
779 { | 900 { |
780 int y; | 901 int y; |
781 const int width = picture->width; | 902 const int width = picture->width; |
782 const int height = picture->height; | 903 const int height = picture->height; |
783 const int argb_stride = 4 * picture->argb_stride; | 904 const int argb_stride = 4 * picture->argb_stride; |
784 uint8_t* dst = (uint8_t*)picture->argb; | 905 uint8_t* dst = (uint8_t*)picture->argb; |
785 const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; | 906 const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; |
786 WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); | 907 WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); |
787 | 908 |
788 // First row, with replicated top samples. | 909 // First row, with replicated top samples. |
789 upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, width); | 910 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); |
790 cur_y += picture->y_stride; | 911 cur_y += picture->y_stride; |
791 dst += argb_stride; | 912 dst += argb_stride; |
792 // Center rows. | 913 // Center rows. |
793 for (y = 1; y + 1 < height; y += 2) { | 914 for (y = 1; y + 1 < height; y += 2) { |
794 const uint8_t* const top_u = cur_u; | 915 const uint8_t* const top_u = cur_u; |
795 const uint8_t* const top_v = cur_v; | 916 const uint8_t* const top_v = cur_v; |
796 cur_u += picture->uv_stride; | 917 cur_u += picture->uv_stride; |
797 cur_v += picture->uv_stride; | 918 cur_v += picture->uv_stride; |
798 upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, | 919 upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, |
799 dst, dst + argb_stride, width); | 920 dst, dst + argb_stride, width); |
(...skipping 12 matching lines...) Expand all Loading... |
812 int x; | 933 int x; |
813 for (x = 0; x < width; ++x) { | 934 for (x = 0; x < width; ++x) { |
814 argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); | 935 argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); |
815 } | 936 } |
816 } | 937 } |
817 } | 938 } |
818 } | 939 } |
819 return 1; | 940 return 1; |
820 } | 941 } |
821 | 942 |
822 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { | 943 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, |
| 944 float dithering) { |
823 if (picture == NULL) return 0; | 945 if (picture == NULL) return 0; |
824 if (picture->argb == NULL) { | 946 if (picture->argb == NULL) { |
825 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); | 947 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); |
826 } else { | 948 } else { |
827 const uint8_t* const argb = (const uint8_t*)picture->argb; | 949 const uint8_t* const argb = (const uint8_t*)picture->argb; |
828 const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1; | 950 const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1; |
829 const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2; | 951 const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2; |
830 const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3; | 952 const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3; |
831 const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0; | 953 const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0; |
832 // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA() | 954 // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA() |
833 // would be calling WebPPictureFree(picture) otherwise. | 955 // would be calling WebPPictureFree(picture) otherwise. |
834 WebPPicture tmp = *picture; | 956 WebPPicture tmp = *picture; |
835 PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d. | 957 PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d. |
836 tmp.use_argb = 0; | 958 tmp.use_argb = 0; |
837 tmp.colorspace = colorspace & WEBP_CSP_UV_MASK; | 959 tmp.colorspace = colorspace & WEBP_CSP_UV_MASK; |
838 if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, &tmp)) { | 960 if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, dithering, |
| 961 &tmp)) { |
839 return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); | 962 return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); |
840 } | 963 } |
841 // Copy back the YUV specs into 'picture'. | 964 // Copy back the YUV specs into 'picture'. |
842 tmp.argb = picture->argb; | 965 tmp.argb = picture->argb; |
843 tmp.argb_stride = picture->argb_stride; | 966 tmp.argb_stride = picture->argb_stride; |
844 tmp.memory_argb_ = picture->memory_argb_; | 967 tmp.memory_argb_ = picture->memory_argb_; |
845 *picture = tmp; | 968 *picture = tmp; |
846 } | 969 } |
847 return 1; | 970 return 1; |
848 } | 971 } |
849 | 972 |
| 973 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { |
| 974 return WebPPictureARGBToYUVADithered(picture, colorspace, 0.f); |
| 975 } |
| 976 |
850 //------------------------------------------------------------------------------ | 977 //------------------------------------------------------------------------------ |
851 // Helper: clean up fully transparent area to help compressibility. | 978 // Helper: clean up fully transparent area to help compressibility. |
852 | 979 |
853 #define SIZE 8 | 980 #define SIZE 8 |
854 #define SIZE2 (SIZE / 2) | 981 #define SIZE2 (SIZE / 2) |
855 static int is_transparent_area(const uint8_t* ptr, int stride, int size) { | 982 static int is_transparent_area(const uint8_t* ptr, int stride, int size) { |
856 int y, x; | 983 int y, x; |
857 for (y = 0; y < size; ++y) { | 984 for (y = 0; y < size; ++y) { |
858 for (x = 0; x < size; ++x) { | 985 for (x = 0; x < size; ++x) { |
859 if (ptr[x]) { | 986 if (ptr[x]) { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
906 } | 1033 } |
907 } | 1034 } |
908 // ignore the left-overs on right/bottom | 1035 // ignore the left-overs on right/bottom |
909 } | 1036 } |
910 } | 1037 } |
911 | 1038 |
912 #undef SIZE | 1039 #undef SIZE |
913 #undef SIZE2 | 1040 #undef SIZE2 |
914 | 1041 |
915 //------------------------------------------------------------------------------ | 1042 //------------------------------------------------------------------------------ |
| 1043 // Blend color and remove transparency info |
| 1044 |
| 1045 #define BLEND(V0, V1, ALPHA) \ |
| 1046 ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16) |
| 1047 #define BLEND_10BIT(V0, V1, ALPHA) \ |
| 1048 ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18) |
| 1049 |
| 1050 void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) { |
| 1051 const int red = (background_rgb >> 16) & 0xff; |
| 1052 const int green = (background_rgb >> 8) & 0xff; |
| 1053 const int blue = (background_rgb >> 0) & 0xff; |
| 1054 VP8Random rg; |
| 1055 int x, y; |
| 1056 if (pic == NULL) return; |
| 1057 VP8InitRandom(&rg, 0.f); |
| 1058 if (!pic->use_argb) { |
| 1059 const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop |
| 1060 const int Y0 = RGBToY(red, green, blue, &rg); |
| 1061 // VP8RGBToU/V expects the u/v values summed over four pixels |
| 1062 const int U0 = RGBToU(4 * red, 4 * green, 4 * blue, &rg); |
| 1063 const int V0 = RGBToV(4 * red, 4 * green, 4 * blue, &rg); |
| 1064 const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT; |
| 1065 if (!has_alpha || pic->a == NULL) return; // nothing to do |
| 1066 for (y = 0; y < pic->height; ++y) { |
| 1067 // Luma blending |
| 1068 uint8_t* const y_ptr = pic->y + y * pic->y_stride; |
| 1069 uint8_t* const a_ptr = pic->a + y * pic->a_stride; |
| 1070 for (x = 0; x < pic->width; ++x) { |
| 1071 const int alpha = a_ptr[x]; |
| 1072 if (alpha < 0xff) { |
| 1073 y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]); |
| 1074 } |
| 1075 } |
| 1076 // Chroma blending every even line |
| 1077 if ((y & 1) == 0) { |
| 1078 uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride; |
| 1079 uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride; |
| 1080 uint8_t* const a_ptr2 = |
| 1081 (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride; |
| 1082 for (x = 0; x < uv_width; ++x) { |
| 1083 // Average four alpha values into a single blending weight. |
| 1084 // TODO(skal): might lead to visible contouring. Can we do better? |
| 1085 const int alpha = |
| 1086 a_ptr[2 * x + 0] + a_ptr[2 * x + 1] + |
| 1087 a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1]; |
| 1088 u[x] = BLEND_10BIT(U0, u[x], alpha); |
| 1089 v[x] = BLEND_10BIT(V0, v[x], alpha); |
| 1090 } |
| 1091 if (pic->width & 1) { // rightmost pixel |
| 1092 const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]); |
| 1093 u[x] = BLEND_10BIT(U0, u[x], alpha); |
| 1094 v[x] = BLEND_10BIT(V0, v[x], alpha); |
| 1095 } |
| 1096 } |
| 1097 memset(a_ptr, 0xff, pic->width); |
| 1098 } |
| 1099 } else { |
| 1100 uint32_t* argb = pic->argb; |
| 1101 const uint32_t background = MakeARGB32(red, green, blue); |
| 1102 for (y = 0; y < pic->height; ++y) { |
| 1103 for (x = 0; x < pic->width; ++x) { |
| 1104 const int alpha = (argb[x] >> 24) & 0xff; |
| 1105 if (alpha != 0xff) { |
| 1106 if (alpha > 0) { |
| 1107 int r = (argb[x] >> 16) & 0xff; |
| 1108 int g = (argb[x] >> 8) & 0xff; |
| 1109 int b = (argb[x] >> 0) & 0xff; |
| 1110 r = BLEND(red, r, alpha); |
| 1111 g = BLEND(green, g, alpha); |
| 1112 b = BLEND(blue, b, alpha); |
| 1113 argb[x] = MakeARGB32(r, g, b); |
| 1114 } else { |
| 1115 argb[x] = background; |
| 1116 } |
| 1117 } |
| 1118 } |
| 1119 argb += pic->argb_stride; |
| 1120 } |
| 1121 } |
| 1122 } |
| 1123 |
| 1124 #undef BLEND |
| 1125 #undef BLEND_10BIT |
| 1126 |
| 1127 //------------------------------------------------------------------------------ |
916 // local-min distortion | 1128 // local-min distortion |
917 // | 1129 // |
918 // For every pixel in the *reference* picture, we search for the local best | 1130 // For every pixel in the *reference* picture, we search for the local best |
919 // match in the compressed image. This is not a symmetrical measure. | 1131 // match in the compressed image. This is not a symmetrical measure. |
920 | 1132 |
921 // search radius. Shouldn't be too large. | 1133 // search radius. Shouldn't be too large. |
922 #define RADIUS 2 | 1134 #define RADIUS 2 |
923 | 1135 |
924 static float AccumulateLSIM(const uint8_t* src, int src_stride, | 1136 static float AccumulateLSIM(const uint8_t* src, int src_stride, |
925 const uint8_t* ref, int ref_stride, | 1137 const uint8_t* ref, int ref_stride, |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1081 *output = wrt.mem; | 1293 *output = wrt.mem; |
1082 return wrt.size; | 1294 return wrt.size; |
1083 } | 1295 } |
1084 | 1296 |
1085 #define ENCODE_FUNC(NAME, IMPORTER) \ | 1297 #define ENCODE_FUNC(NAME, IMPORTER) \ |
1086 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ | 1298 size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ |
1087 uint8_t** out) { \ | 1299 uint8_t** out) { \ |
1088 return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ | 1300 return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ |
1089 } | 1301 } |
1090 | 1302 |
1091 ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB); | 1303 ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB) |
1092 ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR); | 1304 ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR) |
1093 ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA); | 1305 ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA) |
1094 ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA); | 1306 ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA) |
1095 | 1307 |
1096 #undef ENCODE_FUNC | 1308 #undef ENCODE_FUNC |
1097 | 1309 |
1098 #define LOSSLESS_DEFAULT_QUALITY 70. | 1310 #define LOSSLESS_DEFAULT_QUALITY 70. |
1099 #define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \ | 1311 #define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \ |
1100 size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ | 1312 size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ |
1101 return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ | 1313 return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ |
1102 } | 1314 } |
1103 | 1315 |
1104 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB); | 1316 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB) |
1105 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR); | 1317 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR) |
1106 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA); | 1318 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA) |
1107 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA); | 1319 LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA) |
1108 | 1320 |
1109 #undef LOSSLESS_ENCODE_FUNC | 1321 #undef LOSSLESS_ENCODE_FUNC |
1110 | 1322 |
1111 //------------------------------------------------------------------------------ | 1323 //------------------------------------------------------------------------------ |
1112 | 1324 |
1113 #if defined(__cplusplus) || defined(c_plusplus) | |
1114 } // extern "C" | |
1115 #endif | |
OLD | NEW |