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