picture_tools_enc.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. #include <assert.h>
  14. #include "./vp8i_enc.h"
  15. #include "../dsp/yuv.h"
  16. //------------------------------------------------------------------------------
  17. // Helper: clean up fully transparent area to help compressibility.
  18. #define SIZE 8
  19. #define SIZE2 (SIZE / 2)
  20. static int IsTransparentARGBArea(const uint32_t* ptr, int stride, int size) {
  21. int y, x;
  22. for (y = 0; y < size; ++y) {
  23. for (x = 0; x < size; ++x) {
  24. if (ptr[x] & 0xff000000u) {
  25. return 0;
  26. }
  27. }
  28. ptr += stride;
  29. }
  30. return 1;
  31. }
  32. static void Flatten(uint8_t* ptr, int v, int stride, int size) {
  33. int y;
  34. for (y = 0; y < size; ++y) {
  35. memset(ptr, v, size);
  36. ptr += stride;
  37. }
  38. }
  39. static void FlattenARGB(uint32_t* ptr, uint32_t v, int stride, int size) {
  40. int x, y;
  41. for (y = 0; y < size; ++y) {
  42. for (x = 0; x < size; ++x) ptr[x] = v;
  43. ptr += stride;
  44. }
  45. }
  46. // Smoothen the luma components of transparent pixels. Return true if the whole
  47. // block is transparent.
  48. static int SmoothenBlock(const uint8_t* a_ptr, int a_stride, uint8_t* y_ptr,
  49. int y_stride, int width, int height) {
  50. int sum = 0, count = 0;
  51. int x, y;
  52. const uint8_t* alpha_ptr = a_ptr;
  53. uint8_t* luma_ptr = y_ptr;
  54. for (y = 0; y < height; ++y) {
  55. for (x = 0; x < width; ++x) {
  56. if (alpha_ptr[x] != 0) {
  57. ++count;
  58. sum += luma_ptr[x];
  59. }
  60. }
  61. alpha_ptr += a_stride;
  62. luma_ptr += y_stride;
  63. }
  64. if (count > 0 && count < width * height) {
  65. const uint8_t avg_u8 = (uint8_t)(sum / count);
  66. alpha_ptr = a_ptr;
  67. luma_ptr = y_ptr;
  68. for (y = 0; y < height; ++y) {
  69. for (x = 0; x < width; ++x) {
  70. if (alpha_ptr[x] == 0) luma_ptr[x] = avg_u8;
  71. }
  72. alpha_ptr += a_stride;
  73. luma_ptr += y_stride;
  74. }
  75. }
  76. return (count == 0);
  77. }
  78. void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color) {
  79. if (pic != NULL && pic->use_argb) {
  80. int y = pic->height;
  81. uint32_t* argb = pic->argb;
  82. color &= 0xffffffu; // force alpha=0
  83. WebPInitAlphaProcessing();
  84. while (y-- > 0) {
  85. WebPAlphaReplace(argb, pic->width, color);
  86. argb += pic->argb_stride;
  87. }
  88. }
  89. }
  90. void WebPCleanupTransparentArea(WebPPicture* pic) {
  91. int x, y, w, h;
  92. if (pic == NULL) return;
  93. w = pic->width / SIZE;
  94. h = pic->height / SIZE;
  95. // note: we ignore the left-overs on right/bottom, except for SmoothenBlock().
  96. if (pic->use_argb) {
  97. uint32_t argb_value = 0;
  98. for (y = 0; y < h; ++y) {
  99. int need_reset = 1;
  100. for (x = 0; x < w; ++x) {
  101. const int off = (y * pic->argb_stride + x) * SIZE;
  102. if (IsTransparentARGBArea(pic->argb + off, pic->argb_stride, SIZE)) {
  103. if (need_reset) {
  104. argb_value = pic->argb[off];
  105. need_reset = 0;
  106. }
  107. FlattenARGB(pic->argb + off, argb_value, pic->argb_stride, SIZE);
  108. } else {
  109. need_reset = 1;
  110. }
  111. }
  112. }
  113. } else {
  114. const int width = pic->width;
  115. const int height = pic->height;
  116. const int y_stride = pic->y_stride;
  117. const int uv_stride = pic->uv_stride;
  118. const int a_stride = pic->a_stride;
  119. uint8_t* y_ptr = pic->y;
  120. uint8_t* u_ptr = pic->u;
  121. uint8_t* v_ptr = pic->v;
  122. const uint8_t* a_ptr = pic->a;
  123. int values[3] = { 0 };
  124. if (a_ptr == NULL || y_ptr == NULL || u_ptr == NULL || v_ptr == NULL) {
  125. return;
  126. }
  127. for (y = 0; y + SIZE <= height; y += SIZE) {
  128. int need_reset = 1;
  129. for (x = 0; x + SIZE <= width; x += SIZE) {
  130. if (SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
  131. SIZE, SIZE)) {
  132. if (need_reset) {
  133. values[0] = y_ptr[x];
  134. values[1] = u_ptr[x >> 1];
  135. values[2] = v_ptr[x >> 1];
  136. need_reset = 0;
  137. }
  138. Flatten(y_ptr + x, values[0], y_stride, SIZE);
  139. Flatten(u_ptr + (x >> 1), values[1], uv_stride, SIZE2);
  140. Flatten(v_ptr + (x >> 1), values[2], uv_stride, SIZE2);
  141. } else {
  142. need_reset = 1;
  143. }
  144. }
  145. if (x < width) {
  146. SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
  147. width - x, SIZE);
  148. }
  149. a_ptr += SIZE * a_stride;
  150. y_ptr += SIZE * y_stride;
  151. u_ptr += SIZE2 * uv_stride;
  152. v_ptr += SIZE2 * uv_stride;
  153. }
  154. if (y < height) {
  155. const int sub_height = height - y;
  156. for (x = 0; x + SIZE <= width; x += SIZE) {
  157. SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
  158. SIZE, sub_height);
  159. }
  160. if (x < width) {
  161. SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
  162. width - x, sub_height);
  163. }
  164. }
  165. }
  166. }
  167. #undef SIZE
  168. #undef SIZE2
  169. //------------------------------------------------------------------------------
  170. // Blend color and remove transparency info
  171. #define BLEND(V0, V1, ALPHA) \
  172. ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 256) >> 16)
  173. #define BLEND_10BIT(V0, V1, ALPHA) \
  174. ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 1024) >> 18)
  175. static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
  176. return (0xff000000u | (r << 16) | (g << 8) | b);
  177. }
  178. void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
  179. const int red = (background_rgb >> 16) & 0xff;
  180. const int green = (background_rgb >> 8) & 0xff;
  181. const int blue = (background_rgb >> 0) & 0xff;
  182. int x, y;
  183. if (pic == NULL) return;
  184. if (!pic->use_argb) {
  185. const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
  186. const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
  187. // VP8RGBToU/V expects the u/v values summed over four pixels
  188. const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
  189. const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
  190. const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
  191. uint8_t* y_ptr = pic->y;
  192. uint8_t* u_ptr = pic->u;
  193. uint8_t* v_ptr = pic->v;
  194. uint8_t* a_ptr = pic->a;
  195. if (!has_alpha || a_ptr == NULL) return; // nothing to do
  196. for (y = 0; y < pic->height; ++y) {
  197. // Luma blending
  198. for (x = 0; x < pic->width; ++x) {
  199. const uint8_t alpha = a_ptr[x];
  200. if (alpha < 0xff) {
  201. y_ptr[x] = BLEND(Y0, y_ptr[x], alpha);
  202. }
  203. }
  204. // Chroma blending every even line
  205. if ((y & 1) == 0) {
  206. uint8_t* const a_ptr2 =
  207. (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
  208. for (x = 0; x < uv_width; ++x) {
  209. // Average four alpha values into a single blending weight.
  210. // TODO(skal): might lead to visible contouring. Can we do better?
  211. const uint32_t alpha =
  212. a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
  213. a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
  214. u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
  215. v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
  216. }
  217. if (pic->width & 1) { // rightmost pixel
  218. const uint32_t alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
  219. u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
  220. v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
  221. }
  222. } else {
  223. u_ptr += pic->uv_stride;
  224. v_ptr += pic->uv_stride;
  225. }
  226. memset(a_ptr, 0xff, pic->width); // reset alpha value to opaque
  227. a_ptr += pic->a_stride;
  228. y_ptr += pic->y_stride;
  229. }
  230. } else {
  231. uint32_t* argb = pic->argb;
  232. const uint32_t background = MakeARGB32(red, green, blue);
  233. for (y = 0; y < pic->height; ++y) {
  234. for (x = 0; x < pic->width; ++x) {
  235. const int alpha = (argb[x] >> 24) & 0xff;
  236. if (alpha != 0xff) {
  237. if (alpha > 0) {
  238. int r = (argb[x] >> 16) & 0xff;
  239. int g = (argb[x] >> 8) & 0xff;
  240. int b = (argb[x] >> 0) & 0xff;
  241. r = BLEND(red, r, alpha);
  242. g = BLEND(green, g, alpha);
  243. b = BLEND(blue, b, alpha);
  244. argb[x] = MakeARGB32(r, g, b);
  245. } else {
  246. argb[x] = background;
  247. }
  248. }
  249. }
  250. argb += pic->argb_stride;
  251. }
  252. }
  253. }
  254. #undef BLEND
  255. #undef BLEND_10BIT
  256. //------------------------------------------------------------------------------