mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
Add predictive filtering option for Alpha.
Add predictive filtering option for Alpha plane. Valid range for filter option is [0, 5] corresponding to prediction methods none, horizontal, vertical, gradient & paeth filter. The prediction method 5 will try all the prediction methods (0 to 4) and pick the prediction method that gives best compression. Change-Id: I9244d4a9c5017501a9696c7cec5045f04c16d49b
This commit is contained in:
parent
e852f83205
commit
252028aaac
@ -34,6 +34,7 @@ LOCAL_SRC_FILES := \
|
|||||||
src/utils/alpha.c \
|
src/utils/alpha.c \
|
||||||
src/utils/bit_reader.c \
|
src/utils/bit_reader.c \
|
||||||
src/utils/bit_writer.c \
|
src/utils/bit_writer.c \
|
||||||
|
src/utils/filters.c \
|
||||||
src/utils/quant_levels.c \
|
src/utils/quant_levels.c \
|
||||||
src/utils/tcoder.c \
|
src/utils/tcoder.c \
|
||||||
src/utils/thread.c \
|
src/utils/thread.c \
|
||||||
|
@ -178,6 +178,7 @@ X_OBJS= \
|
|||||||
$(DIROBJ)\utils\alpha.obj \
|
$(DIROBJ)\utils\alpha.obj \
|
||||||
$(DIROBJ)\utils\bit_reader.obj \
|
$(DIROBJ)\utils\bit_reader.obj \
|
||||||
$(DIROBJ)\utils\bit_writer.obj \
|
$(DIROBJ)\utils\bit_writer.obj \
|
||||||
|
$(DIROBJ)\utils\filters.obj \
|
||||||
$(DIROBJ)\utils\quant_levels.obj \
|
$(DIROBJ)\utils\quant_levels.obj \
|
||||||
$(DIROBJ)\utils\tcoder.obj \
|
$(DIROBJ)\utils\tcoder.obj \
|
||||||
$(DIROBJ)\utils\thread.obj \
|
$(DIROBJ)\utils\thread.obj \
|
||||||
|
@ -700,6 +700,7 @@ 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(" -noalpha ............... discard any transparency information.\n");
|
printf(" -noalpha ............... discard any transparency information.\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -793,6 +794,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
config.alpha_quality = strtol(argv[++c], NULL, 0);
|
config.alpha_quality = strtol(argv[++c], NULL, 0);
|
||||||
} 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) {
|
||||||
|
config.alpha_filtering = strtol(argv[++c], NULL, 0);
|
||||||
} 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) {
|
||||||
|
@ -71,7 +71,8 @@ DSP_OBJS = src/dsp/cpu.o src/dsp/enc.o \
|
|||||||
src/dsp/dec_neon.o src/dsp/upsampling.o src/dsp/upsampling_sse2.o \
|
src/dsp/dec_neon.o src/dsp/upsampling.o src/dsp/upsampling_sse2.o \
|
||||||
src/dsp/yuv.o
|
src/dsp/yuv.o
|
||||||
UTILS_OBJS = src/utils/alpha.o src/utils/bit_reader.o src/utils/bit_writer.o \
|
UTILS_OBJS = src/utils/alpha.o src/utils/bit_reader.o src/utils/bit_writer.o \
|
||||||
src/utils/quant_levels.o src/utils/thread.o src/utils/tcoder.o
|
src/utils/filters.o src/utils/quant_levels.o src/utils/thread.o \
|
||||||
|
src/utils/tcoder.o
|
||||||
|
|
||||||
OBJS = $(DEC_OBJS) $(ENC_OBJS) $(DSP_OBJS) $(UTILS_OBJS)
|
OBJS = $(DEC_OBJS) $(ENC_OBJS) $(DSP_OBJS) $(UTILS_OBJS)
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/cost.h src/webp/mux.h \
|
|||||||
src/dec/vp8i.h \
|
src/dec/vp8i.h \
|
||||||
src/dsp/yuv.h src/dsp/dsp.h \
|
src/dsp/yuv.h src/dsp/dsp.h \
|
||||||
src/utils/alpha.h src/utils/bit_writer.h src/utils/bit_reader.h \
|
src/utils/alpha.h src/utils/bit_writer.h src/utils/bit_reader.h \
|
||||||
src/utils/thread.h src/utils/tcoder.h
|
src/utils/filters.h src/utils/thread.h src/utils/tcoder.h
|
||||||
|
|
||||||
OUT_LIBS = src/libwebp.a src/mux/libwebpmux.a
|
OUT_LIBS = src/libwebp.a src/mux/libwebpmux.a
|
||||||
OUT_EXAMPLES = examples/cwebp examples/dwebp examples/webpmux
|
OUT_EXAMPLES = examples/cwebp examples/dwebp examples/webpmux
|
||||||
|
@ -41,6 +41,12 @@ 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
|
||||||
|
Specify the predictive filtering method (between 0 and 5) for alpha plane.
|
||||||
|
These correspond to prediction modes none, horizontal, vertical, gradient and
|
||||||
|
paeth filters. The prediction mode 5 will try all the prediction modes (0 to 4)
|
||||||
|
and pick the best prediction mode. The default value is 0 (no prediction).
|
||||||
|
.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)
|
||||||
and 100 (maximum filtering). A value of 0 will turn off any filtering.
|
and 100 (maximum filtering). A value of 0 will turn off any filtering.
|
||||||
|
@ -36,7 +36,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
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,
|
||||||
&tmp_data, &tmp_size)) {
|
config->alpha_filtering, &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.
|
||||||
|
@ -43,6 +43,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_quality = 100;
|
config->alpha_quality = 100;
|
||||||
|
|
||||||
// TODO(skal): tune.
|
// TODO(skal): tune.
|
||||||
@ -112,6 +113,8 @@ int WebPValidateConfig(const WebPConfig* const config) {
|
|||||||
return 0;
|
return 0;
|
||||||
if (config->alpha_compression < 0)
|
if (config->alpha_compression < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (config->alpha_filtering < 0)
|
||||||
|
return 0;
|
||||||
if (config->alpha_quality < 0 || config->alpha_quality > 100)
|
if (config->alpha_quality < 0 || config->alpha_quality > 100)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -3,6 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src
|
|||||||
libwebputils_la_SOURCES = alpha.h alpha.c \
|
libwebputils_la_SOURCES = alpha.h alpha.c \
|
||||||
bit_reader.h bit_reader.c \
|
bit_reader.h bit_reader.c \
|
||||||
bit_writer.h bit_writer.c \
|
bit_writer.h bit_writer.c \
|
||||||
|
filters.h filters.c \
|
||||||
quant_levels.c \
|
quant_levels.c \
|
||||||
tcoder.h tcoderi.h tcoder.c \
|
tcoder.h tcoderi.h tcoder.c \
|
||||||
thread.h thread.c
|
thread.h thread.c
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "./bit_reader.h"
|
#include "./bit_reader.h"
|
||||||
#include "./bit_writer.h"
|
#include "./bit_writer.h"
|
||||||
|
#include "./filters.h"
|
||||||
#include "./tcoder.h"
|
#include "./tcoder.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
@ -228,7 +229,7 @@ static void CopyPlane(const uint8_t* src, int src_stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||||
int quality, int method,
|
int quality, int method, int filter,
|
||||||
uint8_t** output, size_t* output_size) {
|
uint8_t** output, size_t* output_size) {
|
||||||
uint8_t* quant_alpha = NULL;
|
uint8_t* quant_alpha = NULL;
|
||||||
const size_t data_size = height * width;
|
const size_t data_size = height * width;
|
||||||
@ -238,6 +239,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);
|
||||||
|
|
||||||
if (quality < 0 || quality > 100) {
|
if (quality < 0 || quality > 100) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -265,22 +267,60 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
uint8_t header[ALPHA_HEADER_LEN];
|
WEBP_FILTER_TYPE this_filter;
|
||||||
VP8BitWriter bw;
|
size_t best_size = 1 << 30;
|
||||||
VP8BitWriterInit(&bw,
|
uint8_t* tmp_out = NULL;
|
||||||
(method == 0) ? (2 + data_size)
|
uint8_t* const filtered_alpha = (uint8_t*)malloc(data_size);
|
||||||
: (data_size >> 5) /* rough estimate of final size */);
|
if (filtered_alpha == NULL) {
|
||||||
header[0] = method & 0xff; // Compression Method.
|
free(quant_alpha);
|
||||||
header[1] = 0; // reserved byte for later use
|
return 0;
|
||||||
VP8BitWriterAppend(&bw, header, sizeof(header));
|
|
||||||
|
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height, method, &bw);
|
|
||||||
if (!ok) {
|
|
||||||
VP8BitWriterWipeOut(&bw);
|
|
||||||
} else {
|
|
||||||
*output = VP8BitWriterBuf(&bw);
|
|
||||||
*output_size = VP8BitWriterSize(&bw);
|
|
||||||
}
|
}
|
||||||
|
// Filtering.
|
||||||
|
for (this_filter = WEBP_FILTER_NONE; this_filter < WEBP_FILTER_LAST;
|
||||||
|
++this_filter) {
|
||||||
|
uint8_t header[ALPHA_HEADER_LEN];
|
||||||
|
VP8BitWriter bw;
|
||||||
|
WebPFilterFunc filter_func = NULL;
|
||||||
|
const size_t expected_size = (method == 0) ?
|
||||||
|
(ALPHA_HEADER_LEN + data_size) : (data_size >> 5);
|
||||||
|
if (this_filter == WEBP_FILTER_BEST) {
|
||||||
|
continue;
|
||||||
|
} else if (this_filter != filter && filter != WEBP_FILTER_BEST) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
header[0] = ((this_filter & 0x0f) << 4) | (method & 0x0f);
|
||||||
|
header[1] = 0; // reserved byte for later use
|
||||||
|
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) {
|
||||||
|
const size_t this_size = VP8BitWriterSize(&bw);
|
||||||
|
if (this_size < best_size) {
|
||||||
|
free(tmp_out);
|
||||||
|
tmp_out = VP8BitWriterBuf(&bw);
|
||||||
|
best_size = this_size;
|
||||||
|
} else {
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
free(tmp_out);
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
*output_size = best_size;
|
||||||
|
*output = tmp_out;
|
||||||
|
}
|
||||||
|
free(filtered_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(quant_alpha);
|
free(quant_alpha);
|
||||||
@ -338,6 +378,8 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
uint8_t* output) {
|
uint8_t* output) {
|
||||||
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;
|
||||||
|
int filter;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int method;
|
int method;
|
||||||
|
|
||||||
@ -348,9 +390,11 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
method = data[0];
|
method = data[0] & 0x0f;
|
||||||
|
filter = data[0] >> 4;
|
||||||
ok = (data[1] == 0);
|
ok = (data[1] == 0);
|
||||||
if (method < 0 || method > 1 || !ok) {
|
if (method < 0 || method > 1 ||
|
||||||
|
filter < WEBP_FILTER_NONE || filter > WEBP_FILTER_PAETH || !ok) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,9 +410,24 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
VP8InitBitReader(&br, data + ALPHA_HEADER_LEN, data + data_size);
|
VP8InitBitReader(&br, data + ALPHA_HEADER_LEN, data + data_size);
|
||||||
ok = DecompressZlibTCoder(&br, width, decoded_data, decoded_size);
|
ok = DecompressZlibTCoder(&br, width, decoded_data, decoded_size);
|
||||||
}
|
}
|
||||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
|
||||||
|
if (unfilter_func) {
|
||||||
|
unfiltered_data = (uint8_t*)malloc(decoded_size);
|
||||||
|
if (unfiltered_data == NULL) {
|
||||||
|
if (method == 1) free(decoded_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
|
||||||
|
// and apply filter per image-row.
|
||||||
|
unfilter_func(decoded_data, width, height, 1, width, unfiltered_data);
|
||||||
|
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||||
|
CopyPlane(unfiltered_data, width, output, stride, width, height);
|
||||||
|
free(unfiltered_data);
|
||||||
|
} else {
|
||||||
|
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||||
|
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (method == 1) {
|
if (method == 1) {
|
||||||
free(decoded_data);
|
free(decoded_data);
|
||||||
|
@ -26,6 +26,10 @@ extern "C" {
|
|||||||
// lossy. Valid ranges for 'quality' is [0, 100] and 'method' is [0, 1]:
|
// lossy. Valid ranges for 'quality' is [0, 100] and 'method' is [0, 1]:
|
||||||
// 'method = 0' - No compression;
|
// 'method = 0' - No compression;
|
||||||
// 'method = 1' - Backward reference counts encoded with arithmetic encoder;
|
// 'method = 1' - Backward reference counts encoded with arithmetic encoder;
|
||||||
|
// 'filter' values [0, 5] correspond to prediction modes none, horizontal,
|
||||||
|
// vertical, gradient & paeth filters. The prediction value 5 will try all the
|
||||||
|
// prediction modes (0 to 4) and pick the best prediction mode.
|
||||||
|
|
||||||
// 'output' corresponds to the buffer containing compressed alpha data.
|
// 'output' corresponds to the buffer containing compressed alpha data.
|
||||||
// This buffer is allocated by this method and caller should call
|
// This buffer is allocated by this method and caller should call
|
||||||
// free(*output) when done.
|
// free(*output) when done.
|
||||||
@ -33,13 +37,11 @@ extern "C" {
|
|||||||
//
|
//
|
||||||
// Returns 1 on successfully encoding the alpha and
|
// Returns 1 on successfully encoding the alpha and
|
||||||
// 0 if either:
|
// 0 if either:
|
||||||
// data, output or output_size is NULL, or
|
|
||||||
// inappropriate width, height or stride, or
|
|
||||||
// invalid quality or method, or
|
// invalid quality or method, or
|
||||||
// memory allocation for the compressed data fails.
|
// memory allocation for the compressed data fails.
|
||||||
|
|
||||||
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||||
int quality, int method,
|
int quality, int method, int filter,
|
||||||
uint8_t** output, size_t* output_size);
|
uint8_t** output, size_t* output_size);
|
||||||
|
|
||||||
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
||||||
@ -48,8 +50,7 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
//
|
//
|
||||||
// Returns 1 on successfully decoding the compressed alpha and
|
// Returns 1 on successfully decoding the compressed alpha and
|
||||||
// 0 if either:
|
// 0 if either:
|
||||||
// data or output is NULL, or
|
// error in bit-stream header (invalid compression mode or filter), or
|
||||||
// error in bit-stream header (invalid compression mode or qbits), or
|
|
||||||
// error returned by appropriate compression method.
|
// error returned by appropriate compression method.
|
||||||
int DecodeAlpha(const uint8_t* data, size_t data_size,
|
int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||||
int width, int height, int stride, uint8_t* output);
|
int width, int height, int stride, uint8_t* output);
|
||||||
|
267
src/utils/filters.c
Normal file
267
src/utils/filters.c
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
// Copyright 2011 Google Inc.
|
||||||
|
//
|
||||||
|
// This code is licensed under the same terms as WebM:
|
||||||
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||||
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Spatial prediction using various filters
|
||||||
|
//
|
||||||
|
// Author: Urvang (urvang@google.com)
|
||||||
|
|
||||||
|
#include "./filters.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Helpful macro.
|
||||||
|
|
||||||
|
# define SANITY_CHECK(in, out) \
|
||||||
|
assert(in != NULL); \
|
||||||
|
assert(out != NULL); \
|
||||||
|
assert(width > 0); \
|
||||||
|
assert(height > 0); \
|
||||||
|
assert(bpp > 0); \
|
||||||
|
assert(stride >= width * bpp);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Horizontal filter.
|
||||||
|
|
||||||
|
static void HorizontalFilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* filtered_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, filtered_data);
|
||||||
|
|
||||||
|
// Filter line-by-line.
|
||||||
|
for (h = 0; h < height; ++h) {
|
||||||
|
int w;
|
||||||
|
const uint8_t* const scan_line = data + h * stride;
|
||||||
|
uint8_t* const out = filtered_data + h * stride;
|
||||||
|
|
||||||
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
|
out[w] = scan_line[w] - scan_line[w - bpp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HorizontalUnfilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* recon_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, recon_data);
|
||||||
|
|
||||||
|
// Unfilter line-by-line.
|
||||||
|
for (h = 0; h < height; ++h) {
|
||||||
|
int w;
|
||||||
|
const uint8_t* const scan_line = data + h * stride;
|
||||||
|
uint8_t* const out = recon_data + h * stride;
|
||||||
|
|
||||||
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
|
out[w] = scan_line[w] + out[w - bpp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Vertical filter.
|
||||||
|
|
||||||
|
static void VerticalFilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* filtered_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, filtered_data);
|
||||||
|
|
||||||
|
// Copy top scan-line as it is.
|
||||||
|
memcpy((void*)filtered_data, (const void*)data, width * 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 < width * bpp; ++w) {
|
||||||
|
out[w] = scan_line[w] - prev_line[w];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VerticalUnfilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* recon_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, recon_data);
|
||||||
|
|
||||||
|
// Copy top scan-line as it is.
|
||||||
|
memcpy((void*)recon_data, (const void*)data, width * 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_line = out - stride;
|
||||||
|
for (w = 0; w < width * bpp; ++w) {
|
||||||
|
out[w] = scan_line[w] + out_prev_line[w];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Gradient filter.
|
||||||
|
|
||||||
|
static void GradientFilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* filtered_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, filtered_data);
|
||||||
|
|
||||||
|
// Copy top scan-line as it is.
|
||||||
|
memcpy((void*)filtered_data, (const void*)data, width * 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;
|
||||||
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
|
const uint8_t predictor = scan_line[w - bpp] + prev_line[w] -
|
||||||
|
prev_line[w - bpp];
|
||||||
|
out[w] = scan_line[w] - predictor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GradientUnfilter(const uint8_t* data, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* recon_data) {
|
||||||
|
int h;
|
||||||
|
SANITY_CHECK(data, recon_data);
|
||||||
|
|
||||||
|
// Copy top scan-line as it is.
|
||||||
|
memcpy((void*)recon_data, (const void*)data, width * 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_line = out - stride;
|
||||||
|
memcpy((void*)out, (const void*)scan_line, bpp);
|
||||||
|
for (w = bpp; w < width * bpp; ++w) {
|
||||||
|
const uint8_t predictor = out[w - bpp] + out_prev_line[w] -
|
||||||
|
out_prev_line[w - bpp];
|
||||||
|
out[w] = scan_line[w] + predictor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Paeth filter.
|
||||||
|
|
||||||
|
static inline int AbsDiff(int a, int b) {
|
||||||
|
return (a > b) ? a - b : b - a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
||||||
|
NULL, // WEBP_FILTER_NONE
|
||||||
|
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
||||||
|
VerticalFilter, // WEBP_FILTER_VERTICAL
|
||||||
|
GradientFilter, // WEBP_FILTER_GRADIENT
|
||||||
|
PaethFilter, // WEBP_FILTER_PAETH
|
||||||
|
NULL // WEBP_FILTER_BEST
|
||||||
|
};
|
||||||
|
|
||||||
|
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
||||||
|
NULL, // WEBP_FILTER_NONE
|
||||||
|
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
||||||
|
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
||||||
|
GradientUnfilter, // WEBP_FILTER_GRADIENT
|
||||||
|
PaethUnfilter, // WEBP_FILTER_PAETH
|
||||||
|
NULL // WEBP_FILTER_BEST
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
50
src/utils/filters.h
Normal file
50
src/utils/filters.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2011 Google Inc.
|
||||||
|
//
|
||||||
|
// This code is licensed under the same terms as WebM:
|
||||||
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||||
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Spatial prediction using various filters
|
||||||
|
//
|
||||||
|
// Author: Urvang (urvang@google.com)
|
||||||
|
|
||||||
|
#ifndef WEBP_UTILS_FILTERS_H_
|
||||||
|
#define WEBP_UTILS_FILTERS_H_
|
||||||
|
|
||||||
|
#include "../webp/types.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Filters.
|
||||||
|
typedef enum {
|
||||||
|
WEBP_FILTER_NONE = 0,
|
||||||
|
WEBP_FILTER_HORIZONTAL,
|
||||||
|
WEBP_FILTER_VERTICAL,
|
||||||
|
WEBP_FILTER_GRADIENT,
|
||||||
|
WEBP_FILTER_PAETH,
|
||||||
|
WEBP_FILTER_BEST,
|
||||||
|
WEBP_FILTER_LAST,
|
||||||
|
} WEBP_FILTER_TYPE;
|
||||||
|
|
||||||
|
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
||||||
|
int bpp, int stride, uint8_t* out);
|
||||||
|
|
||||||
|
// Filter the given data using the given predictor.
|
||||||
|
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
|
||||||
|
// in raster order.
|
||||||
|
// 'bpp' is number of bytes per pixel, and
|
||||||
|
// 'stride' is number of bytes per scan line (with possible padding).
|
||||||
|
// 'out' should be pre-allocated.
|
||||||
|
extern const WebPFilterFunc WebPFilters[/*WEBP_FILTER_LAST*/];
|
||||||
|
|
||||||
|
// Reconstruct the original data from the given filtered data.
|
||||||
|
extern const WebPFilterFunc WebPUnfilters[/*WEBP_FILTER_LAST*/];
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WEBP_UTILS_FILTERS_H_ */
|
@ -74,6 +74,9 @@ typedef struct {
|
|||||||
int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
|
int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
|
||||||
// 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.
|
||||||
|
// (0 = none, 1 = horizontal, 2 = vertical, 3 = grad,
|
||||||
|
// 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