mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
remove tcoder, switch alpha-plane compression to lossless
* Method #1 is now calling the lossless encoder on the alpha plane. Format is not final, it's just a first draft. We need ad-hoc functions. * removed now useless utils/alpha.* * added utils/quant_levels.h instead * removed the TCoder code altogether Change-Id: I636840b6129a43171b74860e0a0fc5bb1bcffc6a
This commit is contained in:
parent
831bd13168
commit
3e863dda61
@ -39,7 +39,6 @@ LOCAL_SRC_FILES := \
|
|||||||
src/mux/muxedit.c \
|
src/mux/muxedit.c \
|
||||||
src/mux/muxinternal.c \
|
src/mux/muxinternal.c \
|
||||||
src/mux/muxread.c \
|
src/mux/muxread.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/color_cache.c \
|
src/utils/color_cache.c \
|
||||||
@ -47,7 +46,6 @@ LOCAL_SRC_FILES := \
|
|||||||
src/utils/huffman.c \
|
src/utils/huffman.c \
|
||||||
src/utils/quant_levels.c \
|
src/utils/quant_levels.c \
|
||||||
src/utils/rescaler.c \
|
src/utils/rescaler.c \
|
||||||
src/utils/tcoder.c \
|
|
||||||
src/utils/thread.c \
|
src/utils/thread.c \
|
||||||
|
|
||||||
LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
|
LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
|
||||||
|
@ -178,16 +178,19 @@ EX_UTIL_OBJS = \
|
|||||||
ENC_OBJS = \
|
ENC_OBJS = \
|
||||||
$(DIROBJ)\enc\alpha.obj \
|
$(DIROBJ)\enc\alpha.obj \
|
||||||
$(DIROBJ)\enc\analysis.obj \
|
$(DIROBJ)\enc\analysis.obj \
|
||||||
|
$(DIROBJ)\enc\backward_references.obj \
|
||||||
$(DIROBJ)\enc\config.obj \
|
$(DIROBJ)\enc\config.obj \
|
||||||
$(DIROBJ)\enc\cost.obj \
|
$(DIROBJ)\enc\cost.obj \
|
||||||
$(DIROBJ)\enc\filter.obj \
|
$(DIROBJ)\enc\filter.obj \
|
||||||
$(DIROBJ)\enc\frame.obj \
|
$(DIROBJ)\enc\frame.obj \
|
||||||
|
$(DIROBJ)\enc\histogram.obj \
|
||||||
$(DIROBJ)\enc\iterator.obj \
|
$(DIROBJ)\enc\iterator.obj \
|
||||||
$(DIROBJ)\enc\layer.obj \
|
$(DIROBJ)\enc\layer.obj \
|
||||||
$(DIROBJ)\enc\picture.obj \
|
$(DIROBJ)\enc\picture.obj \
|
||||||
$(DIROBJ)\enc\quant.obj \
|
$(DIROBJ)\enc\quant.obj \
|
||||||
$(DIROBJ)\enc\syntax.obj \
|
$(DIROBJ)\enc\syntax.obj \
|
||||||
$(DIROBJ)\enc\tree.obj \
|
$(DIROBJ)\enc\tree.obj \
|
||||||
|
$(DIROBJ)\enc\vp8l.obj \
|
||||||
$(DIROBJ)\enc\webpenc.obj \
|
$(DIROBJ)\enc\webpenc.obj \
|
||||||
|
|
||||||
MUX_OBJS = \
|
MUX_OBJS = \
|
||||||
@ -196,15 +199,14 @@ MUX_OBJS = \
|
|||||||
$(DIROBJ)\mux\muxread.obj \
|
$(DIROBJ)\mux\muxread.obj \
|
||||||
|
|
||||||
UTILS_OBJS = \
|
UTILS_OBJS = \
|
||||||
$(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\color_cache.obj \
|
$(DIROBJ)\utils\color_cache.obj \
|
||||||
$(DIROBJ)\utils\filters.obj \
|
$(DIROBJ)\utils\filters.obj \
|
||||||
$(DIROBJ)\utils\huffman.obj \
|
$(DIROBJ)\utils\huffman.obj \
|
||||||
|
$(DIROBJ)\utils\huffman_encode.obj \
|
||||||
$(DIROBJ)\utils\quant_levels.obj \
|
$(DIROBJ)\utils\quant_levels.obj \
|
||||||
$(DIROBJ)\utils\rescaler.obj \
|
$(DIROBJ)\utils\rescaler.obj \
|
||||||
$(DIROBJ)\utils\tcoder.obj \
|
|
||||||
$(DIROBJ)\utils\thread.obj \
|
$(DIROBJ)\utils\thread.obj \
|
||||||
|
|
||||||
X_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(MUX_OBJS) $(UTILS_OBJS) $(X_OBJS)
|
X_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(MUX_OBJS) $(UTILS_OBJS) $(X_OBJS)
|
||||||
|
@ -115,7 +115,6 @@ MUX_OBJS = \
|
|||||||
src/mux/muxread.o \
|
src/mux/muxread.o \
|
||||||
|
|
||||||
UTILS_OBJS = \
|
UTILS_OBJS = \
|
||||||
src/utils/alpha.o \
|
|
||||||
src/utils/bit_reader.o \
|
src/utils/bit_reader.o \
|
||||||
src/utils/bit_writer.o \
|
src/utils/bit_writer.o \
|
||||||
src/utils/color_cache.o \
|
src/utils/color_cache.o \
|
||||||
@ -124,7 +123,6 @@ UTILS_OBJS = \
|
|||||||
src/utils/huffman_encode.o \
|
src/utils/huffman_encode.o \
|
||||||
src/utils/quant_levels.o \
|
src/utils/quant_levels.o \
|
||||||
src/utils/rescaler.o \
|
src/utils/rescaler.o \
|
||||||
src/utils/tcoder.o \
|
|
||||||
src/utils/thread.o \
|
src/utils/thread.o \
|
||||||
|
|
||||||
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS)
|
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS)
|
||||||
@ -139,15 +137,13 @@ HDRS = \
|
|||||||
src/dsp/yuv.h \
|
src/dsp/yuv.h \
|
||||||
src/enc/cost.h \
|
src/enc/cost.h \
|
||||||
src/enc/vp8enci.h \
|
src/enc/vp8enci.h \
|
||||||
src/utils/alpha.h \
|
|
||||||
src/utils/bit_reader.h \
|
src/utils/bit_reader.h \
|
||||||
src/utils/bit_writer.h \
|
src/utils/bit_writer.h \
|
||||||
src/utils/color_cache.h \
|
src/utils/color_cache.h \
|
||||||
src/utils/filters.h \
|
src/utils/filters.h \
|
||||||
src/utils/huffman.h \
|
src/utils/huffman.h \
|
||||||
|
src/utils/quant_levels.h \
|
||||||
src/utils/rescaler.h \
|
src/utils/rescaler.h \
|
||||||
src/utils/tcoder.h \
|
|
||||||
src/utils/tcoderi.h \
|
|
||||||
src/utils/thread.h \
|
src/utils/thread.h \
|
||||||
src/webp/decode.h \
|
src/webp/decode.h \
|
||||||
src/webp/decode_vp8.h \
|
src/webp/decode_vp8.h \
|
||||||
|
103
src/dec/alpha.c
103
src/dec/alpha.c
@ -11,12 +11,113 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
#include "../utils/alpha.h"
|
#include "../webp/decode.h"
|
||||||
|
#include "../utils/filters.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO(skal): find a common place between enc/ and dec/ for these:
|
||||||
|
#define ALPHA_HEADER_LEN 2
|
||||||
|
#define ALPHA_NO_COMPRESSION 0
|
||||||
|
#define ALPHA_LOSSLESS_COMPRESSION 1
|
||||||
|
|
||||||
|
// TODO(skal): move to dsp/ ?
|
||||||
|
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||||
|
uint8_t* dst, int dst_stride, int width, int height) {
|
||||||
|
while (height-- > 0) {
|
||||||
|
memcpy(dst, src, width);
|
||||||
|
src += src_stride;
|
||||||
|
dst += dst_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
||||||
|
// The 'output' buffer should be pre-allocated and must be of the same
|
||||||
|
// dimension 'height'x'stride', as that of the image.
|
||||||
|
//
|
||||||
|
// Returns 1 on successfully decoding the compressed alpha and
|
||||||
|
// 0 if either:
|
||||||
|
// error in bit-stream header (invalid compression mode or filter), or
|
||||||
|
// error returned by appropriate compression method.
|
||||||
|
|
||||||
|
static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||||
|
int width, int height, int stride, uint8_t* output) {
|
||||||
|
uint8_t* decoded_data = NULL;
|
||||||
|
const size_t decoded_size = height * width;
|
||||||
|
uint8_t* unfiltered_data = NULL;
|
||||||
|
WEBP_FILTER_TYPE filter;
|
||||||
|
int ok = 0;
|
||||||
|
int method;
|
||||||
|
|
||||||
|
assert(width > 0 && height > 0 && stride >= width);
|
||||||
|
assert(data != NULL && output != NULL);
|
||||||
|
|
||||||
|
if (data_size <= ALPHA_HEADER_LEN) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
method = data[0] & 0x0f;
|
||||||
|
filter = data[0] >> 4;
|
||||||
|
ok = (data[1] == 0);
|
||||||
|
if (method < ALPHA_NO_COMPRESSION ||
|
||||||
|
method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||||
|
filter >= WEBP_FILTER_LAST || !ok) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == ALPHA_NO_COMPRESSION) {
|
||||||
|
ok = (data_size >= decoded_size);
|
||||||
|
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
||||||
|
} else {
|
||||||
|
size_t i;
|
||||||
|
int w, h;
|
||||||
|
uint32_t* const output =
|
||||||
|
(uint32_t*)WebPDecodeRGBA(data + ALPHA_HEADER_LEN,
|
||||||
|
data_size - ALPHA_HEADER_LEN,
|
||||||
|
&w, &h);
|
||||||
|
if (w != width || h != height || output == NULL) {
|
||||||
|
free(output);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
decoded_data = (uint8_t*)malloc(decoded_size);
|
||||||
|
if (decoded_data == NULL) {
|
||||||
|
free(output);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < decoded_size; ++i) {
|
||||||
|
decoded_data[i] = (output[i] >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
|
||||||
|
if (unfilter_func) {
|
||||||
|
unfiltered_data = (uint8_t*)malloc(decoded_size);
|
||||||
|
if (unfiltered_data == NULL) {
|
||||||
|
if (method != ALPHA_NO_COMPRESSION) 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 != ALPHA_NO_COMPRESSION) {
|
||||||
|
free(decoded_data);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||||
|
247
src/enc/alpha.c
247
src/enc/alpha.c
@ -13,14 +13,259 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "../utils/alpha.h"
|
|
||||||
#include "../utils/filters.h"
|
#include "../utils/filters.h"
|
||||||
|
#include "../utils/quant_levels.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ALPHA_HEADER_LEN 2
|
||||||
|
#define ALPHA_NO_COMPRESSION 0
|
||||||
|
#define ALPHA_LOSSLESS_COMPRESSION 1
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||||
|
// int quality, int method, int filter,
|
||||||
|
// uint8_t** output, size_t* output_size)
|
||||||
|
//
|
||||||
|
// 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 = 1' - Use lossless coder on the alpha plane only
|
||||||
|
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
||||||
|
// vertical & gradient filters. The prediction mode 4 will try all the
|
||||||
|
// prediction modes (0 to 3) and pick the best prediction mode.
|
||||||
|
|
||||||
|
// 'output' corresponds to the buffer containing compressed alpha data.
|
||||||
|
// This buffer is allocated by this method and caller should call
|
||||||
|
// free(*output) when done.
|
||||||
|
// 'output_size' corresponds to size of this compressed alpha buffer.
|
||||||
|
//
|
||||||
|
// Returns 1 on successfully encoding the alpha and
|
||||||
|
// 0 if either:
|
||||||
|
// invalid quality or method, or
|
||||||
|
// memory allocation for the compressed data fails.
|
||||||
|
|
||||||
|
#ifdef USE_LOSSLESS_ENCODER
|
||||||
|
|
||||||
|
#include "../enc/vp8li.h"
|
||||||
|
#include "../webp/encode.h"
|
||||||
|
|
||||||
|
static int MyWriter(const uint8_t* data, size_t data_size,
|
||||||
|
const WebPPicture* const picture) {
|
||||||
|
VP8BitWriter* const bw = (VP8BitWriter*)picture->custom_ptr;
|
||||||
|
if (data_size > 0) {
|
||||||
|
VP8BitWriterAppend(bw, data, data_size);
|
||||||
|
}
|
||||||
|
return !bw->error_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||||
|
VP8BitWriter* const bw) {
|
||||||
|
|
||||||
|
int ok = 0;
|
||||||
|
WebPConfig config;
|
||||||
|
WebPPicture picture;
|
||||||
|
|
||||||
|
WebPPictureInit(&picture);
|
||||||
|
picture.width = width;
|
||||||
|
picture.height = height;
|
||||||
|
picture.use_argb_input = 1;
|
||||||
|
if (!WebPPictureAlloc(&picture)) return 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint32_t* dst = picture.argb;
|
||||||
|
const uint8_t* src = data;
|
||||||
|
for (j = 0; j < picture.height; ++j) {
|
||||||
|
for (i = 0; i < picture.width; ++i) {
|
||||||
|
dst[i] = (src[i] << 8) | 0xff000000u;
|
||||||
|
}
|
||||||
|
src += width;
|
||||||
|
dst += picture.argb_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
picture.writer = MyWriter;
|
||||||
|
picture.custom_ptr = (void*)bw;
|
||||||
|
|
||||||
|
WebPConfigInit(&config);
|
||||||
|
config.lossless = 1;
|
||||||
|
config.method = 2;
|
||||||
|
config.quality = 100;
|
||||||
|
|
||||||
|
ok = WebPEncode(&config, &picture);
|
||||||
|
WebPPictureFree(&picture);
|
||||||
|
return ok && !bw->error_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
||||||
|
int method, int filter, size_t data_size,
|
||||||
|
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
||||||
|
int ok = 0;
|
||||||
|
const uint8_t* alpha_src;
|
||||||
|
WebPFilterFunc filter_func;
|
||||||
|
uint8_t header[ALPHA_HEADER_LEN];
|
||||||
|
size_t expected_size;
|
||||||
|
|
||||||
|
#ifndef USE_LOSSLESS_ENCODER
|
||||||
|
method = ALPHA_NO_COMPRESSION;
|
||||||
|
#endif
|
||||||
|
expected_size =
|
||||||
|
(method == ALPHA_NO_COMPRESSION) ? (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 == ALPHA_NO_COMPRESSION) {
|
||||||
|
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
||||||
|
ok = ok && !bw->error_;
|
||||||
|
} else {
|
||||||
|
#ifdef USE_LOSSLESS_ENCODER
|
||||||
|
ok = EncodeLossless(alpha_src, width, height, bw);
|
||||||
|
VP8BitWriterFinish(bw);
|
||||||
|
#else
|
||||||
|
assert(0); // not reached.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO(skal): move to dsp/ ?
|
||||||
|
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||||
|
uint8_t* dst, int dst_stride, int width, int height) {
|
||||||
|
while (height-- > 0) {
|
||||||
|
memcpy(dst, src, width);
|
||||||
|
src += src_stride;
|
||||||
|
dst += dst_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||||
|
int quality, int method, int filter,
|
||||||
|
uint8_t** output, size_t* output_size) {
|
||||||
|
uint8_t* quant_alpha = NULL;
|
||||||
|
const size_t data_size = height * width;
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
// quick sanity checks
|
||||||
|
assert(data != NULL && output != NULL && output_size != NULL);
|
||||||
|
assert(width > 0 && height > 0);
|
||||||
|
assert(stride >= width);
|
||||||
|
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
||||||
|
|
||||||
|
if (quality < 0 || quality > 100) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quant_alpha = (uint8_t*)malloc(data_size);
|
||||||
|
if (quant_alpha == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract alpha data (width x height) from raw_data (stride x height).
|
||||||
|
CopyPlane(data, stride, quant_alpha, width, width, height);
|
||||||
|
|
||||||
|
if (quality < 100) { // No Quantization required for 'quality = 100'.
|
||||||
|
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
||||||
|
// mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
|
||||||
|
// and Quality:]70, 100] -> Levels:]16, 256].
|
||||||
|
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
||||||
|
: (16 + (quality - 70) * 8);
|
||||||
|
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
VP8BitWriter bw;
|
||||||
|
size_t best_score;
|
||||||
|
int test_filter;
|
||||||
|
uint8_t* filtered_alpha = NULL;
|
||||||
|
|
||||||
|
// We always test WEBP_FILTER_NONE first.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = EncodeAlphaInternal(quant_alpha, width, height, method, test_filter,
|
||||||
|
data_size, 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 {
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
}
|
||||||
|
VP8BitWriterWipeOut(&tmp_bw);
|
||||||
|
}
|
||||||
|
Ok:
|
||||||
|
if (ok) {
|
||||||
|
*output_size = VP8BitWriterSize(&bw);
|
||||||
|
*output = VP8BitWriterBuf(&bw);
|
||||||
|
}
|
||||||
|
free(filtered_alpha);
|
||||||
|
}
|
||||||
|
End:
|
||||||
|
free(quant_alpha);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
// Main calls
|
||||||
|
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc) {
|
void VP8EncInitAlpha(VP8Encoder* enc) {
|
||||||
enc->has_alpha_ = (enc->pic_->a != NULL);
|
enc->has_alpha_ = (enc->pic_->a != NULL);
|
||||||
|
@ -2,8 +2,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src
|
|||||||
noinst_LTLIBRARIES = libwebputils.la
|
noinst_LTLIBRARIES = libwebputils.la
|
||||||
|
|
||||||
libwebputils_la_SOURCES =
|
libwebputils_la_SOURCES =
|
||||||
libwebputils_la_SOURCES += alpha.c
|
|
||||||
libwebputils_la_SOURCES += alpha.h
|
|
||||||
libwebputils_la_SOURCES += bit_reader.c
|
libwebputils_la_SOURCES += bit_reader.c
|
||||||
libwebputils_la_SOURCES += bit_reader.h
|
libwebputils_la_SOURCES += bit_reader.h
|
||||||
libwebputils_la_SOURCES += bit_writer.c
|
libwebputils_la_SOURCES += bit_writer.c
|
||||||
@ -15,10 +13,8 @@ libwebputils_la_SOURCES += filters.h
|
|||||||
libwebputils_la_SOURCES += huffman.c
|
libwebputils_la_SOURCES += huffman.c
|
||||||
libwebputils_la_SOURCES += huffman.h
|
libwebputils_la_SOURCES += huffman.h
|
||||||
libwebputils_la_SOURCES += quant_levels.c
|
libwebputils_la_SOURCES += quant_levels.c
|
||||||
|
libwebputils_la_SOURCES += quant_levels.h
|
||||||
libwebputils_la_SOURCES += rescaler.c
|
libwebputils_la_SOURCES += rescaler.c
|
||||||
libwebputils_la_SOURCES += tcoder.c
|
|
||||||
libwebputils_la_SOURCES += tcoder.h
|
|
||||||
libwebputils_la_SOURCES += tcoderi.h
|
|
||||||
libwebputils_la_SOURCES += thread.c
|
libwebputils_la_SOURCES += thread.c
|
||||||
libwebputils_la_SOURCES += thread.h
|
libwebputils_la_SOURCES += thread.h
|
||||||
|
|
||||||
|
@ -1,464 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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/
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Alpha plane encoding and decoding library.
|
|
||||||
//
|
|
||||||
// Author: vikasa@google.com (Vikas Arora)
|
|
||||||
|
|
||||||
#include <string.h> // for memcpy()
|
|
||||||
#include "./alpha.h"
|
|
||||||
|
|
||||||
#include "./bit_reader.h"
|
|
||||||
#include "./bit_writer.h"
|
|
||||||
#include "./filters.h"
|
|
||||||
#include "./tcoder.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_SYMBOLS 255
|
|
||||||
#define ALPHA_HEADER_LEN 2
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Zlib-like encoding using TCoder
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int dist; // backward distance (=0 means: literal)
|
|
||||||
int literal; // literal value (if dist = 0)
|
|
||||||
int len; // length of matched string for non-literal
|
|
||||||
} Token;
|
|
||||||
|
|
||||||
#define MIN_LEN 2
|
|
||||||
#define DEFER_SKIP 1 // for deferred evaluation (0 = off)
|
|
||||||
|
|
||||||
#define CACHED_COST(coder, c) ((cost_cache[(c)] == 0.) ? \
|
|
||||||
(cost_cache[(c)] = lit_mode_cost + TCoderSymbolCost((coder), (c))) \
|
|
||||||
: cost_cache[(c)])
|
|
||||||
|
|
||||||
// Record symbol
|
|
||||||
#define RECORD(TOKEN) { \
|
|
||||||
TCoderEncode(coderd, (TOKEN)->dist, NULL); \
|
|
||||||
if ((TOKEN)->dist == 0) { \
|
|
||||||
TCoderEncode(coder, (TOKEN)->literal, NULL); \
|
|
||||||
} else { \
|
|
||||||
TCoderEncode(coderl, (TOKEN)->len - MIN_LEN, NULL); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GetLongestMatch(const uint8_t* const data,
|
|
||||||
const uint8_t* const ref, int max_len) {
|
|
||||||
int n;
|
|
||||||
for (n = 0; (n < max_len) && (data[n] == ref[n]); ++n) { /* do nothing */ }
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int EncodeZlibTCoder(const uint8_t* data, int width, int height,
|
|
||||||
VP8BitWriter* const bw) {
|
|
||||||
int ok = 0;
|
|
||||||
const int data_len = width * height;
|
|
||||||
const int MAX_DIST = 3 * width;
|
|
||||||
const int MAX_LEN = 2 * width;
|
|
||||||
Token* const msg = (Token*)malloc(data_len * sizeof(*msg));
|
|
||||||
int num_tokens;
|
|
||||||
TCoder* const coder = TCoderNew(MAX_SYMBOLS);
|
|
||||||
TCoder* const coderd = TCoderNew(MAX_DIST);
|
|
||||||
TCoder* const coderl = TCoderNew(MAX_LEN - MIN_LEN);
|
|
||||||
|
|
||||||
if (coder == NULL || coderd == NULL || coderl == NULL) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
if (msg == NULL) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int deferred_eval = 0;
|
|
||||||
int n = 0;
|
|
||||||
num_tokens = 0;
|
|
||||||
while (n < data_len) {
|
|
||||||
const double lit_mode_cost = TCoderSymbolCost(coderd, 0);
|
|
||||||
double cost_cache[MAX_SYMBOLS + 1] = { 0. };
|
|
||||||
Token best;
|
|
||||||
int dist = 0;
|
|
||||||
double best_cost = CACHED_COST(coder, data[n]);
|
|
||||||
const int max_len = (MAX_LEN > data_len - n) ? data_len - n : MAX_LEN;
|
|
||||||
|
|
||||||
best.dist = 0;
|
|
||||||
best.literal = data[n];
|
|
||||||
best.len = 1;
|
|
||||||
for (dist = 1; dist <= MAX_DIST && dist <= n; ++dist) {
|
|
||||||
const int pos = n - dist;
|
|
||||||
const int min_len = best.len - 1;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
// Early out: we probe at two locations for a quick match check
|
|
||||||
if (data[pos] != data[n] ||
|
|
||||||
data[pos + min_len] != data[n + min_len]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = GetLongestMatch(data + pos, data + n, max_len);
|
|
||||||
if (len >= MIN_LEN && len >= best.len) {
|
|
||||||
// This is the cost of the coding proposal
|
|
||||||
const double cost = TCoderSymbolCost(coderl, len - MIN_LEN)
|
|
||||||
+ TCoderSymbolCost(coderd, dist);
|
|
||||||
// We're gaining an extra len-best.len coded message over the last
|
|
||||||
// known best. Compute how this would have cost if coded all literal.
|
|
||||||
// (TODO: we should fully re-evaluate at position best.len and not
|
|
||||||
// assume all is going be coded as literals. But it's at least an
|
|
||||||
// upper-bound (worst-case coding). Deferred evaluation used below
|
|
||||||
// partially addresses this.
|
|
||||||
double lit_cost = 0;
|
|
||||||
int i;
|
|
||||||
for (i = best.len; i < len; ++i) {
|
|
||||||
lit_cost += CACHED_COST(coder, data[n + i]);
|
|
||||||
}
|
|
||||||
// So, is it worth ?
|
|
||||||
if (best_cost + lit_cost >= cost) {
|
|
||||||
best_cost = cost;
|
|
||||||
best.len = len;
|
|
||||||
best.dist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len >= max_len) {
|
|
||||||
break; // No need to search further. We already got a max-long match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Deferred evaluation: before finalizing a choice we try to find
|
|
||||||
// best cost at position n + 1 and see if we get a longer
|
|
||||||
// match then current best. If so, we transform the current match
|
|
||||||
// into a literal, go to position n + 1, and try again.
|
|
||||||
{
|
|
||||||
Token* cur = &msg[num_tokens];
|
|
||||||
int forget = 0;
|
|
||||||
if (deferred_eval) {
|
|
||||||
--cur;
|
|
||||||
// If the next match isn't longer, keep previous match
|
|
||||||
if (best.len <= cur->len) {
|
|
||||||
deferred_eval = 0;
|
|
||||||
n += cur->len - DEFER_SKIP;
|
|
||||||
forget = 1; // forget the new match
|
|
||||||
RECORD(cur)
|
|
||||||
} else { // else transform previous match into a shorter one
|
|
||||||
cur->len = DEFER_SKIP;
|
|
||||||
if (DEFER_SKIP == 1) {
|
|
||||||
cur->dist = 0; // literal
|
|
||||||
}
|
|
||||||
// TODO(later): RECORD() macro should be changed to take an extra
|
|
||||||
// "is_final" param, so that we could write the bitstream at once.
|
|
||||||
RECORD(cur)
|
|
||||||
++cur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!forget) {
|
|
||||||
*cur = best;
|
|
||||||
++num_tokens;
|
|
||||||
if (DEFER_SKIP > 0) {
|
|
||||||
deferred_eval = (cur->len > 2) && (cur->len < MAX_LEN / 2);
|
|
||||||
}
|
|
||||||
if (deferred_eval) {
|
|
||||||
// will probe at a later position before finalizing.
|
|
||||||
n += DEFER_SKIP;
|
|
||||||
} else {
|
|
||||||
// Keep the current choice.
|
|
||||||
n += cur->len;
|
|
||||||
RECORD(cur)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final bitstream assembly.
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
TCoderInit(coder);
|
|
||||||
TCoderInit(coderd);
|
|
||||||
TCoderInit(coderl);
|
|
||||||
for (n = 0; n < num_tokens; ++n) {
|
|
||||||
const Token* const t = &msg[n];
|
|
||||||
const int is_literal = (t->dist == 0);
|
|
||||||
TCoderEncode(coderd, t->dist, bw);
|
|
||||||
if (is_literal) { // literal
|
|
||||||
TCoderEncode(coder, t->literal, bw);
|
|
||||||
} else {
|
|
||||||
TCoderEncode(coderl, t->len - MIN_LEN, bw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
End:
|
|
||||||
if (coder) TCoderDelete(coder);
|
|
||||||
if (coderl) TCoderDelete(coderl);
|
|
||||||
if (coderd) TCoderDelete(coderd);
|
|
||||||
free(msg);
|
|
||||||
return ok && !bw->error_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
|
||||||
int method, int filter, size_t data_size,
|
|
||||||
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
|
||||||
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) {
|
|
||||||
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
|
||||||
ok = ok && !bw->error_;
|
|
||||||
} else {
|
|
||||||
ok = EncodeZlibTCoder(alpha_src, width, height, bw);
|
|
||||||
VP8BitWriterFinish(bw);
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// TODO(skal): move to dsp/ ?
|
|
||||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
|
||||||
uint8_t* dst, int dst_stride, int width, int height) {
|
|
||||||
while (height-- > 0) {
|
|
||||||
memcpy(dst, src, width);
|
|
||||||
src += src_stride;
|
|
||||||
dst += dst_stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|
||||||
int quality, int method, int filter,
|
|
||||||
uint8_t** output, size_t* output_size) {
|
|
||||||
uint8_t* quant_alpha = NULL;
|
|
||||||
const size_t data_size = height * width;
|
|
||||||
int ok = 1;
|
|
||||||
|
|
||||||
// quick sanity checks
|
|
||||||
assert(data != NULL && output != NULL && output_size != NULL);
|
|
||||||
assert(width > 0 && height > 0);
|
|
||||||
assert(stride >= width);
|
|
||||||
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
|
||||||
|
|
||||||
if (quality < 0 || quality > 100) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method < 0 || method > 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
quant_alpha = (uint8_t*)malloc(data_size);
|
|
||||||
if (quant_alpha == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract alpha data (width x height) from raw_data (stride x height).
|
|
||||||
CopyPlane(data, stride, quant_alpha, width, width, height);
|
|
||||||
|
|
||||||
if (quality < 100) { // No Quantization required for 'quality = 100'.
|
|
||||||
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
|
||||||
// mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
|
|
||||||
// and Quality:]70, 100] -> Levels:]16, 256].
|
|
||||||
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
|
||||||
: (16 + (quality - 70) * 8);
|
|
||||||
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
VP8BitWriter bw;
|
|
||||||
size_t best_score;
|
|
||||||
int test_filter;
|
|
||||||
uint8_t* filtered_alpha = NULL;
|
|
||||||
|
|
||||||
// We always test WEBP_FILTER_NONE first.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height, method, test_filter,
|
|
||||||
data_size, 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 {
|
|
||||||
VP8BitWriterWipeOut(&bw);
|
|
||||||
}
|
|
||||||
VP8BitWriterWipeOut(&tmp_bw);
|
|
||||||
}
|
|
||||||
Ok:
|
|
||||||
if (ok) {
|
|
||||||
*output_size = VP8BitWriterSize(&bw);
|
|
||||||
*output = VP8BitWriterBuf(&bw);
|
|
||||||
}
|
|
||||||
free(filtered_alpha);
|
|
||||||
}
|
|
||||||
End:
|
|
||||||
free(quant_alpha);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Alpha Decode.
|
|
||||||
|
|
||||||
static int DecompressZlibTCoder(VP8BitReader* const br, int width,
|
|
||||||
uint8_t* output, size_t output_size) {
|
|
||||||
int ok = 0;
|
|
||||||
const int MAX_DIST = 3 * width;
|
|
||||||
const int MAX_LEN = 2 * width;
|
|
||||||
TCoder* const coder = TCoderNew(MAX_SYMBOLS);
|
|
||||||
TCoder* const coderd = TCoderNew(MAX_DIST);
|
|
||||||
TCoder* const coderl = TCoderNew(MAX_LEN - MIN_LEN);
|
|
||||||
|
|
||||||
if (coder == NULL || coderd == NULL || coderl == NULL) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
size_t pos = 0;
|
|
||||||
assert(br != NULL);
|
|
||||||
while (pos < output_size && !br->eof_) {
|
|
||||||
const size_t dist = TCoderDecode(coderd, br);
|
|
||||||
if (dist == 0) {
|
|
||||||
output[pos] = TCoderDecode(coder, br);
|
|
||||||
++pos;
|
|
||||||
} else {
|
|
||||||
const size_t len = MIN_LEN + TCoderDecode(coderl, br);
|
|
||||||
size_t k;
|
|
||||||
if (pos + len > output_size || pos < dist) goto End;
|
|
||||||
for (k = 0; k < len; ++k) {
|
|
||||||
output[pos + k] = output[pos + k - dist];
|
|
||||||
}
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok = !br->eof_;
|
|
||||||
}
|
|
||||||
|
|
||||||
End:
|
|
||||||
if (coder) TCoderDelete(coder);
|
|
||||||
if (coderl) TCoderDelete(coderl);
|
|
||||||
if (coderd) TCoderDelete(coderd);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|
||||||
int width, int height, int stride,
|
|
||||||
uint8_t* output) {
|
|
||||||
uint8_t* decoded_data = NULL;
|
|
||||||
const size_t decoded_size = height * width;
|
|
||||||
uint8_t* unfiltered_data = NULL;
|
|
||||||
WEBP_FILTER_TYPE filter;
|
|
||||||
int ok = 0;
|
|
||||||
int method;
|
|
||||||
|
|
||||||
assert(width > 0 && height > 0 && stride >= width);
|
|
||||||
assert(data != NULL && output != NULL);
|
|
||||||
|
|
||||||
if (data_size <= ALPHA_HEADER_LEN) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
method = data[0] & 0x0f;
|
|
||||||
filter = data[0] >> 4;
|
|
||||||
ok = (data[1] == 0);
|
|
||||||
if (method < 0 || method > 1 ||
|
|
||||||
filter > WEBP_FILTER_GRADIENT || !ok) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == 0) {
|
|
||||||
ok = (data_size >= decoded_size);
|
|
||||||
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
|
||||||
} else if (method == 1) {
|
|
||||||
VP8BitReader br;
|
|
||||||
decoded_data = (uint8_t*)malloc(decoded_size);
|
|
||||||
if (decoded_data == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
VP8InitBitReader(&br, data + ALPHA_HEADER_LEN, data + data_size);
|
|
||||||
ok = DecompressZlibTCoder(&br, width, decoded_data, decoded_size);
|
|
||||||
}
|
|
||||||
if (ok) {
|
|
||||||
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) {
|
|
||||||
free(decoded_data);
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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/
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Alpha plane encoding and decoding library.
|
|
||||||
//
|
|
||||||
// Author: vikasa@google.com (Vikas Arora)
|
|
||||||
|
|
||||||
#ifndef WEBP_UTILS_ALPHA_H_
|
|
||||||
#define WEBP_UTILS_ALPHA_H_
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "../webp/types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 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 = 1' - Backward reference counts encoded with arithmetic encoder;
|
|
||||||
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
|
||||||
// vertical & gradient filters. The prediction mode 4 will try all the
|
|
||||||
// prediction modes (0 to 3) and pick the best prediction mode.
|
|
||||||
|
|
||||||
// 'output' corresponds to the buffer containing compressed alpha data.
|
|
||||||
// This buffer is allocated by this method and caller should call
|
|
||||||
// free(*output) when done.
|
|
||||||
// 'output_size' corresponds to size of this compressed alpha buffer.
|
|
||||||
//
|
|
||||||
// Returns 1 on successfully encoding the alpha and
|
|
||||||
// 0 if either:
|
|
||||||
// invalid quality or method, or
|
|
||||||
// memory allocation for the compressed data fails.
|
|
||||||
|
|
||||||
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|
||||||
int quality, int method, int filter,
|
|
||||||
uint8_t** output, size_t* output_size);
|
|
||||||
|
|
||||||
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
|
||||||
// The 'output' buffer should be pre-allocated and must be of the same
|
|
||||||
// dimension 'height'x'stride', as that of the image.
|
|
||||||
//
|
|
||||||
// Returns 1 on successfully decoding the compressed alpha and
|
|
||||||
// 0 if either:
|
|
||||||
// error in bit-stream header (invalid compression mode or filter), or
|
|
||||||
// error returned by appropriate compression method.
|
|
||||||
int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|
||||||
int width, int height, int stride, uint8_t* output);
|
|
||||||
|
|
||||||
// Replace the input 'data' of size 'width'x'height' with 'num-levels'
|
|
||||||
// quantized values. If not NULL, 'mse' will contain the mean-squared error.
|
|
||||||
// Valid range for 'num_levels' is [2, 256].
|
|
||||||
// Returns false in case of error (data is NULL, or parameters are invalid).
|
|
||||||
int QuantizeLevels(uint8_t* data, int width, int height, int num_levels,
|
|
||||||
float* mse);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* WEBP_UTILS_ALPHA_H_ */
|
|
@ -13,7 +13,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h> // for sqrt()
|
#include <math.h> // for sqrt()
|
||||||
|
|
||||||
#include "./alpha.h"
|
#include "./quant_levels.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
34
src/utils/quant_levels.h
Normal file
34
src/utils/quant_levels.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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/
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Alpha plane quantization utility
|
||||||
|
//
|
||||||
|
// Author: vikasa@google.com (Vikas Arora)
|
||||||
|
|
||||||
|
#ifndef WEBP_UTILS_QUANT_LEVELS_H_
|
||||||
|
#define WEBP_UTILS_QUANT_LEVELS_H_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../webp/types.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Replace the input 'data' of size 'width'x'height' with 'num-levels'
|
||||||
|
// quantized values. If not NULL, 'mse' will contain the mean-squared error.
|
||||||
|
// Valid range for 'num_levels' is [2, 256].
|
||||||
|
// Returns false in case of error (data is NULL, or parameters are invalid).
|
||||||
|
int QuantizeLevels(uint8_t* data, int width, int height, int num_levels,
|
||||||
|
float* mse);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WEBP_UTILS_QUANT_LEVELS_H_ */
|
@ -1,497 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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/
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Tree-coder using VP8's boolean coder
|
|
||||||
//
|
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
|
||||||
//
|
|
||||||
// Rationale:
|
|
||||||
// We extend the boolean (binary) coder to handle arbitrary-sized alphabets,
|
|
||||||
// and not just binary ones.
|
|
||||||
// We dynamically maintain the population count and use the locally-optimal
|
|
||||||
// probability distribution for coding every symbol. Every symbol can be
|
|
||||||
// coded using _any_ binary tree. The boolean coder would traverse it and
|
|
||||||
// branch each nodes left and right with the accumulated probability.
|
|
||||||
//
|
|
||||||
// E.g. with 3 symbols A, B, C already coded 30, 50 and 120 times respectively:
|
|
||||||
//
|
|
||||||
/* Root Node #0 (count=30+50+120=200)
|
|
||||||
| \
|
|
||||||
| A (count=30)
|
|
||||||
Inner-Node #1 (count=50+120=170)
|
|
||||||
| \
|
|
||||||
| C (count=120)
|
|
||||||
B (count=50)
|
|
||||||
*/
|
|
||||||
// If the next symbol to code is "C", we'll first code '0' with probability
|
|
||||||
// p0 = 170/200 (which is the probability of taking the left branch at the
|
|
||||||
// Root Node #0) and then code '1' with a probability p1 = 120/170 (which
|
|
||||||
// is the probability of taking the right branch at the Inner-Node #1). The
|
|
||||||
// total probability p0 * p1 = 120 / 200 is the correct one for symbol 'C'
|
|
||||||
// (up to small rounding differences in the boolean coder).
|
|
||||||
// The alphabet could be coded with _any_ tree, provided the count at the
|
|
||||||
// inner nodes are updated appropriately. Put otherwise, the binary tree
|
|
||||||
// is only used to efficiently update the frequency counts in O(ln(N)) time
|
|
||||||
// instead of O(N).
|
|
||||||
// For instance, we could use the equivalent tree:
|
|
||||||
/* Root (count=200)
|
|
||||||
| \
|
|
||||||
| C (count=120)
|
|
||||||
Inner (count=50+30=80)
|
|
||||||
| \
|
|
||||||
| B (count=50)
|
|
||||||
A (count=30)
|
|
||||||
*/
|
|
||||||
// The frequency distribution would still be respected when coding the symbols.
|
|
||||||
// But! There's a noticeable difference: it only takes _one_ call to VP8PutBit()
|
|
||||||
// when coding the letter 'C' (with probability 120/200), which is the most
|
|
||||||
// frequent symbol. This has an impact on speed, considering that each call
|
|
||||||
// to VP8PutBit/VP8GetBit is costly. Hence, in order to minimize the number
|
|
||||||
// of binary coding, the frequent symbol should be up in the tree.
|
|
||||||
// Using Huffman tree is a solution, but the management and updating can be
|
|
||||||
// quite complicated. Here we opt for a simpler option: we use _ternary_
|
|
||||||
// tree instead, where each inner node can be associated with a symbol, in
|
|
||||||
// addition to the regular left/right branches. When we traverse down
|
|
||||||
// the tree, a stop bit is used to signal whether the traversal is finished
|
|
||||||
// or not. Its probability is proportional to the frequency with which the
|
|
||||||
// node's symbol has been seen (see probaS_). If the traversal is not
|
|
||||||
// finished, we keep branching right or left according with a probability
|
|
||||||
// proportional to each branch's use count (see probaL_).
|
|
||||||
// When a symbol is seen more frequently than its parent, we simply
|
|
||||||
// exchange the two symbols without changing the tree structure or the
|
|
||||||
// left/right branches.
|
|
||||||
// Hence, both tree examples above can be coded using this ternary tree:
|
|
||||||
/* Root #0 (count=200)
|
|
||||||
/ | \
|
|
||||||
/ C \
|
|
||||||
Node #1 Node #2
|
|
||||||
/ | \ / | \
|
|
||||||
x A x x B x <- where 'x' means un-assigned branches.
|
|
||||||
*/
|
|
||||||
// Here, if the symbol 'A' becomes more frequent afterward, we'll just swap it
|
|
||||||
// with 'C' (cf ExchangeSymbol()) without reorganizing the tree.
|
|
||||||
//
|
|
||||||
// Using this simple maintenance, we observed a typical 10-20% reduction
|
|
||||||
// in the number of calls to VP8PutBit(), leading to 3-5% speed gain.
|
|
||||||
//
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "./tcoderi.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) && !defined(NOT_HAVE_LOG2)
|
|
||||||
# define NOT_HAVE_LOG2 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NOT_HAVE_LOG2
|
|
||||||
static double log2(double d) {
|
|
||||||
const double kLog2Reciprocal = 1.442695040888963;
|
|
||||||
return log(d) * kLog2Reciprocal;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For code=00001xxx..., returns the position of the leftmost leading '1' bit.
|
|
||||||
static WEBP_INLINE int CodeLength(int code) {
|
|
||||||
int length = 0;
|
|
||||||
if (code > 0) {
|
|
||||||
while ((code >> length) != 1) ++length;
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
TCoder* TCoderNew(int max_symbol) {
|
|
||||||
const int num_nodes = max_symbol + 1;
|
|
||||||
TCoder* c;
|
|
||||||
uint8_t* memory;
|
|
||||||
int size;
|
|
||||||
if (max_symbol < 0 || max_symbol >= TCODER_MAX_SYMBOL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
size = sizeof(*c) + num_nodes * sizeof(*c->nodes_)
|
|
||||||
+ num_nodes * sizeof(*c->symbols_);
|
|
||||||
memory = (uint8_t*)malloc(size);
|
|
||||||
if (memory == NULL) return NULL;
|
|
||||||
|
|
||||||
c = (TCoder*)memory;
|
|
||||||
memory += sizeof(*c);
|
|
||||||
c->nodes_ = (Node*)memory - 1;
|
|
||||||
memory += num_nodes * sizeof(*c->nodes_);
|
|
||||||
c->symbols_ = (int*)memory;
|
|
||||||
|
|
||||||
c->num_nodes_ = num_nodes;
|
|
||||||
c->frozen_ = 0;
|
|
||||||
|
|
||||||
TCoderInit(c);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE void ResetNode(Node* const node, Symbol_t symbol) {
|
|
||||||
assert(node);
|
|
||||||
node->countS_ = (Count_t)0;
|
|
||||||
node->count_ = (Count_t)0;
|
|
||||||
node->probaS_ = HALF_PROBA;
|
|
||||||
node->probaL_ = HALF_PROBA;
|
|
||||||
node->symbol_ = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wipe the tree clean.
|
|
||||||
static void ResetTree(TCoder* const c) {
|
|
||||||
int pos;
|
|
||||||
assert(c);
|
|
||||||
c->num_symbols_ = 0;
|
|
||||||
c->total_coded_ = 0;
|
|
||||||
for (pos = 1; pos <= c->num_nodes_; ++pos) {
|
|
||||||
ResetNode(&c->nodes_[pos], INVALID_SYMBOL);
|
|
||||||
}
|
|
||||||
c->fixed_symbols_ = 0;
|
|
||||||
c->symbol_bit_cost_ = 5 + CodeLength(c->num_nodes_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ResetSymbolMap(TCoder* const c) {
|
|
||||||
Symbol_t s;
|
|
||||||
assert(c);
|
|
||||||
c->num_symbols_ = 0;
|
|
||||||
for (s = 0; s < c->num_nodes_; ++s) {
|
|
||||||
c->symbols_[s] = INVALID_POS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCoderInit(TCoder* const c) {
|
|
||||||
assert(c);
|
|
||||||
if (!c->frozen_) { // Reset counters
|
|
||||||
ResetTree(c);
|
|
||||||
ResetSymbolMap(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCoderDelete(TCoder* const c) {
|
|
||||||
free(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Tree utils around nodes
|
|
||||||
|
|
||||||
// Total number of visits on this nodes
|
|
||||||
static WEBP_INLINE Count_t TotalCount(const Node* const n) {
|
|
||||||
return n->countS_ + n->count_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if node has no child.
|
|
||||||
static WEBP_INLINE int IsLeaf(const TCoder* const c, int pos) {
|
|
||||||
return (2 * pos > c->num_symbols_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if node has no right child.
|
|
||||||
static WEBP_INLINE int HasOnlyLeftChild(const TCoder* const c, int pos) {
|
|
||||||
return (2 * pos == c->num_symbols_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Node management
|
|
||||||
|
|
||||||
static int NewNode(TCoder* const c, int s) {
|
|
||||||
// For an initial new symbol position, we pick the slot that is the
|
|
||||||
// closest to the top of the tree. It shortens the paths' length.
|
|
||||||
const int pos = 1 + c->num_symbols_;
|
|
||||||
assert(c);
|
|
||||||
assert(c->num_symbols_ < c->num_nodes_);
|
|
||||||
assert(c->symbols_[s] == INVALID_POS);
|
|
||||||
assert(c->nodes_[pos].symbol_ == INVALID_SYMBOL);
|
|
||||||
c->symbols_[s] = pos;
|
|
||||||
ResetNode(&c->nodes_[pos], s);
|
|
||||||
++c->num_symbols_;
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// trivial method, mainly for debug
|
|
||||||
static WEBP_INLINE int SymbolToNode(const TCoder* const c, int s) {
|
|
||||||
const int pos = c->symbols_[s];
|
|
||||||
assert(s >= 0 && s < c->num_nodes_ && s != INVALID_SYMBOL);
|
|
||||||
assert(pos != INVALID_POS);
|
|
||||||
assert(c->nodes_[pos].symbol_ == s);
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SWAP(T, a, b) do { \
|
|
||||||
const T tmp = (a); \
|
|
||||||
(a) = (b); \
|
|
||||||
(b) = tmp; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// Make child symbol bubble up one level
|
|
||||||
static void ExchangeSymbol(const TCoder* const c, const int pos) {
|
|
||||||
const int parent = pos >> 1;
|
|
||||||
Node* const node0 = &c->nodes_[parent]; // parent node
|
|
||||||
Node* const node1 = &c->nodes_[pos]; // child node
|
|
||||||
const Symbol_t S0 = node0->symbol_;
|
|
||||||
const Symbol_t S1 = node1->symbol_;
|
|
||||||
c->symbols_[S1] = parent;
|
|
||||||
c->symbols_[S0] = pos;
|
|
||||||
assert(node1->countS_ >= node0->countS_);
|
|
||||||
node0->count_ -= (node1->countS_ - node0->countS_);
|
|
||||||
assert(node0->count_ > 0);
|
|
||||||
SWAP(Count_t, node0->countS_, node1->countS_);
|
|
||||||
SWAP(Symbol_t, node0->symbol_, node1->symbol_);
|
|
||||||
// Note: probaL_ and probaS_ are recomputed. No need to SWAP them.
|
|
||||||
}
|
|
||||||
#undef SWAP
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// probability computation
|
|
||||||
|
|
||||||
static WEBP_INLINE int CalcProba(Count_t num, Count_t total, int max_proba) {
|
|
||||||
int p;
|
|
||||||
assert(total > 0);
|
|
||||||
p = num * max_proba / total;
|
|
||||||
assert(p >= 0 && p <= MAX_PROBA);
|
|
||||||
return MAX_PROBA - p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE void UpdateNodeProbas(TCoder* const c, int pos) {
|
|
||||||
Node* const node = &c->nodes_[pos];
|
|
||||||
const Count_t total = TotalCount(node);
|
|
||||||
if (total < COUNTER_CUT_OFF)
|
|
||||||
node->probaS_ = CalcProba(node->countS_, total, MAX_PROBA);
|
|
||||||
if (!IsLeaf(c, pos)) {
|
|
||||||
const Count_t total_count = node->count_;
|
|
||||||
if (total_count < COUNTER_CUT_OFF) {
|
|
||||||
const Count_t left_count = TotalCount(&c->nodes_[2 * pos]);
|
|
||||||
node->probaL_ =
|
|
||||||
MAX_PROBA - CalcProba(left_count, total_count, MAX_PROBA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateProbas(TCoder* const c, int pos) {
|
|
||||||
for ( ; pos >= 1; pos >>= 1) {
|
|
||||||
UpdateNodeProbas(c, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static void UpdateTree(TCoder* const c, int pos) {
|
|
||||||
Node* node = &c->nodes_[pos];
|
|
||||||
const int is_fresh_new_symbol = (node->countS_ == 0);
|
|
||||||
assert(c);
|
|
||||||
assert(pos >= 1 && pos <= c->num_nodes_);
|
|
||||||
assert(node->symbol_ != INVALID_SYMBOL);
|
|
||||||
if (!(c->frozen_ || node->countS_ >= COUNTER_CUT_OFF) ||
|
|
||||||
is_fresh_new_symbol) {
|
|
||||||
const int starting_pos = pos; // save for later
|
|
||||||
// Update the counters up the tree, possibly exchanging some nodes
|
|
||||||
++node->countS_;
|
|
||||||
while (pos > 1) {
|
|
||||||
Node* const parent = &c->nodes_[pos >> 1];
|
|
||||||
++parent->count_;
|
|
||||||
if (parent->countS_ < node->countS_) {
|
|
||||||
ExchangeSymbol(c, pos);
|
|
||||||
}
|
|
||||||
pos >>= 1;
|
|
||||||
node = parent;
|
|
||||||
}
|
|
||||||
++c->total_coded_;
|
|
||||||
UpdateProbas(c, starting_pos); // Update the probas along the modified path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Fixed-length symbol coding
|
|
||||||
// Note: the symbol will be coded exactly once at most, so using a fixed length
|
|
||||||
// code is better than Golomb-code (e.g.) on average.
|
|
||||||
|
|
||||||
// We use the exact bit-distribution probability considering the upper-bound
|
|
||||||
// supplied:
|
|
||||||
// Written in binary, a symbol 's' has a probability of having its k-th bit
|
|
||||||
// set to 1 which is given by:
|
|
||||||
// If the k-th bit of max_value is 0:
|
|
||||||
// P0(k) = [(max_value >> (k + 1)) << k] / max_value
|
|
||||||
// If the k-th bit of max_value is 1:
|
|
||||||
// P1(k) = P0(k) + [max_value & ((1 << k) - 1)] / max_value
|
|
||||||
|
|
||||||
static WEBP_INLINE void CodeSymbol(VP8BitWriter* const bw, int s,
|
|
||||||
int max_value) {
|
|
||||||
int i, up = 1;
|
|
||||||
assert(bw);
|
|
||||||
assert(s < max_value);
|
|
||||||
for (i = 0; up < max_value; up <<= 1, ++i) {
|
|
||||||
int den = (max_value >> 1) & ~(up - 1);
|
|
||||||
if (max_value & up) den |= max_value & (up - 1);
|
|
||||||
VP8PutBit(bw, (s >> i) & 1, MAX_PROBA - MAX_PROBA * den / max_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE int DecodeSymbol(VP8BitReader* const br, int max_value) {
|
|
||||||
int i, up = 1, v = 0;
|
|
||||||
assert(br);
|
|
||||||
for (i = 0; up < max_value; ++i) {
|
|
||||||
int den = (max_value >> 1) & ~(up - 1);
|
|
||||||
if (max_value & up) den |= max_value & (up - 1);
|
|
||||||
v |= VP8GetBit(br, MAX_PROBA - MAX_PROBA * den / max_value) << i;
|
|
||||||
up <<= 1;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Encoding
|
|
||||||
|
|
||||||
void TCoderEncode(TCoder* const c, int s, VP8BitWriter* const bw) {
|
|
||||||
int pos;
|
|
||||||
const int is_new_symbol = (c->symbols_[s] == INVALID_POS);
|
|
||||||
assert(c);
|
|
||||||
assert(s >= 0 && s < c->num_nodes_);
|
|
||||||
if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) {
|
|
||||||
if (c->num_symbols_ > 0) {
|
|
||||||
if (bw != NULL) {
|
|
||||||
const int new_symbol_proba =
|
|
||||||
CalcProba(c->num_symbols_, c->total_coded_, HALF_PROBA - 1);
|
|
||||||
VP8PutBit(bw, is_new_symbol, new_symbol_proba);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(is_new_symbol);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(!is_new_symbol);
|
|
||||||
}
|
|
||||||
if (is_new_symbol) {
|
|
||||||
if (bw != NULL) {
|
|
||||||
int k, count = 0;
|
|
||||||
for (k = 0; k < s; ++k) {
|
|
||||||
count += (c->symbols_[k] == INVALID_POS);
|
|
||||||
}
|
|
||||||
CodeSymbol(bw, count, c->num_nodes_ - c->num_symbols_);
|
|
||||||
}
|
|
||||||
pos = NewNode(c, s);
|
|
||||||
} else {
|
|
||||||
pos = SymbolToNode(c, s);
|
|
||||||
if (bw != NULL) {
|
|
||||||
const int length = CodeLength(pos);
|
|
||||||
int parent = 1;
|
|
||||||
int i;
|
|
||||||
for (i = 0; !IsLeaf(c, parent); ++i) {
|
|
||||||
const Node* const node = &c->nodes_[parent];
|
|
||||||
const int symbol_proba = node->probaS_;
|
|
||||||
const int is_stop = (i == length);
|
|
||||||
if (VP8PutBit(bw, is_stop, symbol_proba)) {
|
|
||||||
break;
|
|
||||||
} else if (!HasOnlyLeftChild(c, parent)) {
|
|
||||||
const int left_proba = node->probaL_;
|
|
||||||
const int is_right =
|
|
||||||
(pos >> (length - 1 - i)) & 1; // extract bits #i
|
|
||||||
VP8PutBit(bw, is_right, left_proba);
|
|
||||||
parent = (parent << 1) | is_right;
|
|
||||||
} else {
|
|
||||||
parent <<= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(parent == pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateTree(c, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Decoding
|
|
||||||
|
|
||||||
int TCoderDecode(TCoder* const c, VP8BitReader* const br) {
|
|
||||||
int s;
|
|
||||||
int pos;
|
|
||||||
int is_new_symbol = 0;
|
|
||||||
assert(c);
|
|
||||||
assert(br);
|
|
||||||
// Check if we need to transmit the new symbol's value
|
|
||||||
if (!c->fixed_symbols_ && c->num_symbols_ < c->num_nodes_) {
|
|
||||||
if (c->num_symbols_ > 0) {
|
|
||||||
const int new_symbol_proba =
|
|
||||||
CalcProba(c->num_symbols_, c->total_coded_, HALF_PROBA - 1);
|
|
||||||
is_new_symbol = VP8GetBit(br, new_symbol_proba);
|
|
||||||
} else {
|
|
||||||
is_new_symbol = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Code either the raw value, or the path downward to its node.
|
|
||||||
if (is_new_symbol) {
|
|
||||||
int count = DecodeSymbol(br, c->num_nodes_ - c->num_symbols_);
|
|
||||||
// The 'count' value specifies the number of empty slots to jump
|
|
||||||
// over. We skip the already-used ones.
|
|
||||||
for (s = 0; s < c->num_nodes_; ++s) {
|
|
||||||
if (c->symbols_[s] == INVALID_POS) {
|
|
||||||
if (count-- == 0) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (s == c->num_nodes_) {
|
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
pos = NewNode(c, s);
|
|
||||||
} else {
|
|
||||||
pos = 1;
|
|
||||||
while (!IsLeaf(c, pos)) {
|
|
||||||
const Node* const node = &c->nodes_[pos];
|
|
||||||
// Did we reach the stopping node?
|
|
||||||
const int symbol_proba = node->probaS_;
|
|
||||||
const int is_stop = VP8GetBit(br, symbol_proba);
|
|
||||||
if (is_stop) {
|
|
||||||
break; // reached the stopping node for the coded symbol.
|
|
||||||
} else {
|
|
||||||
// Not yet done, keep traversing and branching.
|
|
||||||
if (!HasOnlyLeftChild(c, pos)) {
|
|
||||||
const int left_proba = node->probaL_;
|
|
||||||
const int is_right = VP8GetBit(br, left_proba);
|
|
||||||
pos = (pos << 1) | is_right;
|
|
||||||
} else {
|
|
||||||
pos <<= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(pos <= c->num_nodes_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(pos <= c->num_symbols_);
|
|
||||||
s = c->nodes_[pos].symbol_;
|
|
||||||
assert(pos == SymbolToNode(c, s));
|
|
||||||
}
|
|
||||||
assert(pos <= c->num_symbols_);
|
|
||||||
UpdateTree(c, pos);
|
|
||||||
return s;
|
|
||||||
|
|
||||||
Error:
|
|
||||||
br->eof_ = 1; // will make decoding abort.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
double TCoderSymbolCost(const TCoder* const c, int symbol) {
|
|
||||||
const int pos = c->symbols_[symbol];
|
|
||||||
assert(c);
|
|
||||||
assert(symbol >= 0 && symbol < c->num_nodes_);
|
|
||||||
if (pos != INVALID_POS) {
|
|
||||||
const Node* const node = &c->nodes_[pos];
|
|
||||||
const Count_t count = node->countS_;
|
|
||||||
assert(count > 0);
|
|
||||||
assert(c->total_coded_ > 0);
|
|
||||||
// Note: we use 1 + total_coded_ as denominator because we most probably
|
|
||||||
// intend to code an extra symbol afterward.
|
|
||||||
// TODO(skal): is log2() too slow ?
|
|
||||||
return -log2(count / (1. + c->total_coded_));
|
|
||||||
}
|
|
||||||
return c->symbol_bit_cost_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
@ -1,86 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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/
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Tree-coder using VP8's boolean coder
|
|
||||||
//
|
|
||||||
// Symbols are stored as nodes of a tree that records their frequencies and
|
|
||||||
// is dynamically updated.
|
|
||||||
//
|
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
|
||||||
//
|
|
||||||
// Encoding example:
|
|
||||||
/*
|
|
||||||
static int Compress(const uint8_t* src, int src_length,
|
|
||||||
uint8_t** output, size_t* output_size) {
|
|
||||||
int i;
|
|
||||||
TCoder* coder = TCoderNew(255);
|
|
||||||
VP8BitWriter bw;
|
|
||||||
|
|
||||||
VP8BitWriterInit(&bw, 0);
|
|
||||||
for (i = 0; i < src_length; ++i)
|
|
||||||
TCoderEncode(coder, src[i], &bw);
|
|
||||||
TCoderDelete(coder);
|
|
||||||
VP8BitWriterFinish(&bw);
|
|
||||||
|
|
||||||
*output = VP8BitWriterBuf(&bw);
|
|
||||||
*output_size = VP8BitWriterSize(&bw);
|
|
||||||
return !bw.error_;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//
|
|
||||||
// Decoding example:
|
|
||||||
/*
|
|
||||||
static int Decompress(const uint8_t* src, size_t src_size,
|
|
||||||
uint8_t* dst, int dst_length) {
|
|
||||||
int i;
|
|
||||||
TCoder* coder = TCoderNew(255);
|
|
||||||
VP8BitReader br;
|
|
||||||
|
|
||||||
VP8InitBitReader(&br, src, src + src_size);
|
|
||||||
for (i = 0; i < dst_length; ++i)
|
|
||||||
dst[i] = TCoderDecode(coder, &br);
|
|
||||||
TCoderDelete(coder);
|
|
||||||
return !br.eof_;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WEBP_UTILS_TCODER_H_
|
|
||||||
#define WEBP_UTILS_TCODER_H_
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct VP8BitReader;
|
|
||||||
struct VP8BitWriter;
|
|
||||||
typedef struct TCoder TCoder;
|
|
||||||
|
|
||||||
// Creates a tree-coder capable of coding symbols in
|
|
||||||
// the [0, max_symbol] range. Returns NULL in case of memory error.
|
|
||||||
// 'max_symbol' must be in the range [0, TCODER_MAX_SYMBOL)
|
|
||||||
#define TCODER_MAX_SYMBOL (1 << 24)
|
|
||||||
TCoder* TCoderNew(int max_symbol);
|
|
||||||
// Re-initialize an existing object, make it ready for a new encoding or
|
|
||||||
// decoding cycle.
|
|
||||||
void TCoderInit(TCoder* const c);
|
|
||||||
// destroys the tree-coder object and frees memory.
|
|
||||||
void TCoderDelete(TCoder* const c);
|
|
||||||
|
|
||||||
// Code next symbol 's'. If the bit-writer 'bw' is NULL, the function will
|
|
||||||
// just record the symbol, and update the internal frequency counters.
|
|
||||||
void TCoderEncode(TCoder* const c, int s, struct VP8BitWriter* const bw);
|
|
||||||
// Decode and return next symbol.
|
|
||||||
int TCoderDecode(TCoder* const c, struct VP8BitReader* const br);
|
|
||||||
|
|
||||||
// Theoretical number of bits needed to code 'symbol' in the current state.
|
|
||||||
double TCoderSymbolCost(const TCoder* const c, int symbol);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // WEBP_UTILS_TCODER_H_
|
|
@ -1,75 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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/
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Internal header for tree-coder
|
|
||||||
//
|
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef WEBP_UTILS_TCODERI_H_
|
|
||||||
#define WEBP_UTILS_TCODERI_H_
|
|
||||||
|
|
||||||
#include "./tcoder.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "../utils/bit_reader.h"
|
|
||||||
#include "../utils/bit_writer.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef int Symbol_t;
|
|
||||||
typedef uint32_t Count_t; // TODO(skal): check overflow during coding.
|
|
||||||
|
|
||||||
#define INVALID_SYMBOL ((Symbol_t)(-1))
|
|
||||||
#define INVALID_POS 0
|
|
||||||
|
|
||||||
#define MAX_PROBA 255
|
|
||||||
#define HALF_PROBA 128
|
|
||||||
|
|
||||||
// Limit the number of tree updates above which we freeze the probabilities.
|
|
||||||
// Mainly for speed reason.
|
|
||||||
// TODO(skal): could be a bitstream parameter?
|
|
||||||
#define COUNTER_CUT_OFF 16383
|
|
||||||
|
|
||||||
typedef struct { // ternary node.
|
|
||||||
Symbol_t symbol_;
|
|
||||||
// Note: theoretically, one of this three field is redundant and could be
|
|
||||||
// omitted, but it'd make the code quite complicated (having to look-up the
|
|
||||||
// parent's total count in order to deduce the missing field). Better not.
|
|
||||||
Count_t countS_; // count for symbol
|
|
||||||
Count_t count_; // count for non-symbol (derived from sub-tree)
|
|
||||||
int probaL_; // cached left proba = TotalCount(left) / count_
|
|
||||||
int probaS_; // cached approximate proba = countS_ / TotalCount
|
|
||||||
} Node;
|
|
||||||
|
|
||||||
struct TCoder {
|
|
||||||
// dynamic fields:
|
|
||||||
int num_symbols_; // number of symbols actually used
|
|
||||||
Count_t total_coded_; // total number of coded symbols
|
|
||||||
int frozen_; // if true, frequencies are not updated
|
|
||||||
int fixed_symbols_; // if true, symbols are not updated
|
|
||||||
|
|
||||||
// constants:
|
|
||||||
int num_nodes_; // max number of symbols or nodes. Constant, > 0.
|
|
||||||
double symbol_bit_cost_; // latest evaluation of the bit-cost per new symbol
|
|
||||||
|
|
||||||
Node* nodes_; // nodes (1-based indexed)
|
|
||||||
int* symbols_; // for each symbol, location of its node
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // WEBP_UTILS_TCODERI_H_
|
|
Loading…
Reference in New Issue
Block a user