mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 05:38:22 +01:00
bitstream update for lossy alpha compression
now, we only use 2 bits for the filtering method, and 2 bits for the compression method. There's two additional bits which are INFORMATIVE, to specify whether the source has been pre-processed (level reduction) during compression. This can be used at decompression time for some post-processing (see DequantizeLevels()). New relevant spec excerpt: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ChunkHeader('ALPH') | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Rsv| P | F | C | Alpha Bitstream... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Compression method (C): 2 bits : The compression method used: * `0`: No compression. * `1`: Backward reference counts encoded with arithmetic encoder. Filtering method (F): 2 bits : The filtering method used: * `0`: None. * `1`: Horizontal filter. * `2`: Vertical filter. * `3`: Gradient filter. Pre-processing (P): 2 bits : These INFORMATIVE bits are used to signal the pre-processing that has been performed during compression. The decoder can use this information to e.g. dither the values or smooth the gradients prior to display. * `0`: no pre-processing * `1`: level reduction Decoders are not required to use this information in any specified way. Reserved (Rsv): 2 bits : SHOULD be `0`. Alpha bitstream: _Chunk Size_ - `1` bytes : Encoded alpha bitstream. This optional chunk contains encoded alpha data for a single tile. Either **ALL or NONE** of the tiles must contain this chunk. The alpha channel data is losslessly stored as raw data (when compression method is '0') or compressed using the lossless format (when the compression method is '1'). Change-Id: Ied8f5fb922707a953e6a2b601c69c73e552dda6b
This commit is contained in:
parent
d73e63a726
commit
638528cd1e
@ -443,10 +443,17 @@ Notes for tiles containing VP8 data:
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ChunkHeader('ALPH') |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| F | C | Reserved | Alpha Bitstream |
|
||||
|Rsv| P | F | C | Alpha Bitstream... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Filtering method (F): 4 bits
|
||||
Compression method (C): 2 bits
|
||||
|
||||
: The compression method used:
|
||||
|
||||
* `0`: No compression.
|
||||
* `1`: Backward reference counts encoded with arithmetic encoder.
|
||||
|
||||
Filtering method (F): 2 bits
|
||||
|
||||
: The filtering method used:
|
||||
|
||||
@ -455,28 +462,31 @@ Filtering method (F): 4 bits
|
||||
* `2`: Vertical filter.
|
||||
* `3`: Gradient filter.
|
||||
|
||||
Compression method (C): 4 bits
|
||||
Pre-processing (P): 2 bits
|
||||
|
||||
: The compression method used:
|
||||
: These INFORMATIVE bits are used to signal the pre-processing that has
|
||||
been performed during compression. The decoder can use this information to
|
||||
e.g. dither the values or smooth the gradients prior to display.
|
||||
|
||||
* `0`: No compression.
|
||||
* `1`: Backward reference counts encoded with arithmetic encoder.
|
||||
* `0`: no pre-processing
|
||||
* `1`: level reduction
|
||||
|
||||
Reserved: 8 bits
|
||||
Decoders are not required to use this information in any specified way.
|
||||
|
||||
Reserved (Rsv): 2 bits
|
||||
|
||||
: SHOULD be `0`.
|
||||
|
||||
Alpha bitstream: _Chunk Size_ - `2` bytes
|
||||
Alpha bitstream: _Chunk Size_ - `1` bytes
|
||||
|
||||
: Encoded alpha bitstream.
|
||||
|
||||
This optional chunk contains encoded alpha data for a single tile.
|
||||
Either **ALL or NONE** of the tiles must contain this chunk.
|
||||
|
||||
The alpha channel can be encoded either losslessly or with lossy
|
||||
preprocessing (quantization). After the optional preprocessing, the
|
||||
alpha values are encoded with a lossless compression method like
|
||||
zlib.
|
||||
The alpha channel data is losslessly stored as raw data (when
|
||||
compression method is '0') or compressed using the lossless format
|
||||
(when the compression method is '1').
|
||||
|
||||
#### Color profile
|
||||
|
||||
|
@ -13,15 +13,17 @@
|
||||
#include "./vp8i.h"
|
||||
#include "../webp/decode.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels.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_HEADER_LEN 1
|
||||
#define ALPHA_NO_COMPRESSION 0
|
||||
#define ALPHA_LOSSLESS_COMPRESSION 1
|
||||
#define ALPHA_PREPROCESSED_LEVELS 1
|
||||
|
||||
// TODO(skal): move to dsp/ ?
|
||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||
@ -49,6 +51,8 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
const size_t decoded_size = height * width;
|
||||
uint8_t* unfiltered_data = NULL;
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
int rsrv;
|
||||
int ok = 0;
|
||||
int method;
|
||||
|
||||
@ -59,12 +63,15 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
method = data[0] & 0x0f;
|
||||
filter = data[0] >> 4;
|
||||
ok = (data[1] == 0);
|
||||
method = (data[0] >> 0) & 0x03;
|
||||
filter = (data[0] >> 2) & 0x03;
|
||||
pre_processing = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (method < ALPHA_NO_COMPRESSION ||
|
||||
method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
filter >= WEBP_FILTER_LAST || !ok) {
|
||||
filter >= WEBP_FILTER_LAST ||
|
||||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -91,15 +98,16 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
decoded_data[i] = (output[i] >> 8) & 0xff;
|
||||
}
|
||||
free(output);
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
|
||||
if (unfilter_func) {
|
||||
if (unfilter_func != NULL) {
|
||||
unfiltered_data = (uint8_t*)malloc(decoded_size);
|
||||
if (unfiltered_data == NULL) {
|
||||
if (method != ALPHA_NO_COMPRESSION) free(decoded_data);
|
||||
return 0;
|
||||
ok = 0;
|
||||
goto Error;
|
||||
}
|
||||
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
|
||||
// and apply filter per image-row.
|
||||
@ -112,6 +120,11 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||
}
|
||||
}
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(decoded_data, width, height);
|
||||
}
|
||||
|
||||
Error:
|
||||
if (method != ALPHA_NO_COMPRESSION) {
|
||||
free(decoded_data);
|
||||
}
|
||||
|
@ -20,9 +20,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ALPHA_HEADER_LEN 2
|
||||
// TODO(skal): find a common place between enc/ and dec/ for these:
|
||||
#define ALPHA_HEADER_LEN 1
|
||||
#define ALPHA_NO_COMPRESSION 0
|
||||
#define ALPHA_LOSSLESS_COMPRESSION 1
|
||||
#define ALPHA_PREPROCESSED_LEVELS 1
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||
@ -105,25 +107,33 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
||||
int method, int filter, size_t data_size,
|
||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
||||
int method, int filter, int reduce_levels,
|
||||
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
||||
int ok = 0;
|
||||
const uint8_t* alpha_src;
|
||||
WebPFilterFunc filter_func;
|
||||
uint8_t header[ALPHA_HEADER_LEN];
|
||||
uint8_t header;
|
||||
size_t expected_size;
|
||||
const size_t data_size = width * height;
|
||||
|
||||
#ifndef USE_LOSSLESS_ENCODER
|
||||
method = ALPHA_NO_COMPRESSION;
|
||||
#endif
|
||||
assert(filter >= 0 && filter < WEBP_FILTER_LAST);
|
||||
assert(method >= ALPHA_NO_COMPRESSION);
|
||||
assert(method <= ALPHA_LOSSLESS_COMPRESSION);
|
||||
assert(sizeof(header) == ALPHA_HEADER_LEN);
|
||||
// TODO(skal): have a common function and #define's to validate alpha params.
|
||||
|
||||
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
|
||||
header = method | (filter << 2);
|
||||
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
|
||||
|
||||
VP8BitWriterInit(bw, expected_size);
|
||||
VP8BitWriterAppend(bw, header, sizeof(header));
|
||||
VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
|
||||
|
||||
filter_func = WebPFilters[filter];
|
||||
if (filter_func) {
|
||||
@ -163,8 +173,9 @@ 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;
|
||||
const size_t data_size = width * height;
|
||||
int ok = 1;
|
||||
const int reduce_levels = (quality < 100);
|
||||
|
||||
// quick sanity checks
|
||||
assert(data != NULL && output != NULL && output_size != NULL);
|
||||
@ -188,7 +199,7 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||
// 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'.
|
||||
if (reduce_levels) { // 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].
|
||||
@ -204,8 +215,9 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||
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);
|
||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||
method, WEBP_FILTER_NONE, reduce_levels,
|
||||
NULL, &bw);
|
||||
if (!ok) {
|
||||
VP8BitWriterWipeOut(&bw);
|
||||
goto End;
|
||||
@ -235,8 +247,9 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
||||
continue;
|
||||
}
|
||||
|
||||
ok = EncodeAlphaInternal(quant_alpha, width, height, method, test_filter,
|
||||
data_size, filtered_alpha, &tmp_bw);
|
||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||
method, test_filter, reduce_levels,
|
||||
filtered_alpha, &tmp_bw);
|
||||
if (ok) {
|
||||
const size_t score = VP8BitWriterSize(&tmp_bw);
|
||||
if (score < best_score) {
|
||||
|
Loading…
Reference in New Issue
Block a user