go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

picture_tools_enc.c (8966B)


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