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 (at) gmail.com) 13 14 #include <assert.h> 15 16 #include "./vp8i_enc.h" 17 #include "../dsp/yuv.h" 18 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) 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 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 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 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 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 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 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