From 3e863dda61929ac06ad48a775248c630101fbb00 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 21 May 2012 06:22:06 -0700 Subject: [PATCH] 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 --- Android.mk | 2 - Makefile.vc | 6 +- makefile.unix | 6 +- src/dec/alpha.c | 103 +++++++- src/enc/alpha.c | 247 ++++++++++++++++++- src/utils/Makefile.am | 6 +- src/utils/alpha.c | 464 ------------------------------------ src/utils/alpha.h | 69 ------ src/utils/quant_levels.c | 2 +- src/utils/quant_levels.h | 34 +++ src/utils/tcoder.c | 497 --------------------------------------- src/utils/tcoder.h | 86 ------- src/utils/tcoderi.h | 75 ------ 13 files changed, 389 insertions(+), 1208 deletions(-) delete mode 100644 src/utils/alpha.c delete mode 100644 src/utils/alpha.h create mode 100644 src/utils/quant_levels.h delete mode 100644 src/utils/tcoder.c delete mode 100644 src/utils/tcoder.h delete mode 100644 src/utils/tcoderi.h diff --git a/Android.mk b/Android.mk index 6ed2295f..43d24a59 100644 --- a/Android.mk +++ b/Android.mk @@ -39,7 +39,6 @@ LOCAL_SRC_FILES := \ src/mux/muxedit.c \ src/mux/muxinternal.c \ src/mux/muxread.c \ - src/utils/alpha.c \ src/utils/bit_reader.c \ src/utils/bit_writer.c \ src/utils/color_cache.c \ @@ -47,7 +46,6 @@ LOCAL_SRC_FILES := \ src/utils/huffman.c \ src/utils/quant_levels.c \ src/utils/rescaler.c \ - src/utils/tcoder.c \ src/utils/thread.c \ LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \ diff --git a/Makefile.vc b/Makefile.vc index e17c9199..b81aaea3 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -178,16 +178,19 @@ EX_UTIL_OBJS = \ ENC_OBJS = \ $(DIROBJ)\enc\alpha.obj \ $(DIROBJ)\enc\analysis.obj \ + $(DIROBJ)\enc\backward_references.obj \ $(DIROBJ)\enc\config.obj \ $(DIROBJ)\enc\cost.obj \ $(DIROBJ)\enc\filter.obj \ $(DIROBJ)\enc\frame.obj \ + $(DIROBJ)\enc\histogram.obj \ $(DIROBJ)\enc\iterator.obj \ $(DIROBJ)\enc\layer.obj \ $(DIROBJ)\enc\picture.obj \ $(DIROBJ)\enc\quant.obj \ $(DIROBJ)\enc\syntax.obj \ $(DIROBJ)\enc\tree.obj \ + $(DIROBJ)\enc\vp8l.obj \ $(DIROBJ)\enc\webpenc.obj \ MUX_OBJS = \ @@ -196,15 +199,14 @@ MUX_OBJS = \ $(DIROBJ)\mux\muxread.obj \ UTILS_OBJS = \ - $(DIROBJ)\utils\alpha.obj \ $(DIROBJ)\utils\bit_reader.obj \ $(DIROBJ)\utils\bit_writer.obj \ $(DIROBJ)\utils\color_cache.obj \ $(DIROBJ)\utils\filters.obj \ $(DIROBJ)\utils\huffman.obj \ + $(DIROBJ)\utils\huffman_encode.obj \ $(DIROBJ)\utils\quant_levels.obj \ $(DIROBJ)\utils\rescaler.obj \ - $(DIROBJ)\utils\tcoder.obj \ $(DIROBJ)\utils\thread.obj \ X_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(MUX_OBJS) $(UTILS_OBJS) $(X_OBJS) diff --git a/makefile.unix b/makefile.unix index 9fd4baa8..3749e5b1 100644 --- a/makefile.unix +++ b/makefile.unix @@ -115,7 +115,6 @@ MUX_OBJS = \ src/mux/muxread.o \ UTILS_OBJS = \ - src/utils/alpha.o \ src/utils/bit_reader.o \ src/utils/bit_writer.o \ src/utils/color_cache.o \ @@ -124,7 +123,6 @@ UTILS_OBJS = \ src/utils/huffman_encode.o \ src/utils/quant_levels.o \ src/utils/rescaler.o \ - src/utils/tcoder.o \ src/utils/thread.o \ LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) @@ -139,15 +137,13 @@ HDRS = \ src/dsp/yuv.h \ src/enc/cost.h \ src/enc/vp8enci.h \ - src/utils/alpha.h \ src/utils/bit_reader.h \ src/utils/bit_writer.h \ src/utils/color_cache.h \ src/utils/filters.h \ src/utils/huffman.h \ + src/utils/quant_levels.h \ src/utils/rescaler.h \ - src/utils/tcoder.h \ - src/utils/tcoderi.h \ src/utils/thread.h \ src/webp/decode.h \ src/webp/decode_vp8.h \ diff --git a/src/dec/alpha.c b/src/dec/alpha.c index f9187495..acb0d63e 100644 --- a/src/dec/alpha.c +++ b/src/dec/alpha.c @@ -11,12 +11,113 @@ #include #include "./vp8i.h" -#include "../utils/alpha.h" +#include "../webp/decode.h" +#include "../utils/filters.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #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, diff --git a/src/enc/alpha.c b/src/enc/alpha.c index 1172ae50..0f601110 100644 --- a/src/enc/alpha.c +++ b/src/enc/alpha.c @@ -13,14 +13,259 @@ #include #include "./vp8enci.h" -#include "../utils/alpha.h" #include "../utils/filters.h" +#include "../utils/quant_levels.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #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) { enc->has_alpha_ = (enc->pic_->a != NULL); diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 92649b0f..e9f5571b 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -2,8 +2,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src noinst_LTLIBRARIES = libwebputils.la libwebputils_la_SOURCES = -libwebputils_la_SOURCES += alpha.c -libwebputils_la_SOURCES += alpha.h libwebputils_la_SOURCES += bit_reader.c libwebputils_la_SOURCES += bit_reader.h libwebputils_la_SOURCES += bit_writer.c @@ -15,10 +13,8 @@ libwebputils_la_SOURCES += filters.h libwebputils_la_SOURCES += huffman.c libwebputils_la_SOURCES += huffman.h libwebputils_la_SOURCES += quant_levels.c +libwebputils_la_SOURCES += quant_levels.h 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.h diff --git a/src/utils/alpha.c b/src/utils/alpha.c deleted file mode 100644 index 0c08ddbf..00000000 --- a/src/utils/alpha.c +++ /dev/null @@ -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 // 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 diff --git a/src/utils/alpha.h b/src/utils/alpha.h deleted file mode 100644 index 50c2508c..00000000 --- a/src/utils/alpha.h +++ /dev/null @@ -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 - -#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_ */ diff --git a/src/utils/quant_levels.c b/src/utils/quant_levels.c index 9470b93d..9bf1f870 100644 --- a/src/utils/quant_levels.c +++ b/src/utils/quant_levels.c @@ -13,7 +13,7 @@ #include #include // for sqrt() -#include "./alpha.h" +#include "./quant_levels.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/utils/quant_levels.h b/src/utils/quant_levels.h new file mode 100644 index 00000000..2523d16e --- /dev/null +++ b/src/utils/quant_levels.h @@ -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 + +#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_ */ diff --git a/src/utils/tcoder.c b/src/utils/tcoder.c deleted file mode 100644 index 035fa1f9..00000000 --- a/src/utils/tcoder.c +++ /dev/null @@ -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 diff --git a/src/utils/tcoder.h b/src/utils/tcoder.h deleted file mode 100644 index b4390a54..00000000 --- a/src/utils/tcoder.h +++ /dev/null @@ -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_ diff --git a/src/utils/tcoderi.h b/src/utils/tcoderi.h deleted file mode 100644 index 61eeb6e7..00000000 --- a/src/utils/tcoderi.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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_