mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
use header-less lossless bitstream for alpha channel
This saves ~26 bytes of headers. * introduce new VP8LDecodeAlphaImageStream() for decoding * use VP8LEncodeStream() for encoding * refactor code a bit still TODO: make the alpha-quality/enc-method user-configurable Change-Id: I23e599bebe335cfb5868e746e076c3358ef12e71
This commit is contained in:
parent
75d7f3b222
commit
39bf5d6497
@ -11,7 +11,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "./vp8i.h"
|
||||
#include "../webp/decode.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels.h"
|
||||
|
||||
@ -79,26 +79,12 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
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);
|
||||
ok = 1;
|
||||
if (decoded_data == NULL) return 0;
|
||||
ok = VP8LDecodeAlphaImageStream(width, height,
|
||||
data + ALPHA_HEADER_LEN,
|
||||
data_size - ALPHA_HEADER_LEN,
|
||||
decoded_data);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
@ -119,9 +105,9 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||
}
|
||||
}
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(decoded_data, width, height);
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(decoded_data, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
|
112
src/dec/vp8l.c
112
src/dec/vp8l.c
@ -455,7 +455,7 @@ static int EmitRows(WEBP_CSP_MODE colorspace,
|
||||
// Note that 'pixel_stride' is in units of 'uint32_t' (and not 'bytes).
|
||||
// Returns true if the crop window is not empty.
|
||||
static int SetCropWindow(VP8Io* const io, int y_start, int y_end,
|
||||
uint32_t** const in_data, int pixel_stride) {
|
||||
const uint32_t** const in_data, int pixel_stride) {
|
||||
assert(y_start < y_end);
|
||||
assert(io->crop_left < io->crop_right);
|
||||
if (y_end > io->crop_bottom) {
|
||||
@ -497,52 +497,59 @@ static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr,
|
||||
|
||||
typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
|
||||
|
||||
// Processes (transforms, scales & color-converts) the rows decoded after the
|
||||
// last call.
|
||||
static void ProcessRows(VP8LDecoder* const dec, int row) {
|
||||
static void ApplyTransforms(VP8LDecoder* const dec, int num_rows,
|
||||
const uint32_t* const rows) {
|
||||
int n = dec->next_transform_;
|
||||
VP8Io* const io = dec->io_;
|
||||
WebPDecParams* const params = (WebPDecParams*)io->opaque;
|
||||
const WebPDecBuffer* const output = params->output;
|
||||
const int argb_offset = dec->width_ * dec->last_row_;
|
||||
const int num_rows = row - dec->last_row_;
|
||||
const int cache_pixs = dec->width_ * num_rows;
|
||||
uint32_t* rows_data = dec->argb_cache_;
|
||||
int num_rows_out = num_rows; // Default.
|
||||
|
||||
if (num_rows <= 0) return; // Nothing to be done.
|
||||
const int start_row = dec->last_row_;
|
||||
const int end_row = start_row + num_rows;
|
||||
|
||||
// Inverse transforms.
|
||||
// TODO: most transforms only need to operate on the cropped region only.
|
||||
memcpy(rows_data, dec->argb_ + argb_offset, cache_pixs * sizeof(*rows_data));
|
||||
memcpy(rows_data, rows, cache_pixs * sizeof(*rows_data));
|
||||
while (n-- > 0) {
|
||||
VP8LTransform* const transform = &dec->transforms_[n];
|
||||
VP8LInverseTransform(transform, dec->last_row_, row,
|
||||
dec->argb_ + argb_offset, rows_data);
|
||||
VP8LInverseTransform(transform, start_row, end_row, rows, rows_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Processes (transforms, scales & color-converts) the rows decoded after the
|
||||
// last call.
|
||||
static void ProcessRows(VP8LDecoder* const dec, int row) {
|
||||
const uint32_t* const rows = dec->argb_ + dec->width_ * dec->last_row_;
|
||||
const int num_rows = row - dec->last_row_;
|
||||
|
||||
if (num_rows <= 0) return; // Nothing to be done.
|
||||
ApplyTransforms(dec, num_rows, rows);
|
||||
|
||||
// Emit output.
|
||||
{
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = output->colorspace;
|
||||
VP8Io* const io = dec->io_;
|
||||
const uint32_t* rows_data = dec->argb_cache_;
|
||||
if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
|
||||
num_rows_out = 0; // Nothing to output.
|
||||
// Nothing to output (this time).
|
||||
} else {
|
||||
WebPDecParams* const params = (WebPDecParams*)io->opaque;
|
||||
const WebPDecBuffer* const output = params->output;
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
||||
const int in_stride = io->width * sizeof(*rows_data);
|
||||
num_rows_out = io->use_scaling ?
|
||||
const WEBP_CSP_MODE colorspace = output->colorspace;
|
||||
const int num_rows_out = io->use_scaling ?
|
||||
EmitRescaledRows(dec, colorspace, rows_data, in_stride, io->mb_h,
|
||||
rgba, buf->stride) :
|
||||
EmitRows(colorspace, rows_data, in_stride, io->mb_w, io->mb_h,
|
||||
rgba, buf->stride);
|
||||
// Update 'last_out_row_'.
|
||||
dec->last_out_row_ += num_rows_out;
|
||||
assert(dec->last_out_row_ <= output->height);
|
||||
}
|
||||
}
|
||||
|
||||
// Update 'last_row' and 'last_out_row'.
|
||||
// Update 'last_row_'.
|
||||
dec->last_row_ = row;
|
||||
assert(dec->last_row_ <= io->height);
|
||||
dec->last_out_row_ += num_rows_out;
|
||||
assert(dec->last_out_row_ <= output->height);
|
||||
assert(dec->last_row_ <= dec->height_);
|
||||
}
|
||||
|
||||
static int DecodeImageData(VP8LDecoder* const dec,
|
||||
@ -940,6 +947,63 @@ static int AllocateARGBBuffers(VP8LDecoder* const dec, int final_width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Special row-processing that only stores the alpha data.
|
||||
|
||||
static void ExtractAlphaRows(VP8LDecoder* const dec, int row) {
|
||||
const int num_rows = row - dec->last_row_;
|
||||
const int cache_pixs = dec->width_ * num_rows;
|
||||
const int argb_offset = dec->width_ * dec->last_row_;
|
||||
const uint32_t* const in = dec->argb_ + argb_offset;
|
||||
|
||||
if (num_rows <= 0) return; // Nothing to be done.
|
||||
ApplyTransforms(dec, num_rows, in);
|
||||
|
||||
// Extract alpha (which is stored in the green plane).
|
||||
{
|
||||
uint8_t* const dst = (uint8_t*)dec->io_->opaque + argb_offset;
|
||||
const uint32_t* const src = dec->argb_cache_;
|
||||
int i;
|
||||
for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff;
|
||||
}
|
||||
|
||||
dec->last_row_ = dec->last_out_row_ = row;
|
||||
}
|
||||
|
||||
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
|
||||
const size_t data_size, uint8_t* const output) {
|
||||
VP8Io io;
|
||||
int ok = 0;
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) return 0;
|
||||
|
||||
dec->width_ = width;
|
||||
dec->height_ = height;
|
||||
dec->io_ = &io;
|
||||
|
||||
VP8InitIo(&io);
|
||||
WebPInitCustomIo(NULL, &io); // Just a sanity Init. io won't be used.
|
||||
io.opaque = output;
|
||||
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
VP8LInitBitReader(&dec->br_, data, data_size);
|
||||
|
||||
dec->action_ = READ_HDR;
|
||||
if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Err;
|
||||
|
||||
// Allocate output (note that dec->width_ may have changed here).
|
||||
if (!AllocateARGBBuffers(dec, width)) goto Err;
|
||||
|
||||
// Decode (with special row processing).
|
||||
dec->action_ = READ_DATA;
|
||||
ok = DecodeImageData(dec, dec->argb_, dec->width_, dec->height_,
|
||||
ExtractAlphaRows);
|
||||
|
||||
Err:
|
||||
VP8LDelete(dec);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
|
||||
|
@ -106,6 +106,13 @@ int VP8LGetInfo(const uint8_t* data,
|
||||
size_t data_size, // data available so far
|
||||
int *width, int *height);
|
||||
|
||||
|
||||
// Decodes a raw image stream (without header) and store the alpha data
|
||||
// into *output, which must be of size width x height. Returns false in case
|
||||
// of error.
|
||||
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
|
||||
const size_t data_size, uint8_t* const output);
|
||||
|
||||
// Allocates and initialize a new lossless decoder instance.
|
||||
VP8LDecoder* VP8LNew(void);
|
||||
|
||||
|
@ -28,8 +28,8 @@ extern "C" {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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)
|
||||
// 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
|
||||
@ -54,16 +54,6 @@ extern "C" {
|
||||
#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) {
|
||||
@ -71,6 +61,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||
int ok = 0;
|
||||
WebPConfig config;
|
||||
WebPPicture picture;
|
||||
VP8LBitWriter tmp_bw;
|
||||
|
||||
WebPPictureInit(&picture);
|
||||
picture.width = width;
|
||||
@ -78,6 +69,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||
picture.use_argb_input = 1;
|
||||
if (!WebPPictureAlloc(&picture)) return 0;
|
||||
|
||||
// Transfer the alpha values to the green channel.
|
||||
{
|
||||
int i, j;
|
||||
uint32_t* dst = picture.argb;
|
||||
@ -90,16 +82,21 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||
dst += picture.argb_stride;
|
||||
}
|
||||
}
|
||||
picture.writer = MyWriter;
|
||||
picture.custom_ptr = (void*)bw;
|
||||
|
||||
WebPConfigInit(&config);
|
||||
config.lossless = 1;
|
||||
config.method = 2;
|
||||
config.quality = 100;
|
||||
config.quality = 100; // TODO(skal): make it adjustable.
|
||||
|
||||
ok = WebPEncode(&config, &picture);
|
||||
VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
|
||||
ok = (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
|
||||
WebPPictureFree(&picture);
|
||||
if (ok) {
|
||||
const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw);
|
||||
const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw);
|
||||
VP8BitWriterAppend(bw, data, data_size);
|
||||
}
|
||||
VP8LBitWriterDestroy(&tmp_bw);
|
||||
return ok && !bw->error_;
|
||||
}
|
||||
|
||||
@ -107,7 +104,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user