2012-01-06 23:49:06 +01:00
|
|
|
// Copyright 2011 Google Inc. All Rights Reserved.
|
2011-02-19 08:33:46 +01:00
|
|
|
//
|
2013-06-07 08:05:58 +02:00
|
|
|
// Use of this source code is governed by a BSD-style license
|
|
|
|
// that can be found in the COPYING file in the root of the source
|
|
|
|
// tree. An additional intellectual property rights grant can be found
|
|
|
|
// in the file PATENTS. All contributing project authors may
|
|
|
|
// be found in the AUTHORS file in the root of the source tree.
|
2011-02-19 08:33:46 +01:00
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// WebP encoder: main entry point
|
|
|
|
//
|
|
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
|
|
|
2011-06-02 15:55:03 +02:00
|
|
|
#include <assert.h>
|
2011-02-19 08:33:46 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
2011-12-01 11:00:03 +01:00
|
|
|
#include "./vp8enci.h"
|
2012-03-28 13:07:42 +02:00
|
|
|
#include "./vp8li.h"
|
2012-08-01 21:06:04 +02:00
|
|
|
#include "../utils/utils.h"
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
// #define PRINT_MEMORY_INFO
|
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PRINT_MEMORY_INFO
|
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-03-25 00:17:10 +01:00
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
int WebPGetEncoderVersion(void) {
|
2011-03-25 00:17:10 +01:00
|
|
|
return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
// WebPPicture
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static int DummyWriter(const uint8_t* data, size_t data_size,
|
|
|
|
const WebPPicture* const picture) {
|
2011-03-25 23:04:11 +01:00
|
|
|
// The following are to prevent 'unused variable' error message.
|
|
|
|
(void)data;
|
|
|
|
(void)data_size;
|
|
|
|
(void)picture;
|
2011-02-19 08:33:46 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-07-18 00:01:30 +02:00
|
|
|
int WebPPictureInitInternal(WebPPicture* picture, int version) {
|
2012-07-19 07:24:33 +02:00
|
|
|
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
|
2011-02-19 08:33:46 +01:00
|
|
|
return 0; // caller/system version mismatch!
|
|
|
|
}
|
2012-04-30 08:57:57 +02:00
|
|
|
if (picture != NULL) {
|
2011-02-19 08:33:46 +01:00
|
|
|
memset(picture, 0, sizeof(*picture));
|
|
|
|
picture->writer = DummyWriter;
|
2011-06-02 15:55:03 +02:00
|
|
|
WebPEncodingSetError(picture, VP8_ENC_OK);
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
// VP8Encoder
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static void ResetSegmentHeader(VP8Encoder* const enc) {
|
|
|
|
VP8SegmentHeader* const hdr = &enc->segment_hdr_;
|
|
|
|
hdr->num_segments_ = enc->config_->segments;
|
|
|
|
hdr->update_map_ = (hdr->num_segments_ > 1);
|
|
|
|
hdr->size_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ResetFilterHeader(VP8Encoder* const enc) {
|
|
|
|
VP8FilterHeader* const hdr = &enc->filter_hdr_;
|
|
|
|
hdr->simple_ = 1;
|
|
|
|
hdr->level_ = 0;
|
|
|
|
hdr->sharpness_ = 0;
|
|
|
|
hdr->i4x4_lf_delta_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ResetBoundaryPredictions(VP8Encoder* const enc) {
|
|
|
|
// init boundary values once for all
|
|
|
|
// Note: actually, initializing the preds_[] is only needed for intra4.
|
|
|
|
int i;
|
|
|
|
uint8_t* const top = enc->preds_ - enc->preds_w_;
|
|
|
|
uint8_t* const left = enc->preds_ - 1;
|
|
|
|
for (i = -1; i < 4 * enc->mb_w_; ++i) {
|
|
|
|
top[i] = B_DC_PRED;
|
|
|
|
}
|
|
|
|
for (i = 0; i < 4 * enc->mb_h_; ++i) {
|
|
|
|
left[i * enc->preds_w_] = B_DC_PRED;
|
|
|
|
}
|
|
|
|
enc->nz_[-1] = 0; // constant
|
|
|
|
}
|
|
|
|
|
2013-02-26 14:22:44 +01:00
|
|
|
// Mapping from config->method_ to coding tools used.
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// fast probe | x | | | x | | | |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// dynamic proba | ~ | x | x | x | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// fast mode analysis| | | | | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// basic rd-opt | | | | x | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// disto-score i4/16 | | | x | | | | |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// rd-opt i4/16 | | | ~ | x | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
2013-03-12 00:37:42 +01:00
|
|
|
// token buffer (opt)| | | | x | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
2013-02-26 14:22:44 +01:00
|
|
|
// Trellis | | | | | | x |Ful|
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
|
|
|
// full-SNS | | | | | x | x | x |
|
|
|
|
//-------------------+---+---+---+---+---+---+---+
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static void MapConfigToTools(VP8Encoder* const enc) {
|
2013-03-12 00:37:42 +01:00
|
|
|
const WebPConfig* const config = enc->config_;
|
|
|
|
const int method = config->method;
|
|
|
|
const int limit = 100 - config->partition_limit;
|
2011-02-19 08:33:46 +01:00
|
|
|
enc->method_ = method;
|
2013-02-26 11:20:59 +01:00
|
|
|
enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
|
|
|
|
: (method >= 5) ? RD_OPT_TRELLIS
|
|
|
|
: (method >= 3) ? RD_OPT_BASIC
|
|
|
|
: RD_OPT_NONE;
|
2011-08-24 00:58:22 +02:00
|
|
|
enc->max_i4_header_bits_ =
|
|
|
|
256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
|
|
|
|
(limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
|
2013-03-01 01:21:34 +01:00
|
|
|
|
2013-03-12 00:37:42 +01:00
|
|
|
enc->thread_level_ = config->thread_level;
|
|
|
|
|
|
|
|
enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
|
|
|
|
if (!config->low_memory) {
|
|
|
|
#if !defined(DISABLE_TOKEN_BUFFER)
|
2013-09-11 21:15:28 +02:00
|
|
|
enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats
|
2013-03-12 00:37:42 +01:00
|
|
|
#endif
|
|
|
|
if (enc->use_tokens_) {
|
|
|
|
enc->num_parts_ = 1; // doesn't work with multi-partition
|
|
|
|
}
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Memory scaling with dimensions:
|
|
|
|
// memory (bytes) ~= 2.25 * w + 0.0625 * w * h
|
|
|
|
//
|
|
|
|
// Typical memory footprint (768x510 picture)
|
|
|
|
// Memory used:
|
|
|
|
// encoder: 33919
|
|
|
|
// block cache: 2880
|
|
|
|
// info: 3072
|
|
|
|
// preds: 24897
|
|
|
|
// top samples: 1623
|
|
|
|
// non-zero: 196
|
|
|
|
// lf-stats: 2048
|
|
|
|
// total: 68635
|
|
|
|
// Transcient object sizes:
|
|
|
|
// VP8EncIterator: 352
|
|
|
|
// VP8ModeScore: 912
|
|
|
|
// VP8SegmentInfo: 532
|
|
|
|
// VP8Proba: 31032
|
|
|
|
// LFStats: 2048
|
|
|
|
// Picture size (yuv): 589824
|
|
|
|
|
2012-03-28 13:07:42 +02:00
|
|
|
static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
|
|
|
WebPPicture* const picture) {
|
2011-02-19 08:33:46 +01:00
|
|
|
const int use_filter =
|
|
|
|
(config->filter_strength > 0) || (config->autofilter > 0);
|
|
|
|
const int mb_w = (picture->width + 15) >> 4;
|
|
|
|
const int mb_h = (picture->height + 15) >> 4;
|
|
|
|
const int preds_w = 4 * mb_w + 1;
|
|
|
|
const int preds_h = 4 * mb_h + 1;
|
|
|
|
const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
|
|
|
|
const int top_stride = mb_w * 16;
|
2013-04-13 19:53:20 +02:00
|
|
|
const size_t nz_size = (mb_w + 1) * sizeof(uint32_t) + ALIGN_CST;
|
2011-02-19 08:33:46 +01:00
|
|
|
const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
|
2013-07-27 02:08:37 +02:00
|
|
|
const size_t samples_size = 2 * top_stride * sizeof(uint8_t) // top-luma/u/v
|
|
|
|
+ ALIGN_CST; // align all
|
2011-05-06 04:32:38 +02:00
|
|
|
const size_t lf_stats_size =
|
|
|
|
config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
VP8Encoder* enc;
|
|
|
|
uint8_t* mem;
|
2012-08-01 21:06:04 +02:00
|
|
|
const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct
|
|
|
|
+ ALIGN_CST // cache alignment
|
|
|
|
+ info_size // modes info
|
|
|
|
+ preds_size // prediction modes
|
|
|
|
+ samples_size // top/left samples
|
|
|
|
+ nz_size // coeff context bits
|
|
|
|
+ lf_stats_size; // autofilter stats
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
#ifdef PRINT_MEMORY_INFO
|
|
|
|
printf("===================================\n");
|
|
|
|
printf("Memory used:\n"
|
|
|
|
" encoder: %ld\n"
|
|
|
|
" info: %ld\n"
|
|
|
|
" preds: %ld\n"
|
|
|
|
" top samples: %ld\n"
|
|
|
|
" non-zero: %ld\n"
|
|
|
|
" lf-stats: %ld\n"
|
|
|
|
" total: %ld\n",
|
2013-08-31 23:38:11 +02:00
|
|
|
sizeof(VP8Encoder) + ALIGN_CST, info_size,
|
2011-02-19 08:33:46 +01:00
|
|
|
preds_size, samples_size, nz_size, lf_stats_size, size);
|
2013-04-13 19:53:20 +02:00
|
|
|
printf("Transient object sizes:\n"
|
2011-02-19 08:33:46 +01:00
|
|
|
" VP8EncIterator: %ld\n"
|
|
|
|
" VP8ModeScore: %ld\n"
|
|
|
|
" VP8SegmentInfo: %ld\n"
|
|
|
|
" VP8Proba: %ld\n"
|
|
|
|
" LFStats: %ld\n",
|
|
|
|
sizeof(VP8EncIterator), sizeof(VP8ModeScore),
|
|
|
|
sizeof(VP8SegmentInfo), sizeof(VP8Proba),
|
|
|
|
sizeof(LFStats));
|
|
|
|
printf("Picture size (yuv): %ld\n",
|
|
|
|
mb_w * mb_h * 384 * sizeof(uint8_t));
|
|
|
|
printf("===================================\n");
|
|
|
|
#endif
|
2012-08-01 21:06:04 +02:00
|
|
|
mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
|
2011-06-02 15:55:03 +02:00
|
|
|
if (mem == NULL) {
|
|
|
|
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
enc = (VP8Encoder*)mem;
|
|
|
|
mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
|
|
|
|
memset(enc, 0, sizeof(*enc));
|
|
|
|
enc->num_parts_ = 1 << config->partitions;
|
|
|
|
enc->mb_w_ = mb_w;
|
|
|
|
enc->mb_h_ = mb_h;
|
|
|
|
enc->preds_w_ = preds_w;
|
|
|
|
enc->mb_info_ = (VP8MBInfo*)mem;
|
|
|
|
mem += info_size;
|
|
|
|
enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
|
|
|
|
mem += preds_w * preds_h * sizeof(uint8_t);
|
2013-05-15 09:21:34 +02:00
|
|
|
enc->nz_ = 1 + (uint32_t*)DO_ALIGN(mem);
|
2011-02-19 08:33:46 +01:00
|
|
|
mem += nz_size;
|
2011-05-06 04:32:38 +02:00
|
|
|
enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
|
2011-02-19 08:33:46 +01:00
|
|
|
mem += lf_stats_size;
|
|
|
|
|
|
|
|
// top samples (all 16-aligned)
|
|
|
|
mem = (uint8_t*)DO_ALIGN(mem);
|
|
|
|
enc->y_top_ = (uint8_t*)mem;
|
|
|
|
enc->uv_top_ = enc->y_top_ + top_stride;
|
|
|
|
mem += 2 * top_stride;
|
2013-05-15 09:21:34 +02:00
|
|
|
assert(mem <= (uint8_t*)enc + size);
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
enc->config_ = config;
|
|
|
|
enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
|
|
|
|
enc->pic_ = picture;
|
2011-12-01 11:24:50 +01:00
|
|
|
enc->percent_ = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
MapConfigToTools(enc);
|
|
|
|
VP8EncDspInit();
|
|
|
|
VP8DefaultProbas(enc);
|
|
|
|
ResetSegmentHeader(enc);
|
|
|
|
ResetFilterHeader(enc);
|
|
|
|
ResetBoundaryPredictions(enc);
|
|
|
|
|
2011-05-03 02:19:00 +02:00
|
|
|
VP8EncInitAlpha(enc);
|
2011-12-01 07:44:15 +01:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
2011-05-03 02:19:00 +02:00
|
|
|
VP8EncInitLayer(enc);
|
|
|
|
#endif
|
|
|
|
|
2013-03-12 00:37:42 +01:00
|
|
|
VP8TBufferInit(&enc->tokens_);
|
2011-02-19 08:33:46 +01:00
|
|
|
return enc;
|
|
|
|
}
|
|
|
|
|
2013-03-01 01:21:34 +01:00
|
|
|
static int DeleteVP8Encoder(VP8Encoder* enc) {
|
|
|
|
int ok = 1;
|
2012-04-30 08:57:57 +02:00
|
|
|
if (enc != NULL) {
|
2013-03-01 01:21:34 +01:00
|
|
|
ok = VP8EncDeleteAlpha(enc);
|
2011-12-01 07:44:15 +01:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
2011-05-03 02:19:00 +02:00
|
|
|
VP8EncDeleteLayer(enc);
|
|
|
|
#endif
|
2013-03-12 00:37:42 +01:00
|
|
|
VP8TBufferClear(&enc->tokens_);
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
free(enc);
|
|
|
|
}
|
2013-03-01 01:21:34 +01:00
|
|
|
return ok;
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static double GetPSNR(uint64_t err, uint64_t size) {
|
2013-09-11 21:15:28 +02:00
|
|
|
return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void FinalizePSNR(const VP8Encoder* const enc) {
|
|
|
|
WebPAuxStats* stats = enc->pic_->stats;
|
|
|
|
const uint64_t size = enc->sse_count_;
|
|
|
|
const uint64_t* const sse = enc->sse_;
|
|
|
|
stats->PSNR[0] = (float)GetPSNR(sse[0], size);
|
|
|
|
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
|
|
|
|
stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
|
|
|
|
stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
|
2012-07-25 01:15:36 +02:00
|
|
|
stats->PSNR[4] = (float)GetPSNR(sse[3], size);
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void StoreStats(VP8Encoder* const enc) {
|
|
|
|
WebPAuxStats* const stats = enc->pic_->stats;
|
2012-04-30 08:57:57 +02:00
|
|
|
if (stats != NULL) {
|
2011-02-19 08:33:46 +01:00
|
|
|
int i, s;
|
|
|
|
for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
|
|
|
|
stats->segment_level[i] = enc->dqm_[i].fstrength_;
|
|
|
|
stats->segment_quant[i] = enc->dqm_[i].quant_;
|
|
|
|
for (s = 0; s <= 2; ++s) {
|
|
|
|
stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FinalizePSNR(enc);
|
|
|
|
stats->coded_size = enc->coded_size_;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
|
|
stats->block_count[i] = enc->block_count_[i];
|
|
|
|
}
|
|
|
|
}
|
2012-05-29 08:02:02 +02:00
|
|
|
WebPReportProgress(enc->pic_, 100, &enc->percent_); // done!
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
2012-05-22 11:51:38 +02:00
|
|
|
int WebPEncodingSetError(const WebPPicture* const pic,
|
|
|
|
WebPEncodingError error) {
|
2011-12-01 11:24:50 +01:00
|
|
|
assert((int)error < VP8_ENC_ERROR_LAST);
|
2011-06-02 15:55:03 +02:00
|
|
|
assert((int)error >= VP8_ENC_OK);
|
2012-05-22 11:51:38 +02:00
|
|
|
((WebPPicture*)pic)->error_code = error;
|
2011-06-02 15:55:03 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-29 08:02:02 +02:00
|
|
|
int WebPReportProgress(const WebPPicture* const pic,
|
|
|
|
int percent, int* const percent_store) {
|
|
|
|
if (percent_store != NULL && percent != *percent_store) {
|
|
|
|
*percent_store = percent;
|
2011-12-01 11:24:50 +01:00
|
|
|
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
|
|
|
|
// user abort requested
|
|
|
|
WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1; // ok
|
|
|
|
}
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2012-07-18 00:01:30 +02:00
|
|
|
int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
|
2013-03-01 01:21:34 +01:00
|
|
|
int ok = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2011-06-02 15:55:03 +02:00
|
|
|
if (pic == NULL)
|
|
|
|
return 0;
|
|
|
|
WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
|
|
|
|
if (config == NULL) // bad params
|
|
|
|
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
2011-02-19 08:33:46 +01:00
|
|
|
if (!WebPValidateConfig(config))
|
2011-06-02 15:55:03 +02:00
|
|
|
return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
|
2011-02-27 19:22:54 +01:00
|
|
|
if (pic->width <= 0 || pic->height <= 0)
|
2011-06-02 15:55:03 +02:00
|
|
|
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
2011-09-12 21:27:21 +02:00
|
|
|
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
|
2011-06-02 15:55:03 +02:00
|
|
|
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2012-07-28 04:53:16 +02:00
|
|
|
if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
|
|
|
|
|
2012-03-28 13:07:42 +02:00
|
|
|
if (!config->lossless) {
|
|
|
|
VP8Encoder* enc = NULL;
|
2012-06-28 09:34:23 +02:00
|
|
|
if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
|
2013-03-29 12:28:49 +01:00
|
|
|
// Make sure we have YUVA samples.
|
2013-10-17 22:36:49 +02:00
|
|
|
float dithering = 0.f;
|
|
|
|
if (config->preprocessing & 2) {
|
|
|
|
const float x = config->quality / 100.f;
|
|
|
|
const float x2 = x * x;
|
|
|
|
// slowly decreasing from max dithering at low quality (q->0)
|
|
|
|
// to 0.5 dithering amplitude at high quality (q->100)
|
|
|
|
dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
|
|
|
|
}
|
|
|
|
if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-06-28 09:34:23 +02:00
|
|
|
}
|
2012-03-28 13:07:42 +02:00
|
|
|
|
|
|
|
enc = InitVP8Encoder(config, pic);
|
|
|
|
if (enc == NULL) return 0; // pic->error is already set.
|
|
|
|
// Note: each of the tasks below account for 20% in the progress report.
|
2013-03-01 01:21:34 +01:00
|
|
|
ok = VP8EncAnalyze(enc);
|
|
|
|
|
|
|
|
// Analysis is done, proceed to actual coding.
|
|
|
|
ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
|
2013-03-12 00:37:42 +01:00
|
|
|
if (!enc->use_tokens_) {
|
2013-04-05 20:33:44 +02:00
|
|
|
ok = ok && VP8EncLoop(enc);
|
2013-03-12 00:37:42 +01:00
|
|
|
} else {
|
2013-04-05 20:33:44 +02:00
|
|
|
ok = ok && VP8EncTokenLoop(enc);
|
2013-03-12 00:37:42 +01:00
|
|
|
}
|
2013-03-01 01:21:34 +01:00
|
|
|
ok = ok && VP8EncFinishAlpha(enc);
|
2011-12-01 07:44:15 +01:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
2013-03-01 01:21:34 +01:00
|
|
|
ok = ok && VP8EncFinishLayer(enc);
|
2011-05-03 02:19:00 +02:00
|
|
|
#endif
|
2013-03-12 00:37:42 +01:00
|
|
|
|
2013-03-01 01:21:34 +01:00
|
|
|
ok = ok && VP8EncWrite(enc);
|
2012-03-28 13:07:42 +02:00
|
|
|
StoreStats(enc);
|
|
|
|
if (!ok) {
|
|
|
|
VP8EncFreeBitWriters(enc);
|
|
|
|
}
|
2013-03-01 01:21:34 +01:00
|
|
|
ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
|
2012-03-28 13:07:42 +02:00
|
|
|
} else {
|
2013-03-29 12:28:49 +01:00
|
|
|
// Make sure we have ARGB samples.
|
|
|
|
if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-28 13:07:42 +02:00
|
|
|
|
2012-04-02 12:58:36 +02:00
|
|
|
ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
|
2011-12-01 11:24:50 +01:00
|
|
|
}
|
2011-06-02 15:55:03 +02:00
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
} // extern "C"
|
|
|
|
#endif
|