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 <stdlib.h>
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
#include "../webp/decode.h"
|
#include "./vp8li.h"
|
||||||
#include "../utils/filters.h"
|
#include "../utils/filters.h"
|
||||||
#include "../utils/quant_levels.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);
|
ok = (data_size >= decoded_size);
|
||||||
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
||||||
} else {
|
} 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);
|
decoded_data = (uint8_t*)malloc(decoded_size);
|
||||||
if (decoded_data == NULL) {
|
if (decoded_data == NULL) return 0;
|
||||||
free(output);
|
ok = VP8LDecodeAlphaImageStream(width, height,
|
||||||
return 0;
|
data + ALPHA_HEADER_LEN,
|
||||||
}
|
data_size - ALPHA_HEADER_LEN,
|
||||||
for (i = 0; i < decoded_size; ++i) {
|
decoded_data);
|
||||||
decoded_data[i] = (output[i] >> 8) & 0xff;
|
|
||||||
}
|
|
||||||
free(output);
|
|
||||||
ok = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
@ -119,10 +105,10 @@ static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
|||||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||||
ok = DequantizeLevels(decoded_data, width, height);
|
ok = DequantizeLevels(decoded_data, width, height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
if (method != ALPHA_NO_COMPRESSION) {
|
if (method != ALPHA_NO_COMPRESSION) {
|
||||||
|
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).
|
// Note that 'pixel_stride' is in units of 'uint32_t' (and not 'bytes).
|
||||||
// Returns true if the crop window is not empty.
|
// Returns true if the crop window is not empty.
|
||||||
static int SetCropWindow(VP8Io* const io, int y_start, int y_end,
|
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(y_start < y_end);
|
||||||
assert(io->crop_left < io->crop_right);
|
assert(io->crop_left < io->crop_right);
|
||||||
if (y_end > io->crop_bottom) {
|
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);
|
typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
|
||||||
|
|
||||||
// Processes (transforms, scales & color-converts) the rows decoded after the
|
static void ApplyTransforms(VP8LDecoder* const dec, int num_rows,
|
||||||
// last call.
|
const uint32_t* const rows) {
|
||||||
static void ProcessRows(VP8LDecoder* const dec, int row) {
|
|
||||||
int n = dec->next_transform_;
|
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;
|
const int cache_pixs = dec->width_ * num_rows;
|
||||||
uint32_t* rows_data = dec->argb_cache_;
|
uint32_t* rows_data = dec->argb_cache_;
|
||||||
int num_rows_out = num_rows; // Default.
|
const int start_row = dec->last_row_;
|
||||||
|
const int end_row = start_row + num_rows;
|
||||||
if (num_rows <= 0) return; // Nothing to be done.
|
|
||||||
|
|
||||||
// Inverse transforms.
|
// Inverse transforms.
|
||||||
// TODO: most transforms only need to operate on the cropped region only.
|
// 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) {
|
while (n-- > 0) {
|
||||||
VP8LTransform* const transform = &dec->transforms_[n];
|
VP8LTransform* const transform = &dec->transforms_[n];
|
||||||
VP8LInverseTransform(transform, dec->last_row_, row,
|
VP8LInverseTransform(transform, start_row, end_row, rows, rows_data);
|
||||||
dec->argb_ + argb_offset, 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.
|
// Emit output.
|
||||||
{
|
{
|
||||||
|
VP8Io* const io = dec->io_;
|
||||||
|
const uint32_t* rows_data = dec->argb_cache_;
|
||||||
|
if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
|
||||||
|
// 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;
|
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||||
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
||||||
const WEBP_CSP_MODE colorspace = output->colorspace;
|
|
||||||
if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
|
|
||||||
num_rows_out = 0; // Nothing to output.
|
|
||||||
} else {
|
|
||||||
const int in_stride = io->width * sizeof(*rows_data);
|
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,
|
EmitRescaledRows(dec, colorspace, rows_data, in_stride, io->mb_h,
|
||||||
rgba, buf->stride) :
|
rgba, buf->stride) :
|
||||||
EmitRows(colorspace, rows_data, in_stride, io->mb_w, io->mb_h,
|
EmitRows(colorspace, rows_data, in_stride, io->mb_w, io->mb_h,
|
||||||
rgba, buf->stride);
|
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;
|
dec->last_row_ = row;
|
||||||
assert(dec->last_row_ <= io->height);
|
assert(dec->last_row_ <= dec->height_);
|
||||||
dec->last_out_row_ += num_rows_out;
|
|
||||||
assert(dec->last_out_row_ <= output->height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int DecodeImageData(VP8LDecoder* const dec,
|
static int DecodeImageData(VP8LDecoder* const dec,
|
||||||
@ -940,6 +947,63 @@ static int AllocateARGBBuffers(VP8LDecoder* const dec, int final_width) {
|
|||||||
return 1;
|
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) {
|
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
|
size_t data_size, // data available so far
|
||||||
int *width, int *height);
|
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.
|
// Allocates and initialize a new lossless decoder instance.
|
||||||
VP8LDecoder* VP8LNew(void);
|
VP8LDecoder* VP8LNew(void);
|
||||||
|
|
||||||
|
@ -54,16 +54,6 @@ extern "C" {
|
|||||||
#ifdef USE_LOSSLESS_ENCODER
|
#ifdef USE_LOSSLESS_ENCODER
|
||||||
|
|
||||||
#include "../enc/vp8li.h"
|
#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,
|
static int EncodeLossless(const uint8_t* data, int width, int height,
|
||||||
VP8BitWriter* const bw) {
|
VP8BitWriter* const bw) {
|
||||||
@ -71,6 +61,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
int ok = 0;
|
int ok = 0;
|
||||||
WebPConfig config;
|
WebPConfig config;
|
||||||
WebPPicture picture;
|
WebPPicture picture;
|
||||||
|
VP8LBitWriter tmp_bw;
|
||||||
|
|
||||||
WebPPictureInit(&picture);
|
WebPPictureInit(&picture);
|
||||||
picture.width = width;
|
picture.width = width;
|
||||||
@ -78,6 +69,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
picture.use_argb_input = 1;
|
picture.use_argb_input = 1;
|
||||||
if (!WebPPictureAlloc(&picture)) return 0;
|
if (!WebPPictureAlloc(&picture)) return 0;
|
||||||
|
|
||||||
|
// Transfer the alpha values to the green channel.
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
uint32_t* dst = picture.argb;
|
uint32_t* dst = picture.argb;
|
||||||
@ -90,16 +82,21 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
dst += picture.argb_stride;
|
dst += picture.argb_stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
picture.writer = MyWriter;
|
|
||||||
picture.custom_ptr = (void*)bw;
|
|
||||||
|
|
||||||
WebPConfigInit(&config);
|
WebPConfigInit(&config);
|
||||||
config.lossless = 1;
|
config.lossless = 1;
|
||||||
config.method = 2;
|
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);
|
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_;
|
return ok && !bw->error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user