mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-28 14:38:21 +01:00
Introduce a 'fast' alpha mode
.. where only 2 filtering modes are potentially tried, instead of all of them. This is fast than the exhaustive 'best' mode, and not much worse. Options for cwebp are: -alpha_filter none -alpha_filter fast (<- default) -alpha_filter best (<- slow) Change-Id: I8cb90ee11b8f981811e013ea4ad5bf72ba3ea7d4
This commit is contained in:
parent
ad1e163a0d
commit
8ca2076de1
2
README
2
README
@ -149,6 +149,8 @@ options:
|
|||||||
-map <int> ............. print map of extra info.
|
-map <int> ............. print map of extra info.
|
||||||
-d <file.pgm> .......... dump the compressed output (PGM file).
|
-d <file.pgm> .......... dump the compressed output (PGM file).
|
||||||
-alpha_method <int> .... Transparency-compression method (0..1)
|
-alpha_method <int> .... Transparency-compression method (0..1)
|
||||||
|
-alpha_filter <string> . predictive filtering for alpha plane.
|
||||||
|
One of: none, fast (default) or best.
|
||||||
-noalpha ............... discard any transparency information.
|
-noalpha ............... discard any transparency information.
|
||||||
|
|
||||||
-short ................. condense printed message
|
-short ................. condense printed message
|
||||||
|
@ -700,7 +700,8 @@ static void HelpLong(void) {
|
|||||||
printf(" -map <int> ............. print map of extra info.\n");
|
printf(" -map <int> ............. print map of extra info.\n");
|
||||||
printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n");
|
printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n");
|
||||||
printf(" -alpha_method <int> .... Transparency-compression method (0..1)\n");
|
printf(" -alpha_method <int> .... Transparency-compression method (0..1)\n");
|
||||||
printf(" -alpha_filter <int> .... predictive filtering for Alpha (0..5)\n");
|
printf(" -alpha_filter <string> . predictive filtering for alpha plane.\n");
|
||||||
|
printf(" One of: none, fast (default) or best.\n");
|
||||||
printf(" -noalpha ............... discard any transparency information.\n");
|
printf(" -noalpha ............... discard any transparency information.\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -795,7 +796,17 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
||||||
config.alpha_compression = strtol(argv[++c], NULL, 0);
|
config.alpha_compression = strtol(argv[++c], NULL, 0);
|
||||||
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
||||||
config.alpha_filtering = strtol(argv[++c], NULL, 0);
|
++c;
|
||||||
|
if (!strcmp(argv[c], "none")) {
|
||||||
|
config.alpha_filtering = 0;
|
||||||
|
} else if (!strcmp(argv[c], "fast")) {
|
||||||
|
config.alpha_filtering = 1;
|
||||||
|
} else if (!strcmp(argv[c], "best")) {
|
||||||
|
config.alpha_filtering = 2;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
} else if (!strcmp(argv[c], "-noalpha")) {
|
} else if (!strcmp(argv[c], "-noalpha")) {
|
||||||
keep_alpha = 0;
|
keep_alpha = 0;
|
||||||
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
||||||
|
12
man/cwebp.1
12
man/cwebp.1
@ -41,11 +41,13 @@ Specify the compression factor for alpha compression between 0 and 100.
|
|||||||
Lossless compression of alpha is achieved using a value of 100, while the lower
|
Lossless compression of alpha is achieved using a value of 100, while the lower
|
||||||
values result in a lossy compression. The default is 100.
|
values result in a lossy compression. The default is 100.
|
||||||
.TP
|
.TP
|
||||||
.B \-alpha_filter int
|
.B \-alpha_filter string
|
||||||
Specify the predictive filtering method (between 0 and 5) for alpha plane.
|
Specify the predictive filtering method for alpha plane. One of 'none', 'fast'
|
||||||
These correspond to prediction modes none, horizontal, vertical, gradient and
|
or 'best', in increasing complexity and slowness order. Default is 'fast'.
|
||||||
paeth filters. The prediction mode 5 will try all the prediction modes (0 to 4)
|
Internally, alpha filtering is performed using four possible predictions (none,
|
||||||
and pick the best prediction mode. The default value is 0 (no prediction).
|
horizontal, vertical, gradient). The 'best' mode will try each modes in turn and
|
||||||
|
pick the one which gives the smaller size. The 'fast' mode will just try to
|
||||||
|
form an a-priori guess without testing all modes.
|
||||||
.TP
|
.TP
|
||||||
.B \-f int
|
.B \-f int
|
||||||
Specify the strength of the deblocking filter, between 0 (no filtering)
|
Specify the strength of the deblocking filter, between 0 (no filtering)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "../utils/alpha.h"
|
#include "../utils/alpha.h"
|
||||||
|
#include "../utils/filters.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -33,10 +34,15 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
const WebPPicture* pic = enc->pic_;
|
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 WEBP_FILTER_TYPE filter =
|
||||||
|
(config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
|
||||||
|
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
||||||
|
WEBP_FILTER_BEST;
|
||||||
|
|
||||||
assert(pic->a);
|
assert(pic->a);
|
||||||
if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride,
|
if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride,
|
||||||
config->alpha_quality, config->alpha_compression,
|
config->alpha_quality, config->alpha_compression,
|
||||||
config->alpha_filtering, &tmp_data, &tmp_size)) {
|
filter, &tmp_data, &tmp_size)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (tmp_size != (uint32_t)tmp_size) { // Sanity check.
|
if (tmp_size != (uint32_t)tmp_size) { // Sanity check.
|
||||||
|
@ -42,7 +42,7 @@ int WebPConfigInitInternal(WebPConfig* const config,
|
|||||||
config->autofilter = 0;
|
config->autofilter = 0;
|
||||||
config->partition_limit = 0;
|
config->partition_limit = 0;
|
||||||
config->alpha_compression = 1;
|
config->alpha_compression = 1;
|
||||||
config->alpha_filtering = 0;
|
config->alpha_filtering = 1;
|
||||||
config->alpha_quality = 100;
|
config->alpha_quality = 100;
|
||||||
|
|
||||||
// TODO(skal): tune.
|
// TODO(skal): tune.
|
||||||
|
@ -203,14 +203,35 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height,
|
|||||||
return ok && !bw->error_;
|
return ok && !bw->error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
||||||
int method, VP8BitWriter* const bw) {
|
int method, int filter, size_t data_size,
|
||||||
|
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
const uint8_t* alpha_src;
|
||||||
|
WebPFilterFunc filter_func;
|
||||||
|
uint8_t header[ALPHA_HEADER_LEN];
|
||||||
|
const size_t expected_size = (method == 0) ?
|
||||||
|
(ALPHA_HEADER_LEN + data_size) : (data_size >> 5);
|
||||||
|
header[0] = (filter << 4) | method;
|
||||||
|
header[1] = 0; // reserved byte for later use
|
||||||
|
VP8BitWriterInit(bw, expected_size);
|
||||||
|
VP8BitWriterAppend(bw, header, sizeof(header));
|
||||||
|
|
||||||
|
filter_func = WebPFilters[filter];
|
||||||
|
if (filter_func) {
|
||||||
|
filter_func(data, width, height, 1, width, tmp_alpha);
|
||||||
|
alpha_src = tmp_alpha;
|
||||||
|
} else {
|
||||||
|
alpha_src = data;
|
||||||
|
}
|
||||||
|
|
||||||
if (method == 0) {
|
if (method == 0) {
|
||||||
ok = VP8BitWriterAppend(bw, data, width * height);
|
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
||||||
ok = ok && !bw->error_;
|
ok = ok && !bw->error_;
|
||||||
} else if (method == 1) {
|
} else {
|
||||||
ok = EncodeZlibTCoder(data, width, height, bw);
|
ok = EncodeZlibTCoder(alpha_src, width, height, bw);
|
||||||
VP8BitWriterFinish(bw);
|
VP8BitWriterFinish(bw);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
@ -239,7 +260,7 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
assert(data != NULL && output != NULL && output_size != NULL);
|
assert(data != NULL && output != NULL && output_size != NULL);
|
||||||
assert(width > 0 && height > 0);
|
assert(width > 0 && height > 0);
|
||||||
assert(stride >= width);
|
assert(stride >= width);
|
||||||
assert(filter < WEBP_FILTER_LAST);
|
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
||||||
|
|
||||||
if (quality < 0 || quality > 100) {
|
if (quality < 0 || quality > 100) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -267,62 +288,67 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
WEBP_FILTER_TYPE this_filter;
|
|
||||||
size_t best_size = 1 << 30;
|
|
||||||
uint8_t* tmp_out = NULL;
|
|
||||||
uint8_t* const filtered_alpha = (uint8_t*)malloc(data_size);
|
|
||||||
if (filtered_alpha == NULL) {
|
|
||||||
free(quant_alpha);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Filtering.
|
|
||||||
for (this_filter = WEBP_FILTER_NONE; this_filter < WEBP_FILTER_LAST;
|
|
||||||
++this_filter) {
|
|
||||||
uint8_t header[ALPHA_HEADER_LEN];
|
|
||||||
VP8BitWriter bw;
|
VP8BitWriter bw;
|
||||||
WebPFilterFunc filter_func = NULL;
|
size_t best_score;
|
||||||
const size_t expected_size = (method == 0) ?
|
int test_filter;
|
||||||
(ALPHA_HEADER_LEN + data_size) : (data_size >> 5);
|
uint8_t* filtered_alpha = NULL;
|
||||||
if (this_filter == WEBP_FILTER_BEST) {
|
|
||||||
continue;
|
// We always test WEBP_FILTER_NONE first.
|
||||||
} else if (this_filter != filter && filter != WEBP_FILTER_BEST) {
|
ok = EncodeAlphaInternal(quant_alpha, width, height, method,
|
||||||
|
WEBP_FILTER_NONE, data_size, NULL, &bw);
|
||||||
|
if (!ok) {
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
best_score = VP8BitWriterSize(&bw);
|
||||||
|
|
||||||
|
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
||||||
|
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
||||||
|
}
|
||||||
|
// Stop?
|
||||||
|
if (filter == WEBP_FILTER_NONE) {
|
||||||
|
goto Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered_alpha = (uint8_t*)malloc(data_size);
|
||||||
|
ok = (filtered_alpha != NULL);
|
||||||
|
if (!ok) {
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the other mode(s).
|
||||||
|
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
||||||
|
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
||||||
|
++test_filter) {
|
||||||
|
VP8BitWriter tmp_bw;
|
||||||
|
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
header[0] = ((this_filter & 0x0f) << 4) | (method & 0x0f);
|
ok = EncodeAlphaInternal(quant_alpha, width, height, method, test_filter,
|
||||||
header[1] = 0; // reserved byte for later use
|
data_size, filtered_alpha, &tmp_bw);
|
||||||
VP8BitWriterInit(&bw, expected_size);
|
|
||||||
VP8BitWriterAppend(&bw, header, sizeof(header));
|
|
||||||
|
|
||||||
filter_func = WebPFilters[this_filter];
|
|
||||||
if (filter_func) {
|
|
||||||
filter_func(quant_alpha, width, height, 1, width, filtered_alpha);
|
|
||||||
ok = EncodeAlphaInternal(filtered_alpha, width, height, method, &bw);
|
|
||||||
} else {
|
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height, method, &bw);
|
|
||||||
}
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
const size_t this_size = VP8BitWriterSize(&bw);
|
const size_t score = VP8BitWriterSize(&tmp_bw);
|
||||||
if (this_size < best_size) {
|
if (score < best_score) {
|
||||||
free(tmp_out);
|
// swap bitwriter objects.
|
||||||
tmp_out = VP8BitWriterBuf(&bw);
|
VP8BitWriter tmp = tmp_bw;
|
||||||
best_size = this_size;
|
tmp_bw = bw;
|
||||||
|
bw = tmp;
|
||||||
|
best_score = score;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
VP8BitWriterWipeOut(&bw);
|
VP8BitWriterWipeOut(&bw);
|
||||||
}
|
}
|
||||||
} else {
|
VP8BitWriterWipeOut(&tmp_bw);
|
||||||
free(tmp_out);
|
|
||||||
VP8BitWriterWipeOut(&bw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok:
|
||||||
if (ok) {
|
if (ok) {
|
||||||
*output_size = best_size;
|
*output_size = VP8BitWriterSize(&bw);
|
||||||
*output = tmp_out;
|
*output = VP8BitWriterBuf(&bw);
|
||||||
}
|
}
|
||||||
free(filtered_alpha);
|
free(filtered_alpha);
|
||||||
}
|
}
|
||||||
|
End:
|
||||||
free(quant_alpha);
|
free(quant_alpha);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@ -379,7 +405,7 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
uint8_t* decoded_data = NULL;
|
uint8_t* decoded_data = NULL;
|
||||||
const size_t decoded_size = height * width;
|
const size_t decoded_size = height * width;
|
||||||
uint8_t* unfiltered_data = NULL;
|
uint8_t* unfiltered_data = NULL;
|
||||||
int filter;
|
WEBP_FILTER_TYPE filter;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int method;
|
int method;
|
||||||
|
|
||||||
@ -394,7 +420,7 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
filter = data[0] >> 4;
|
filter = data[0] >> 4;
|
||||||
ok = (data[1] == 0);
|
ok = (data[1] == 0);
|
||||||
if (method < 0 || method > 1 ||
|
if (method < 0 || method > 1 ||
|
||||||
filter < WEBP_FILTER_NONE || filter > WEBP_FILTER_PAETH || !ok) {
|
filter > WEBP_FILTER_GRADIENT || !ok) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +115,11 @@ static void VerticalUnfilter(const uint8_t* data, int width, int height,
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Gradient filter.
|
// Gradient filter.
|
||||||
|
|
||||||
|
static WEBP_INLINE uint8_t GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
||||||
|
const int g = a + b - c;
|
||||||
|
return (g < 0) ? 0 : (g > 255) ? 255 : (uint8_t)g;
|
||||||
|
}
|
||||||
|
|
||||||
static void GradientFilter(const uint8_t* data, int width, int height,
|
static void GradientFilter(const uint8_t* data, int width, int height,
|
||||||
int bpp, int stride, uint8_t* filtered_data) {
|
int bpp, int stride, uint8_t* filtered_data) {
|
||||||
int h;
|
int h;
|
||||||
@ -131,9 +136,9 @@ static void GradientFilter(const uint8_t* data, int width, int height,
|
|||||||
const uint8_t* const prev_line = scan_line - stride;
|
const uint8_t* const prev_line = scan_line - stride;
|
||||||
memcpy((void*)out, (const void*)scan_line, bpp);
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
const uint8_t predictor = scan_line[w - bpp] + prev_line[w] -
|
out[w] = scan_line[w] - GradientPredictor(scan_line[w - bpp],
|
||||||
prev_line[w - bpp];
|
prev_line[w],
|
||||||
out[w] = scan_line[w] - predictor;
|
prev_line[w - bpp]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,110 +159,81 @@ static void GradientUnfilter(const uint8_t* data, int width, int height,
|
|||||||
const uint8_t* const out_prev_line = out - stride;
|
const uint8_t* const out_prev_line = out - stride;
|
||||||
memcpy((void*)out, (const void*)scan_line, bpp);
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
const uint8_t predictor = out[w - bpp] + out_prev_line[w] -
|
out[w] = scan_line[w] + GradientPredictor(out[w - bpp],
|
||||||
out_prev_line[w - bpp];
|
out_prev_line[w],
|
||||||
out[w] = scan_line[w] + predictor;
|
out_prev_line[w - bpp]);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Paeth filter.
|
|
||||||
|
|
||||||
static WEBP_INLINE int AbsDiff(int a, int b) {
|
|
||||||
return (a > b) ? a - b : b - a;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE uint8_t PaethPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
|
||||||
const int p = a + b - c; // Base.
|
|
||||||
const int pa = AbsDiff(p, a);
|
|
||||||
const int pb = AbsDiff(p, b);
|
|
||||||
const int pc = AbsDiff(p, c);
|
|
||||||
|
|
||||||
// Return nearest to base of a, b, c.
|
|
||||||
return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PaethFilter(const uint8_t* data, int width, int height,
|
|
||||||
int bpp, int stride, uint8_t* filtered_data) {
|
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
SANITY_CHECK(data, filtered_data);
|
|
||||||
|
|
||||||
// Top scan line (special case).
|
|
||||||
memcpy((void*)filtered_data, (const void*)data, bpp);
|
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
|
||||||
// Note: PaethPredictor(scan_line[w - bpp], 0, 0) == scan_line[w - bpp].
|
|
||||||
filtered_data[w] = data[w] - data[w - bpp];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter line-by-line.
|
|
||||||
for (h = 1; h < height; ++h) {
|
|
||||||
int w;
|
|
||||||
const uint8_t* const scan_line = data + h * stride;
|
|
||||||
uint8_t* const out = filtered_data + h * stride;
|
|
||||||
const uint8_t* const prev_line = scan_line - stride;
|
|
||||||
for (w = 0; w < bpp; ++w) {
|
|
||||||
// Note: PaethPredictor(0, prev_line[w], 0) == prev_line[w].
|
|
||||||
out[w] = scan_line[w] - prev_line[w];
|
|
||||||
}
|
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
|
||||||
out[w] = scan_line[w] - PaethPredictor(scan_line[w - bpp], prev_line[w],
|
|
||||||
prev_line[w - bpp]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PaethUnfilter(const uint8_t* data, int width, int height,
|
|
||||||
int bpp, int stride, uint8_t* recon_data) {
|
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
SANITY_CHECK(data, recon_data);
|
|
||||||
|
|
||||||
// Top scan line (special case).
|
|
||||||
memcpy((void*)recon_data, (const void*)data, bpp);
|
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
|
||||||
// Note: PaethPredictor(out[w - bpp], 0, 0) == out[w - bpp].
|
|
||||||
recon_data[w] = data[w] + recon_data[w - bpp];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unfilter line-by-line.
|
|
||||||
for (h = 1; h < height; ++h) {
|
|
||||||
int w;
|
|
||||||
const uint8_t* const scan_line = data + h * stride;
|
|
||||||
uint8_t* const out = recon_data + h * stride;
|
|
||||||
const uint8_t* const out_prev = out - stride;
|
|
||||||
for (w = 0; w < bpp; ++w) {
|
|
||||||
// Note: PaethPredictor(0, out_prev[w], 0) == out_prev[w].
|
|
||||||
out[w] = scan_line[w] + out_prev[w];
|
|
||||||
}
|
|
||||||
for (w = bpp; w < width * bpp; ++w) {
|
|
||||||
out[w] = scan_line[w] + PaethPredictor(out[w - bpp], out_prev[w],
|
|
||||||
out_prev[w - bpp]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SANITY_CHECK
|
#undef SANITY_CHECK
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Quick estimate of a potentially interesting filter mode to try, in addition
|
||||||
|
// to the default NONE.
|
||||||
|
|
||||||
|
#define SMAX 16
|
||||||
|
#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
|
||||||
|
|
||||||
|
WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||||
|
int width, int height, int stride) {
|
||||||
|
int i, j;
|
||||||
|
int bins[WEBP_FILTER_LAST][SMAX];
|
||||||
|
memset(bins, 0, sizeof(bins));
|
||||||
|
// We only sample every other pixels. That's enough.
|
||||||
|
for (j = 2; j < height - 1; j += 2) {
|
||||||
|
const uint8_t* const p = data + j * stride;
|
||||||
|
int mean = p[0];
|
||||||
|
for (i = 2; i < width - 1; i += 2) {
|
||||||
|
const int diff0 = SDIFF(p[i], mean);
|
||||||
|
const int diff1 = SDIFF(p[i], p[i - 1]);
|
||||||
|
const int diff2 = SDIFF(p[i], p[i - width]);
|
||||||
|
const int grad_pred =
|
||||||
|
GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]);
|
||||||
|
const int diff3 = SDIFF(p[i], grad_pred);
|
||||||
|
bins[WEBP_FILTER_NONE][diff0] = 1;
|
||||||
|
bins[WEBP_FILTER_HORIZONTAL][diff1] = 1;
|
||||||
|
bins[WEBP_FILTER_VERTICAL][diff2] = 1;
|
||||||
|
bins[WEBP_FILTER_GRADIENT][diff3] = 1;
|
||||||
|
mean = (3 * mean + p[i] + 2) >> 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE;
|
||||||
|
int best_score = 0x7fffffff;
|
||||||
|
for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) {
|
||||||
|
int score = 0;
|
||||||
|
for (i = 0; i < SMAX; ++i) {
|
||||||
|
if (bins[filter][i] > 0) {
|
||||||
|
score += i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (score < best_score) {
|
||||||
|
best_score = score;
|
||||||
|
best_filter = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best_filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SMAX
|
||||||
|
#undef SDIFF
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
||||||
NULL, // WEBP_FILTER_NONE
|
NULL, // WEBP_FILTER_NONE
|
||||||
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
||||||
VerticalFilter, // WEBP_FILTER_VERTICAL
|
VerticalFilter, // WEBP_FILTER_VERTICAL
|
||||||
GradientFilter, // WEBP_FILTER_GRADIENT
|
GradientFilter // WEBP_FILTER_GRADIENT
|
||||||
PaethFilter, // WEBP_FILTER_PAETH
|
|
||||||
NULL // WEBP_FILTER_BEST
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
||||||
NULL, // WEBP_FILTER_NONE
|
NULL, // WEBP_FILTER_NONE
|
||||||
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
||||||
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
||||||
GradientUnfilter, // WEBP_FILTER_GRADIENT
|
GradientUnfilter // WEBP_FILTER_GRADIENT
|
||||||
PaethUnfilter, // WEBP_FILTER_PAETH
|
|
||||||
NULL // WEBP_FILTER_BEST
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -24,9 +24,9 @@ typedef enum {
|
|||||||
WEBP_FILTER_HORIZONTAL,
|
WEBP_FILTER_HORIZONTAL,
|
||||||
WEBP_FILTER_VERTICAL,
|
WEBP_FILTER_VERTICAL,
|
||||||
WEBP_FILTER_GRADIENT,
|
WEBP_FILTER_GRADIENT,
|
||||||
WEBP_FILTER_PAETH,
|
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
|
||||||
WEBP_FILTER_BEST,
|
WEBP_FILTER_BEST,
|
||||||
WEBP_FILTER_LAST,
|
WEBP_FILTER_FAST
|
||||||
} WEBP_FILTER_TYPE;
|
} WEBP_FILTER_TYPE;
|
||||||
|
|
||||||
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
||||||
@ -38,10 +38,14 @@ typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
|||||||
// 'bpp' is number of bytes per pixel, and
|
// 'bpp' is number of bytes per pixel, and
|
||||||
// 'stride' is number of bytes per scan line (with possible padding).
|
// 'stride' is number of bytes per scan line (with possible padding).
|
||||||
// 'out' should be pre-allocated.
|
// 'out' should be pre-allocated.
|
||||||
extern const WebPFilterFunc WebPFilters[/*WEBP_FILTER_LAST*/];
|
extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
||||||
|
|
||||||
// Reconstruct the original data from the given filtered data.
|
// Reconstruct the original data from the given filtered data.
|
||||||
extern const WebPFilterFunc WebPUnfilters[/*WEBP_FILTER_LAST*/];
|
extern const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
||||||
|
|
||||||
|
// Fast estimate of a potentially good filter.
|
||||||
|
extern WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||||
|
int width, int height, int stride);
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -75,8 +75,7 @@ typedef struct {
|
|||||||
// 1 = backward reference counts encoded with
|
// 1 = backward reference counts encoded with
|
||||||
// arithmetic encoder). Default is 1.
|
// arithmetic encoder). Default is 1.
|
||||||
int alpha_filtering; // Predictive filtering method for alpha plane.
|
int alpha_filtering; // Predictive filtering method for alpha plane.
|
||||||
// (0 = none, 1 = horizontal, 2 = vertical, 3 = grad,
|
// 0: none, 1: fast, 2: best. Default if 1.
|
||||||
// 4 = Paeth and 5 = Best of (0 .. 4).
|
|
||||||
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
||||||
// Default is 100.
|
// Default is 100.
|
||||||
} WebPConfig;
|
} WebPConfig;
|
||||||
|
Loading…
Reference in New Issue
Block a user