133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// Copyright 2014 Google Inc. All Rights Reserved.
233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//
333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// Use of this source code is governed by a BSD-style license
433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// that can be found in the COPYING file in the root of the source
533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// tree. An additional intellectual property rights grant can be found
633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// in the file PATENTS. All contributing project authors may
733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// be found in the AUTHORS file in the root of the source tree.
833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// -----------------------------------------------------------------------------
933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//
1033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// WebPPicture tools: alpha handling, etc.
1133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//
1233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// Author: Skal (pascal.massimino@gmail.com)
1333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
1433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#include "./vp8enci.h"
1533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#include "../dsp/yuv.h"
1633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
1733f74dabbc7920a65ed435d7417987589febdc16Vikas Arorastatic WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
1833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  return (0xff000000u | (r << 16) | (g << 8) | b);
1933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
2033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
2133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//------------------------------------------------------------------------------
2233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// Helper: clean up fully transparent area to help compressibility.
2333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
2433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#define SIZE 8
2533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#define SIZE2 (SIZE / 2)
2633f74dabbc7920a65ed435d7417987589febdc16Vikas Arorastatic int is_transparent_area(const uint8_t* ptr, int stride, int size) {
2733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int y, x;
2833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  for (y = 0; y < size; ++y) {
2933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (x = 0; x < size; ++x) {
3033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      if (ptr[x]) {
3133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        return 0;
3233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
3333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
3433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ptr += stride;
3533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
3633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  return 1;
3733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
3833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
3933f74dabbc7920a65ed435d7417987589febdc16Vikas Arorastatic int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) {
4033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int y, x;
4133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  for (y = 0; y < size; ++y) {
4233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (x = 0; x < size; ++x) {
4333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      if (ptr[x] & 0xff000000u) {
4433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        return 0;
4533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
4633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
4733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ptr += stride;
4833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
4933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  return 1;
5033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
5133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
5233f74dabbc7920a65ed435d7417987589febdc16Vikas Arorastatic void flatten(uint8_t* ptr, int v, int stride, int size) {
5333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int y;
5433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  for (y = 0; y < size; ++y) {
5533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    memset(ptr, v, size);
5633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ptr += stride;
5733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
5833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
5933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
6033f74dabbc7920a65ed435d7417987589febdc16Vikas Arorastatic void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) {
6133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int x, y;
6233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  for (y = 0; y < size; ++y) {
6333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (x = 0; x < size; ++x) ptr[x] = v;
6433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ptr += stride;
6533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
6633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
6733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
6833f74dabbc7920a65ed435d7417987589febdc16Vikas Aroravoid WebPCleanupTransparentArea(WebPPicture* pic) {
6933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int x, y, w, h;
7033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  if (pic == NULL) return;
7133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  w = pic->width / SIZE;
7233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  h = pic->height / SIZE;
7333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
7433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  // note: we ignore the left-overs on right/bottom
7533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  if (pic->use_argb) {
7633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    uint32_t argb_value = 0;
7733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (y = 0; y < h; ++y) {
7833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      int need_reset = 1;
7933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      for (x = 0; x < w; ++x) {
8033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int off = (y * pic->argb_stride + x) * SIZE;
8133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) {
8233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          if (need_reset) {
8333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            argb_value = pic->argb[off];
8433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            need_reset = 0;
8533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          }
8633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE);
8733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        } else {
8833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          need_reset = 1;
8933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
9033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
9133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
9233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  } else {
9333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const uint8_t* const a_ptr = pic->a;
9433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    int values[3] = { 0 };
9533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    if (a_ptr == NULL) return;    // nothing to do
9633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (y = 0; y < h; ++y) {
9733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      int need_reset = 1;
9833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      for (x = 0; x < w; ++x) {
9933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int off_a = (y * pic->a_stride + x) * SIZE;
10033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int off_y = (y * pic->y_stride + x) * SIZE;
10133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int off_uv = (y * pic->uv_stride + x) * SIZE2;
10233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
10333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          if (need_reset) {
10433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            values[0] = pic->y[off_y];
10533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            values[1] = pic->u[off_uv];
10633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            values[2] = pic->v[off_uv];
10733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            need_reset = 0;
10833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          }
10933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
11033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
11133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
11233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        } else {
11333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          need_reset = 1;
11433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
11533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
11633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
11733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
11833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
11933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
12033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#undef SIZE
12133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#undef SIZE2
12233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
12333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//------------------------------------------------------------------------------
12433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// Blend color and remove transparency info
12533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
12633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#define BLEND(V0, V1, ALPHA) \
12733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
12833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#define BLEND_10BIT(V0, V1, ALPHA) \
12933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
13033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
13133f74dabbc7920a65ed435d7417987589febdc16Vikas Aroravoid WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
13233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  const int red = (background_rgb >> 16) & 0xff;
13333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  const int green = (background_rgb >> 8) & 0xff;
13433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  const int blue = (background_rgb >> 0) & 0xff;
13533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  int x, y;
13633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  if (pic == NULL) return;
13733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  if (!pic->use_argb) {
13833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const int uv_width = (pic->width >> 1);  // omit last pixel during u/v loop
13933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
14033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    // VP8RGBToU/V expects the u/v values summed over four pixels
14133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
14233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
14333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
14433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    if (!has_alpha || pic->a == NULL) return;    // nothing to do
14533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (y = 0; y < pic->height; ++y) {
14633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      // Luma blending
14733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      uint8_t* const y_ptr = pic->y + y * pic->y_stride;
14833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      uint8_t* const a_ptr = pic->a + y * pic->a_stride;
14933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      for (x = 0; x < pic->width; ++x) {
15033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int alpha = a_ptr[x];
15133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        if (alpha < 0xff) {
15233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
15333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
15433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
15533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      // Chroma blending every even line
15633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      if ((y & 1) == 0) {
15733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
15833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
15933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        uint8_t* const a_ptr2 =
16033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
16133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        for (x = 0; x < uv_width; ++x) {
16233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          // Average four alpha values into a single blending weight.
16333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          // TODO(skal): might lead to visible contouring. Can we do better?
16433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          const int alpha =
16533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora              a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
16633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora              a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
16733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          u[x] = BLEND_10BIT(U0, u[x], alpha);
16833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          v[x] = BLEND_10BIT(V0, v[x], alpha);
16933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
17033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        if (pic->width & 1) {   // rightmost pixel
17133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
17233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          u[x] = BLEND_10BIT(U0, u[x], alpha);
17333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          v[x] = BLEND_10BIT(V0, v[x], alpha);
17433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
17533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
17633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      memset(a_ptr, 0xff, pic->width);
17733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
17833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  } else {
17933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    uint32_t* argb = pic->argb;
18033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    const uint32_t background = MakeARGB32(red, green, blue);
18133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    for (y = 0; y < pic->height; ++y) {
18233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      for (x = 0; x < pic->width; ++x) {
18333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        const int alpha = (argb[x] >> 24) & 0xff;
18433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        if (alpha != 0xff) {
18533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          if (alpha > 0) {
18633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            int r = (argb[x] >> 16) & 0xff;
18733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            int g = (argb[x] >>  8) & 0xff;
18833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            int b = (argb[x] >>  0) & 0xff;
18933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            r = BLEND(red, r, alpha);
19033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            g = BLEND(green, g, alpha);
19133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            b = BLEND(blue, b, alpha);
19233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            argb[x] = MakeARGB32(r, g, b);
19333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          } else {
19433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora            argb[x] = background;
19533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora          }
19633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora        }
19733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      }
19833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora      argb += pic->argb_stride;
19933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora    }
20033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora  }
20133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora}
20233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
20333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#undef BLEND
20433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#undef BLEND_10BIT
20533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora
20633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora//------------------------------------------------------------------------------
207