diff --git a/src/dec/alpha.c b/src/dec/alpha.c index 03a30083..e2bfeee6 100644 --- a/src/dec/alpha.c +++ b/src/dec/alpha.c @@ -11,7 +11,7 @@ #include #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: diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index c29c3f1d..f3d80bd2 100644 --- a/src/dec/vp8l.c +++ b/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) { diff --git a/src/dec/vp8li.h b/src/dec/vp8li.h index f6738914..79233eb3 100644 --- a/src/dec/vp8li.h +++ b/src/dec/vp8li.h @@ -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); diff --git a/src/enc/alpha.c b/src/enc/alpha.c index a9d3ca7f..3911f9f2 100644 --- a/src/enc/alpha.c +++ b/src/enc/alpha.c @@ -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;