Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: third_party/libwebp/enc/picture.c

Issue 116213006: Update libwebp to 0.4.0 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: After Blink Roll Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/libwebp/enc/layer.c ('k') | third_party/libwebp/enc/quant.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « third_party/libwebp/enc/layer.c ('k') | third_party/libwebp/enc/quant.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698