From 2ab4b72f5397a11cdbfebe4da0bff8180daf0586 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 25 Apr 2011 16:58:04 -0700 Subject: [PATCH] EXPERIMENTAL: add support for alpha channel This is a (minor) bitstream change: if the 'color_space' bit is set to '1' (which is normally an undefined/invalid behaviour), we add extra data at the end of partition #0 (so-called 'extensions') Namely, we add the size of the extension data as 3 bytes (little-endian), followed by a set of bits telling which extensions we're incorporating. The data then _preceeds_ this trailing tags. This is all experimental, and you'll need to have '#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code (at your own risk! :)) Still, this hack produces almost-valid WebP file for decoders that don't check this color_space bit. In particular, previous 'dwebp' (and for instance Chrome) will recognize this files and decode them, but without the alpha of course. Other decoder will just see random extra stuff at the end of partition #0. To experiment with the alpha-channel, you need to compile on Unix platform and use PNGs for input/output. If 'alpha.png' is a source with alpha channel, then you can try (on Unix): cwebp alpha.png -o alpha.webp dwebp alpha.webp -o test.png cwebp now has a '-noalpha' flag to ignore any alpha information from the source, if present. More hacking and experimenting welcome! Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e --- README | 4 ++ configure.ac | 4 ++ examples/Makefile.am | 4 +- examples/cwebp.c | 43 ++++++++++++++----- examples/dwebp.c | 47 +++++++++++++++++---- makefile.unix | 6 +-- src/Makefile.am | 2 +- src/dec/Makefile.am | 2 +- src/dec/alpha.c | 69 ++++++++++++++++++++++++++++++ src/dec/frame.c | 19 ++++++++- src/dec/vp8.c | 31 ++++++++++++++ src/dec/vp8i.h | 9 ++++ src/dec/webp.c | 39 ++++++++++++++--- src/dec/yuv.h | 5 --- src/enc/Makefile.am | 2 +- src/enc/alpha.c | 98 +++++++++++++++++++++++++++++++++++++++++++ src/enc/bit_writer.c | 10 +++++ src/enc/bit_writer.h | 2 + src/enc/config.c | 3 ++ src/enc/picture.c | 45 ++++++++++++++++---- src/enc/syntax.c | 38 +++++++++++++++++ src/enc/vp8enci.h | 9 ++++ src/enc/webpenc.c | 8 +++- src/webp/decode.h | 9 ++-- src/webp/decode_vp8.h | 5 ++- src/webp/encode.h | 19 ++++++--- src/webp/types.h | 4 ++ 27 files changed, 477 insertions(+), 59 deletions(-) create mode 100644 src/dec/alpha.c create mode 100644 src/enc/alpha.c diff --git a/README b/README index 3b7a6764..61c0e903 100644 --- a/README +++ b/README @@ -112,6 +112,8 @@ options: -f ............... filter strength (0=off..100) -sharpness ....... filter sharpness (0:most .. 7:least sharp) -strong ................ use strong filter instead of simple. + -alpha_comp ...... set the transparency-compression + -noalpha ............... discard any transparency information. -pass ............ analysis pass number (1..10) -partitions ...... number of partitions to use (0..3) -crop .. crop picture with the given rectangle @@ -120,6 +122,8 @@ options: -short ................. condense printed message -quiet ................. don't print anything. + -version ............... print version number and exit. + -noasm ................. disable all assembly optimizations. -v ..................... verbose, e.g. print encoding/decoding times Experimental Options: diff --git a/configure.ac b/configure.ac index 62657e77..45b3a06e 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,10 @@ AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) AC_SUBST([pkgconfigdir]) +dnl === Check libz is present + +AC_CHECK_LIB(z, gzsetparams, [AC_CHECK_HEADER(zlib.h,,)], [AC_MSG_ERROR(zlib library not found)]) + dnl === check for PNG support === PNG_INCLUDES="" diff --git a/examples/Makefile.am b/examples/Makefile.am index 7c5ec30a..0350b452 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -4,8 +4,8 @@ bin_PROGRAMS = dwebp cwebp dwebp_SOURCES = dwebp.c stopwatch.h dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES) $(JPEG_INCLUDES) -dwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) +dwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) -lz cwebp_SOURCES = cwebp.c stopwatch.h cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES) $(JPEG_INCLUDES) -cwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) +cwebp_LDADD = ../src/libwebp.la $(PNG_LIBS) $(JPEG_LIBS) -lz diff --git a/examples/cwebp.c b/examples/cwebp.c index bd963159..38cccdf3 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -162,7 +162,8 @@ static HRESULT ReadPictureWithWIC(const char* filename, return hr; } -static int ReadPicture(const char* const filename, WebPPicture* const pic) { +static int ReadPicture(const char* const filename, WebPPicture* const pic, + int keep_alpha) { int ok; if (pic->width != 0 && pic->height != 0) { // If image size is specified, infer it as YUV format. @@ -282,10 +283,11 @@ static void PNGAPI error_function(png_structp png, png_const_charp dummy) { longjmp(png_jmpbuf(png), 1); } -static int ReadPNG(FILE* in_file, WebPPicture* const pic) { +static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) { png_structp png; png_infop info; int color_type, bit_depth, interlaced; + int has_alpha; int num_passes; int p; int ok = 0; @@ -327,12 +329,16 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic) { if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png); } + has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA); + + if (!keep_alpha) { + png_set_strip_alpha(png); + has_alpha = 0; + } - // TODO(skal): Strip Alpha for now (till Alpha is supported). - png_set_strip_alpha(png); num_passes = png_set_interlace_handling(png); png_read_update_info(png, info); - stride = 3 * width * sizeof(*rgb); + stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb); rgb = (uint8_t*)malloc(stride * height); if (rgb == NULL) goto Error; for (p = 0; p < num_passes; ++p) { @@ -346,14 +352,15 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic) { pic->width = width; pic->height = height; - ok = WebPPictureImportRGB(pic, rgb, stride); + ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride) + : WebPPictureImportRGB(pic, rgb, stride); free(rgb); End: return ok; } #else -static int ReadPNG(FILE* in_file, WebPPicture* const pic) { +static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) { printf("PNG support not compiled. Please install the libpng development " "package before building.\n"); return 0; @@ -385,7 +392,8 @@ static InputFileFormat GetImageType(FILE* in_file) { return format; } -static int ReadPicture(const char* const filename, WebPPicture* const pic) { +static int ReadPicture(const char* const filename, WebPPicture* const pic, + int keep_alpha) { int ok = 0; FILE* in_file = fopen(filename, "rb"); if (in_file == NULL) { @@ -397,7 +405,7 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic) { // If no size specified, try to decode it as PNG/JPEG (as appropriate). const InputFileFormat format = GetImageType(in_file); if (format == PNG) { - ok = ReadPNG(in_file, pic); + ok = ReadPNG(in_file, pic, keep_alpha); } else if (format == JPEG) { ok = ReadJPEG(in_file, pic); } @@ -475,6 +483,10 @@ static void PrintExtraInfo(const WebPPicture* const pic, int short_output) { 100.f * stats->header_bytes[0] / stats->coded_size, stats->header_bytes[1], 100.f * stats->header_bytes[1] / stats->coded_size); + if (stats->alpha_data_size) { + fprintf(stderr, " transparency: %6d\n", + stats->alpha_data_size); + } fprintf(stderr, " Residuals bytes " "|segment 1|segment 2|segment 3" "|segment 4| total\n"); @@ -590,6 +602,8 @@ static void HelpLong(void) { printf(" -sharpness ....... " "filter sharpness (0:most .. 7:least sharp)\n"); printf(" -strong ................ use strong filter instead of simple.\n"); + printf(" -alpha_comp ...... set the transparency-compression\n"); + 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(" -map ............. print map of extra info.\n"); @@ -618,12 +632,15 @@ int main(int argc, const char *argv[]) { int c; int short_output = 0; int quiet = 0; + int keep_alpha = 0; int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_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; @@ -675,6 +692,10 @@ int main(int argc, const char *argv[]) { config.preprocessing = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-segments") && c < argc - 1) { config.segments = strtol(argv[++c], NULL, 0); + } else if (!strcmp(argv[c], "-alpha_comp") && c < argc - 1) { + config.alpha_compression = strtol(argv[++c], NULL, 0); + } else if (!strcmp(argv[c], "-noalpha")) { + keep_alpha = 0; } else if (!strcmp(argv[c], "-map") && c < argc - 1) { picture.extra_info_type = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { @@ -734,7 +755,7 @@ int main(int argc, const char *argv[]) { // Read the input if (verbose) StopwatchReadAndReset(&stop_watch); - if (!ReadPicture(in_file, &picture)) { + if (!ReadPicture(in_file, &picture, keep_alpha)) { fprintf(stderr, "Error! Cannot read input picture\n"); goto Error; } diff --git a/examples/dwebp.c b/examples/dwebp.c index 45673862..29c5d202 100644 --- a/examples/dwebp.c +++ b/examples/dwebp.c @@ -109,7 +109,8 @@ static HRESULT WriteUsingWIC(const char* out_file_name, REFGUID container_guid, } static int WritePNG(const char* out_file_name, unsigned char* rgb, int stride, - uint32_t width, uint32_t height) { + uint32_t width, uint32_t height, int has_alpha) { + assert(!has_alpha); // TODO(mikolaj) return SUCCEEDED(WriteUsingWIC(out_file_name, MAKE_REFGUID(GUID_ContainerFormatPng), rgb, stride, width, height)); @@ -122,7 +123,7 @@ static void PNGAPI error_function(png_structp png, png_const_charp dummy) { } static int WritePNG(FILE* out_file, unsigned char* rgb, int stride, - png_uint_32 width, png_uint_32 height) { + png_uint_32 width, png_uint_32 height, int has_alpha) { png_structp png; png_infop info; png_uint_32 y; @@ -142,7 +143,8 @@ static int WritePNG(FILE* out_file, unsigned char* rgb, int stride, return 0; } png_init_io(png, out_file); - png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, + png_set_IHDR(png, info, width, height, 8, + has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png, info); @@ -159,7 +161,7 @@ static int WritePNG(FILE* out_file, unsigned char* rgb, int stride, typedef uint32_t png_uint_32; static int WritePNG(FILE* out_file, unsigned char* rgb, int stride, - png_uint_32 width, png_uint_32 height) { + png_uint_32 width, png_uint_32 height, int has_alpha) { printf("PNG support not compiled. Please install the libpng development " "package before building.\n"); printf("You can run with -ppm flag to decode in PPM format.\n"); @@ -167,12 +169,28 @@ static int WritePNG(FILE* out_file, unsigned char* rgb, int stride, } #endif -static int WritePPM(FILE* fout, unsigned char* rgb, +static int WritePPM(FILE* fout, const unsigned char* rgb, uint32_t width, uint32_t height) { fprintf(fout, "P6\n%d %d\n255\n", width, height); return (fwrite(rgb, width * height, 3, fout) == 3); } +static int WriteAlphaPlane(FILE* fout, const unsigned char* rgba, + uint32_t width, uint32_t height) { + uint32_t y; + fprintf(fout, "P5\n%d %d\n255\n", width, height); + for (y = 0; y < height; ++y) { + const unsigned char* line = rgba + y * (width * 4); + uint32_t x; + for (x = 0; x < width; ++x) { + if (fputc(line[4 * x + 3], fout) == EOF) { + return 0; + } + } + } + return 1; +} + static int WritePGM(FILE* fout, unsigned char* y_plane, unsigned char *u, unsigned char* v, int y_stride, int uv_stride, @@ -202,6 +220,7 @@ typedef enum { PNG = 0, PPM, PGM, + ALPHA_PLANE_ONLY // this is for experimenting only } OutputFileFormat; static void Help(void) { @@ -222,6 +241,7 @@ int main(int argc, const char *argv[]) { const char *out_file = NULL; int width, height, stride, uv_stride; + int has_alpha = 0; uint8_t* out = NULL, *u = NULL, *v = NULL; OutputFileFormat format = PNG; Stopwatch stop_watch; @@ -232,6 +252,8 @@ int main(int argc, const char *argv[]) { return 0; } else if (!strcmp(argv[c], "-o") && c < argc - 1) { out_file = argv[++c]; + } else if (!strcmp(argv[c], "-alpha")) { + format = ALPHA_PLANE_ONLY; } else if (!strcmp(argv[c], "-ppm")) { format = PPM; } else if (!strcmp(argv[c], "-version")) { @@ -284,8 +306,12 @@ int main(int argc, const char *argv[]) { case PNG: #ifdef _WIN32 out = WebPDecodeBGR((const uint8_t*)data, data_size, &width, &height); + stride = 3 * width; + has_alpha = 0; #else - out = WebPDecodeRGB((const uint8_t*)data, data_size, &width, &height); + out = WebPDecodeRGBA((const uint8_t*)data, data_size, &width, &height); + stride = 4 * width; + has_alpha = 1; #endif break; case PPM: @@ -295,6 +321,9 @@ int main(int argc, const char *argv[]) { out = WebPDecodeYUV((const uint8_t*)data, data_size, &width, &height, &u, &v, &stride, &uv_stride); break; + case ALPHA_PLANE_ONLY: + out = WebPDecodeRGBA((const uint8_t*)data, data_size, &width, &height); + break; default: free(data); return -1; @@ -331,14 +360,16 @@ int main(int argc, const char *argv[]) { int ok = 1; if (format == PNG) { #ifdef _WIN32 - ok &= WritePNG(out_file, out, 3 * width, width, height); + ok &= WritePNG(out_file, out, stride, width, height, has_alpha); #else - ok &= WritePNG(fout, out, 3 * width, width, height); + ok &= WritePNG(fout, out, stride, width, height, has_alpha); #endif } else if (format == PPM) { ok &= WritePPM(fout, out, width, height); } else if (format == PGM) { ok &= WritePGM(fout, out, u, v, stride, uv_stride, width, height); + } else if (format == ALPHA_PLANE_ONLY) { + ok &= WriteAlphaPlane(fout, out, width, height); } if (fout) fclose(fout); diff --git a/makefile.unix b/makefile.unix index 8d7799cb..3f50b076 100644 --- a/makefile.unix +++ b/makefile.unix @@ -13,7 +13,7 @@ # These flag assume you have libpng and libjpeg installed. If not, either # follow below install instructions or just comment out the next lines. EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -EXTRA_LIBS= -lpng -ljpeg +EXTRA_LIBS= -lpng -ljpeg -lz ifeq ("$(HOSTTYPE)", "intel-mac") EXTRA_FLAGS += -I/opt/local/include EXTRA_LIBS += -L/opt/local/lib @@ -46,13 +46,13 @@ CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS) LDFLAGS = src/libwebp.a $(EXTRA_LIBS) -lm OBJS = src/enc/webpenc.o src/enc/bit_writer.o src/enc/syntax.o \ - src/enc/dsp.o src/enc/dsp_sse2.o \ + src/enc/dsp.o src/enc/dsp_sse2.o src/enc/alpha.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/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/idec.o src/dec/alpha.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/Makefile.am b/src/Makefile.am index d408e318..4bbe7189 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ lib_LTLIBRARIES = libwebp.la libwebp_la_SOURCES = libwebp_la_LIBADD = dec/libwebpdecode.la \ enc/libwebpencode.la -libwebp_la_LDFLAGS = -version-info 0:0:0 +libwebp_la_LDFLAGS = -version-info 0:0:0 -lz libwebpinclude_HEADERS = webp/types.h webp/decode.h webp/decode_vp8.h \ webp/encode.h libwebpincludedir = $(includedir)/webp diff --git a/src/dec/Makefile.am b/src/dec/Makefile.am index 835141a3..fc36aafe 100644 --- a/src/dec/Makefile.am +++ b/src/dec/Makefile.am @@ -1,7 +1,7 @@ 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 + quant.c tree.c vp8.c webp.c yuv.c idec.c alpha.c libwebpdecode_la_LDFLAGS = -version-info 0:0:0 libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h libwebpdecodeincludedir = $(includedir)/webp diff --git a/src/dec/alpha.c b/src/dec/alpha.c new file mode 100644 index 00000000..585695e2 --- /dev/null +++ b/src/dec/alpha.c @@ -0,0 +1,69 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Alpha-plane decompression. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "vp8i.h" + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +#include "zlib.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +//----------------------------------------------------------------------------- + +const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + int row, int num_rows) { + uint8_t* output = dec->alpha_plane_; + const int stride = dec->pic_hdr_.width_; + if (row < 0 || row + num_rows > dec->pic_hdr_.height_) { + return NULL; // sanity check + } + if (row == 0) { + // TODO(skal): for now, we just decompress everything during the first call. + // Later, we'll decode progressively, but we need to store the + // z_stream state. + const uint8_t* data = dec->alpha_data_; + size_t data_size = dec->alpha_data_size_; + const size_t output_size = stride * dec->pic_hdr_.height_; + int ret = Z_OK; + z_stream strm; + + memset(&strm, 0, sizeof(strm)); + if (inflateInit(&strm) != Z_OK) { + return 0; + } + strm.avail_in = data_size; + strm.next_in = (unsigned char*)data; + do { + strm.avail_out = output_size; + strm.next_out = output; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { + break; + } + } while (strm.avail_out == 0); + + inflateEnd(&strm); + if (ret != Z_STREAM_END) { + return NULL; // error + } + } + return output + row * stride; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif + +#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/dec/frame.c b/src/dec/frame.c index 44c63579..e61b0452 100644 --- a/src/dec/frame.c +++ b/src/dec/frame.c @@ -33,10 +33,12 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { const int coeffs_size = 384 * sizeof(*dec->coeffs_); const int cache_height = (16 + kFilterExtraRows[dec->filter_type_]) * 3 / 2; const int cache_size = top_size * cache_height; + const int alpha_size = + dec->alpha_data_ ? (dec->pic_hdr_.width_ * dec->pic_hdr_.height_) : 0; const int needed = intra_pred_mode_size + top_size + info_size + yuv_size + coeffs_size - + cache_size + ALIGN_MASK; + + cache_size + alpha_size + ALIGN_MASK; uint8_t* mem; if (needed > dec->mem_size_) { @@ -84,6 +86,10 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { } mem += cache_size; + // alpha plane + dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL; + mem += alpha_size; + // note: left-info is initialized once for all. memset(dec->mb_info_ - 1, 0, (mb_w + 1) * sizeof(*dec->mb_info_)); @@ -100,6 +106,7 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { io->y_stride = dec->cache_y_stride_; io->uv_stride = dec->cache_uv_stride_; io->fancy_upscaling = 0; // default + io->a = NULL; // Init critical function pointers and look-up tables. VP8DspInitTables(); @@ -250,6 +257,16 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { } io->mb_y = y_start; io->mb_h = y_end - y_start; + io->a = NULL; +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dec->alpha_data_) { + io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start); + if (io->a == NULL) { + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Could not decode alpha data."); + } + } +#endif if (!io->put(io)) { return 0; } diff --git a/src/dec/vp8.c b/src/dec/vp8.c index 43a0c354..ed706126 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -305,6 +305,10 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "bad partition length"); } + + dec->alpha_data_ = NULL; + dec->alpha_data_size_ = 0; + br = &dec->br_; VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); buf += frm_hdr->partition_length_; @@ -323,6 +327,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse filter header"); } + status = ParsePartitions(dec, buf, buf_size); if (status != VP8_STATUS_OK) { return VP8SetError(dec, status, "cannot parse partitions"); @@ -368,6 +373,32 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { VP8ParseProba(br, dec); +#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) { + 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; + } + } +#endif + // sanitized state dec->ready_ = 1; return 1; diff --git a/src/dec/vp8i.h b/src/dec/vp8i.h index b2ad9a33..91da59a6 100644 --- a/src/dec/vp8i.h +++ b/src/dec/vp8i.h @@ -246,6 +246,11 @@ struct VP8Decoder { // Filtering side-info 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 }; //----------------------------------------------------------------------------- @@ -274,6 +279,10 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io); // Decode one macroblock. Returns false if there is not enough data. int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); +// in alpha.c +const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + int row, int num_rows); + // in dsp.c typedef void (*VP8Idct)(const int16_t* coeffs, uint8_t* dst); extern VP8Idct VP8Transform; diff --git a/src/dec/webp.c b/src/dec/webp.c index 3bf6f55b..d8094264 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -139,10 +139,10 @@ static inline void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \ } // All variants implemented. -UPSCALE_FUNC(UpscaleRgbLinePair, VP8YuvToRgb, 3) -UPSCALE_FUNC(UpscaleBgrLinePair, VP8YuvToBgr, 3) -UPSCALE_FUNC(UpscaleRgbaLinePair, VP8YuvToRgba, 4) -UPSCALE_FUNC(UpscaleBgraLinePair, VP8YuvToBgra, 4) +UPSCALE_FUNC(UpscaleRgbLinePair, VP8YuvToRgb, 3) +UPSCALE_FUNC(UpscaleBgrLinePair, VP8YuvToBgr, 3) +UPSCALE_FUNC(UpscaleRgbaLinePair, VP8YuvToRgb, 4) +UPSCALE_FUNC(UpscaleBgraLinePair, VP8YuvToBgr, 4) // Main driver function. static inline @@ -266,15 +266,42 @@ static int CustomPut(const VP8Io* io) { } else if (p->mode == MODE_BGR) { VP8YuvToBgr(y, u, v, dst + i * 3); } else if (p->mode == MODE_RGBA) { - VP8YuvToRgba(y, u, v, dst + i * 4); + VP8YuvToRgb(y, u, v, dst + i * 4); } else { - VP8YuvToBgra(y, u, v, dst + i * 4); + VP8YuvToBgr(y, u, v, dst + i * 4); } } dst += p->stride; } } } + + // Alpha handling + if (p->mode == MODE_RGBA || p->mode == MODE_BGRA) { + int i, j; + uint8_t* dst = p->output + io->mb_y * p->stride + 3; + const uint8_t* alpha = io->a; + const int has_alpha = (alpha != NULL); +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (has_alpha) { + for (j = 0; j < mb_h; ++j) { + for (i = 0; i < w; ++i) { + dst[4 * i] = alpha[i]; + } + alpha += io->width; + dst += p->stride; + } + } +#endif + if (!has_alpha) { // fill-in with 0xFFs + for (j = 0; j < mb_h; ++j) { + for (i = 0; i < w; ++i) { + dst[4 * i] = 0xff; + } + dst += p->stride; + } + } + } return 1; } diff --git a/src/dec/yuv.h b/src/dec/yuv.h index 50e63f9b..1af0f089 100644 --- a/src/dec/yuv.h +++ b/src/dec/yuv.h @@ -36,11 +36,6 @@ inline static void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v, rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN]; } -inline static void VP8YuvToRgba(int y, int u, int v, uint8_t* const rgba) { - VP8YuvToRgb(y, u, v, rgba); - rgba[3] = 0xff; -} - inline static void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v, uint8_t* const bgr) { const int r_off = VP8kVToR[v]; diff --git a/src/enc/Makefile.am b/src/enc/Makefile.am index 6cd0b2d3..34b6b4bf 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 + syntax.c tree.c vp8enci.h webpenc.c alpha.c libwebpencode_la_LDFLAGS = -version-info 0:0:0 -lm libwebpencodeinclude_HEADERS = ../webp/encode.h ../webp/types.h libwebpencodeincludedir = $(includedir)/webp diff --git a/src/enc/alpha.c b/src/enc/alpha.c new file mode 100644 index 00000000..dc764e59 --- /dev/null +++ b/src/enc/alpha.c @@ -0,0 +1,98 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Alpha-plane compression. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "vp8enci.h" + +#include "zlib.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +//----------------------------------------------------------------------------- + +static int CompressAlpha(const uint8_t* data, size_t data_size, + uint8_t** output, size_t* output_size, + int algo) { + int ret = Z_OK; + z_stream strm; + const int CHUNK_SIZE = 8192; + *output = NULL; + *output_size = 0; + memset(&strm, 0, sizeof(strm)); + if (deflateInit(&strm, algo ? Z_BEST_SPEED : Z_BEST_COMPRESSION) != Z_OK) { + return 0; + } + strm.next_in = (unsigned char*)data; + strm.avail_in = data_size; + do { + size_t size_out; + unsigned char chunk[CHUNK_SIZE]; + strm.next_out = chunk; + strm.avail_out = CHUNK_SIZE; + ret = deflate(&strm, Z_FINISH); + if (ret == Z_STREAM_ERROR) { + break; + } + size_out = CHUNK_SIZE - strm.avail_out; + if (size_out) { + size_t new_size = *output_size + size_out; + uint8_t* new_output = realloc(*output, new_size); + if (new_output == NULL) { + ret = Z_MEM_ERROR; + break; + } + memcpy(new_output + *output_size, chunk, size_out); + *output_size = new_size; + *output = new_output; + } + } while (ret != Z_STREAM_END || strm.avail_out == 0); + + deflateEnd(&strm); + if (ret != Z_STREAM_END) { + free(*output); + output_size = 0; + return 0; + } + return 1; +} + +#endif /* WEBP_EXPERIMENTAL_FEATURES */ + +int VP8EncProcessAlpha(VP8Encoder* enc) { + const WebPPicture* pic_ = enc->pic_; + enc->alpha_data_ = NULL; + enc->alpha_data_size_ = 0; +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (pic_->a == NULL) { + return 1; + } + if (!CompressAlpha(pic_->a, pic_->width * pic_->height, + &enc->alpha_data_, &enc->alpha_data_size_, + enc->config_->alpha_compression)) { + return 0; + } +#endif /* WEBP_EXPERIMENTAL_FEATURES */ + return 1; +} + +void VP8EncDeleteAlpha(VP8Encoder* enc) { + free(enc->alpha_data_); + enc->alpha_data_ = NULL; + enc->alpha_data_size_ = 0; +} + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/enc/bit_writer.c b/src/enc/bit_writer.c index 3656a7e2..24bbd335 100644 --- a/src/enc/bit_writer.c +++ b/src/enc/bit_writer.c @@ -168,6 +168,16 @@ uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) { return bw->buf_; } +int VP8BitWriterAppend(VP8BitWriter* const bw, + const uint8_t* data, size_t size) { + assert(data); + if (bw->nb_bits_ != -8) return 0; // kFlush() must have been called + if (!BitWriterResize(bw, size)) return 0; + memcpy(bw->buf_ + bw->pos_, data, size); + bw->pos_ += size; + return 1; +} + //----------------------------------------------------------------------------- #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/enc/bit_writer.h b/src/enc/bit_writer.h index 3773c9cb..69e247a1 100644 --- a/src/enc/bit_writer.h +++ b/src/enc/bit_writer.h @@ -39,6 +39,8 @@ int VP8PutBit(VP8BitWriter* const bw, int bit, int prob); int VP8PutBitUniform(VP8BitWriter* const bw, int bit); void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits); void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits); +int VP8BitWriterAppend(VP8BitWriter* const bw, + const uint8_t* data, size_t size); // return approximate write position (in bits) static inline uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) { diff --git a/src/enc/config.c b/src/enc/config.c index 86ef5ce2..0a1ccbbe 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -41,6 +41,7 @@ int WebPConfigInitInternal(WebPConfig* const config, config->show_compressed = 0; config->preprocessing = 0; config->autofilter = 0; + config->alpha_compression = 0; // TODO(skal): tune. switch (preset) { @@ -105,6 +106,8 @@ int WebPValidateConfig(const WebPConfig* const config) { return 0; if (config->partitions < 0 || config->partitions > 3) return 0; + if (config->alpha_compression < 0) + return 0; return 1; } diff --git a/src/enc/picture.c b/src/enc/picture.c index 6c12ea48..03109eee 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -46,10 +46,30 @@ int WebPPictureAlloc(WebPPicture* const picture) { return 1; } +int WebPPictureAddAlphaPlane(WebPPicture* const picture) { + if (picture) { + const int width = picture->width; + const int height = picture->height; + const uint64_t a_size = (uint64_t)width * 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; + } + free(picture->a); // erase previous buffer + picture->a = (uint8_t*)malloc((size_t)a_size); + return (picture->a != NULL); + } + return 1; +} + void WebPPictureFree(WebPPicture* const picture) { if (picture) { free(picture->y); picture->y = picture->u = picture->v = NULL; + free(picture->a); + picture->a = NULL; } } @@ -198,7 +218,7 @@ static inline int rgb_to_v(int r, int g, int b) { static int Import(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride, - int step, int swap) { + int step, int swap, int alpha_offset) { int x, y; const uint8_t* const r_ptr = rgb + (swap ? 2 : 0); const uint8_t* const g_ptr = rgb + 1; @@ -227,6 +247,17 @@ static int Import(WebPPicture* const picture, RGB_TO_UV(x, y, SUM1); } } + if (alpha_offset >= 0) { + if (!WebPPictureAddAlphaPlane(picture)) { + return 0; + } + for (y = 0; y < picture->height; ++y) { + for (x = 0; x < picture->width; ++x) { + picture->a[x + y * picture->width] = + rgb[step * x + y * rgb_stride + alpha_offset]; + } + } + } return 1; } #undef SUM4 @@ -238,25 +269,25 @@ static int Import(WebPPicture* const picture, int WebPPictureImportRGB(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride) { if (!WebPPictureAlloc(picture)) return 0; - return Import(picture, rgb, rgb_stride, 3, 0); + return Import(picture, rgb, rgb_stride, 3, 0, -1); } int WebPPictureImportBGR(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride) { if (!WebPPictureAlloc(picture)) return 0; - return Import(picture, rgb, rgb_stride, 3, 1); + return Import(picture, rgb, rgb_stride, 3, 1, -1); } int WebPPictureImportRGBA(WebPPicture* const picture, const uint8_t* const rgba, int rgba_stride) { if (!WebPPictureAlloc(picture)) return 0; - return Import(picture, rgba, rgba_stride, 4, 0); + return Import(picture, rgba, rgba_stride, 4, 0, 3); } int WebPPictureImportBGRA(WebPPicture* const picture, const uint8_t* const rgba, int rgba_stride) { if (!WebPPictureAlloc(picture)) return 0; - return Import(picture, rgba, rgba_stride, 4, 1); + return Import(picture, rgba, rgba_stride, 4, 1, 3); } //----------------------------------------------------------------------------- @@ -264,7 +295,7 @@ int WebPPictureImportBGRA(WebPPicture* const picture, typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); -static size_t Encode(const uint8_t* rgb, int width, int height, int stride, +static size_t Encode(const uint8_t* rgba, int width, int height, int stride, Importer import, float quality_factor, uint8_t** output) { size_t output_size = 0; WebPPicture pic; @@ -286,7 +317,7 @@ static size_t Encode(const uint8_t* rgb, int width, int height, int stride, wrt.size = &output_size; InitMemoryWriter(&wrt); - ok = import(&pic, rgb, stride) && WebPEncode(&config, &pic); + ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic); WebPPictureFree(&pic); if (!ok) { free(*output); diff --git a/src/enc/syntax.c b/src/enc/syntax.c index a788f3c3..3ddcad88 100644 --- a/src/enc/syntax.c +++ b/src/enc/syntax.c @@ -155,14 +155,46 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, //----------------------------------------------------------------------------- +static int WriteExtensions(VP8Encoder* const enc) { +#ifdef WEBP_EXPERIMENTAL_FEATURES + const int EXT_SIZE = 4; + 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_) { + 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)) { + return 0; + } +#endif + return 1; +} + +//----------------------------------------------------------------------------- + static size_t GeneratePartition0(VP8Encoder* const enc) { VP8BitWriter* const bw = &enc->bw_; const int mb_size = enc->mb_w_ * enc->mb_h_; uint64_t pos1, pos2, pos3; + const int need_extensions = (enc->alpha_data_size_ > 0); pos1 = VP8BitWriterPos(bw); VP8BitWriterInit(bw, mb_size * 7 / 8); // ~7 bits per macroblock +#ifdef WEBP_EXPERIMENTAL_FEATURES + VP8PutBitUniform(bw, need_extensions); // extensions +#else VP8PutBitUniform(bw, 0); // colorspace +#endif VP8PutBitUniform(bw, 0); // clamp type PutSegmentHeader(bw, enc); @@ -174,11 +206,17 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { pos2 = VP8BitWriterPos(bw); VP8CodeIntraModes(enc); VP8BitWriterFinish(bw); + + if (need_extensions && !WriteExtensions(enc)) { + return 0; + } + pos3 = VP8BitWriterPos(bw); if (enc->pic_->stats) { 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_; } return !bw->error_; } diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index c496568a..7e28351a 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -326,6 +326,10 @@ struct VP8Encoder { VP8BitWriter bw_; // part0 VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions + // transparency blob + uint8_t* alpha_data_; + size_t alpha_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 @@ -414,6 +418,11 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality); // Pick best modes and fills the levels. Returns true if skipped. 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 + // in dsp.c // Transforms // VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 59221d7f..2c3d2811 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -246,7 +246,12 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config, } static void DeleteEncoder(VP8Encoder* enc) { - free(enc); + if (enc) { + if (enc->alpha_data_) { + VP8EncDeleteAlpha(enc); + } + free(enc); + } } //----------------------------------------------------------------------------- @@ -306,6 +311,7 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { ok = VP8EncAnalyze(enc) && VP8StatLoop(enc) && VP8EncLoop(enc) + && VP8EncProcessAlpha(enc) && VP8EncWrite(enc); StoreStats(enc); DeleteEncoder(enc); diff --git a/src/webp/decode.h b/src/webp/decode.h index 60047e37..6c63d54b 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -178,11 +178,10 @@ VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data, VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data, uint32_t data_size); -// Returns the r/g/b/(a) image decoded so far. Returns NULL if output params -// are not initialized yet. The r/g/b/(a) output type corresponds to the mode -// specified in WebPINew()/WebPINewRGB(). *last_y is the index of last decoded -// row in raster scan order. Some pointers (*last_y, *width etc.) can be NULL if -// corresponding information is not needed. +// Returns the RGB image decoded so far. Returns NULL if output params are not +// initialized yet. *last_y is the index of last decoded row in raster scan +// order. Some pointers (*last_y, *width etc.) can be NULL if corresponding +// information is not needed. uint8_t* WebPIDecGetRGB(const WebPIDecoder* const idec, int *last_y, int* width, int* height, int* stride); diff --git a/src/webp/decode_vp8.h b/src/webp/decode_vp8.h index 153a4c54..63757233 100644 --- a/src/webp/decode_vp8.h +++ b/src/webp/decode_vp8.h @@ -18,7 +18,7 @@ extern "C" { #endif -#define WEBP_DECODER_ABI_VERSION 0x0001 +#define WEBP_DECODER_ABI_VERSION 0x0002 //----------------------------------------------------------------------------- // Lower-level API @@ -80,6 +80,9 @@ struct VP8Io { // of more visible blocking. Note that output will also be non-compliant // with the VP8 specifications. int bypass_filtering; + + // pointer to the alpha data (if present) corresponding to the rows + const uint8_t* a; }; // Internal, version-checked, entry point diff --git a/src/webp/encode.h b/src/webp/encode.h index e0cc5dc3..6828a4bf 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -20,7 +20,7 @@ extern "C" { #endif -#define WEBP_ENCODER_ABI_VERSION 0x0001 +#define WEBP_ENCODER_ABI_VERSION 0x0002 // Return the encoder's version number, packed in hexadecimal using 8bits for // each of major/minor/revision. E.g: v2.5.7 is 0x020507. @@ -32,7 +32,6 @@ int WebPGetEncoderVersion(void); // Returns the size of the compressed data (pointed to by *output), or 0 if // an error occurred. The compressed data must be released by the caller // using the call 'free(*output)'. -// Currently, alpha values are discarded. size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride, float quality_factor, uint8_t** output); size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride, @@ -66,6 +65,7 @@ typedef struct { int preprocessing; // preprocessing filter (0=none, 1=segment-smooth) int partitions; // log2(number of token partitions) in [0..3] // Default is set to 0 for easier progressive decoding. + int alpha_compression; // Algorithm for optimizing the alpha plane (0 = none) } WebPConfig; // Enumerate some predefined settings for WebPConfig, depending on the type @@ -120,6 +120,8 @@ typedef struct { int segment_size[4]; // number of macroblocks in each segments int segment_quant[4]; // quantizer values for each segments int segment_level[4]; // filtering strength for each segments [0..63] + + int alpha_data_size; // size of the transparency data } WebPAuxStats; // Signature for output function. Should return 1 if writing was successful. @@ -134,7 +136,7 @@ struct WebPPicture { int width, height; // dimensions. uint8_t *y, *u, *v; // pointers to luma/chroma planes. int y_stride, uv_stride; // luma/chroma strides. - uint8_t *a; // pointer to the alpha plane (unused for now). + uint8_t *a; // pointer to the width x height alpha plane // output WebPWriterFunction writer; // can be NULL @@ -173,6 +175,10 @@ static inline int WebPPictureInit(WebPPicture* const picture) { // 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); @@ -187,16 +193,17 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst); int WebPPictureCrop(WebPPicture* const picture, int left, int top, int width, int height); -// Colorspace conversion function. Previous buffer will be free'd, if any. +// 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. // Returns 0 in case of memory error. int WebPPictureImportRGB(WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride); -// Same, but for RGBA buffer. Alpha information is ignored. +// Same, but for RGBA buffer int WebPPictureImportRGBA(WebPPicture* const picture, const uint8_t* const rgba, int rgba_stride); -// Variant of the above, but taking BGR input: +// Variant of the above, but taking BGR(A) input: int WebPPictureImportBGR(WebPPicture* const picture, const uint8_t* const bgr, int bgr_stride); int WebPPictureImportBGRA(WebPPicture* const picture, diff --git a/src/webp/types.h b/src/webp/types.h index 003c3f76..c3f67f6f 100644 --- a/src/webp/types.h +++ b/src/webp/types.h @@ -12,6 +12,10 @@ #ifndef WEBP_WEBP_TYPES_H_ #define WEBP_WEBP_TYPES_H_ +// This is for experimentation only! Bitstreams generated will surely +// be invalid, non-decodable ones! USE WITH CARE! +// #define WEBP_EXPERIMENTAL_FEATURES // activate alpha support, yuv444, etc. + #ifndef _MSC_VER #include #ifdef ANSI