| OLD | NEW |
| (Empty) |
| 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 | |
| 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 //------------------------------------------------------------------------------ | |
| OLD | NEW |