Path: blob/master/thirdparty/libwebp/src/enc/webp_enc.c
21344 views
// Copyright 2011 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// WebP encoder: main entry point10//11// Author: Skal ([email protected])1213#include <assert.h>14#include <math.h>15#include <stdlib.h>16#include <string.h>1718#include "src/dec/common_dec.h"19#include "src/webp/types.h"20#include "src/dsp/dsp.h"21#include "src/enc/cost_enc.h"22#include "src/enc/vp8i_enc.h"23#include "src/enc/vp8li_enc.h"24#include "src/utils/utils.h"25#include "src/webp/encode.h"2627// #define PRINT_MEMORY_INFO2829#ifdef PRINT_MEMORY_INFO30#include <stdio.h>31#endif3233//------------------------------------------------------------------------------3435int WebPGetEncoderVersion(void) {36return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;37}3839//------------------------------------------------------------------------------40// VP8Encoder41//------------------------------------------------------------------------------4243static void ResetSegmentHeader(VP8Encoder* const enc) {44VP8EncSegmentHeader* const hdr = &enc->segment_hdr;45hdr->num_segments = enc->config->segments;46hdr->update_map = (hdr->num_segments > 1);47hdr->size = 0;48}4950static void ResetFilterHeader(VP8Encoder* const enc) {51VP8EncFilterHeader* const hdr = &enc->filter_hdr;52hdr->simple = 1;53hdr->level = 0;54hdr->sharpness = 0;55hdr->i4x4_lf_delta = 0;56}5758static void ResetBoundaryPredictions(VP8Encoder* const enc) {59// init boundary values once for all60// Note: actually, initializing the 'preds[]' is only needed for intra4.61int i;62uint8_t* const top = enc->preds - enc->preds_w;63uint8_t* const left = enc->preds - 1;64for (i = -1; i < 4 * enc->mb_w; ++i) {65top[i] = B_DC_PRED;66}67for (i = 0; i < 4 * enc->mb_h; ++i) {68left[i * enc->preds_w] = B_DC_PRED;69}70enc->nz[-1] = 0; // constant71}7273// Mapping from config->method to coding tools used.74//-------------------+---+---+---+---+---+---+---+75// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |76//-------------------+---+---+---+---+---+---+---+77// fast probe | x | | | x | | | |78//-------------------+---+---+---+---+---+---+---+79// dynamic proba | ~ | x | x | x | x | x | x |80//-------------------+---+---+---+---+---+---+---+81// fast mode analysis|[x]|[x]| | | x | x | x |82//-------------------+---+---+---+---+---+---+---+83// basic rd-opt | | | | x | x | x | x |84//-------------------+---+---+---+---+---+---+---+85// disto-refine i4/16| x | x | x | | | | |86//-------------------+---+---+---+---+---+---+---+87// disto-refine uv | | x | x | | | | |88//-------------------+---+---+---+---+---+---+---+89// rd-opt i4/16 | | | ~ | x | x | x | x |90//-------------------+---+---+---+---+---+---+---+91// token buffer (opt)| | | | x | x | x | x |92//-------------------+---+---+---+---+---+---+---+93// Trellis | | | | | | x |Ful|94//-------------------+---+---+---+---+---+---+---+95// full-SNS | | | | | x | x | x |96//-------------------+---+---+---+---+---+---+---+9798static void MapConfigToTools(VP8Encoder* const enc) {99const WebPConfig* const config = enc->config;100const int method = config->method;101const int limit = 100 - config->partition_limit;102enc->method = method;103enc->rd_opt_level = (method >= 6) ? RD_OPT_TRELLIS_ALL104: (method >= 5) ? RD_OPT_TRELLIS105: (method >= 3) ? RD_OPT_BASIC106: RD_OPT_NONE;107enc->max_i4_header_bits =108256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block109(limit * limit) / (100 * 100); // ... modulated with a quadratic curve.110111// partition0 = 512k max.112enc->mb_header_limit =113(score_t)256 * 510 * 8 * 1024 / (enc->mb_w * enc->mb_h);114115enc->thread_level = config->thread_level;116117enc->do_search = (config->target_size > 0 || config->target_PSNR > 0);118if (!config->low_memory) {119#if !defined(DISABLE_TOKEN_BUFFER)120enc->use_tokens = (enc->rd_opt_level >= RD_OPT_BASIC); // need rd stats121#endif122if (enc->use_tokens) {123enc->num_parts = 1; // doesn't work with multi-partition124}125}126}127128// Memory scaling with dimensions:129// memory (bytes) ~= 2.25 * w + 0.0625 * w * h130//131// Typical memory footprint (614x440 picture)132// encoder: 22111133// info: 4368134// preds: 17741135// top samples: 1263136// non-zero: 175137// lf-stats: 0138// total: 45658139// Transient object sizes:140// VP8EncIterator: 3360141// VP8ModeScore: 872142// VP8SegmentInfo: 732143// VP8EncProba: 18352144// LFStats: 2048145// Picture size (yuv): 419328146147static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,148WebPPicture* const picture) {149VP8Encoder* enc;150const int use_filter =151(config->filter_strength > 0) || (config->autofilter > 0);152const int mb_w = (picture->width + 15) >> 4;153const int mb_h = (picture->height + 15) >> 4;154const int preds_w = 4 * mb_w + 1;155const int preds_h = 4 * mb_h + 1;156const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds);157const int top_stride = mb_w * 16;158const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz) + WEBP_ALIGN_CST;159const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info);160const size_t samples_size =1612 * top_stride * sizeof(*enc->y_top) // top-luma/u/v162+ WEBP_ALIGN_CST; // align all163const size_t lf_stats_size =164config->autofilter ? sizeof(*enc->lf_stats) + WEBP_ALIGN_CST : 0;165const size_t top_derr_size =166(config->quality <= ERROR_DIFFUSION_QUALITY || config->pass > 1) ?167mb_w * sizeof(*enc->top_derr) : 0;168uint8_t* mem;169const uint64_t size = (uint64_t)sizeof(*enc) // main struct170+ WEBP_ALIGN_CST // cache alignment171+ info_size // modes info172+ preds_size // prediction modes173+ samples_size // top/left samples174+ top_derr_size // top diffusion error175+ nz_size // coeff context bits176+ lf_stats_size; // autofilter stats177178#ifdef PRINT_MEMORY_INFO179printf("===================================\n");180printf("Memory used:\n"181" encoder: %ld\n"182" info: %ld\n"183" preds: %ld\n"184" top samples: %ld\n"185" top diffusion: %ld\n"186" non-zero: %ld\n"187" lf-stats: %ld\n"188" total: %ld\n",189sizeof(*enc) + WEBP_ALIGN_CST, info_size,190preds_size, samples_size, top_derr_size, nz_size, lf_stats_size, size);191printf("Transient object sizes:\n"192" VP8EncIterator: %ld\n"193" VP8ModeScore: %ld\n"194" VP8SegmentInfo: %ld\n"195" VP8EncProba: %ld\n"196" LFStats: %ld\n",197sizeof(VP8EncIterator), sizeof(VP8ModeScore),198sizeof(VP8SegmentInfo), sizeof(VP8EncProba),199sizeof(LFStats));200printf("Picture size (yuv): %ld\n",201mb_w * mb_h * 384 * sizeof(uint8_t));202printf("===================================\n");203#endif204mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));205if (mem == NULL) {206WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);207return NULL;208}209enc = (VP8Encoder*)mem;210mem = (uint8_t*)WEBP_ALIGN(mem + sizeof(*enc));211memset(enc, 0, sizeof(*enc));212enc->num_parts = 1 << config->partitions;213enc->mb_w = mb_w;214enc->mb_h = mb_h;215enc->preds_w = preds_w;216enc->mb_info = (VP8MBInfo*)mem;217mem += info_size;218enc->preds = mem + 1 + enc->preds_w;219mem += preds_size;220enc->nz = 1 + (uint32_t*)WEBP_ALIGN(mem);221mem += nz_size;222enc->lf_stats = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL;223mem += lf_stats_size;224225// top samples (all 16-aligned)226mem = (uint8_t*)WEBP_ALIGN(mem);227enc->y_top = mem;228enc->uv_top = enc->y_top + top_stride;229mem += 2 * top_stride;230enc->top_derr = top_derr_size ? (DError*)mem : NULL;231mem += top_derr_size;232assert(mem <= (uint8_t*)enc + size);233234enc->config = config;235enc->profile = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;236enc->pic = picture;237enc->percent = 0;238239MapConfigToTools(enc);240VP8EncDspInit();241VP8DefaultProbas(enc);242ResetSegmentHeader(enc);243ResetFilterHeader(enc);244ResetBoundaryPredictions(enc);245VP8EncDspCostInit();246VP8EncInitAlpha(enc);247248// lower quality means smaller output -> we modulate a little the page249// size based on quality. This is just a crude 1rst-order prediction.250{251const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6]252VP8TBufferInit(&enc->tokens, (int)(mb_w * mb_h * 4 * scale));253}254return enc;255}256257static int DeleteVP8Encoder(VP8Encoder* enc) {258int ok = 1;259if (enc != NULL) {260ok = VP8EncDeleteAlpha(enc);261VP8TBufferClear(&enc->tokens);262WebPSafeFree(enc);263}264return ok;265}266267//------------------------------------------------------------------------------268269#if !defined(WEBP_DISABLE_STATS)270static double GetPSNR(uint64_t err, uint64_t size) {271return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;272}273274static void FinalizePSNR(const VP8Encoder* const enc) {275WebPAuxStats* stats = enc->pic->stats;276const uint64_t size = enc->sse_count;277const uint64_t* const sse = enc->sse;278stats->PSNR[0] = (float)GetPSNR(sse[0], size);279stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);280stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);281stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);282stats->PSNR[4] = (float)GetPSNR(sse[3], size);283}284#endif // !defined(WEBP_DISABLE_STATS)285286static void StoreStats(VP8Encoder* const enc) {287#if !defined(WEBP_DISABLE_STATS)288WebPAuxStats* const stats = enc->pic->stats;289if (stats != NULL) {290int i, s;291for (i = 0; i < NUM_MB_SEGMENTS; ++i) {292stats->segment_level[i] = enc->dqm[i].fstrength;293stats->segment_quant[i] = enc->dqm[i].quant;294for (s = 0; s <= 2; ++s) {295stats->residual_bytes[s][i] = enc->residual_bytes[s][i];296}297}298FinalizePSNR(enc);299stats->coded_size = enc->coded_size;300for (i = 0; i < 3; ++i) {301stats->block_count[i] = enc->block_count[i];302}303}304#else // defined(WEBP_DISABLE_STATS)305WebPReportProgress(enc->pic, 100, &enc->percent); // done!306#endif // !defined(WEBP_DISABLE_STATS)307}308309int WebPEncodingSetError(const WebPPicture* const pic,310WebPEncodingError error) {311assert((int)error < VP8_ENC_ERROR_LAST);312assert((int)error >= VP8_ENC_OK);313// The oldest error reported takes precedence over the new one.314if (pic->error_code == VP8_ENC_OK) {315((WebPPicture*)pic)->error_code = error;316}317return 0;318}319320int WebPReportProgress(const WebPPicture* const pic,321int percent, int* const percent_store) {322if (percent_store != NULL && percent != *percent_store) {323*percent_store = percent;324if (pic->progress_hook && !pic->progress_hook(percent, pic)) {325// user abort requested326return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);327}328}329return 1; // ok330}331//------------------------------------------------------------------------------332333int WebPEncode(const WebPConfig* config, WebPPicture* pic) {334int ok = 0;335if (pic == NULL) return 0;336337pic->error_code = VP8_ENC_OK; // all ok so far338if (config == NULL) { // bad params339return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);340}341if (!WebPValidateConfig(config)) {342return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);343}344if (!WebPValidatePicture(pic)) return 0;345if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) {346return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);347}348349if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));350351if (!config->lossless) {352VP8Encoder* enc = NULL;353354if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) {355// Make sure we have YUVA samples.356if (config->use_sharp_yuv || (config->preprocessing & 4)) {357if (!WebPPictureSharpARGBToYUVA(pic)) {358return 0;359}360} else {361float dithering = 0.f;362if (config->preprocessing & 2) {363const float x = config->quality / 100.f;364const float x2 = x * x;365// slowly decreasing from max dithering at low quality (q->0)366// to 0.5 dithering amplitude at high quality (q->100)367dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;368}369if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {370return 0;371}372}373}374375if (!config->exact) {376WebPCleanupTransparentArea(pic);377}378379enc = InitVP8Encoder(config, pic);380if (enc == NULL) return 0; // pic->error is already set.381// Note: each of the tasks below account for 20% in the progress report.382ok = VP8EncAnalyze(enc);383384// Analysis is done, proceed to actual coding.385ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel386if (!enc->use_tokens) {387ok = ok && VP8EncLoop(enc);388} else {389ok = ok && VP8EncTokenLoop(enc);390}391ok = ok && VP8EncFinishAlpha(enc);392393ok = ok && VP8EncWrite(enc);394StoreStats(enc);395if (!ok) {396VP8EncFreeBitWriters(enc);397}398ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok399} else {400// Make sure we have ARGB samples.401if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {402return 0;403}404405if (!config->exact) {406WebPReplaceTransparentPixels(pic, 0x000000);407}408409ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.410}411412return ok;413}414415416