diff --git a/Android.mk b/Android.mk index 9c0b7308..c087446e 100644 --- a/Android.mk +++ b/Android.mk @@ -2,15 +2,18 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ + src/dec/alpha.c \ src/dec/bits.c \ src/dec/dsp.c \ src/dec/frame.c \ src/dec/idec.c \ + src/dec/layer.c \ src/dec/quant.c \ src/dec/tree.c \ src/dec/vp8.c \ src/dec/webp.c \ src/dec/yuv.c \ + src/enc/alpha.c \ src/enc/analysis.c \ src/enc/bit_writer.c \ src/enc/config.c \ @@ -18,6 +21,7 @@ LOCAL_SRC_FILES := \ src/enc/filter.c \ src/enc/frame.c \ src/enc/iterator.c \ + src/enc/layer.c \ src/enc/picture.c \ src/enc/quant.c \ src/enc/syntax.c \ diff --git a/Makefile.vc b/Makefile.vc index 95d77d92..502cad60 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -121,6 +121,8 @@ X_OBJS= \ $(DIROBJ)\dec\webp.obj \ $(DIROBJ)\dec\yuv.obj \ $(DIROBJ)\dec\idec.obj \ + $(DIROBJ)\dec\alpha.obj \ + $(DIROBJ)\dec\layer.obj \ $(DIROBJ)\enc\analysis.obj \ $(DIROBJ)\enc\bit_writer.obj \ $(DIROBJ)\enc\config.obj \ @@ -134,6 +136,8 @@ X_OBJS= \ $(DIROBJ)\enc\syntax.obj \ $(DIROBJ)\enc\tree.obj \ $(DIROBJ)\enc\webpenc.obj \ + $(DIROBJ)\enc\alpha.obj \ + $(DIROBJ)\enc\layer.obj \ $(RESOURCE) EXAMPLES_OBJS = \ diff --git a/examples/cwebp.c b/examples/cwebp.c index 9ad5e7b0..e564fa3a 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -334,6 +334,11 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) { png_set_strip_alpha(png); has_alpha = 0; } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (has_alpha) { + pic->colorspace |= WEBP_CSP_ALPHA_BIT; + } +#endif num_passes = png_set_interlace_handling(png); png_read_update_info(png, info); @@ -486,6 +491,10 @@ static void PrintExtraInfo(const WebPPicture* const pic, int short_output) { fprintf(stderr, " transparency: %6d\n", stats->alpha_data_size); } + if (stats->layer_data_size) { + fprintf(stderr, " enhancement: %6d\n", + stats->layer_data_size); + } fprintf(stderr, " Residuals bytes " "|segment 1|segment 2|segment 3" "|segment 4| total\n"); @@ -605,8 +614,13 @@ static void HelpLong(void) { printf(" -noalpha ............... discard any transparency information.\n"); printf(" -pass ............ analysis pass number (1..10)\n"); printf(" -crop .. crop picture with the given rectangle\n"); + printf(" -resize ........ resize picture (after any cropping)\n"); +#ifdef WEBP_EXPERIMENTAL_FEATURES + printf(" -444 / -422 / -gray ..... Change colorspace\n"); +#endif printf(" -map ............. print map of extra info.\n"); printf(" -d .......... dump the compressed output (PGM file).\n"); + printf("\n"); printf(" -short ................. condense printed message\n"); printf(" -quiet ................. don't print anything.\n"); @@ -633,13 +647,16 @@ int main(int argc, const char *argv[]) { int quiet = 0; int keep_alpha = 0; int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0; + int resize_w = 0, resize_h = 0; WebPPicture picture; WebPConfig config; WebPAuxStats stats; Stopwatch stop_watch; + #ifdef WEBP_EXPERIMENTAL_FEATURES keep_alpha = 1; #endif + if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) { fprintf(stderr, "Error! Version mismatch!\n"); goto Error; @@ -697,12 +714,23 @@ int main(int argc, const char *argv[]) { keep_alpha = 0; } else if (!strcmp(argv[c], "-map") && c < argc - 1) { picture.extra_info_type = strtol(argv[++c], NULL, 0); +#ifdef WEBP_EXPERIMENTAL_FEATURES + } else if (!strcmp(argv[c], "-444")) { + picture.colorspace = WEBP_YUV444; + } else if (!strcmp(argv[c], "-422")) { + picture.colorspace = WEBP_YUV422; + } else if (!strcmp(argv[c], "-gray")) { + picture.colorspace = WEBP_YUV400; +#endif } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { crop = 1; crop_x = strtol(argv[++c], NULL, 0); crop_y = strtol(argv[++c], NULL, 0); crop_w = strtol(argv[++c], NULL, 0); crop_h = strtol(argv[++c], NULL, 0); + } else if (!strcmp(argv[c], "-resize") && c < argc - 2) { + resize_w = strtol(argv[++c], NULL, 0); + resize_h = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-noasm")) { VP8GetCPUInfo = NULL; } else if (!strcmp(argv[c], "-version")) { @@ -792,7 +820,15 @@ int main(int argc, const char *argv[]) { fprintf(stderr, "Error! Cannot crop picture\n"); goto Error; } - if (picture.extra_info_type > 0) AllocExtraInfo(&picture); + if ((resize_w | resize_h) > 0) { + if (!WebPPictureRescale(&picture, resize_w, resize_h)) { + fprintf(stderr, "Error! Cannot resize picture\n"); + goto Error; + } + } + if (picture.extra_info_type > 0) { + AllocExtraInfo(&picture); + } if (!WebPEncode(&config, &picture)) { fprintf(stderr, "Error! Cannot encode picture as WebP\n"); goto Error; diff --git a/makefile.unix b/makefile.unix index 3f50b076..ffc382fc 100644 --- a/makefile.unix +++ b/makefile.unix @@ -33,6 +33,9 @@ endif # 'make -f makefile.unix EXTRA_FLAGS=-m32' to that effect. # EXTRA_FLAGS += -m32 +# Extra flags to enable experimental features and code +# EXTRA_FLAGS += -DUSE_EXPERIMENTAL_FEATURES + # Extra flags to emulate C89 strictness with the full ANSI EXTRA_FLAGS += -Wextra -Wold-style-definition EXTRA_FLAGS += -Wmissing-prototypes @@ -50,9 +53,10 @@ OBJS = src/enc/webpenc.o src/enc/bit_writer.o src/enc/syntax.o \ src/enc/tree.o src/enc/config.o src/enc/frame.o \ src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \ src/enc/cost.o src/enc/picture.o src/enc/filter.o \ + src/enc/layer.o \ src/dec/bits.o src/dec/dsp.o src/dec/frame.o src/dec/webp.o \ src/dec/quant.o src/dec/tree.o src/dec/vp8.o src/dec/yuv.o \ - src/dec/idec.o src/dec/alpha.o + src/dec/idec.o src/dec/alpha.o src/dec/layer.o HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/bit_writer.h \ src/enc/cost.h src/dec/bits.h src/dec/vp8i.h src/dec/yuv.h OUTPUT = examples/cwebp examples/dwebp src/libwebp.a diff --git a/src/dec/Makefile.am b/src/dec/Makefile.am index 6e17d693..73270986 100644 --- a/src/dec/Makefile.am +++ b/src/dec/Makefile.am @@ -1,7 +1,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ - quant.c tree.c vp8.c webp.c yuv.c idec.c alpha.c + quant.c tree.c vp8.c webp.c yuv.c idec.c alpha.c \ + layer.c libwebpdecode_la_LDFLAGS = -version-info 0:0:0 libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE) libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h diff --git a/src/dec/bits.h b/src/dec/bits.h index 82e4c3ae..e0572e4b 100644 --- a/src/dec/bits.h +++ b/src/dec/bits.h @@ -13,7 +13,7 @@ #define WEBP_DEC_BITS_H_ #include -#include "webp/decode_vp8.h" +#include "../webp/decode_vp8.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/dec/frame.c b/src/dec/frame.c index 3841e05c..37fd8353 100644 --- a/src/dec/frame.c +++ b/src/dec/frame.c @@ -10,7 +10,7 @@ // Author: Skal (pascal.massimino@gmail.com) #include -#include "vp8i.h" +#include "./vp8i.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/dec/layer.c b/src/dec/layer.c new file mode 100644 index 00000000..357ad21d --- /dev/null +++ b/src/dec/layer.c @@ -0,0 +1,34 @@ +// Copyright 2011 Google Inc. +// +// 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/ +// ----------------------------------------------------------------------------- +// +// Enhancement layer (for YUV444/422) +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include "vp8i.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +//----------------------------------------------------------------------------- + +int VP8DecodeLayer(VP8Decoder* const dec) { + assert(dec); + assert(dec->layer_data_size_ > 0); + (void)dec; + + // TODO: handle enhancement layer here. + + return 1; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/dec/vp8.c b/src/dec/vp8.c index 1432a869..24bf8683 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -375,26 +375,30 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { #ifdef WEBP_EXPERIMENTAL_FEATURES // Extensions if (dec->pic_hdr_.colorspace_) { - const uint32_t EXT_SIZE = 4; - uint32_t ext_size; - uint8_t ext_bits; - const uint8_t* ext_bytes_end = buf - EXT_SIZE; - if (frm_hdr->partition_length_ <= EXT_SIZE) { + const size_t kTrailerSize = 8; + const uint8_t kTrailerMarker = 0x01; + uint8_t* const ext_buf = buf - kTrailerSize; + size_t size; + + if (frm_hdr->partition_length_ < kTrailerSize || + ext_buf[kTrailerSize - 1] != kTrailerMarker) { + Error: return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "RIFF: Inconsistent extra information."); } - ext_size = (ext_bytes_end[0] << 16) | (ext_bytes_end[1] << 8) - | (ext_bytes_end[2]); - ext_bits = ext_bytes_end[3]; - ext_bytes_end -= ext_size; - if (!(ext_bits & 0x01) || (ext_size + EXT_SIZE > frm_hdr->partition_length_)) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "RIFF: Inconsistent extra information."); - } - if (!!(ext_bits & 0x02)) { // has alpha data - dec->alpha_data_size_ = ext_size; - dec->alpha_data_ = ext_bytes_end; + // Alpha + size = (ext_buf[4] << 0) | (ext_buf[5] << 8) | (ext_buf[6] << 16); + if (frm_hdr->partition_length_ < size + kTrailerSize) { + goto Error; } + dec->alpha_data_ = (size > 0) ? ext_buf - size : NULL; + dec->alpha_data_size_ = size; + + // Layer + size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16); + dec->layer_data_size_ = size; + dec->layer_data_ = NULL; // will be set later + dec->layer_colorspace_ = ext_buf[3]; } #endif @@ -651,6 +655,14 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { } #endif +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dec->layer_data_size_ > 0) { + if (!VP8DecodeLayer(dec)) { + return 0; + } + } +#endif + return 1; } diff --git a/src/dec/vp8i.h b/src/dec/vp8i.h index 91da59a6..abd0d05a 100644 --- a/src/dec/vp8i.h +++ b/src/dec/vp8i.h @@ -244,13 +244,17 @@ struct VP8Decoder { uint32_t non_zero_ac_; // Filtering side-info - int filter_type_; // 0=off, 1=simple, 2=complex + int filter_type_; // 0=off, 1=simple, 2=complex uint8_t filter_levels_[NUM_MB_SEGMENTS]; // precalculated per-segment // extensions const uint8_t* alpha_data_; // compressed alpha data (if present) size_t alpha_data_size_; uint8_t* alpha_plane_; // output + + int layer_colorspace_; + const uint8_t* layer_data_; // compressed layer data (if present) + size_t layer_data_size_; }; //----------------------------------------------------------------------------- @@ -283,6 +287,9 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, int row, int num_rows); +// in layer.c +int VP8DecodeLayer(VP8Decoder* const dec); + // in dsp.c typedef void (*VP8Idct)(const int16_t* coeffs, uint8_t* dst); extern VP8Idct VP8Transform; diff --git a/src/dec/webpi.h b/src/dec/webpi.h index cf5bc0ef..8abe312f 100644 --- a/src/dec/webpi.h +++ b/src/dec/webpi.h @@ -16,7 +16,7 @@ extern "C" { #endif -#include "webp/decode_vp8.h" +#include "../webp/decode_vp8.h" // Decoding output parameters. typedef struct { diff --git a/src/dec/yuv.h b/src/dec/yuv.h index 1af0f089..1398f2e0 100644 --- a/src/dec/yuv.h +++ b/src/dec/yuv.h @@ -12,7 +12,7 @@ #ifndef WEBP_DEC_YUV_H_ #define WEBP_DEC_YUV_H_ -#include "webp/decode_vp8.h" +#include "../webp/decode_vp8.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/enc/Makefile.am b/src/enc/Makefile.am index 3718b963..42aeffd5 100644 --- a/src/enc/Makefile.am +++ b/src/enc/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src libwebpencode_la_SOURCES = analysis.c bit_writer.c bit_writer.h \ config.c cost.c cost.h dsp.c dsp_sse2.c filter.c \ frame.c iterator.c picture.c quant.c \ - syntax.c tree.c vp8enci.h webpenc.c alpha.c + syntax.c tree.c vp8enci.h webpenc.c alpha.c layer.c libwebpencode_la_LDFLAGS = -version-info 0:0:0 -lm libwebpencode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE) libwebpencodeinclude_HEADERS = ../webp/encode.h ../webp/types.h diff --git a/src/enc/alpha.c b/src/enc/alpha.c index a3f269d2..cc1d5c59 100644 --- a/src/enc/alpha.c +++ b/src/enc/alpha.c @@ -9,6 +9,7 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include #include "vp8enci.h" @@ -72,19 +73,29 @@ static int CompressAlpha(const uint8_t* data, size_t data_size, #endif /* WEBP_EXPERIMENTAL_FEATURES */ -int VP8EncProcessAlpha(VP8Encoder* enc) { +void VP8EncInitAlpha(VP8Encoder* enc) { + enc->has_alpha_ = (enc->pic_->a != NULL); enc->alpha_data_ = NULL; enc->alpha_data_size_ = 0; +} + +void VP8EncCodeAlphaBlock(VP8EncIterator* it) { + (void)it; + // Nothing for now. We just ZLIB-compress in the end. +} + +int VP8EncFinishAlpha(VP8Encoder* enc) { + if (enc->has_alpha_) { #ifdef WEBP_EXPERIMENTAL_FEATURES - if (enc->pic_->a) { const WebPPicture* pic = enc->pic_; + assert(pic->a); if (!CompressAlpha(pic->a, pic->width * pic->height, &enc->alpha_data_, &enc->alpha_data_size_, enc->config_->alpha_compression)) { return 0; } +#endif } -#endif /* WEBP_EXPERIMENTAL_FEATURES */ return 1; } @@ -92,6 +103,7 @@ void VP8EncDeleteAlpha(VP8Encoder* enc) { free(enc->alpha_data_); enc->alpha_data_ = NULL; enc->alpha_data_size_ = 0; + enc->has_alpha_ = 0; } #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/enc/config.c b/src/enc/config.c index 0a1ccbbe..fc7c75dc 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -10,7 +10,7 @@ // Author: Skal (pascal.massimino@gmail.com) #include -#include "webp/encode.h" +#include "../webp/encode.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/enc/frame.c b/src/enc/frame.c index 9864c1d9..132077b3 100644 --- a/src/enc/frame.c +++ b/src/enc/frame.c @@ -568,6 +568,14 @@ int VP8EncLoop(VP8Encoder* const enc) { } else { // reset predictors after a skip ResetAfterSkip(&it); } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (enc->has_alpha_) { + VP8EncCodeAlphaBlock(&it); + } + if (enc->use_layer_) { + VP8EncCodeLayerBlock(&it); + } +#endif StoreSideInfo(&it); VP8StoreFilterStats(&it); VP8IteratorExport(&it); diff --git a/src/enc/iterator.c b/src/enc/iterator.c index 991644d0..772f97f2 100644 --- a/src/enc/iterator.c +++ b/src/enc/iterator.c @@ -148,11 +148,13 @@ void VP8IteratorExport(const VP8EncIterator* const it) { memcpy(ydst + i * pic->y_stride, ysrc + i * BPS, w); } // U/V plane - w = (w + 1) / 2; - h = (h + 1) / 2; - for (i = 0; i < h; ++i) { - memcpy(udst + i * pic->uv_stride, usrc + i * BPS, w); - memcpy(vdst + i * pic->uv_stride, vsrc + i * BPS, w); + { + const int uv_w = (w + 1) / 2; + const int uv_h = (h + 1) / 2; + for (i = 0; i < uv_h; ++i) { + memcpy(udst + i * pic->uv_stride, usrc + i * BPS, uv_w); + memcpy(vdst + i * pic->uv_stride, vsrc + i * BPS, uv_w); + } } } } diff --git a/src/enc/layer.c b/src/enc/layer.c new file mode 100644 index 00000000..ec4dc87c --- /dev/null +++ b/src/enc/layer.c @@ -0,0 +1,55 @@ +// Copyright 2011 Google Inc. +// +// 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/ +// ----------------------------------------------------------------------------- +// +// Enhancement layer (for YUV444/422) +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include "vp8enci.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +#endif /* WEBP_EXPERIMENTAL_FEATURES */ + +//----------------------------------------------------------------------------- + +void VP8EncInitLayer(VP8Encoder* const enc) { + enc->use_layer_ = (enc->pic_->u0 != NULL); + enc->layer_data_size_ = 0; + enc->layer_data_ = NULL; + if (enc->use_layer_) { + VP8BitWriterInit(&enc->layer_bw_, enc->mb_w_ * enc->mb_h_ * 3); + } +} + +void VP8EncCodeLayerBlock(VP8EncIterator* it) { + (void)it; // remove a warning +#ifdef WEBP_EXPERIMENTAL_FEATURES +#endif /* WEBP_EXPERIMENTAL_FEATURES */ +} + +int VP8EncFinishLayer(VP8Encoder* const enc) { + if (enc->use_layer_) { + enc->layer_data_ = VP8BitWriterFinish(&enc->layer_bw_); + enc->layer_data_size_ = VP8BitWriterSize(&enc->layer_bw_); + } + return 1; +} + +void VP8EncDeleteLayer(VP8Encoder* enc) { + free(enc->layer_data_); +} + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/enc/picture.c b/src/enc/picture.c index 1d454be6..3ed6cc0a 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -23,77 +23,115 @@ extern "C" { int WebPPictureAlloc(WebPPicture* const picture) { if (picture) { + const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; + const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; const int width = picture->width; const int height = picture->height; - const int uv_width = (width + 1) / 2; - const int uv_height = (height + 1) / 2; - const uint64_t y_size = (uint64_t)width * height; - const uint64_t uv_size = (uint64_t)uv_width * uv_height; - const uint64_t total_size = y_size + 2 * uv_size; + const int y_stride = width; + const int uv_width = (width + 1) / 2, uv_height = (height + 1) / 2; + const int uv_stride = uv_width; + int uv0_stride = 0; + int a_width, a_stride; + uint64_t y_size, uv_size, uv0_size, a_size, total_size; + uint8_t* mem; + + // U/V + switch (uv_csp) { + case WEBP_YUV420: + break; +#ifdef WEBP_EXPERIMENTAL_FEATURES + case WEBP_YUV400: // for now, we'll just reset the U/V samples + break; + case WEBP_YUV422: + uv0_stride = uv_width; + break; + case WEBP_YUV444: + uv0_stride = width; + break; +#endif + default: + return 0; + } + uv0_size = height * uv0_stride; + + // alpha + a_width = has_alpha ? width : 0; + a_stride = a_width; + y_size = (uint64_t)y_stride * height; + uv_size = (uint64_t)uv_stride * uv_height; + a_size = (uint64_t)a_stride * height; + + total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; + // Security and validation checks - if (uv_width <= 0 || uv_height <= 0 || // check param error + if (width <= 0 || height <= 0 || // check for luma/alpha param error + uv_width < 0 || uv_height < 0 || // check for u/v param error y_size >= (1ULL << 40) || // check for reasonable global size (size_t)total_size != total_size) { // check for overflow on 32bit return 0; } - picture->y_stride = width; - picture->uv_stride = uv_width; + picture->y_stride = y_stride; + picture->uv_stride = uv_stride; + picture->a_stride = a_stride; + picture->uv0_stride = uv0_stride; WebPPictureFree(picture); // erase previous buffer - picture->y = (uint8_t*)malloc((size_t)total_size); - if (picture->y == NULL) return 0; - picture->u = picture->y + y_size; - picture->v = picture->u + uv_size; - } - return 1; -} + mem = (uint8_t*)malloc((size_t)total_size); + if (mem == NULL) return 0; -int WebPPictureAddAlphaPlane(WebPPicture* const picture) { - if (picture) { - const int width = picture->width; - const int height = picture->height; - const int a_stride = width; - const uint64_t a_size = (uint64_t)a_stride * height; - // Security and validation checks - if (width <= 0 || height <= 0 || // check param error - a_size >= (1ULL << 40) || // check for reasonable global size - (size_t)a_size != a_size) { // check for overflow on 32bit - return 0; + picture->y = mem; + mem += y_size; + + picture->u = mem; + mem += uv_size; + picture->v = mem; + mem += uv_size; + + if (a_size) { + picture->a = mem; + mem += a_size; + } + if (uv0_size) { + picture->u0 = mem; + mem += uv0_size; + picture->v0 = mem; + mem += uv0_size; } - free(picture->a); // erase previous buffer - picture->a = (uint8_t*)malloc((size_t)a_size); - picture->a_stride = a_stride; - return (picture->a != NULL); } return 1; } +// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them +// into 'dst'. Mark 'dst' as not owning any memory. 'src' can be NULL. +static void WebPPictureGrabSpecs(const WebPPicture* const src, + WebPPicture* const dst) { + if (src) *dst = *src; + dst->y = dst->u = dst->v = NULL; + dst->u0 = dst->v0 = NULL; + dst->a = NULL; +} + +// Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* const picture) { if (picture) { free(picture->y); - picture->y = picture->u = picture->v = NULL; - free(picture->a); - picture->a = NULL; + WebPPictureGrabSpecs(NULL, picture); } } //----------------------------------------------------------------------------- +// Picture copying int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { int y; if (src == NULL || dst == NULL) return 0; if (src == dst) return 1; - *dst = *src; - dst->y = NULL; - dst->a = NULL; + + WebPPictureGrabSpecs(src, dst); if (!WebPPictureAlloc(dst)) return 0; - if (src->a != NULL && !WebPPictureAddAlphaPlane(dst)) return 0; + for (y = 0; y < dst->height; ++y) { memcpy(dst->y + y * dst->y_stride, src->y + y * src->y_stride, src->width); - if (dst->a != NULL) { - memcpy(dst->a + y * dst->a_stride, - src->a + y * src->a_stride, src->width); - } } for (y = 0; y < (dst->height + 1) / 2; ++y) { memcpy(dst->u + y * dst->uv_stride, @@ -101,9 +139,32 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { memcpy(dst->v + y * dst->uv_stride, src->v + y * src->uv_stride, (src->width + 1) / 2); } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dst->a != NULL) { + for (y = 0; y < dst->height; ++y) { + memcpy(dst->a + y * dst->a_stride, + src->a + y * src->a_stride, src->width); + } + } + if (dst->u0 != NULL) { + int uv0_width = src->width; + if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { + uv0_width = (uv0_width + 1) / 2; + } + for (y = 0; y < dst->height; ++y) { + memcpy(dst->u0 + y * dst->uv0_stride, + src->u0 + y * src->uv0_stride, uv0_width); + memcpy(dst->v0 + y * dst->uv0_stride, + src->v0 + y * src->uv0_stride, uv0_width); + } + } +#endif return 1; } +//----------------------------------------------------------------------------- +// Picture cropping + int WebPPictureCrop(WebPPicture* const pic, int left, int top, int width, int height) { WebPPicture tmp; @@ -114,32 +175,202 @@ int WebPPictureCrop(WebPPicture* const pic, if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0; if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0; - tmp = *pic; - tmp.y = NULL; - tmp.a = NULL; + WebPPictureGrabSpecs(pic, &tmp); tmp.width = width; tmp.height = height; if (!WebPPictureAlloc(&tmp)) return 0; - if (pic->a != NULL && !WebPPictureAddAlphaPlane(&tmp)) return 0; for (y = 0; y < height; ++y) { memcpy(tmp.y + y * tmp.y_stride, pic->y + (top + y) * pic->y_stride + left, width); - if (tmp.a) { - memcpy(tmp.a + y * tmp.a_stride, - pic->a + (top + y) * pic->a_stride + left, width); - } } for (y = 0; y < (height + 1) / 2; ++y) { const int offset = (y + top / 2) * pic->uv_stride + left / 2; memcpy(tmp.u + y * tmp.uv_stride, pic->u + offset, (width + 1) / 2); memcpy(tmp.v + y * tmp.uv_stride, pic->v + offset, (width + 1) / 2); } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (tmp.a) { + for (y = 0; y < height; ++y) { + memcpy(tmp.a + y * tmp.a_stride, + pic->a + (top + y) * pic->a_stride + left, width); + } + } + if (tmp.u0) { + int w = width; + int l = left; + if (tmp.colorspace == WEBP_YUV422) { + w = (w + 1) / 2; + l = (l + 1) / 2; + } + for (y = 0; y < height; ++y) { + memcpy(tmp.u0 + y * tmp.uv0_stride, + pic->u0 + (top + y) * pic->uv0_stride + l, w); + memcpy(tmp.v0 + y * tmp.uv0_stride, + pic->v0 + (top + y) * pic->uv0_stride + l, w); + } + } +#endif + WebPPictureFree(pic); *pic = tmp; return 1; } +//----------------------------------------------------------------------------- +// Simple picture rescaler + +#define RFIX 20 +#define MULT(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX) +static inline void ImportRow(const uint8_t* src, int src_width, + int32_t* frow, int32_t* irow, int dst_width) { + const int x_expand = (src_width < dst_width); + const int fx_scale = (1 << RFIX) / dst_width; + int x_in = 0; + int x_out; + int x_accum = 0; + if (!x_expand) { + int sum = 0; + for (x_out = 0; x_out < dst_width; ++x_out) { + x_accum += src_width - dst_width; + for (; x_accum > 0; x_accum -= dst_width) { + sum += src[x_in++]; + } + { // Emit next horizontal pixel. + const int32_t base = src[x_in++]; + const int32_t frac = base * (-x_accum); + frow[x_out] = (sum + base) * dst_width - frac; + sum = MULT(frac, fx_scale); // fresh fractional start for next pixel + } + } + } else { // simple bilinear interpolation + int left = src[0], right = src[0]; + for (x_out = 0; x_out < dst_width; ++x_out) { + if (x_accum < 0) { + left = right; + right = src[++x_in]; + x_accum += dst_width - 1; + } + frow[x_out] = right * (dst_width - 1) + (left - right) * x_accum; + x_accum -= src_width - 1; + } + } + // Accumulate the new row's contribution + for (x_out = 0; x_out < dst_width; ++x_out) { + irow[x_out] += frow[x_out]; + } +} + +static void ExportRow(int32_t* frow, int32_t* irow, uint8_t* dst, int dst_width, + const int yscale, const int fxy_scale) { + int x_out; + for (x_out = 0; x_out < dst_width; ++x_out) { + const int frac = MULT(frow[x_out], yscale); + const int v = MULT(irow[x_out] - frac, fxy_scale); + dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; + irow[x_out] = frac; // new fractional start + } +} + +static void RescalePlane(const uint8_t* src, + int src_width, int src_height, int src_stride, + uint8_t* dst, + int dst_width, int dst_height, int dst_stride, + int32_t* const work) { + const int x_expand = (src_width < dst_width); + const int fy_scale = (1 << RFIX) / dst_height; + const int fxy_scale = x_expand ? + ((int64_t)dst_height << RFIX) / (dst_width * src_height) : + ((int64_t)dst_height << RFIX) / (src_width * src_height); + int y_accum = src_height; + int y; + int32_t* irow = work; // integral contribution + int32_t* frow = work + dst_width; // fractional contribution + + memset(work, 0, 2 * dst_width * sizeof(*work)); + for (y = 0; y < src_height; ++y) { + // import new contribution of one source row. + ImportRow(src, src_width, frow, irow, dst_width); + src += src_stride; + // emit output row(s) + y_accum -= dst_height; + for (; y_accum <= 0; y_accum += src_height) { + const int yscale = fy_scale * (-y_accum); + ExportRow(frow, irow, dst, dst_width, yscale, fxy_scale); + dst += dst_stride; + } + } +} +#undef MULT +#undef RFIX + +int WebPPictureRescale(WebPPicture* const pic, int width, int height) { + WebPPicture tmp; + int prev_width, prev_height; + int32_t* work; + + if (pic == NULL) return 0; + prev_width = pic->width; + prev_height = pic->height; + // if width is unspecified, scale original proportionally to height ratio. + if (width == 0) { + width = (prev_width * height + prev_height / 2) / prev_height; + } + // if height is unspecified, scale original proportionally to width ratio. + if (height == 0) { + height = (prev_height * width + prev_width / 2) / prev_width; + } + // Check if the overall dimensions still make sense. + if (width <= 0 || height <= 0) return 0; + + WebPPictureGrabSpecs(pic, &tmp); + tmp.width = width; + tmp.height = height; + if (!WebPPictureAlloc(&tmp)) return 0; + + work = malloc(2 * width * sizeof(int32_t)); + if (work == NULL) { + WebPPictureFree(&tmp); + return 0; + } + + RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, + tmp.y, width, height, tmp.y_stride, work); + RescalePlane(pic->u, + (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride, + tmp.u, + (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work); + RescalePlane(pic->v, + (prev_width + 1) / 2, (prev_height + 1) / 2, pic->uv_stride, + tmp.v, + (width + 1) / 2, (height + 1) / 2, tmp.uv_stride, work); + +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (tmp.a) { + RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, + tmp.a, width, height, tmp.a_stride, work); + } + if (tmp.u0) { + int s = 1; + if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { + s = 2; + } + RescalePlane( + pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, + tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work); + RescalePlane( + pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, + tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work); + } +#endif + + WebPPictureFree(pic); + free(work); + *pic = tmp; + return 1; +} + //----------------------------------------------------------------------------- // Write-to-memory @@ -232,49 +463,98 @@ static inline int rgb_to_v(int r, int g, int b) { picture->v[dst] = rgb_to_v(r, g, b); \ } +#define RGB_TO_UV0(x_in, x_out, y, SUM) { \ + const int src = (step * (x_in) + (y) * rgb_stride); \ + const int dst = (x_out) + (y) * picture->uv0_stride; \ + const int r = SUM(r_ptr + src); \ + const int g = SUM(g_ptr + src); \ + const int b = SUM(b_ptr + src); \ + picture->u0[dst] = rgb_to_u(r, g, b); \ + picture->v0[dst] = rgb_to_v(r, g, b); \ +} + +static void MakeGray(WebPPicture* const picture) { + int y; + const int uv_width = (picture->width + 1) >> 1; + for (y = 0; y < ((picture->height + 1) >> 1); ++y) { + memset(picture->u + y * picture->uv_stride, 128, uv_width); + memset(picture->v + y * picture->uv_stride, 128, uv_width); + } +} + static int Import(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride, int step, int swap_rb, int import_alpha) { + const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; int x, y; const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0); const uint8_t* const g_ptr = rgb + 1; const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2); - const uint8_t* const a_ptr = rgb + 3; + const int width = picture->width; + const int height = picture->height; - for (y = 0; y < picture->height; ++y) { - for (x = 0; x < picture->width; ++x) { + // Import luma plane + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; picture->y[x + y * picture->y_stride] = rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); } } - for (y = 0; y < (picture->height >> 1); ++y) { - for (x = 0; x < (picture->width >> 1); ++x) { - RGB_TO_UV(x, y, SUM4); + + // Downsample U/V plane + if (uv_csp != WEBP_YUV400) { + for (y = 0; y < (height >> 1); ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM4); + } + if (picture->width & 1) { + RGB_TO_UV(x, y, SUM2V); + } } - if (picture->width & 1) { - RGB_TO_UV(x, y, SUM2V); - } - } - if (picture->height & 1) { - for (x = 0; x < (picture->width >> 1); ++x) { - RGB_TO_UV(x, y, SUM2H); - } - if (picture->width & 1) { - RGB_TO_UV(x, y, SUM1); + if (height & 1) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV(x, y, SUM1); + } } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + // Store original U/V samples too + if (uv_csp == WEBP_YUV422) { + for (y = 0; y < height; ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV0(2 * x, x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV0(2 * x, x, y, SUM1); + } + } + } else if (uv_csp == WEBP_YUV444) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + RGB_TO_UV0(x, x, y, SUM1); + } + } + } +#endif + } else { + MakeGray(picture); } + if (import_alpha) { +#ifdef WEBP_EXPERIMENTAL_FEATURES + const uint8_t* const a_ptr = rgb + 3; assert(step >= 4); - if (!WebPPictureAddAlphaPlane(picture)) { - return 0; - } - for (y = 0; y < picture->height; ++y) { - for (x = 0; x < picture->width; ++x) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { picture->a[x + y * picture->a_stride] = a_ptr[step * x + y * rgb_stride]; } } +#endif } return 1; } @@ -286,24 +566,28 @@ static int Import(WebPPicture* const picture, int WebPPictureImportRGB(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride) { + picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; if (!WebPPictureAlloc(picture)) return 0; return Import(picture, rgb, rgb_stride, 3, 0, 0); } int WebPPictureImportBGR(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride) { + picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; if (!WebPPictureAlloc(picture)) return 0; return Import(picture, rgb, rgb_stride, 3, 1, 0); } int WebPPictureImportRGBA(WebPPicture* const picture, const uint8_t* const rgba, int rgba_stride) { + picture->colorspace |= WEBP_CSP_ALPHA_BIT; if (!WebPPictureAlloc(picture)) return 0; return Import(picture, rgba, rgba_stride, 4, 0, 1); } int WebPPictureImportBGRA(WebPPicture* const picture, const uint8_t* const rgba, int rgba_stride) { + picture->colorspace |= WEBP_CSP_ALPHA_BIT; if (!WebPPictureAlloc(picture)) return 0; return Import(picture, rgba, rgba_stride, 4, 1, 1); } diff --git a/src/enc/syntax.c b/src/enc/syntax.c index aab2785e..073d72ee 100644 --- a/src/enc/syntax.c +++ b/src/enc/syntax.c @@ -156,24 +156,40 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, //----------------------------------------------------------------------------- #ifdef WEBP_EXPERIMENTAL_FEATURES + +static void PutLE24(uint8_t* buf, size_t value) { + buf[0] = (value >> 0) & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; +} + static int WriteExtensions(VP8Encoder* const enc) { - const int EXT_SIZE = 4; + const int kTrailerSize = 8; + uint8_t buffer[kTrailerSize]; VP8BitWriter* const bw = &enc->bw_; - uint8_t trailer[EXT_SIZE]; - uint32_t ext_size = 0; - uint8_t ext_bits = 0x01; - if (enc->alpha_data_size_) { + + // Layer (bytes 0..3) + PutLE24(buffer + 0, enc->layer_data_size_); + buffer[3] = enc->pic_->colorspace & WEBP_CSP_UV_MASK; + if (enc->layer_data_size_ > 0) { + assert(enc->use_layer_); + // append layer data to last partition + if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1], + enc->layer_data_, enc->layer_data_size_)) { + return 0; + } + } + // Alpha (bytes 4..6) + PutLE24(buffer + 4, enc->alpha_data_size_); + if (enc->alpha_data_size_ > 0) { + assert(enc->has_alpha_); if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) { return 0; } - ext_size += enc->alpha_data_size_; - ext_bits |= 0x02; } - trailer[0] = (ext_size >> 16) & 0xff; - trailer[1] = (ext_size >> 8) & 0xff; - trailer[2] = (ext_size >> 0) & 0xff; - trailer[EXT_SIZE - 1] = ext_bits; - if (!VP8BitWriterAppend(bw, trailer, EXT_SIZE)) { + + buffer[kTrailerSize - 1] = 0x01; // marker + if (!VP8BitWriterAppend(bw, buffer, kTrailerSize)) { return 0; } return 1; @@ -187,7 +203,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { const int mb_size = enc->mb_w_ * enc->mb_h_; uint64_t pos1, pos2, pos3; #ifdef WEBP_EXPERIMENTAL_FEATURES - const int need_extensions = (enc->alpha_data_size_ > 0); + const int need_extensions = enc->has_alpha_ || enc->use_layer_; #endif pos1 = VP8BitWriterPos(bw); @@ -221,6 +237,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3); enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3); enc->pic_->stats->alpha_data_size = enc->alpha_data_size_; + enc->pic_->stats->layer_data_size = enc->layer_data_size_; } return !bw->error_; } diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index 3efeb103..14667354 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -13,7 +13,7 @@ #define WEBP_ENC_VP8ENCI_H_ #include "string.h" // for memcpy() -#include "webp/encode.h" +#include "../webp/encode.h" #include "bit_writer.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -330,9 +330,16 @@ struct VP8Encoder { VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions // transparency blob - uint8_t* alpha_data_; + int has_alpha_; + uint8_t* alpha_data_; // non-NULL if transparency is present size_t alpha_data_size_; + // enhancement layer + int use_layer_; + VP8BitWriter layer_bw_; + uint8_t* layer_data_; + size_t layer_data_size_; + // quantization info (one set of DC/AC dequant factor per segment) VP8SegmentInfo dqm_[NUM_MB_SEGMENTS]; int base_quant_; // nominal quantizer value. Only used @@ -427,9 +434,16 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality); int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt); // in alpha.c -// Compress transparency information into enc->alpha_data_. Return true if ok. -int VP8EncProcessAlpha(VP8Encoder* enc); -void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data +void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression +void VP8EncCodeAlphaBlock(VP8EncIterator* it); // analyze or code a macroblock +int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data +void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data + + // in layer.c +void VP8EncInitLayer(VP8Encoder* const enc); // init everything +void VP8EncCodeLayerBlock(VP8EncIterator* it); // code one more macroblock +int VP8EncFinishLayer(VP8Encoder* const enc); // finalize coding +void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory // in dsp.c // Transforms diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 2ada7be4..19ef0b77 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -242,12 +242,20 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config, ResetFilterHeader(enc); ResetBoundaryPredictions(enc); +#ifdef WEBP_EXPERIMENTAL_FEATURES + VP8EncInitAlpha(enc); + VP8EncInitLayer(enc); +#endif + return enc; } static void DeleteEncoder(VP8Encoder* enc) { if (enc) { +#ifdef WEBP_EXPERIMENTAL_FEATURES VP8EncDeleteAlpha(enc); + VP8EncDeleteLayer(enc); +#endif free(enc); } } @@ -309,7 +317,10 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { ok = VP8EncAnalyze(enc) && VP8StatLoop(enc) && VP8EncLoop(enc) - && VP8EncProcessAlpha(enc) +#ifdef WEBP_EXPERIMENTAL_FEATURES + && VP8EncFinishAlpha(enc) + && VP8EncFinishLayer(enc) +#endif && VP8EncWrite(enc); StoreStats(enc); DeleteEncoder(enc); diff --git a/src/webp/decode.h b/src/webp/decode.h index 60047e37..a53eb599 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -12,7 +12,7 @@ #ifndef WEBP_WEBP_DECODE_H_ #define WEBP_WEBP_DECODE_H_ -#include "webp/types.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/webp/decode_vp8.h b/src/webp/decode_vp8.h index 63757233..3f24ea24 100644 --- a/src/webp/decode_vp8.h +++ b/src/webp/decode_vp8.h @@ -12,7 +12,7 @@ #ifndef WEBP_WEBP_DECODE_VP8_H_ #define WEBP_WEBP_DECODE_VP8_H_ -#include "webp/decode.h" +#include "./decode.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/src/webp/encode.h b/src/webp/encode.h index f9868937..e6cf3218 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -14,7 +14,7 @@ #include -#include "webp/types.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -122,6 +122,7 @@ typedef struct { int segment_level[4]; // filtering strength for each segments [0..63] int alpha_data_size; // size of the transparency data + int layer_data_size; // size of the enhancement layer data } WebPAuxStats; // Signature for output function. Should return 1 if writing was successful. @@ -130,9 +131,24 @@ typedef struct { typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size, const WebPPicture* const picture); +typedef enum { + // chroma sampling + WEBP_YUV420 = 0, // 4:2:0 + WEBP_YUV422 = 1, // 4:2:2 + WEBP_YUV444 = 2, // 4:4:4 + WEBP_YUV400 = 3, // grayscale + WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors + // alpha channel variants + WEBP_YUV420A = 4, + WEBP_YUV422A = 5, + WEBP_YUV444A = 6, + WEBP_YUV400A = 7, // grayscale + alpha + WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present +} WebPEncCSP; + struct WebPPicture { // input - int colorspace; // colorspace: should be 0 for now (=Y'CbCr). + WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr). int width, height; // dimensions. uint8_t *y, *u, *v; // pointers to luma/chroma planes. int y_stride, uv_stride; // luma/chroma strides. @@ -155,6 +171,10 @@ struct WebPPicture { // where to store statistics, if not NULL: WebPAuxStats* stats; + + // original samples (for non-YUV420 modes) + uint8_t *u0, *v0; + int uv0_stride; }; // Internal, version-checked, entry point @@ -171,15 +191,11 @@ static inline int WebPPictureInit(WebPPicture* const picture) { // WebPPicture utils // Convenience allocation / deallocation based on picture->width/height: -// Allocate y/u/v buffers as per width/height specification. +// Allocate y/u/v buffers as per colorspace/width/height specification. // Note! This function will free the previous buffer if needed. // Returns 0 in case of memory error. int WebPPictureAlloc(WebPPicture* const picture); -// This function will add storage for a transparency plane to a picture, using -// its width and depth. -int WebPPictureAddAlphaPlane(WebPPicture* const picture); - // Release memory allocated by WebPPictureAlloc() or WebPPictureImport*() // Note that this function does _not_ free the memory pointed to by 'picture'. void WebPPictureFree(WebPPicture* const picture); @@ -194,6 +210,11 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst); int WebPPictureCrop(WebPPicture* const picture, int left, int top, int width, int height); +// Rescale a picture to new dimension width x height. +// Now gamma correction is applied. +// Returns false in case of error (invalid parameter or insufficient memory). +int WebPPictureRescale(WebPPicture* const pic, int width, int height); + // Colorspace conversion function to import RGB samples. // Previous buffer will be free'd, if any. // *rgb buffer should have a size of at least height * rgb_stride.