1a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Copyright 2011 Google Inc. All Rights Reserved.
2466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
30406ce1417f76f2034833414dcecc9f56253640cVikas Arora// Use of this source code is governed by a BSD-style license
40406ce1417f76f2034833414dcecc9f56253640cVikas Arora// that can be found in the COPYING file in the root of the source
50406ce1417f76f2034833414dcecc9f56253640cVikas Arora// tree. An additional intellectual property rights grant can be found
60406ce1417f76f2034833414dcecc9f56253640cVikas Arora// in the file PATENTS. All contributing project authors may
70406ce1417f76f2034833414dcecc9f56253640cVikas Arora// be found in the AUTHORS file in the root of the source tree.
8466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// -----------------------------------------------------------------------------
9466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
10466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Alpha-plane compression.
11466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora//
12466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora// Author: Skal (pascal.massimino@gmail.com)
13466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
14466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#include <assert.h>
15466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#include <stdlib.h>
16466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
17a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "./vp8enci.h"
18a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../utils/filters.h"
19a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../utils/quant_levels.h"
20a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "webp/format_constants.h"
21466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
22466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#if defined(__cplusplus) || defined(c_plusplus)
23466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Aroraextern "C" {
24466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#endif
25466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
26a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
27a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Encodes the given alpha data via specified compression method 'method'.
28a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// The pre-processing (quantization) is performed if 'quality' is less than 100.
29a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// For such cases, the encoding is lossy. The valid range is [0, 100] for
30a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'quality' and [0, 1] for 'method':
31a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//   'method = 0' - No compression;
32a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//   'method = 1' - Use lossless coder on the alpha plane only
33a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
34a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// vertical & gradient filters. The prediction mode 4 will try all the
35a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// prediction modes 0 to 3 and pick the best one.
36a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'effort_level': specifies how much effort must be spent to try and reduce
37a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//  the compressed output size. In range 0 (quick) to 6 (slow).
38a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//
39a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output' corresponds to the buffer containing compressed alpha data.
40a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//          This buffer is allocated by this method and caller should call
41a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//          free(*output) when done.
42a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output_size' corresponds to size of this compressed alpha buffer.
43a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//
44a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Returns 1 on successfully encoding the alpha and
45a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//         0 if either:
46a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//           invalid quality or method, or
47a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//           memory allocation for the compressed data fails.
48a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
49a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../enc/vp8li.h"
50a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
51a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeLossless(const uint8_t* const data, int width, int height,
52a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                          int effort_level,  // in [0..6] range
53a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                          VP8BitWriter* const bw,
54a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                          WebPAuxStats* const stats) {
55a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 0;
56a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPConfig config;
57a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPicture picture;
58a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8LBitWriter tmp_bw;
59a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
60a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPictureInit(&picture);
61a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.width = width;
62a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.height = height;
63a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.use_argb = 1;
64a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  picture.stats = stats;
65a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (!WebPPictureAlloc(&picture)) return 0;
66a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
67a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // Transfer the alpha values to the green channel.
68a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  {
69a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    int i, j;
70a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    uint32_t* dst = picture.argb;
71a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const uint8_t* src = data;
72a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    for (j = 0; j < picture.height; ++j) {
73a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      for (i = 0; i < picture.width; ++i) {
74a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        dst[i] = (src[i] << 8) | 0xff000000u;
75a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
76a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      src += width;
77a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      dst += picture.argb_stride;
78a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
79a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
80a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
81a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPConfigInit(&config);
82a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  config.lossless = 1;
83a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  config.method = effort_level;  // impact is very small
841e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  // Set a moderate default quality setting for alpha.
850406ce1417f76f2034833414dcecc9f56253640cVikas Arora  config.quality = 10.f * effort_level;
861e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  assert(config.quality >= 0 && config.quality <= 100.f);
87a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
88a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
89a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
90a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPPictureFree(&picture);
91a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (ok) {
921e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    const uint8_t* const buffer = VP8LBitWriterFinish(&tmp_bw);
931e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    const size_t buffer_size = VP8LBitWriterNumBytes(&tmp_bw);
941e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    VP8BitWriterAppend(bw, buffer, buffer_size);
95a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
96a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8LBitWriterDestroy(&tmp_bw);
97a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok && !bw->error_;
98a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
99a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
100a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
101a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
102a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
103a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               int method, int filter, int reduce_levels,
104a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               int effort_level,  // in [0..6] range
105a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               uint8_t* const tmp_alpha,
106a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               VP8BitWriter* const bw,
107a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                               WebPAuxStats* const stats) {
108a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 0;
109a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const uint8_t* alpha_src;
110a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  WebPFilterFunc filter_func;
111a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t header;
112a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  size_t expected_size;
113a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t data_size = width * height;
114a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
115a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
116a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(filter >= 0 && filter < WEBP_FILTER_LAST);
117a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(method >= ALPHA_NO_COMPRESSION);
118a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(method <= ALPHA_LOSSLESS_COMPRESSION);
119a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(sizeof(header) == ALPHA_HEADER_LEN);
120a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // TODO(skal): have a common function and #define's to validate alpha params.
121a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
122a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  expected_size =
123a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      (method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
124a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                       : (data_size >> 5);
125a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  header = method | (filter << 2);
126a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
127a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
128a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8BitWriterInit(bw, expected_size);
129a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
130a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
131a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  filter_func = WebPFilters[filter];
1320406ce1417f76f2034833414dcecc9f56253640cVikas Arora  if (filter_func != NULL) {
1330406ce1417f76f2034833414dcecc9f56253640cVikas Arora    filter_func(data, width, height, width, tmp_alpha);
134a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    alpha_src = tmp_alpha;
135a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }  else {
136a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    alpha_src = data;
137a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
138a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
139a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (method == ALPHA_NO_COMPRESSION) {
140a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = VP8BitWriterAppend(bw, alpha_src, width * height);
141a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = ok && !bw->error_;
142a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  } else {
143a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
144a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    VP8BitWriterFinish(bw);
145a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
146a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
147a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
148a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
149a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// -----------------------------------------------------------------------------
150a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
151a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// TODO(skal): move to dsp/ ?
152a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic void CopyPlane(const uint8_t* src, int src_stride,
153a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                      uint8_t* dst, int dst_stride, int width, int height) {
154a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  while (height-- > 0) {
155a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    memcpy(dst, src, width);
156a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    src += src_stride;
157a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    dst += dst_stride;
158a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
159a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora}
160a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
1610406ce1417f76f2034833414dcecc9f56253640cVikas Arorastatic int GetNumColors(const uint8_t* data, int width, int height,
1620406ce1417f76f2034833414dcecc9f56253640cVikas Arora                        int stride) {
1630406ce1417f76f2034833414dcecc9f56253640cVikas Arora  int j;
1640406ce1417f76f2034833414dcecc9f56253640cVikas Arora  int colors = 0;
1650406ce1417f76f2034833414dcecc9f56253640cVikas Arora  uint8_t color[256] = { 0 };
1660406ce1417f76f2034833414dcecc9f56253640cVikas Arora
1670406ce1417f76f2034833414dcecc9f56253640cVikas Arora  for (j = 0; j < height; ++j) {
1680406ce1417f76f2034833414dcecc9f56253640cVikas Arora    int i;
1690406ce1417f76f2034833414dcecc9f56253640cVikas Arora    const uint8_t* const p = data + j * stride;
1700406ce1417f76f2034833414dcecc9f56253640cVikas Arora    for (i = 0; i < width; ++i) {
1710406ce1417f76f2034833414dcecc9f56253640cVikas Arora      color[p[i]] = 1;
1720406ce1417f76f2034833414dcecc9f56253640cVikas Arora    }
1730406ce1417f76f2034833414dcecc9f56253640cVikas Arora  }
1740406ce1417f76f2034833414dcecc9f56253640cVikas Arora  for (j = 0; j < 256; ++j) {
1750406ce1417f76f2034833414dcecc9f56253640cVikas Arora    if (color[j] > 0) ++colors;
1760406ce1417f76f2034833414dcecc9f56253640cVikas Arora  }
1770406ce1417f76f2034833414dcecc9f56253640cVikas Arora  return colors;
1780406ce1417f76f2034833414dcecc9f56253640cVikas Arora}
1790406ce1417f76f2034833414dcecc9f56253640cVikas Arora
180a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlpha(VP8Encoder* const enc,
181a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       int quality, int method, int filter,
182a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       int effort_level,
183a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                       uint8_t** const output, size_t* const output_size) {
184a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const WebPPicture* const pic = enc->pic_;
185a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int width = pic->width;
186a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int height = pic->height;
187466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
188a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint8_t* quant_alpha = NULL;
189a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const size_t data_size = width * height;
190a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  uint64_t sse = 0;
191a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  int ok = 1;
192a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  const int reduce_levels = (quality < 100);
193466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
194a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // quick sanity checks
195a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert((uint64_t)data_size == (uint64_t)width * height);  // as per spec
196a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(enc != NULL && pic != NULL && pic->a != NULL);
197a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(output != NULL && output_size != NULL);
198a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(width > 0 && height > 0);
199a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(pic->a_stride >= width);
200a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
201a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
202a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (quality < 0 || quality > 100) {
203a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    return 0;
204a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
205466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
206a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
207a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    return 0;
208a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
209466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
210a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  quant_alpha = (uint8_t*)malloc(data_size);
211a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (quant_alpha == NULL) {
212466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    return 0;
213466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
214a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
215a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  // Extract alpha data (width x height) from raw_data (stride x height).
216a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
217a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
218a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (reduce_levels) {  // No Quantization required for 'quality = 100'.
219a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
220a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
221a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // and Quality:]70, 100] -> Levels:]16, 256].
222a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
223a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                             : (16 + (quality - 70) * 8);
224a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
225a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  }
226a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
227a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  if (ok) {
228a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    VP8BitWriter bw;
229a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    int test_filter;
230a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    uint8_t* filtered_alpha = NULL;
2310406ce1417f76f2034833414dcecc9f56253640cVikas Arora    int try_filter_none = (effort_level > 3);
2320406ce1417f76f2034833414dcecc9f56253640cVikas Arora
2330406ce1417f76f2034833414dcecc9f56253640cVikas Arora    if (filter == WEBP_FILTER_FAST) {  // Quick estimate of the best candidate.
2340406ce1417f76f2034833414dcecc9f56253640cVikas Arora      const int kMinColorsForFilterNone = 16;
2350406ce1417f76f2034833414dcecc9f56253640cVikas Arora      const int kMaxColorsForFilterNone = 192;
2360406ce1417f76f2034833414dcecc9f56253640cVikas Arora      const int num_colors = GetNumColors(quant_alpha, width, height, width);
2370406ce1417f76f2034833414dcecc9f56253640cVikas Arora      // For low number of colors, NONE yeilds better compression.
2380406ce1417f76f2034833414dcecc9f56253640cVikas Arora      filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
2390406ce1417f76f2034833414dcecc9f56253640cVikas Arora               EstimateBestFilter(quant_alpha, width, height, width);
2400406ce1417f76f2034833414dcecc9f56253640cVikas Arora      // For large number of colors, try FILTER_NONE in addition to the best
2410406ce1417f76f2034833414dcecc9f56253640cVikas Arora      // filter as well.
2420406ce1417f76f2034833414dcecc9f56253640cVikas Arora      if (num_colors > kMaxColorsForFilterNone) {
2430406ce1417f76f2034833414dcecc9f56253640cVikas Arora        try_filter_none = 1;
2440406ce1417f76f2034833414dcecc9f56253640cVikas Arora      }
245466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
246a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
2470406ce1417f76f2034833414dcecc9f56253640cVikas Arora    // Test for WEBP_FILTER_NONE for higher effort levels.
2480406ce1417f76f2034833414dcecc9f56253640cVikas Arora    if (try_filter_none || filter == WEBP_FILTER_NONE) {
2490406ce1417f76f2034833414dcecc9f56253640cVikas Arora      ok = EncodeAlphaInternal(quant_alpha, width, height,
2500406ce1417f76f2034833414dcecc9f56253640cVikas Arora                               method, WEBP_FILTER_NONE, reduce_levels,
2510406ce1417f76f2034833414dcecc9f56253640cVikas Arora                               effort_level, NULL, &bw, pic->stats);
2520406ce1417f76f2034833414dcecc9f56253640cVikas Arora
2530406ce1417f76f2034833414dcecc9f56253640cVikas Arora      if (!ok) {
2540406ce1417f76f2034833414dcecc9f56253640cVikas Arora        VP8BitWriterWipeOut(&bw);
2550406ce1417f76f2034833414dcecc9f56253640cVikas Arora        goto End;
2560406ce1417f76f2034833414dcecc9f56253640cVikas Arora      }
257a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
258a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // Stop?
259a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (filter == WEBP_FILTER_NONE) {
260a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      goto Ok;
261466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
262466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
263a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    filtered_alpha = (uint8_t*)malloc(data_size);
264a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    ok = (filtered_alpha != NULL);
265a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (!ok) {
266a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      goto End;
267a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
268a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
269a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    // Try the other mode(s).
270a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    {
271a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      WebPAuxStats best_stats;
2720406ce1417f76f2034833414dcecc9f56253640cVikas Arora      size_t best_score = try_filter_none ?
2730406ce1417f76f2034833414dcecc9f56253640cVikas Arora                          VP8BitWriterSize(&bw) : (size_t)~0U;
2740406ce1417f76f2034833414dcecc9f56253640cVikas Arora      int wipe_tmp_bw = try_filter_none;
275a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
276a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      memset(&best_stats, 0, sizeof(best_stats));  // prevent spurious warning
277a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (pic->stats != NULL) best_stats = *pic->stats;
2780406ce1417f76f2034833414dcecc9f56253640cVikas Arora      for (test_filter =
2790406ce1417f76f2034833414dcecc9f56253640cVikas Arora           try_filter_none ? WEBP_FILTER_HORIZONTAL : WEBP_FILTER_NONE;
280a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora           ok && (test_filter <= WEBP_FILTER_GRADIENT);
281a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora           ++test_filter) {
282a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        VP8BitWriter tmp_bw;
283a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        if (filter != WEBP_FILTER_BEST && test_filter != filter) {
284a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora          continue;
285a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        }
286a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        ok = EncodeAlphaInternal(quant_alpha, width, height,
287a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                 method, test_filter, reduce_levels,
288a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                 effort_level, filtered_alpha, &tmp_bw,
289a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora                                 pic->stats);
290a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        if (ok) {
291a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora          const size_t score = VP8BitWriterSize(&tmp_bw);
292a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora          if (score < best_score) {
293a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            // swap bitwriter objects.
294a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            VP8BitWriter tmp = tmp_bw;
295a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            tmp_bw = bw;
296a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            bw = tmp;
297a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            best_score = score;
298a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora            if (pic->stats != NULL) best_stats = *pic->stats;
299a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora          }
300a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        } else {
301a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora          VP8BitWriterWipeOut(&bw);
302a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        }
3030406ce1417f76f2034833414dcecc9f56253640cVikas Arora        if (wipe_tmp_bw) {
3040406ce1417f76f2034833414dcecc9f56253640cVikas Arora          VP8BitWriterWipeOut(&tmp_bw);
3050406ce1417f76f2034833414dcecc9f56253640cVikas Arora        }
3060406ce1417f76f2034833414dcecc9f56253640cVikas Arora        wipe_tmp_bw = 1;  // For next filter trial for WEBP_FILTER_BEST.
307a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
308a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (pic->stats != NULL) *pic->stats = best_stats;
309a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
310a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora Ok:
311a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    if (ok) {
312a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      *output_size = VP8BitWriterSize(&bw);
313a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      *output = VP8BitWriterBuf(&bw);
314a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      if (pic->stats != NULL) {         // need stats?
315a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        pic->stats->coded_size += (int)(*output_size);
316a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora        enc->sse_[3] = sse;
317a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora      }
318a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
319a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    free(filtered_alpha);
320466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
321a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora End:
322a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  free(quant_alpha);
323a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return ok;
324466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
325466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
326466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
327a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------
328a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Main calls
329a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora
3301e7bf8805bd030c19924a5306837ecd72c295751Vikas Arorastatic int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
3311e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const WebPConfig* config = enc->config_;
3321e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  uint8_t* alpha_data = NULL;
3331e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  size_t alpha_size = 0;
3341e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const int effort_level = config->method;  // maps to [0..6]
3351e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  const WEBP_FILTER_TYPE filter =
3361e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
3371e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
3381e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora                                       WEBP_FILTER_BEST;
3391e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
3401e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora                   filter, effort_level, &alpha_data, &alpha_size)) {
3411e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    return 0;
3421e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
3431e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (alpha_size != (uint32_t)alpha_size) {  // Sanity check.
3441e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    free(alpha_data);
3451e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    return 0;
3461e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
3471e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  enc->alpha_data_size_ = (uint32_t)alpha_size;
3481e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  enc->alpha_data_ = alpha_data;
3491e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  (void)dummy;
3501e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return 1;
3511e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora}
3521e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
353a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroravoid VP8EncInitAlpha(VP8Encoder* const enc) {
354a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
355466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_ = NULL;
356466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_size_ = 0;
3571e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->thread_level_ > 0) {
3581e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorker* const worker = &enc->alpha_worker_;
3591e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorkerInit(worker);
3601e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->data1 = enc;
3611e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->data2 = NULL;
3621e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    worker->hook = (WebPWorkerHook)CompressAlphaJob;
3631e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
364466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
365466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
3661e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncStartAlpha(VP8Encoder* const enc) {
367466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  if (enc->has_alpha_) {
3681e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    if (enc->thread_level_ > 0) {
3691e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      WebPWorker* const worker = &enc->alpha_worker_;
3701e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      if (!WebPWorkerReset(worker)) {    // Makes sure worker is good to go.
3711e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora        return 0;
3721e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      }
3731e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      WebPWorkerLaunch(worker);
3741e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      return 1;
3751e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    } else {
3761e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      return CompressAlphaJob(enc, NULL);   // just do the job right away
377466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora    }
3781e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
3791e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return 1;
3801e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora}
3811e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
3821e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncFinishAlpha(VP8Encoder* const enc) {
3831e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->has_alpha_) {
3841e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    if (enc->thread_level_ > 0) {
3851e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      WebPWorker* const worker = &enc->alpha_worker_;
3861e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora      if (!WebPWorkerSync(worker)) return 0;  // error
387a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora    }
388466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  }
389a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora  return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
390466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
391466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
3921e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncDeleteAlpha(VP8Encoder* const enc) {
3931e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  int ok = 1;
3941e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  if (enc->thread_level_ > 0) {
3951e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorker* const worker = &enc->alpha_worker_;
3961e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    ok = WebPWorkerSync(worker);  // finish anything left in flight
3971e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora    WebPWorkerEnd(worker);  // still need to end the worker, even if !ok
3981e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  }
399466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  free(enc->alpha_data_);
400466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_ = NULL;
401466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->alpha_data_size_ = 0;
402466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora  enc->has_alpha_ = 0;
4031e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return ok;
404466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}
405466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora
406466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#if defined(__cplusplus) || defined(c_plusplus)
407466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora}    // extern "C"
408466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora#endif
409