1// Copyright 2011 Google Inc. All Rights Reserved. 2// 3// This code is licensed under the same terms as WebM: 4// Software License Agreement: http://www.webmproject.org/license/software/ 5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ 6// ----------------------------------------------------------------------------- 7// 8// Alpha-plane compression. 9// 10// Author: Skal (pascal.massimino@gmail.com) 11 12#include <assert.h> 13#include <stdlib.h> 14 15#include "./vp8enci.h" 16#include "../utils/filters.h" 17#include "../utils/quant_levels.h" 18#include "webp/format_constants.h" 19 20#if defined(__cplusplus) || defined(c_plusplus) 21extern "C" { 22#endif 23 24// ----------------------------------------------------------------------------- 25// Encodes the given alpha data via specified compression method 'method'. 26// The pre-processing (quantization) is performed if 'quality' is less than 100. 27// For such cases, the encoding is lossy. The valid range is [0, 100] for 28// 'quality' and [0, 1] for 'method': 29// 'method = 0' - No compression; 30// 'method = 1' - Use lossless coder on the alpha plane only 31// 'filter' values [0, 4] correspond to prediction modes none, horizontal, 32// vertical & gradient filters. The prediction mode 4 will try all the 33// prediction modes 0 to 3 and pick the best one. 34// 'effort_level': specifies how much effort must be spent to try and reduce 35// the compressed output size. In range 0 (quick) to 6 (slow). 36// 37// 'output' corresponds to the buffer containing compressed alpha data. 38// This buffer is allocated by this method and caller should call 39// free(*output) when done. 40// 'output_size' corresponds to size of this compressed alpha buffer. 41// 42// Returns 1 on successfully encoding the alpha and 43// 0 if either: 44// invalid quality or method, or 45// memory allocation for the compressed data fails. 46 47#include "../enc/vp8li.h" 48 49static int EncodeLossless(const uint8_t* const data, int width, int height, 50 int effort_level, // in [0..6] range 51 VP8BitWriter* const bw, 52 WebPAuxStats* const stats) { 53 int ok = 0; 54 WebPConfig config; 55 WebPPicture picture; 56 VP8LBitWriter tmp_bw; 57 58 WebPPictureInit(&picture); 59 picture.width = width; 60 picture.height = height; 61 picture.use_argb = 1; 62 picture.stats = stats; 63 if (!WebPPictureAlloc(&picture)) return 0; 64 65 // Transfer the alpha values to the green channel. 66 { 67 int i, j; 68 uint32_t* dst = picture.argb; 69 const uint8_t* src = data; 70 for (j = 0; j < picture.height; ++j) { 71 for (i = 0; i < picture.width; ++i) { 72 dst[i] = (src[i] << 8) | 0xff000000u; 73 } 74 src += width; 75 dst += picture.argb_stride; 76 } 77 } 78 79 WebPConfigInit(&config); 80 config.lossless = 1; 81 config.method = effort_level; // impact is very small 82 // Set moderate default quality setting for alpha. Higher qualities (80 and 83 // above) could be very slow. 84 config.quality = 10.f + 15.f * effort_level; 85 if (config.quality > 100.f) config.quality = 100.f; 86 87 ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3); 88 ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK); 89 WebPPictureFree(&picture); 90 if (ok) { 91 const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw); 92 const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw); 93 VP8BitWriterAppend(bw, data, data_size); 94 } 95 VP8LBitWriterDestroy(&tmp_bw); 96 return ok && !bw->error_; 97} 98 99// ----------------------------------------------------------------------------- 100 101static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, 102 int method, int filter, int reduce_levels, 103 int effort_level, // in [0..6] range 104 uint8_t* const tmp_alpha, 105 VP8BitWriter* const bw, 106 WebPAuxStats* const stats) { 107 int ok = 0; 108 const uint8_t* alpha_src; 109 WebPFilterFunc filter_func; 110 uint8_t header; 111 size_t expected_size; 112 const size_t data_size = width * height; 113 114 assert((uint64_t)data_size == (uint64_t)width * height); // as per spec 115 assert(filter >= 0 && filter < WEBP_FILTER_LAST); 116 assert(method >= ALPHA_NO_COMPRESSION); 117 assert(method <= ALPHA_LOSSLESS_COMPRESSION); 118 assert(sizeof(header) == ALPHA_HEADER_LEN); 119 // TODO(skal): have a common function and #define's to validate alpha params. 120 121 expected_size = 122 (method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size) 123 : (data_size >> 5); 124 header = method | (filter << 2); 125 if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; 126 127 VP8BitWriterInit(bw, expected_size); 128 VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN); 129 130 filter_func = WebPFilters[filter]; 131 if (filter_func) { 132 filter_func(data, width, height, 1, width, tmp_alpha); 133 alpha_src = tmp_alpha; 134 } else { 135 alpha_src = data; 136 } 137 138 if (method == ALPHA_NO_COMPRESSION) { 139 ok = VP8BitWriterAppend(bw, alpha_src, width * height); 140 ok = ok && !bw->error_; 141 } else { 142 ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats); 143 VP8BitWriterFinish(bw); 144 } 145 return ok; 146} 147 148// ----------------------------------------------------------------------------- 149 150// TODO(skal): move to dsp/ ? 151static void CopyPlane(const uint8_t* src, int src_stride, 152 uint8_t* dst, int dst_stride, int width, int height) { 153 while (height-- > 0) { 154 memcpy(dst, src, width); 155 src += src_stride; 156 dst += dst_stride; 157 } 158} 159 160static int EncodeAlpha(VP8Encoder* const enc, 161 int quality, int method, int filter, 162 int effort_level, 163 uint8_t** const output, size_t* const output_size) { 164 const WebPPicture* const pic = enc->pic_; 165 const int width = pic->width; 166 const int height = pic->height; 167 168 uint8_t* quant_alpha = NULL; 169 const size_t data_size = width * height; 170 uint64_t sse = 0; 171 int ok = 1; 172 const int reduce_levels = (quality < 100); 173 174 // quick sanity checks 175 assert((uint64_t)data_size == (uint64_t)width * height); // as per spec 176 assert(enc != NULL && pic != NULL && pic->a != NULL); 177 assert(output != NULL && output_size != NULL); 178 assert(width > 0 && height > 0); 179 assert(pic->a_stride >= width); 180 assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); 181 182 if (quality < 0 || quality > 100) { 183 return 0; 184 } 185 186 if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { 187 return 0; 188 } 189 190 quant_alpha = (uint8_t*)malloc(data_size); 191 if (quant_alpha == NULL) { 192 return 0; 193 } 194 195 // Extract alpha data (width x height) from raw_data (stride x height). 196 CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height); 197 198 if (reduce_levels) { // No Quantization required for 'quality = 100'. 199 // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence 200 // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] 201 // and Quality:]70, 100] -> Levels:]16, 256]. 202 const int alpha_levels = (quality <= 70) ? (2 + quality / 5) 203 : (16 + (quality - 70) * 8); 204 ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse); 205 } 206 207 if (ok) { 208 VP8BitWriter bw; 209 int test_filter; 210 uint8_t* filtered_alpha = NULL; 211 212 // We always test WEBP_FILTER_NONE first. 213 ok = EncodeAlphaInternal(quant_alpha, width, height, 214 method, WEBP_FILTER_NONE, reduce_levels, 215 effort_level, NULL, &bw, pic->stats); 216 if (!ok) { 217 VP8BitWriterWipeOut(&bw); 218 goto End; 219 } 220 221 if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate? 222 filter = EstimateBestFilter(quant_alpha, width, height, width); 223 } 224 // Stop? 225 if (filter == WEBP_FILTER_NONE) { 226 goto Ok; 227 } 228 229 filtered_alpha = (uint8_t*)malloc(data_size); 230 ok = (filtered_alpha != NULL); 231 if (!ok) { 232 goto End; 233 } 234 235 // Try the other mode(s). 236 { 237 WebPAuxStats best_stats; 238 size_t best_score = VP8BitWriterSize(&bw); 239 240 memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning 241 if (pic->stats != NULL) best_stats = *pic->stats; 242 for (test_filter = WEBP_FILTER_HORIZONTAL; 243 ok && (test_filter <= WEBP_FILTER_GRADIENT); 244 ++test_filter) { 245 VP8BitWriter tmp_bw; 246 if (filter != WEBP_FILTER_BEST && test_filter != filter) { 247 continue; 248 } 249 ok = EncodeAlphaInternal(quant_alpha, width, height, 250 method, test_filter, reduce_levels, 251 effort_level, filtered_alpha, &tmp_bw, 252 pic->stats); 253 if (ok) { 254 const size_t score = VP8BitWriterSize(&tmp_bw); 255 if (score < best_score) { 256 // swap bitwriter objects. 257 VP8BitWriter tmp = tmp_bw; 258 tmp_bw = bw; 259 bw = tmp; 260 best_score = score; 261 if (pic->stats != NULL) best_stats = *pic->stats; 262 } 263 } else { 264 VP8BitWriterWipeOut(&bw); 265 } 266 VP8BitWriterWipeOut(&tmp_bw); 267 } 268 if (pic->stats != NULL) *pic->stats = best_stats; 269 } 270 Ok: 271 if (ok) { 272 *output_size = VP8BitWriterSize(&bw); 273 *output = VP8BitWriterBuf(&bw); 274 if (pic->stats != NULL) { // need stats? 275 pic->stats->coded_size += (int)(*output_size); 276 enc->sse_[3] = sse; 277 } 278 } 279 free(filtered_alpha); 280 } 281 End: 282 free(quant_alpha); 283 return ok; 284} 285 286 287//------------------------------------------------------------------------------ 288// Main calls 289 290void VP8EncInitAlpha(VP8Encoder* const enc) { 291 enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); 292 enc->alpha_data_ = NULL; 293 enc->alpha_data_size_ = 0; 294} 295 296int VP8EncFinishAlpha(VP8Encoder* const enc) { 297 if (enc->has_alpha_) { 298 const WebPConfig* config = enc->config_; 299 uint8_t* tmp_data = NULL; 300 size_t tmp_size = 0; 301 const int effort_level = config->method; // maps to [0..6] 302 const WEBP_FILTER_TYPE filter = 303 (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : 304 (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : 305 WEBP_FILTER_BEST; 306 307 if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, 308 filter, effort_level, &tmp_data, &tmp_size)) { 309 return 0; 310 } 311 if (tmp_size != (uint32_t)tmp_size) { // Sanity check. 312 free(tmp_data); 313 return 0; 314 } 315 enc->alpha_data_size_ = (uint32_t)tmp_size; 316 enc->alpha_data_ = tmp_data; 317 } 318 return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); 319} 320 321void VP8EncDeleteAlpha(VP8Encoder* const enc) { 322 free(enc->alpha_data_); 323 enc->alpha_data_ = NULL; 324 enc->alpha_data_size_ = 0; 325 enc->has_alpha_ = 0; 326} 327 328#if defined(__cplusplus) || defined(c_plusplus) 329} // extern "C" 330#endif 331