mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 05:38:22 +01:00
add stats for lossless
* Extend AuxStats with new fields it's slightly ABI-incompatible, but i guess it's ok for 0.1.99+ I expect to add more stats later, possibly (predictor stats, etc.) * Have cwebp report the features used by lossless compression (either for alpha or full lossless coding) * Print the PSNR for alpha (useful in case of -alpha_q) * clean-up alpha.c signatures + misc cleanup (added const '* const ptr', etc.) Change-Id: I157a21581f1793cb0c6cc0882e7b0a2dde68a970
This commit is contained in:
parent
d39177b74c
commit
7d853d79dc
@ -614,6 +614,25 @@ static void PrintValues(const int values[4]) {
|
|||||||
fprintf(stderr, "|\n");
|
fprintf(stderr, "|\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
||||||
|
const char* const description) {
|
||||||
|
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
||||||
|
description, stats->lossless_size);
|
||||||
|
if (stats->lossless_features) {
|
||||||
|
fprintf(stderr, " * Lossless features used:");
|
||||||
|
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
|
||||||
|
if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
|
||||||
|
if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
|
||||||
|
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
|
||||||
|
stats->histogram_bits, stats->transform_bits, stats->cache_bits);
|
||||||
|
if (stats->palette_size > 0) {
|
||||||
|
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
||||||
int short_output,
|
int short_output,
|
||||||
const char* const file_name) {
|
const char* const file_name) {
|
||||||
@ -624,6 +643,7 @@ static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
|||||||
fprintf(stderr, "File: %s\n", file_name);
|
fprintf(stderr, "File: %s\n", file_name);
|
||||||
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
||||||
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
|
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
|
||||||
|
PrintFullLosslessInfo(stats, "ARGB");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,9 +678,9 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
|||||||
100.f * stats->header_bytes[0] / stats->coded_size,
|
100.f * stats->header_bytes[0] / stats->coded_size,
|
||||||
stats->header_bytes[1],
|
stats->header_bytes[1],
|
||||||
100.f * stats->header_bytes[1] / stats->coded_size);
|
100.f * stats->header_bytes[1] / stats->coded_size);
|
||||||
if (stats->alpha_data_size) {
|
if (stats->alpha_data_size > 0) {
|
||||||
fprintf(stderr, " transparency: %6d\n",
|
fprintf(stderr, " transparency: %6d (%.1f dB)\n",
|
||||||
stats->alpha_data_size);
|
stats->alpha_data_size, stats->PSNR[4]);
|
||||||
}
|
}
|
||||||
if (stats->layer_data_size) {
|
if (stats->layer_data_size) {
|
||||||
fprintf(stderr, " enhancement: %6d\n",
|
fprintf(stderr, " enhancement: %6d\n",
|
||||||
@ -686,8 +706,11 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
|||||||
fprintf(stderr, " segments total: ");
|
fprintf(stderr, " segments total: ");
|
||||||
PrintByteCount(totals, stats->coded_size, NULL);
|
PrintByteCount(totals, stats->coded_size, NULL);
|
||||||
}
|
}
|
||||||
|
if (stats->lossless_size > 0) {
|
||||||
|
PrintFullLosslessInfo(stats, "alpha");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pic->extra_info) {
|
if (pic->extra_info != NULL) {
|
||||||
const int mb_w = (pic->width + 15) / 16;
|
const int mb_w = (pic->width + 15) / 16;
|
||||||
const int mb_h = (pic->height + 15) / 16;
|
const int mb_h = (pic->height + 15) / 16;
|
||||||
const int type = pic->extra_info_type;
|
const int type = pic->extra_info_type;
|
||||||
@ -1100,8 +1123,10 @@ int main(int argc, const char *argv[]) {
|
|||||||
fprintf(stderr, "be performed, but its results discarded.\n\n");
|
fprintf(stderr, "be performed, but its results discarded.\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
picture.stats = &stats;
|
if (!quiet) {
|
||||||
stats.user_data = (void*)in_file;
|
picture.stats = &stats;
|
||||||
|
stats.user_data = (void*)in_file;
|
||||||
|
}
|
||||||
|
|
||||||
// Compress
|
// Compress
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
117
src/enc/alpha.c
117
src/enc/alpha.c
@ -22,19 +22,15 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
// Encodes the given alpha data via specified compression method 'method'.
|
||||||
// int quality, int method, int filter, int effort_level,
|
// The pre-processing (quantization) is performed if 'quality' is less than 100.
|
||||||
// uint8_t** output, size_t* output_size)
|
// For such cases, the encoding is lossy. The valid range is [0, 100] for
|
||||||
//
|
// 'quality' and [0, 1] for 'method':
|
||||||
// Encodes the given alpha data 'data' of size 'stride'x'height' via specified
|
|
||||||
// compression method 'method'. The pre-processing (Quantization) is
|
|
||||||
// performed if 'quality' is less than 100. For such cases, the encoding is
|
|
||||||
// lossy. Valid ranges for 'quality' is [0, 100] and 'method' is [0, 1]:
|
|
||||||
// 'method = 0' - No compression;
|
// 'method = 0' - No compression;
|
||||||
// 'method = 1' - Use lossless coder on the alpha plane only
|
// 'method = 1' - Use lossless coder on the alpha plane only
|
||||||
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
||||||
// vertical & gradient filters. The prediction mode 4 will try all the
|
// vertical & gradient filters. The prediction mode 4 will try all the
|
||||||
// prediction modes (0 to 3) and pick the best prediction mode.
|
// prediction modes 0 to 3 and pick the best one.
|
||||||
// 'effort_level': specifies how much effort must be spent to try and reduce
|
// 'effort_level': specifies how much effort must be spent to try and reduce
|
||||||
// the compressed output size. In range 0 (quick) to 6 (slow).
|
// the compressed output size. In range 0 (quick) to 6 (slow).
|
||||||
//
|
//
|
||||||
@ -50,10 +46,10 @@ extern "C" {
|
|||||||
|
|
||||||
#include "../enc/vp8li.h"
|
#include "../enc/vp8li.h"
|
||||||
|
|
||||||
static int EncodeLossless(const uint8_t* data, int width, int height,
|
static int EncodeLossless(const uint8_t* const data, int width, int height,
|
||||||
int effort_level, // in [0..6] range
|
int effort_level, // in [0..6] range
|
||||||
VP8BitWriter* const bw) {
|
VP8BitWriter* const bw,
|
||||||
|
WebPAuxStats* const stats) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
WebPConfig config;
|
WebPConfig config;
|
||||||
WebPPicture picture;
|
WebPPicture picture;
|
||||||
@ -63,6 +59,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
picture.width = width;
|
picture.width = width;
|
||||||
picture.height = height;
|
picture.height = height;
|
||||||
picture.use_argb = 1;
|
picture.use_argb = 1;
|
||||||
|
picture.stats = stats;
|
||||||
if (!WebPPictureAlloc(&picture)) return 0;
|
if (!WebPPictureAlloc(&picture)) return 0;
|
||||||
|
|
||||||
// Transfer the alpha values to the green channel.
|
// Transfer the alpha values to the green channel.
|
||||||
@ -101,10 +98,12 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
|
||||||
int method, int filter, int reduce_levels,
|
int method, int filter, int reduce_levels,
|
||||||
int effort_level, // in [0..6] range
|
int effort_level, // in [0..6] range
|
||||||
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
uint8_t* const tmp_alpha,
|
||||||
|
VP8BitWriter* const bw,
|
||||||
|
WebPAuxStats* const stats) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
const uint8_t* alpha_src;
|
const uint8_t* alpha_src;
|
||||||
WebPFilterFunc filter_func;
|
WebPFilterFunc filter_func;
|
||||||
@ -139,7 +138,7 @@ static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
|||||||
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
||||||
ok = ok && !bw->error_;
|
ok = ok && !bw->error_;
|
||||||
} else {
|
} else {
|
||||||
ok = EncodeLossless(alpha_src, width, height, effort_level, bw);
|
ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
|
||||||
VP8BitWriterFinish(bw);
|
VP8BitWriterFinish(bw);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
@ -157,19 +156,25 @@ static void CopyPlane(const uint8_t* src, int src_stride,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
static int EncodeAlpha(VP8Encoder* const enc,
|
||||||
int quality, int method, int filter,
|
int quality, int method, int filter,
|
||||||
int effort_level,
|
int effort_level,
|
||||||
uint8_t** output, size_t* output_size) {
|
uint8_t** const output, size_t* const output_size) {
|
||||||
|
const WebPPicture* const pic = enc->pic_;
|
||||||
|
const int width = pic->width;
|
||||||
|
const int height = pic->height;
|
||||||
|
|
||||||
uint8_t* quant_alpha = NULL;
|
uint8_t* quant_alpha = NULL;
|
||||||
const size_t data_size = width * height;
|
const size_t data_size = width * height;
|
||||||
|
uint64_t sse = 0;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const int reduce_levels = (quality < 100);
|
const int reduce_levels = (quality < 100);
|
||||||
|
|
||||||
// quick sanity checks
|
// quick sanity checks
|
||||||
assert(data != NULL && output != NULL && output_size != NULL);
|
assert(enc != NULL && pic != NULL && pic->a != NULL);
|
||||||
|
assert(output != NULL && output_size != NULL);
|
||||||
assert(width > 0 && height > 0);
|
assert(width > 0 && height > 0);
|
||||||
assert(stride >= width);
|
assert(pic->a_stride >= width);
|
||||||
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
||||||
|
|
||||||
if (quality < 0 || quality > 100) {
|
if (quality < 0 || quality > 100) {
|
||||||
@ -186,7 +191,7 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract alpha data (width x height) from raw_data (stride x height).
|
// Extract alpha data (width x height) from raw_data (stride x height).
|
||||||
CopyPlane(data, stride, quant_alpha, width, width, height);
|
CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
|
||||||
|
|
||||||
if (reduce_levels) { // No Quantization required for 'quality = 100'.
|
if (reduce_levels) { // No Quantization required for 'quality = 100'.
|
||||||
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
||||||
@ -194,24 +199,22 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
// and Quality:]70, 100] -> Levels:]16, 256].
|
// and Quality:]70, 100] -> Levels:]16, 256].
|
||||||
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
||||||
: (16 + (quality - 70) * 8);
|
: (16 + (quality - 70) * 8);
|
||||||
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL);
|
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
VP8BitWriter bw;
|
VP8BitWriter bw;
|
||||||
size_t best_score;
|
|
||||||
int test_filter;
|
int test_filter;
|
||||||
uint8_t* filtered_alpha = NULL;
|
uint8_t* filtered_alpha = NULL;
|
||||||
|
|
||||||
// We always test WEBP_FILTER_NONE first.
|
// We always test WEBP_FILTER_NONE first.
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||||
method, WEBP_FILTER_NONE, reduce_levels,
|
method, WEBP_FILTER_NONE, reduce_levels,
|
||||||
effort_level, NULL, &bw);
|
effort_level, NULL, &bw, pic->stats);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
VP8BitWriterWipeOut(&bw);
|
VP8BitWriterWipeOut(&bw);
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
best_score = VP8BitWriterSize(&bw);
|
|
||||||
|
|
||||||
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
||||||
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
||||||
@ -228,35 +231,46 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try the other mode(s).
|
// Try the other mode(s).
|
||||||
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
{
|
||||||
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
WebPAuxStats best_stats;
|
||||||
++test_filter) {
|
size_t best_score = VP8BitWriterSize(&bw);
|
||||||
VP8BitWriter tmp_bw;
|
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||||
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
||||||
continue;
|
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
||||||
}
|
++test_filter) {
|
||||||
|
VP8BitWriter tmp_bw;
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
||||||
method, test_filter, reduce_levels,
|
continue;
|
||||||
effort_level, filtered_alpha, &tmp_bw);
|
|
||||||
if (ok) {
|
|
||||||
const size_t score = VP8BitWriterSize(&tmp_bw);
|
|
||||||
if (score < best_score) {
|
|
||||||
// swap bitwriter objects.
|
|
||||||
VP8BitWriter tmp = tmp_bw;
|
|
||||||
tmp_bw = bw;
|
|
||||||
bw = tmp;
|
|
||||||
best_score = score;
|
|
||||||
}
|
}
|
||||||
} else {
|
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||||
VP8BitWriterWipeOut(&bw);
|
method, test_filter, reduce_levels,
|
||||||
|
effort_level, filtered_alpha, &tmp_bw,
|
||||||
|
pic->stats);
|
||||||
|
if (ok) {
|
||||||
|
const size_t score = VP8BitWriterSize(&tmp_bw);
|
||||||
|
if (score < best_score) {
|
||||||
|
// swap bitwriter objects.
|
||||||
|
VP8BitWriter tmp = tmp_bw;
|
||||||
|
tmp_bw = bw;
|
||||||
|
bw = tmp;
|
||||||
|
best_score = score;
|
||||||
|
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
}
|
||||||
|
VP8BitWriterWipeOut(&tmp_bw);
|
||||||
}
|
}
|
||||||
VP8BitWriterWipeOut(&tmp_bw);
|
if (pic->stats != NULL) *pic->stats = best_stats;
|
||||||
}
|
}
|
||||||
Ok:
|
Ok:
|
||||||
if (ok) {
|
if (ok) {
|
||||||
*output_size = VP8BitWriterSize(&bw);
|
*output_size = VP8BitWriterSize(&bw);
|
||||||
*output = VP8BitWriterBuf(&bw);
|
*output = VP8BitWriterBuf(&bw);
|
||||||
|
if (pic->stats != NULL) { // need stats?
|
||||||
|
pic->stats->coded_size += *output_size;
|
||||||
|
enc->sse_[3] = sse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(filtered_alpha);
|
free(filtered_alpha);
|
||||||
}
|
}
|
||||||
@ -269,16 +283,15 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Main calls
|
// Main calls
|
||||||
|
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc) {
|
void VP8EncInitAlpha(VP8Encoder* const enc) {
|
||||||
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
|
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
|
||||||
enc->alpha_data_ = NULL;
|
enc->alpha_data_ = NULL;
|
||||||
enc->alpha_data_size_ = 0;
|
enc->alpha_data_size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc) {
|
int VP8EncFinishAlpha(VP8Encoder* const enc) {
|
||||||
if (enc->has_alpha_) {
|
if (enc->has_alpha_) {
|
||||||
const WebPConfig* config = enc->config_;
|
const WebPConfig* config = enc->config_;
|
||||||
const WebPPicture* pic = enc->pic_;
|
|
||||||
uint8_t* tmp_data = NULL;
|
uint8_t* tmp_data = NULL;
|
||||||
size_t tmp_size = 0;
|
size_t tmp_size = 0;
|
||||||
const int effort_level = config->method; // maps to [0..6]
|
const int effort_level = config->method; // maps to [0..6]
|
||||||
@ -287,9 +300,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
||||||
WEBP_FILTER_BEST;
|
WEBP_FILTER_BEST;
|
||||||
|
|
||||||
assert(pic->a);
|
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
|
||||||
if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride,
|
|
||||||
config->alpha_quality, config->alpha_compression,
|
|
||||||
filter, effort_level, &tmp_data, &tmp_size)) {
|
filter, effort_level, &tmp_data, &tmp_size)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -303,7 +314,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8EncDeleteAlpha(VP8Encoder* enc) {
|
void VP8EncDeleteAlpha(VP8Encoder* const enc) {
|
||||||
free(enc->alpha_data_);
|
free(enc->alpha_data_);
|
||||||
enc->alpha_data_ = NULL;
|
enc->alpha_data_ = NULL;
|
||||||
enc->alpha_data_size_ = 0;
|
enc->alpha_data_size_ = 0;
|
||||||
|
@ -402,7 +402,7 @@ struct VP8Encoder {
|
|||||||
|
|
||||||
// probabilities and statistics
|
// probabilities and statistics
|
||||||
VP8Proba proba_;
|
VP8Proba proba_;
|
||||||
uint64_t sse_[3]; // sum of Y/U/V squared errors for all macroblocks
|
uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
|
||||||
uint64_t sse_count_; // pixel count for the sse_[] stats
|
uint64_t sse_count_; // pixel count for the sse_[] stats
|
||||||
int coded_size_;
|
int coded_size_;
|
||||||
int residual_bytes_[3][4];
|
int residual_bytes_[3][4];
|
||||||
@ -488,9 +488,9 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
|
|||||||
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
||||||
|
|
||||||
// in alpha.c
|
// in alpha.c
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression
|
void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data
|
int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
|
||||||
void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data
|
void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
|
||||||
|
|
||||||
// in layer.c
|
// in layer.c
|
||||||
void VP8EncInitLayer(VP8Encoder* const enc); // init everything
|
void VP8EncInitLayer(VP8Encoder* const enc); // init everything
|
||||||
|
@ -610,7 +610,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
|
|
||||||
// Check if it would be a good idea to subtract green from red and blue. We
|
// Check if it would be a good idea to subtract green from red and blue. We
|
||||||
// only impact entropy in red/blue components, don't bother to look at others.
|
// only impact entropy in red/blue components, don't bother to look at others.
|
||||||
static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc,
|
static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
|
||||||
int width, int height,
|
int width, int height,
|
||||||
VP8LBitWriter* const bw) {
|
VP8LBitWriter* const bw) {
|
||||||
if (!enc->use_palette_) {
|
if (!enc->use_palette_) {
|
||||||
@ -639,7 +639,8 @@ static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc,
|
|||||||
free(histo);
|
free(histo);
|
||||||
|
|
||||||
// Check if subtracting green yields low entropy.
|
// Check if subtracting green yields low entropy.
|
||||||
if (bit_cost_after < bit_cost_before) {
|
enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
|
||||||
|
if (enc->use_subtract_green_) {
|
||||||
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
|
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
|
||||||
VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
|
VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
|
||||||
VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
|
VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
|
||||||
@ -938,6 +939,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
const int width = picture->width;
|
const int width = picture->width;
|
||||||
const int height = picture->height;
|
const int height = picture->height;
|
||||||
VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
|
VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
|
||||||
|
const size_t byte_position = VP8LBitWriterNumBytes(bw);
|
||||||
|
|
||||||
if (enc == NULL) {
|
if (enc == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
@ -1017,6 +1019,20 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (picture->stats != NULL) {
|
||||||
|
WebPAuxStats* const stats = picture->stats;
|
||||||
|
stats->lossless_features = 0;
|
||||||
|
if (enc->use_predict_) stats->lossless_features |= 1;
|
||||||
|
if (enc->use_cross_color_) stats->lossless_features |= 2;
|
||||||
|
if (enc->use_subtract_green_) stats->lossless_features |= 4;
|
||||||
|
if (enc->use_palette_) stats->lossless_features |= 8;
|
||||||
|
stats->histogram_bits = enc->histo_bits_;
|
||||||
|
stats->transform_bits = enc->transform_bits_;
|
||||||
|
stats->cache_bits = enc->cache_bits_;
|
||||||
|
stats->palette_size = enc->palette_size_;
|
||||||
|
stats->lossless_size = VP8LBitWriterNumBytes(bw) - byte_position;
|
||||||
|
}
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
VP8LEncoderDelete(enc);
|
VP8LEncoderDelete(enc);
|
||||||
return err;
|
return err;
|
||||||
@ -1045,6 +1061,16 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
err = VP8_ENC_ERROR_USER_ABORT;
|
err = VP8_ENC_ERROR_USER_ABORT;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
// Reset stats (for pure lossless coding)
|
||||||
|
if (picture->stats != NULL) {
|
||||||
|
WebPAuxStats* const stats = picture->stats;
|
||||||
|
memset(stats, 0, sizeof(*stats));
|
||||||
|
stats->PSNR[0] = 99.;
|
||||||
|
stats->PSNR[1] = 99.;
|
||||||
|
stats->PSNR[2] = 99.;
|
||||||
|
stats->PSNR[3] = 99.;
|
||||||
|
stats->PSNR[4] = 99.;
|
||||||
|
}
|
||||||
|
|
||||||
// Write image size.
|
// Write image size.
|
||||||
VP8LBitWriterInit(&bw, (width * height) >> 1);
|
VP8LBitWriterInit(&bw, (width * height) >> 1);
|
||||||
@ -1075,15 +1101,10 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
|
|
||||||
if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
|
if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
|
||||||
|
|
||||||
// Collect some stats if needed.
|
// Save size.
|
||||||
if (picture->stats != NULL) {
|
if (picture->stats != NULL) {
|
||||||
WebPAuxStats* const stats = picture->stats;
|
picture->stats->coded_size += (int)coded_size;
|
||||||
memset(stats, 0, sizeof(*stats));
|
picture->stats->lossless_size = (int)coded_size;
|
||||||
stats->PSNR[0] = 99.;
|
|
||||||
stats->PSNR[1] = 99.;
|
|
||||||
stats->PSNR[2] = 99.;
|
|
||||||
stats->PSNR[3] = 99.;
|
|
||||||
stats->coded_size = (int)coded_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picture->extra_info != NULL) {
|
if (picture->extra_info != NULL) {
|
||||||
|
@ -38,6 +38,7 @@ typedef struct {
|
|||||||
|
|
||||||
// Encoding parameters derived from image characteristics.
|
// Encoding parameters derived from image characteristics.
|
||||||
int use_cross_color_;
|
int use_cross_color_;
|
||||||
|
int use_subtract_green_;
|
||||||
int use_predict_;
|
int use_predict_;
|
||||||
int use_palette_;
|
int use_palette_;
|
||||||
int palette_size_;
|
int palette_size_;
|
||||||
|
@ -284,6 +284,7 @@ static void FinalizePSNR(const VP8Encoder* const enc) {
|
|||||||
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
|
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
|
||||||
stats->PSNR[2] = (float)GetPSNR(sse[2], 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);
|
stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
|
||||||
|
stats->PSNR[4] = (float)GetPSNR(sse[3], size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StoreStats(VP8Encoder* const enc) {
|
static void StoreStats(VP8Encoder* const enc) {
|
||||||
|
@ -157,7 +157,7 @@ typedef struct WebPPicture WebPPicture; // main structure for I/O
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int coded_size; // final size
|
int coded_size; // final size
|
||||||
|
|
||||||
float PSNR[4]; // peak-signal-to-noise ratio for Y/U/V/All
|
float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
|
||||||
int block_count[3]; // number of intra4/intra16/skipped macroblocks
|
int block_count[3]; // number of intra4/intra16/skipped macroblocks
|
||||||
int header_bytes[2]; // approximate number of bytes spent for header
|
int header_bytes[2]; // approximate number of bytes spent for header
|
||||||
// and mode-partition #0
|
// and mode-partition #0
|
||||||
@ -173,7 +173,16 @@ typedef struct {
|
|||||||
void* user_data; // this field is free to be set to any value and
|
void* user_data; // this field is free to be set to any value and
|
||||||
// used during callbacks (like progress-report e.g.).
|
// used during callbacks (like progress-report e.g.).
|
||||||
|
|
||||||
uint32_t pad[6]; // padding for later use
|
// lossless encoder statistics
|
||||||
|
uint32_t lossless_features; // bit0:predictor bit1:cross-color transform
|
||||||
|
// bit2:subtract-green bit3:color indexing
|
||||||
|
int histogram_bits; // number of precision bits of histogram
|
||||||
|
int transform_bits; // precision bits for transform
|
||||||
|
int cache_bits; // number of bits for color cache lookup
|
||||||
|
int palette_size; // number of color in palette, if used
|
||||||
|
int lossless_size; // final lossless size
|
||||||
|
|
||||||
|
uint32_t pad[4]; // padding for later use
|
||||||
} WebPAuxStats;
|
} WebPAuxStats;
|
||||||
|
|
||||||
// Signature for output function. Should return true if writing was successful.
|
// Signature for output function. Should return true if writing was successful.
|
||||||
|
Loading…
Reference in New Issue
Block a user