1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // WebP encoder: main entry point 11 // 12 // Author: Skal (pascal.massimino (at) gmail.com) 13 14 #include <assert.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <math.h> 18 19 #include "./vp8enci.h" 20 #include "./vp8li.h" 21 #include "../utils/utils.h" 22 23 // #define PRINT_MEMORY_INFO 24 25 #if defined(__cplusplus) || defined(c_plusplus) 26 extern "C" { 27 #endif 28 29 #ifdef PRINT_MEMORY_INFO 30 #include <stdio.h> 31 #endif 32 33 //------------------------------------------------------------------------------ 34 35 int WebPGetEncoderVersion(void) { 36 return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION; 37 } 38 39 //------------------------------------------------------------------------------ 40 // WebPPicture 41 //------------------------------------------------------------------------------ 42 43 static int DummyWriter(const uint8_t* data, size_t data_size, 44 const WebPPicture* const picture) { 45 // The following are to prevent 'unused variable' error message. 46 (void)data; 47 (void)data_size; 48 (void)picture; 49 return 1; 50 } 51 52 int WebPPictureInitInternal(WebPPicture* picture, int version) { 53 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { 54 return 0; // caller/system version mismatch! 55 } 56 if (picture != NULL) { 57 memset(picture, 0, sizeof(*picture)); 58 picture->writer = DummyWriter; 59 WebPEncodingSetError(picture, VP8_ENC_OK); 60 } 61 return 1; 62 } 63 64 //------------------------------------------------------------------------------ 65 // VP8Encoder 66 //------------------------------------------------------------------------------ 67 68 static void ResetSegmentHeader(VP8Encoder* const enc) { 69 VP8SegmentHeader* const hdr = &enc->segment_hdr_; 70 hdr->num_segments_ = enc->config_->segments; 71 hdr->update_map_ = (hdr->num_segments_ > 1); 72 hdr->size_ = 0; 73 } 74 75 static void ResetFilterHeader(VP8Encoder* const enc) { 76 VP8FilterHeader* const hdr = &enc->filter_hdr_; 77 hdr->simple_ = 1; 78 hdr->level_ = 0; 79 hdr->sharpness_ = 0; 80 hdr->i4x4_lf_delta_ = 0; 81 } 82 83 static void ResetBoundaryPredictions(VP8Encoder* const enc) { 84 // init boundary values once for all 85 // Note: actually, initializing the preds_[] is only needed for intra4. 86 int i; 87 uint8_t* const top = enc->preds_ - enc->preds_w_; 88 uint8_t* const left = enc->preds_ - 1; 89 for (i = -1; i < 4 * enc->mb_w_; ++i) { 90 top[i] = B_DC_PRED; 91 } 92 for (i = 0; i < 4 * enc->mb_h_; ++i) { 93 left[i * enc->preds_w_] = B_DC_PRED; 94 } 95 enc->nz_[-1] = 0; // constant 96 } 97 98 // Mapping from config->method_ to coding tools used. 99 //-------------------+---+---+---+---+---+---+---+ 100 // Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 | 101 //-------------------+---+---+---+---+---+---+---+ 102 // fast probe | x | | | x | | | | 103 //-------------------+---+---+---+---+---+---+---+ 104 // dynamic proba | ~ | x | x | x | x | x | x | 105 //-------------------+---+---+---+---+---+---+---+ 106 // fast mode analysis| | | | | x | x | x | 107 //-------------------+---+---+---+---+---+---+---+ 108 // basic rd-opt | | | | x | x | x | x | 109 //-------------------+---+---+---+---+---+---+---+ 110 // disto-score i4/16 | | | x | | | | | 111 //-------------------+---+---+---+---+---+---+---+ 112 // rd-opt i4/16 | | | ~ | x | x | x | x | 113 //-------------------+---+---+---+---+---+---+---+ 114 // token buffer (opt)| | | | x | x | x | x | 115 //-------------------+---+---+---+---+---+---+---+ 116 // Trellis | | | | | | x |Ful| 117 //-------------------+---+---+---+---+---+---+---+ 118 // full-SNS | | | | | x | x | x | 119 //-------------------+---+---+---+---+---+---+---+ 120 121 static void MapConfigToTools(VP8Encoder* const enc) { 122 const WebPConfig* const config = enc->config_; 123 const int method = config->method; 124 const int limit = 100 - config->partition_limit; 125 enc->method_ = method; 126 enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL 127 : (method >= 5) ? RD_OPT_TRELLIS 128 : (method >= 3) ? RD_OPT_BASIC 129 : RD_OPT_NONE; 130 enc->max_i4_header_bits_ = 131 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block 132 (limit * limit) / (100 * 100); // ... modulated with a quadratic curve. 133 134 enc->thread_level_ = config->thread_level; 135 136 enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); 137 if (!config->low_memory) { 138 #if !defined(DISABLE_TOKEN_BUFFER) 139 enc->use_tokens_ = (method >= 3) && !enc->do_search_; 140 #endif 141 if (enc->use_tokens_) { 142 enc->num_parts_ = 1; // doesn't work with multi-partition 143 } 144 } 145 } 146 147 // Memory scaling with dimensions: 148 // memory (bytes) ~= 2.25 * w + 0.0625 * w * h 149 // 150 // Typical memory footprint (768x510 picture) 151 // Memory used: 152 // encoder: 33919 153 // block cache: 2880 154 // info: 3072 155 // preds: 24897 156 // top samples: 1623 157 // non-zero: 196 158 // lf-stats: 2048 159 // total: 68635 160 // Transcient object sizes: 161 // VP8EncIterator: 352 162 // VP8ModeScore: 912 163 // VP8SegmentInfo: 532 164 // VP8Proba: 31032 165 // LFStats: 2048 166 // Picture size (yuv): 589824 167 168 static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 169 WebPPicture* const picture) { 170 const int use_filter = 171 (config->filter_strength > 0) || (config->autofilter > 0); 172 const int mb_w = (picture->width + 15) >> 4; 173 const int mb_h = (picture->height + 15) >> 4; 174 const int preds_w = 4 * mb_w + 1; 175 const int preds_h = 4 * mb_h + 1; 176 const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); 177 const int top_stride = mb_w * 16; 178 const size_t nz_size = (mb_w + 1) * sizeof(uint32_t); 179 const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t); 180 const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo); 181 const size_t samples_size = (2 * top_stride + // top-luma/u/v 182 16 + 16 + 16 + 8 + 1 + // left y/u/v 183 2 * ALIGN_CST) // align all 184 * sizeof(uint8_t); 185 const size_t lf_stats_size = 186 config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0; 187 VP8Encoder* enc; 188 uint8_t* mem; 189 const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct 190 + ALIGN_CST // cache alignment 191 + cache_size // working caches 192 + info_size // modes info 193 + preds_size // prediction modes 194 + samples_size // top/left samples 195 + nz_size // coeff context bits 196 + lf_stats_size; // autofilter stats 197 198 #ifdef PRINT_MEMORY_INFO 199 printf("===================================\n"); 200 printf("Memory used:\n" 201 " encoder: %ld\n" 202 " block cache: %ld\n" 203 " info: %ld\n" 204 " preds: %ld\n" 205 " top samples: %ld\n" 206 " non-zero: %ld\n" 207 " lf-stats: %ld\n" 208 " total: %ld\n", 209 sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size, 210 preds_size, samples_size, nz_size, lf_stats_size, size); 211 printf("Transcient object sizes:\n" 212 " VP8EncIterator: %ld\n" 213 " VP8ModeScore: %ld\n" 214 " VP8SegmentInfo: %ld\n" 215 " VP8Proba: %ld\n" 216 " LFStats: %ld\n", 217 sizeof(VP8EncIterator), sizeof(VP8ModeScore), 218 sizeof(VP8SegmentInfo), sizeof(VP8Proba), 219 sizeof(LFStats)); 220 printf("Picture size (yuv): %ld\n", 221 mb_w * mb_h * 384 * sizeof(uint8_t)); 222 printf("===================================\n"); 223 #endif 224 mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); 225 if (mem == NULL) { 226 WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); 227 return NULL; 228 } 229 enc = (VP8Encoder*)mem; 230 mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc)); 231 memset(enc, 0, sizeof(*enc)); 232 enc->num_parts_ = 1 << config->partitions; 233 enc->mb_w_ = mb_w; 234 enc->mb_h_ = mb_h; 235 enc->preds_w_ = preds_w; 236 enc->yuv_in_ = (uint8_t*)mem; 237 mem += YUV_SIZE; 238 enc->yuv_out_ = (uint8_t*)mem; 239 mem += YUV_SIZE; 240 enc->yuv_out2_ = (uint8_t*)mem; 241 mem += YUV_SIZE; 242 enc->yuv_p_ = (uint8_t*)mem; 243 mem += PRED_SIZE; 244 enc->mb_info_ = (VP8MBInfo*)mem; 245 mem += info_size; 246 enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; 247 mem += preds_w * preds_h * sizeof(uint8_t); 248 enc->nz_ = 1 + (uint32_t*)mem; 249 mem += nz_size; 250 enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL; 251 mem += lf_stats_size; 252 253 // top samples (all 16-aligned) 254 mem = (uint8_t*)DO_ALIGN(mem); 255 enc->y_top_ = (uint8_t*)mem; 256 enc->uv_top_ = enc->y_top_ + top_stride; 257 mem += 2 * top_stride; 258 mem = (uint8_t*)DO_ALIGN(mem + 1); 259 enc->y_left_ = (uint8_t*)mem; 260 mem += 16 + 16; 261 enc->u_left_ = (uint8_t*)mem; 262 mem += 16; 263 enc->v_left_ = (uint8_t*)mem; 264 mem += 8; 265 266 enc->config_ = config; 267 enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; 268 enc->pic_ = picture; 269 enc->percent_ = 0; 270 271 MapConfigToTools(enc); 272 VP8EncDspInit(); 273 VP8DefaultProbas(enc); 274 ResetSegmentHeader(enc); 275 ResetFilterHeader(enc); 276 ResetBoundaryPredictions(enc); 277 278 VP8EncInitAlpha(enc); 279 #ifdef WEBP_EXPERIMENTAL_FEATURES 280 VP8EncInitLayer(enc); 281 #endif 282 283 VP8TBufferInit(&enc->tokens_); 284 return enc; 285 } 286 287 static int DeleteVP8Encoder(VP8Encoder* enc) { 288 int ok = 1; 289 if (enc != NULL) { 290 ok = VP8EncDeleteAlpha(enc); 291 #ifdef WEBP_EXPERIMENTAL_FEATURES 292 VP8EncDeleteLayer(enc); 293 #endif 294 VP8TBufferClear(&enc->tokens_); 295 free(enc); 296 } 297 return ok; 298 } 299 300 //------------------------------------------------------------------------------ 301 302 static double GetPSNR(uint64_t err, uint64_t size) { 303 return err ? 10. * log10(255. * 255. * size / err) : 99.; 304 } 305 306 static void FinalizePSNR(const VP8Encoder* const enc) { 307 WebPAuxStats* stats = enc->pic_->stats; 308 const uint64_t size = enc->sse_count_; 309 const uint64_t* const sse = enc->sse_; 310 stats->PSNR[0] = (float)GetPSNR(sse[0], size); 311 stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4); 312 stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4); 313 stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2); 314 stats->PSNR[4] = (float)GetPSNR(sse[3], size); 315 } 316 317 static void StoreStats(VP8Encoder* const enc) { 318 WebPAuxStats* const stats = enc->pic_->stats; 319 if (stats != NULL) { 320 int i, s; 321 for (i = 0; i < NUM_MB_SEGMENTS; ++i) { 322 stats->segment_level[i] = enc->dqm_[i].fstrength_; 323 stats->segment_quant[i] = enc->dqm_[i].quant_; 324 for (s = 0; s <= 2; ++s) { 325 stats->residual_bytes[s][i] = enc->residual_bytes_[s][i]; 326 } 327 } 328 FinalizePSNR(enc); 329 stats->coded_size = enc->coded_size_; 330 for (i = 0; i < 3; ++i) { 331 stats->block_count[i] = enc->block_count_[i]; 332 } 333 } 334 WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! 335 } 336 337 int WebPEncodingSetError(const WebPPicture* const pic, 338 WebPEncodingError error) { 339 assert((int)error < VP8_ENC_ERROR_LAST); 340 assert((int)error >= VP8_ENC_OK); 341 ((WebPPicture*)pic)->error_code = error; 342 return 0; 343 } 344 345 int WebPReportProgress(const WebPPicture* const pic, 346 int percent, int* const percent_store) { 347 if (percent_store != NULL && percent != *percent_store) { 348 *percent_store = percent; 349 if (pic->progress_hook && !pic->progress_hook(percent, pic)) { 350 // user abort requested 351 WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); 352 return 0; 353 } 354 } 355 return 1; // ok 356 } 357 //------------------------------------------------------------------------------ 358 359 int WebPEncode(const WebPConfig* config, WebPPicture* pic) { 360 int ok = 0; 361 362 if (pic == NULL) 363 return 0; 364 WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far 365 if (config == NULL) // bad params 366 return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); 367 if (!WebPValidateConfig(config)) 368 return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); 369 if (pic->width <= 0 || pic->height <= 0) 370 return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); 371 if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) 372 return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); 373 374 if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats)); 375 376 if (!config->lossless) { 377 VP8Encoder* enc = NULL; 378 if (pic->y == NULL || pic->u == NULL || pic->v == NULL) { 379 // Make sure we have YUVA samples. 380 if (!WebPPictureARGBToYUVA(pic, WEBP_YUV420)) return 0; 381 } 382 383 enc = InitVP8Encoder(config, pic); 384 if (enc == NULL) return 0; // pic->error is already set. 385 // Note: each of the tasks below account for 20% in the progress report. 386 ok = VP8EncAnalyze(enc); 387 388 // Analysis is done, proceed to actual coding. 389 ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel 390 if (!enc->use_tokens_) { 391 ok = ok && VP8EncLoop(enc); 392 } else { 393 ok = ok && VP8EncTokenLoop(enc); 394 } 395 ok = ok && VP8EncFinishAlpha(enc); 396 #ifdef WEBP_EXPERIMENTAL_FEATURES 397 ok = ok && VP8EncFinishLayer(enc); 398 #endif 399 400 ok = ok && VP8EncWrite(enc); 401 StoreStats(enc); 402 if (!ok) { 403 VP8EncFreeBitWriters(enc); 404 } 405 ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok 406 } else { 407 // Make sure we have ARGB samples. 408 if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) { 409 return 0; 410 } 411 412 ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. 413 } 414 415 return ok; 416 } 417 418 #if defined(__cplusplus) || defined(c_plusplus) 419 } // extern "C" 420 #endif 421