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" 2033f74dabbc7920a65ed435d7417987589febdc16Vikas Arora#include "../utils/utils.h" 21a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "webp/format_constants.h" 22466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 23a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// ----------------------------------------------------------------------------- 24a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Encodes the given alpha data via specified compression method 'method'. 25a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// The pre-processing (quantization) is performed if 'quality' is less than 100. 26a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// For such cases, the encoding is lossy. The valid range is [0, 100] for 27a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'quality' and [0, 1] for 'method': 28a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'method = 0' - No compression; 29a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'method = 1' - Use lossless coder on the alpha plane only 30a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'filter' values [0, 4] correspond to prediction modes none, horizontal, 31a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// vertical & gradient filters. The prediction mode 4 will try all the 32a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// prediction modes 0 to 3 and pick the best one. 33a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'effort_level': specifies how much effort must be spent to try and reduce 34a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// the compressed output size. In range 0 (quick) to 6 (slow). 35a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 36a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output' corresponds to the buffer containing compressed alpha data. 37a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// This buffer is allocated by this method and caller should call 3833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora// WebPSafeFree(*output) when done. 39a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 'output_size' corresponds to size of this compressed alpha buffer. 40a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 41a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Returns 1 on successfully encoding the alpha and 42a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// 0 if either: 43a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// invalid quality or method, or 44a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// memory allocation for the compressed data fails. 45a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 46a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora#include "../enc/vp8li.h" 47a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 48a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeLossless(const uint8_t* const data, int width, int height, 49a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int effort_level, // in [0..6] range 508c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriter* const bw, 51a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPAuxStats* const stats) { 52a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int ok = 0; 53a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPConfig config; 54a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPPicture picture; 55a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 56a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPPictureInit(&picture); 57a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora picture.width = width; 58a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora picture.height = height; 59a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora picture.use_argb = 1; 60a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora picture.stats = stats; 61a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (!WebPPictureAlloc(&picture)) return 0; 62a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 63a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // Transfer the alpha values to the green channel. 64a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora { 65a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int i, j; 66a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint32_t* dst = picture.argb; 67a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const uint8_t* src = data; 68a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora for (j = 0; j < picture.height; ++j) { 69a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora for (i = 0; i < picture.width; ++i) { 708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora dst[i] = src[i] << 8; // we leave A/R/B channels zero'd. 71a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 72a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora src += width; 73a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora dst += picture.argb_stride; 74a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 75a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 76a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 77a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPConfigInit(&config); 78a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora config.lossless = 1; 79a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora config.method = effort_level; // impact is very small 808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // Set a low default quality for encoding alpha. Ensure that Alpha quality at 818b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // lower methods (3 and below) is less than the threshold for triggering 828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // costly 'BackwardReferencesTraceBackwards'. 838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora config.quality = 8.f * effort_level; 841e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora assert(config.quality >= 0 && config.quality <= 100.f); 85a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 868c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = (VP8LEncodeStream(&config, &picture, bw) == VP8_ENC_OK); 87a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPPictureFree(&picture); 888c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = ok && !bw->error_; 898c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (!ok) { 908c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriterDestroy(bw); 918c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora return 0; 92a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 938c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora return 1; 948c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora 95a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora} 96a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 97a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// ----------------------------------------------------------------------------- 98a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 998b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// Small struct to hold the result of a filter mode compression attempt. 1008b720228d581a84fd173b6dcb2fa295b59db489aVikas Aroratypedef struct { 1018b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora size_t score; 1028b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora VP8BitWriter bw; 1038b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora WebPAuxStats stats; 1048b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora} FilterTrial; 1058b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 1068b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// This function always returns an initialized 'bw' object, even upon error. 107a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlphaInternal(const uint8_t* const data, int width, int height, 108a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int method, int filter, int reduce_levels, 109a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int effort_level, // in [0..6] range 110a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint8_t* const tmp_alpha, 1118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora FilterTrial* result) { 112a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int ok = 0; 113a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const uint8_t* alpha_src; 114a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora WebPFilterFunc filter_func; 115a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint8_t header; 116a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const size_t data_size = width * height; 1178c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora const uint8_t* output = NULL; 1188c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora size_t output_size = 0; 1198c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriter tmp_bw; 120a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 121a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert((uint64_t)data_size == (uint64_t)width * height); // as per spec 122a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(filter >= 0 && filter < WEBP_FILTER_LAST); 123a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(method >= ALPHA_NO_COMPRESSION); 124a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(method <= ALPHA_LOSSLESS_COMPRESSION); 125a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(sizeof(header) == ALPHA_HEADER_LEN); 126a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // TODO(skal): have a common function and #define's to validate alpha params. 127a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 128a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora filter_func = WebPFilters[filter]; 1290406ce1417f76f2034833414dcecc9f56253640cVikas Arora if (filter_func != NULL) { 1300406ce1417f76f2034833414dcecc9f56253640cVikas Arora filter_func(data, width, height, width, tmp_alpha); 131a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora alpha_src = tmp_alpha; 132a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } else { 133a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora alpha_src = data; 134a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 135a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 1368c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (method != ALPHA_NO_COMPRESSION) { 1378c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3); 1388c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = ok && EncodeLossless(alpha_src, width, height, effort_level, 1398c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora &tmp_bw, &result->stats); 1408c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (ok) { 1418c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora output = VP8LBitWriterFinish(&tmp_bw); 1428c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora output_size = VP8LBitWriterNumBytes(&tmp_bw); 1438c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (output_size > data_size) { 1448c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora // compressed size is larger than source! Revert to uncompressed mode. 1458c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora method = ALPHA_NO_COMPRESSION; 1468c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriterDestroy(&tmp_bw); 1478c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora } 1488c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora } else { 1498c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriterDestroy(&tmp_bw); 1508c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora return 0; 1518c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora } 1528c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora } 1538c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora 154a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (method == ALPHA_NO_COMPRESSION) { 1558c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora output = alpha_src; 1568c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora output_size = data_size; 1578c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = 1; 1588c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora } 1598c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora 1608c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora // Emit final result. 1618c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora header = method | (filter << 2); 1628c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; 1638c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora 1648c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size); 1658c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN); 1668c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = ok && VP8BitWriterAppend(&result->bw, output, output_size); 1678c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora 1688c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora if (method != ALPHA_NO_COMPRESSION) { 1698c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora VP8LBitWriterDestroy(&tmp_bw); 170a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 1718c098653157979e397d3954fc2ea0ee43bae6ab2Vikas Arora ok = ok && !result->bw.error_; 1728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora result->score = VP8BitWriterSize(&result->bw); 173a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora return ok; 174a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora} 175a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 176a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// ----------------------------------------------------------------------------- 177a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 178a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// TODO(skal): move to dsp/ ? 179a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic void CopyPlane(const uint8_t* src, int src_stride, 180a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint8_t* dst, int dst_stride, int width, int height) { 181a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora while (height-- > 0) { 182a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora memcpy(dst, src, width); 183a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora src += src_stride; 184a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora dst += dst_stride; 185a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 186a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora} 187a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 1880406ce1417f76f2034833414dcecc9f56253640cVikas Arorastatic int GetNumColors(const uint8_t* data, int width, int height, 1890406ce1417f76f2034833414dcecc9f56253640cVikas Arora int stride) { 1900406ce1417f76f2034833414dcecc9f56253640cVikas Arora int j; 1910406ce1417f76f2034833414dcecc9f56253640cVikas Arora int colors = 0; 1920406ce1417f76f2034833414dcecc9f56253640cVikas Arora uint8_t color[256] = { 0 }; 1930406ce1417f76f2034833414dcecc9f56253640cVikas Arora 1940406ce1417f76f2034833414dcecc9f56253640cVikas Arora for (j = 0; j < height; ++j) { 1950406ce1417f76f2034833414dcecc9f56253640cVikas Arora int i; 1960406ce1417f76f2034833414dcecc9f56253640cVikas Arora const uint8_t* const p = data + j * stride; 1970406ce1417f76f2034833414dcecc9f56253640cVikas Arora for (i = 0; i < width; ++i) { 1980406ce1417f76f2034833414dcecc9f56253640cVikas Arora color[p[i]] = 1; 1990406ce1417f76f2034833414dcecc9f56253640cVikas Arora } 2000406ce1417f76f2034833414dcecc9f56253640cVikas Arora } 2010406ce1417f76f2034833414dcecc9f56253640cVikas Arora for (j = 0; j < 256; ++j) { 2020406ce1417f76f2034833414dcecc9f56253640cVikas Arora if (color[j] > 0) ++colors; 2030406ce1417f76f2034833414dcecc9f56253640cVikas Arora } 2040406ce1417f76f2034833414dcecc9f56253640cVikas Arora return colors; 2050406ce1417f76f2034833414dcecc9f56253640cVikas Arora} 2060406ce1417f76f2034833414dcecc9f56253640cVikas Arora 2078b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE) 2088b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1) 2098b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 2108b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora// Given the input 'filter' option, return an OR'd bit-set of filters to try. 2118b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic uint32_t GetFilterMap(const uint8_t* alpha, int width, int height, 2128b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora int filter, int effort_level) { 2138b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora uint32_t bit_map = 0U; 2148b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (filter == WEBP_FILTER_FAST) { 2158b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // Quick estimate of the best candidate. 2168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora int try_filter_none = (effort_level > 3); 2178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora const int kMinColorsForFilterNone = 16; 2188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora const int kMaxColorsForFilterNone = 192; 2198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora const int num_colors = GetNumColors(alpha, width, height, width); 2208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // For low number of colors, NONE yields better compression. 2218b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE : 2228b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora EstimateBestFilter(alpha, width, height, width); 2238b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora bit_map |= 1 << filter; 2248b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // For large number of colors, try FILTER_NONE in addition to the best 2258b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // filter as well. 2268b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (try_filter_none || num_colors > kMaxColorsForFilterNone) { 2278b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora bit_map |= FILTER_TRY_NONE; 2288b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2298b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } else if (filter == WEBP_FILTER_NONE) { 2308b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora bit_map = FILTER_TRY_NONE; 2318b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } else { // WEBP_FILTER_BEST -> try all 2328b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora bit_map = FILTER_TRY_ALL; 2338b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2348b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora return bit_map; 2358b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora} 2368b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 2378b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic void InitFilterTrial(FilterTrial* const score) { 2388b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora score->score = (size_t)~0U; 2398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora VP8BitWriterInit(&score->bw, 0); 2408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora} 2418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 2428b720228d581a84fd173b6dcb2fa295b59db489aVikas Arorastatic int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, 2438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora size_t data_size, int method, int filter, 2448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora int reduce_levels, int effort_level, 2458b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora uint8_t** const output, 2468b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora size_t* const output_size, 2478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora WebPAuxStats* const stats) { 2488b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora int ok = 1; 2498b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora FilterTrial best; 2508b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora uint32_t try_map = 2518b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora GetFilterMap(alpha, width, height, filter, effort_level); 2528b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora InitFilterTrial(&best); 2538b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (try_map != FILTER_TRY_NONE) { 25433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); 2558b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (filtered_alpha == NULL) return 0; 2568b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 2578b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) { 2588b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (try_map & 1) { 2598b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora FilterTrial trial; 2608b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora ok = EncodeAlphaInternal(alpha, width, height, method, filter, 2618b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora reduce_levels, effort_level, filtered_alpha, 2628b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora &trial); 2638b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (ok && trial.score < best.score) { 2648b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora VP8BitWriterWipeOut(&best.bw); 2658b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora best = trial; 2668b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } else { 2678b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora VP8BitWriterWipeOut(&trial.bw); 2688b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2698b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2708b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 27133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPSafeFree(filtered_alpha); 2728b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } else { 2738b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE, 2748b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora reduce_levels, effort_level, NULL, &best); 2758b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2768b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (ok) { 2778b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (stats != NULL) *stats = best.stats; 2788b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora *output_size = VP8BitWriterSize(&best.bw); 2798b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora *output = VP8BitWriterBuf(&best.bw); 2808b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } else { 2818b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora VP8BitWriterWipeOut(&best.bw); 2828b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 2838b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora return ok; 2848b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora} 2858b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 286a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arorastatic int EncodeAlpha(VP8Encoder* const enc, 287a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int quality, int method, int filter, 288a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int effort_level, 289a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint8_t** const output, size_t* const output_size) { 290a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const WebPPicture* const pic = enc->pic_; 291a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const int width = pic->width; 292a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const int height = pic->height; 293466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 294a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint8_t* quant_alpha = NULL; 295a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const size_t data_size = width * height; 296a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora uint64_t sse = 0; 297a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora int ok = 1; 298a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const int reduce_levels = (quality < 100); 299466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 300a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // quick sanity checks 301a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert((uint64_t)data_size == (uint64_t)width * height); // as per spec 302a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(enc != NULL && pic != NULL && pic->a != NULL); 303a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(output != NULL && output_size != NULL); 304a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(width > 0 && height > 0); 305a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(pic->a_stride >= width); 306a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); 307a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 308a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (quality < 0 || quality > 100) { 309a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora return 0; 310a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 311466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 312a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { 313a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora return 0; 314a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 315466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 3168b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (method == ALPHA_NO_COMPRESSION) { 3178b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora // Don't filter, as filtering will make no impact on compressed size. 3188b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora filter = WEBP_FILTER_NONE; 3198b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora } 3208b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 32133f74dabbc7920a65ed435d7417987589febdc16Vikas Arora quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); 322a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (quant_alpha == NULL) { 323466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora return 0; 324466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora } 325a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 326a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // Extract alpha data (width x height) from raw_data (stride x height). 327a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height); 328a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 329a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (reduce_levels) { // No Quantization required for 'quality = 100'. 330a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence 331a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] 332a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora // and Quality:]70, 100] -> Levels:]16, 256]. 333a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora const int alpha_levels = (quality <= 70) ? (2 + quality / 5) 334a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora : (16 + (quality - 70) * 8); 335a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse); 336a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 337a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 338a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora if (ok) { 3398b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method, 3408b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora filter, reduce_levels, effort_level, output, 3418b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora output_size, pic->stats); 3428b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora if (pic->stats != NULL) { // need stats? 3438b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora pic->stats->coded_size += (int)(*output_size); 3448b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora enc->sse_[3] = sse; 345a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 346466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora } 3478b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora 34833f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPSafeFree(quant_alpha); 349a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora return ok; 350466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora} 351466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 352a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora//------------------------------------------------------------------------------ 353a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora// Main calls 354a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora 3551e7bf8805bd030c19924a5306837ecd72c295751Vikas Arorastatic int CompressAlphaJob(VP8Encoder* const enc, void* dummy) { 3561e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora const WebPConfig* config = enc->config_; 3571e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora uint8_t* alpha_data = NULL; 3581e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora size_t alpha_size = 0; 3591e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora const int effort_level = config->method; // maps to [0..6] 3601e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora const WEBP_FILTER_TYPE filter = 3611e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : 3621e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : 3631e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora WEBP_FILTER_BEST; 3641e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, 3651e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora filter, effort_level, &alpha_data, &alpha_size)) { 3661e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 0; 3671e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 3681e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (alpha_size != (uint32_t)alpha_size) { // Sanity check. 36933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPSafeFree(alpha_data); 3701e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 0; 3711e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 3721e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora enc->alpha_data_size_ = (uint32_t)alpha_size; 3731e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora enc->alpha_data_ = alpha_data; 3741e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora (void)dummy; 3751e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 1; 3761e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora} 3771e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora 378a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Aroravoid VP8EncInitAlpha(VP8Encoder* const enc) { 379a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); 380466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora enc->alpha_data_ = NULL; 381466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora enc->alpha_data_size_ = 0; 3821e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (enc->thread_level_ > 0) { 3831e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora WebPWorker* const worker = &enc->alpha_worker_; 38433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPGetWorkerInterface()->Init(worker); 3851e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora worker->data1 = enc; 3861e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora worker->data2 = NULL; 3871e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora worker->hook = (WebPWorkerHook)CompressAlphaJob; 3881e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 389466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora} 390466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 3911e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncStartAlpha(VP8Encoder* const enc) { 392466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora if (enc->has_alpha_) { 3931e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (enc->thread_level_ > 0) { 3941e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora WebPWorker* const worker = &enc->alpha_worker_; 39533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora // Makes sure worker is good to go. 39633f74dabbc7920a65ed435d7417987589febdc16Vikas Arora if (!WebPGetWorkerInterface()->Reset(worker)) { 3971e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 0; 3981e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 39933f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPGetWorkerInterface()->Launch(worker); 4001e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 1; 4011e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } else { 4021e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return CompressAlphaJob(enc, NULL); // just do the job right away 403466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora } 4041e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 4051e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return 1; 4061e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora} 4071e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora 4081e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncFinishAlpha(VP8Encoder* const enc) { 4091e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (enc->has_alpha_) { 4101e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (enc->thread_level_ > 0) { 4111e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora WebPWorker* const worker = &enc->alpha_worker_; 41233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora if (!WebPGetWorkerInterface()->Sync(worker)) return 0; // error 413a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora } 414466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora } 415a2415724fb3466168b2af5b08bd94ba732c0e753Vikas Arora return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); 416466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora} 417466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 4181e7bf8805bd030c19924a5306837ecd72c295751Vikas Aroraint VP8EncDeleteAlpha(VP8Encoder* const enc) { 4191e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora int ok = 1; 4201e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora if (enc->thread_level_ > 0) { 4211e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora WebPWorker* const worker = &enc->alpha_worker_; 42233f74dabbc7920a65ed435d7417987589febdc16Vikas Arora // finish anything left in flight 42333f74dabbc7920a65ed435d7417987589febdc16Vikas Arora ok = WebPGetWorkerInterface()->Sync(worker); 42433f74dabbc7920a65ed435d7417987589febdc16Vikas Arora // still need to end the worker, even if !ok 42533f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPGetWorkerInterface()->End(worker); 4261e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora } 42733f74dabbc7920a65ed435d7417987589febdc16Vikas Arora WebPSafeFree(enc->alpha_data_); 428466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora enc->alpha_data_ = NULL; 429466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora enc->alpha_data_size_ = 0; 430466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora enc->has_alpha_ = 0; 4311e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora return ok; 432466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora} 433466727975bcc57c0c5597bcd0747a2fe4777b303Vikas Arora 434