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