1 // Copyright 2014 Google Inc. All Rights Reserved.
2 //
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
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // WebPPicture tools: alpha handling, etc.
11 //
12 // Author: Skal (pascal.massimino@gmail.com)
13
14 #include <assert.h>
15
16 #include "./vp8enci.h"
17 #include "../dsp/yuv.h"
18
MakeARGB32(int r,int g,int b)19 static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
20 return (0xff000000u | (r << 16) | (g << 8) | b);
21 }
22
23 //------------------------------------------------------------------------------
24 // Helper: clean up fully transparent area to help compressibility.
25
26 #define SIZE 8
27 #define SIZE2 (SIZE / 2)
is_transparent_area(const uint8_t * ptr,int stride,int size)28 static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
29 int y, x;
30 for (y = 0; y < size; ++y) {
31 for (x = 0; x < size; ++x) {
32 if (ptr[x]) {
33 return 0;
34 }
35 }
36 ptr += stride;
37 }
38 return 1;
39 }
40
is_transparent_argb_area(const uint32_t * ptr,int stride,int size)41 static int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) {
42 int y, x;
43 for (y = 0; y < size; ++y) {
44 for (x = 0; x < size; ++x) {
45 if (ptr[x] & 0xff000000u) {
46 return 0;
47 }
48 }
49 ptr += stride;
50 }
51 return 1;
52 }
53
flatten(uint8_t * ptr,int v,int stride,int size)54 static void flatten(uint8_t* ptr, int v, int stride, int size) {
55 int y;
56 for (y = 0; y < size; ++y) {
57 memset(ptr, v, size);
58 ptr += stride;
59 }
60 }
61
flatten_argb(uint32_t * ptr,uint32_t v,int stride,int size)62 static void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) {
63 int x, y;
64 for (y = 0; y < size; ++y) {
65 for (x = 0; x < size; ++x) ptr[x] = v;
66 ptr += stride;
67 }
68 }
69
WebPCleanupTransparentArea(WebPPicture * pic)70 void WebPCleanupTransparentArea(WebPPicture* pic) {
71 int x, y, w, h;
72 if (pic == NULL) return;
73 w = pic->width / SIZE;
74 h = pic->height / SIZE;
75
76 // note: we ignore the left-overs on right/bottom
77 if (pic->use_argb) {
78 uint32_t argb_value = 0;
79 for (y = 0; y < h; ++y) {
80 int need_reset = 1;
81 for (x = 0; x < w; ++x) {
82 const int off = (y * pic->argb_stride + x) * SIZE;
83 if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) {
84 if (need_reset) {
85 argb_value = pic->argb[off];
86 need_reset = 0;
87 }
88 flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE);
89 } else {
90 need_reset = 1;
91 }
92 }
93 }
94 } else {
95 const uint8_t* const a_ptr = pic->a;
96 int values[3] = { 0 };
97 if (a_ptr == NULL) return; // nothing to do
98 for (y = 0; y < h; ++y) {
99 int need_reset = 1;
100 for (x = 0; x < w; ++x) {
101 const int off_a = (y * pic->a_stride + x) * SIZE;
102 const int off_y = (y * pic->y_stride + x) * SIZE;
103 const int off_uv = (y * pic->uv_stride + x) * SIZE2;
104 if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
105 if (need_reset) {
106 values[0] = pic->y[off_y];
107 values[1] = pic->u[off_uv];
108 values[2] = pic->v[off_uv];
109 need_reset = 0;
110 }
111 flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
112 flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
113 flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
114 } else {
115 need_reset = 1;
116 }
117 }
118 }
119 }
120 }
121
122 #undef SIZE
123 #undef SIZE2
124
WebPCleanupTransparentAreaLossless(WebPPicture * const pic)125 void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) {
126 int x, y, w, h;
127 uint32_t* argb;
128 assert(pic != NULL && pic->use_argb);
129 w = pic->width;
130 h = pic->height;
131 argb = pic->argb;
132
133 for (y = 0; y < h; ++y) {
134 for (x = 0; x < w; ++x) {
135 if ((argb[x] & 0xff000000) == 0) {
136 argb[x] = 0x00000000;
137 }
138 }
139 argb += pic->argb_stride;
140 }
141 }
142
143 //------------------------------------------------------------------------------
144 // Blend color and remove transparency info
145
146 #define BLEND(V0, V1, ALPHA) \
147 ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
148 #define BLEND_10BIT(V0, V1, ALPHA) \
149 ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
150
WebPBlendAlpha(WebPPicture * pic,uint32_t background_rgb)151 void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
152 const int red = (background_rgb >> 16) & 0xff;
153 const int green = (background_rgb >> 8) & 0xff;
154 const int blue = (background_rgb >> 0) & 0xff;
155 int x, y;
156 if (pic == NULL) return;
157 if (!pic->use_argb) {
158 const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
159 const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
160 // VP8RGBToU/V expects the u/v values summed over four pixels
161 const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
162 const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
163 const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
164 if (!has_alpha || pic->a == NULL) return; // nothing to do
165 for (y = 0; y < pic->height; ++y) {
166 // Luma blending
167 uint8_t* const y_ptr = pic->y + y * pic->y_stride;
168 uint8_t* const a_ptr = pic->a + y * pic->a_stride;
169 for (x = 0; x < pic->width; ++x) {
170 const int alpha = a_ptr[x];
171 if (alpha < 0xff) {
172 y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
173 }
174 }
175 // Chroma blending every even line
176 if ((y & 1) == 0) {
177 uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
178 uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
179 uint8_t* const a_ptr2 =
180 (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
181 for (x = 0; x < uv_width; ++x) {
182 // Average four alpha values into a single blending weight.
183 // TODO(skal): might lead to visible contouring. Can we do better?
184 const int alpha =
185 a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
186 a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
187 u[x] = BLEND_10BIT(U0, u[x], alpha);
188 v[x] = BLEND_10BIT(V0, v[x], alpha);
189 }
190 if (pic->width & 1) { // rightmost pixel
191 const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
192 u[x] = BLEND_10BIT(U0, u[x], alpha);
193 v[x] = BLEND_10BIT(V0, v[x], alpha);
194 }
195 }
196 memset(a_ptr, 0xff, pic->width);
197 }
198 } else {
199 uint32_t* argb = pic->argb;
200 const uint32_t background = MakeARGB32(red, green, blue);
201 for (y = 0; y < pic->height; ++y) {
202 for (x = 0; x < pic->width; ++x) {
203 const int alpha = (argb[x] >> 24) & 0xff;
204 if (alpha != 0xff) {
205 if (alpha > 0) {
206 int r = (argb[x] >> 16) & 0xff;
207 int g = (argb[x] >> 8) & 0xff;
208 int b = (argb[x] >> 0) & 0xff;
209 r = BLEND(red, r, alpha);
210 g = BLEND(green, g, alpha);
211 b = BLEND(blue, b, alpha);
212 argb[x] = MakeARGB32(r, g, b);
213 } else {
214 argb[x] = background;
215 }
216 }
217 }
218 argb += pic->argb_stride;
219 }
220 }
221 }
222
223 #undef BLEND
224 #undef BLEND_10BIT
225
226 //------------------------------------------------------------------------------
227