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
This commit is contained in:
Pascal Massimino 2011-04-25 16:58:04 -07:00
parent cfbf88a6c4
commit 2ab4b72f53
27 changed files with 477 additions and 59 deletions

4
README
View File

@ -112,6 +112,8 @@ options:
-f <int> ............... filter strength (0=off..100)
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp)
-strong ................ use strong filter instead of simple.
-alpha_comp <int> ...... set the transparency-compression
-noalpha ............... discard any transparency information.
-pass <int> ............ analysis pass number (1..10)
-partitions <int> ...... number of partitions to use (0..3)
-crop <x> <y> <w> <h> .. 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:

View File

@ -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=""

View File

@ -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

View File

@ -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);
// TODO(skal): Strip Alpha for now (till Alpha is supported).
if (!keep_alpha) {
png_set_strip_alpha(png);
has_alpha = 0;
}
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 <int> ....... "
"filter sharpness (0:most .. 7:least sharp)\n");
printf(" -strong ................ use strong filter instead of simple.\n");
printf(" -alpha_comp <int> ...... set the transparency-compression\n");
printf(" -noalpha ............... discard any transparency information.\n");
printf(" -pass <int> ............ analysis pass number (1..10)\n");
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
printf(" -map <int> ............. 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;
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

69
src/dec/alpha.c Normal file
View File

@ -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 <stdlib.h>
#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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -141,8 +141,8 @@ 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(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;
}

View File

@ -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];

View File

@ -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

98
src/enc/alpha.c Normal file
View File

@ -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 <stdlib.h>
#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

View File

@ -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)

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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_;
}

View File

@ -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

View File

@ -246,7 +246,12 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
}
static void DeleteEncoder(VP8Encoder* 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);

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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 <inttypes.h>
#ifdef ANSI