Merge "multi-threaded alpha encoding for lossy"

This commit is contained in:
skal 2013-03-01 16:00:10 -08:00 committed by Gerrit Code Review
commit ad2503203a
9 changed files with 96 additions and 33 deletions

1
README
View File

@ -158,6 +158,7 @@ options:
-pass <int> ............ analysis pass number (1..10) -pass <int> ............ analysis pass number (1..10)
-crop <x> <y> <w> <h> .. crop picture with the given rectangle -crop <x> <y> <w> <h> .. crop picture with the given rectangle
-resize <w> <h> ........ resize picture (after any cropping) -resize <w> <h> ........ resize picture (after any cropping)
-mt .................... use multi-threading if available
-map <int> ............. print map of extra info. -map <int> ............. print map of extra info.
-print_psnr ............ prints averaged PSNR distortion. -print_psnr ............ prints averaged PSNR distortion.
-print_ssim ............ prints averaged SSIM distortion. -print_ssim ............ prints averaged SSIM distortion.

View File

@ -546,6 +546,7 @@ static void HelpLong(void) {
printf(" -pass <int> ............ analysis pass number (1..10)\n"); printf(" -pass <int> ............ analysis pass number (1..10)\n");
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n"); printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
printf(" -resize <w> <h> ........ resize picture (after any cropping)\n"); printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
printf(" -mt .................... use multi-threading if available\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" -444 / -422 / -gray ..... Change colorspace\n"); printf(" -444 / -422 / -gray ..... Change colorspace\n");
#endif #endif
@ -724,6 +725,8 @@ int main(int argc, const char *argv[]) {
config.autofilter = 1; config.autofilter = 1;
} else if (!strcmp(argv[c], "-jpeg_like")) { } else if (!strcmp(argv[c], "-jpeg_like")) {
config.emulate_jpeg_size = 1; config.emulate_jpeg_size = 1;
} else if (!strcmp(argv[c], "-mt")) {
++config.thread_level; // increase thread level
} else if (!strcmp(argv[c], "-strong")) { } else if (!strcmp(argv[c], "-strong")) {
config.filter_type = 1; config.filter_type = 1;
} else if (!strcmp(argv[c], "-nostrong")) { } else if (!strcmp(argv[c], "-nostrong")) {

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH CWEBP 1 "February 15, 2013" .TH CWEBP 1 "February 28, 2013"
.SH NAME .SH NAME
cwebp \- compress an image file to a WebP file cwebp \- compress an image file to a WebP file
.SH SYNOPSIS .SH SYNOPSIS
@ -82,6 +82,10 @@ of JPEG compression. This flag will generally produce an output file of
similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
with less visual distortion. with less visual distortion.
.TP .TP
.B \-mt
Use multi-threading for encoding, if possible. This option is only effective
when using lossy compression on a source with a transparency channel.
.TP
.B \-af .B \-af
Turns auto-filter on. This algorithm will spend additional time optimizing Turns auto-filter on. This algorithm will spend additional time optimizing
the filtering strength to reach a well-balanced quality. the filtering strength to reach a well-balanced quality.

View File

@ -286,42 +286,80 @@ static int EncodeAlpha(VP8Encoder* const enc,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main calls // Main calls
void VP8EncInitAlpha(VP8Encoder* const enc) { static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
enc->alpha_data_ = NULL;
enc->alpha_data_size_ = 0;
}
int VP8EncFinishAlpha(VP8Encoder* const enc) {
if (enc->has_alpha_) {
const WebPConfig* config = enc->config_; const WebPConfig* config = enc->config_;
uint8_t* tmp_data = NULL; uint8_t* alpha_data = NULL;
size_t tmp_size = 0; size_t alpha_size = 0;
const int effort_level = config->method; // maps to [0..6] const int effort_level = config->method; // maps to [0..6]
const WEBP_FILTER_TYPE filter = const WEBP_FILTER_TYPE filter =
(config->alpha_filtering == 0) ? WEBP_FILTER_NONE : (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST : (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
WEBP_FILTER_BEST; WEBP_FILTER_BEST;
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
filter, effort_level, &tmp_data, &tmp_size)) { filter, effort_level, &alpha_data, &alpha_size)) {
return 0; return 0;
} }
if (tmp_size != (uint32_t)tmp_size) { // Sanity check. if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
free(tmp_data); free(alpha_data);
return 0; return 0;
} }
enc->alpha_data_size_ = (uint32_t)tmp_size; enc->alpha_data_size_ = (uint32_t)alpha_size;
enc->alpha_data_ = tmp_data; enc->alpha_data_ = alpha_data;
(void)dummy;
return 1;
}
void VP8EncInitAlpha(VP8Encoder* const enc) {
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
enc->alpha_data_ = NULL;
enc->alpha_data_size_ = 0;
if (enc->thread_level_ > 0) {
WebPWorker* const worker = &enc->alpha_worker_;
WebPWorkerInit(worker);
worker->data1 = enc;
worker->data2 = NULL;
worker->hook = (WebPWorkerHook)CompressAlphaJob;
}
}
int VP8EncStartAlpha(VP8Encoder* const enc) {
if (enc->has_alpha_) {
if (enc->thread_level_ > 0) {
WebPWorker* const worker = &enc->alpha_worker_;
if (!WebPWorkerReset(worker)) { // Makes sure worker is good to go.
return 0;
}
WebPWorkerLaunch(worker);
return 1;
} else {
return CompressAlphaJob(enc, NULL); // just do the job right away
}
}
return 1;
}
int VP8EncFinishAlpha(VP8Encoder* const enc) {
if (enc->has_alpha_) {
if (enc->thread_level_ > 0) {
WebPWorker* const worker = &enc->alpha_worker_;
if (!WebPWorkerSync(worker)) return 0; // error
}
} }
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
} }
void VP8EncDeleteAlpha(VP8Encoder* const enc) { int VP8EncDeleteAlpha(VP8Encoder* const enc) {
int ok = 1;
if (enc->thread_level_ > 0) {
WebPWorker* const worker = &enc->alpha_worker_;
ok = WebPWorkerSync(worker); // finish anything left in flight
WebPWorkerEnd(worker); // still need to end the worker, even if !ok
}
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;
enc->has_alpha_ = 0; enc->has_alpha_ = 0;
return ok;
} }
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

View File

@ -47,6 +47,7 @@ int WebPConfigInitInternal(WebPConfig* config,
config->lossless = 0; config->lossless = 0;
config->image_hint = WEBP_HINT_DEFAULT; config->image_hint = WEBP_HINT_DEFAULT;
config->emulate_jpeg_size = 0; config->emulate_jpeg_size = 0;
config->thread_level = 0;
// TODO(skal): tune. // TODO(skal): tune.
switch (preset) { switch (preset) {
@ -125,6 +126,8 @@ int WebPValidateConfig(const WebPConfig* config) {
return 0; return 0;
if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1) if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
return 0; return 0;
if (config->thread_level < 0 || config->thread_level > 1)
return 0;
return 1; return 1;
} }

View File

@ -625,7 +625,10 @@ static void SetBlock(uint8_t* p, int value, int size) {
#endif #endif
static void ResetSSE(VP8Encoder* const enc) { static void ResetSSE(VP8Encoder* const enc) {
memset(enc->sse_, 0, sizeof(enc->sse_)); enc->sse_[0] = 0;
enc->sse_[1] = 0;
enc->sse_[2] = 0;
// Note: enc->sse_[3] is managed by alpha.c
enc->sse_count_ = 0; enc->sse_count_ = 0;
} }

View File

@ -16,6 +16,7 @@
#include "../webp/encode.h" #include "../webp/encode.h"
#include "../dsp/dsp.h" #include "../dsp/dsp.h"
#include "../utils/bit_writer.h" #include "../utils/bit_writer.h"
#include "../utils/thread.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -386,6 +387,7 @@ struct VP8Encoder {
int has_alpha_; int has_alpha_;
uint8_t* alpha_data_; // non-NULL if transparency is present uint8_t* alpha_data_; // non-NULL if transparency is present
uint32_t alpha_data_size_; uint32_t alpha_data_size_;
WebPWorker alpha_worker_;
// enhancement layer // enhancement layer
int use_layer_; int use_layer_;
@ -416,6 +418,7 @@ struct VP8Encoder {
int method_; // 0=fastest, 6=best/slowest. int method_; // 0=fastest, 6=best/slowest.
VP8RDLevel rd_opt_level_; // Deduced from method_. VP8RDLevel rd_opt_level_; // Deduced from method_.
int max_i4_header_bits_; // partition #0 safeness factor int max_i4_header_bits_; // partition #0 safeness factor
int thread_level_; // derived from config->thread_level
// Memory // Memory
VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1) VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1)
@ -499,8 +502,9 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
// in alpha.c // in alpha.c
void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process
int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data int 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

View File

@ -125,6 +125,8 @@ static void MapConfigToTools(VP8Encoder* const enc) {
enc->max_i4_header_bits_ = enc->max_i4_header_bits_ =
256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
(limit * limit) / (100 * 100); // ... modulated with a quadratic curve. (limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
enc->thread_level_ = enc->config_->thread_level;
} }
// Memory scaling with dimensions: // Memory scaling with dimensions:
@ -266,14 +268,16 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
return enc; return enc;
} }
static void DeleteVP8Encoder(VP8Encoder* enc) { static int DeleteVP8Encoder(VP8Encoder* enc) {
int ok = 1;
if (enc != NULL) { if (enc != NULL) {
VP8EncDeleteAlpha(enc); ok = VP8EncDeleteAlpha(enc);
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
VP8EncDeleteLayer(enc); VP8EncDeleteLayer(enc);
#endif #endif
free(enc); free(enc);
} }
return ok;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -336,7 +340,7 @@ int WebPReportProgress(const WebPPicture* const pic,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int WebPEncode(const WebPConfig* config, WebPPicture* pic) { int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
int ok; int ok = 0;
if (pic == NULL) if (pic == NULL)
return 0; return 0;
@ -365,19 +369,21 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
enc = InitVP8Encoder(config, pic); enc = InitVP8Encoder(config, pic);
if (enc == NULL) return 0; // pic->error is already set. if (enc == NULL) return 0; // pic->error is already set.
// Note: each of the tasks below account for 20% in the progress report. // Note: each of the tasks below account for 20% in the progress report.
ok = VP8EncAnalyze(enc) ok = VP8EncAnalyze(enc);
&& VP8StatLoop(enc)
&& VP8EncLoop(enc) // Analysis is done, proceed to actual coding.
&& VP8EncFinishAlpha(enc) ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
ok = ok && VP8StatLoop(enc) && VP8EncLoop(enc);
ok = ok && VP8EncFinishAlpha(enc);
#ifdef WEBP_EXPERIMENTAL_FEATURES #ifdef WEBP_EXPERIMENTAL_FEATURES
&& VP8EncFinishLayer(enc) ok = ok && VP8EncFinishLayer(enc);
#endif #endif
&& VP8EncWrite(enc); ok = ok && VP8EncWrite(enc);
StoreStats(enc); StoreStats(enc);
if (!ok) { if (!ok) {
VP8EncFreeBitWriters(enc); VP8EncFreeBitWriters(enc);
} }
DeleteVP8Encoder(enc); ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
} else { } else {
if (pic->argb == NULL) if (pic->argb == NULL)
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);

View File

@ -125,8 +125,9 @@ struct WebPConfig {
// to better match the expected output size from // to better match the expected output size from
// JPEG compression. Generally, the output size will // JPEG compression. Generally, the output size will
// be similar but the degradation will be lower. // be similar but the degradation will be lower.
int thread_level; // If non-zero, try and use multi-threaded encoding.
uint32_t pad[7]; // padding for later use uint32_t pad[6]; // padding for later use
}; };
// Enumerate some predefined settings for WebPConfig, depending on the type // Enumerate some predefined settings for WebPConfig, depending on the type