diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index f6e255ce..0a1cd59a 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -79,34 +79,37 @@ static int DecodeImageStream(int xsize, int ysize, //------------------------------------------------------------------------------ int VP8LCheckSignature(const uint8_t* const data, size_t size) { - return (size >= 1) && - (data[0] == VP8L_MAGIC_BYTE || data[0] == VP8L_MAGIC_BYTE_RSVD); + return (size >= 1) && (data[0] == VP8L_MAGIC_BYTE); } -static int ReadImageSize(VP8LBitReader* const br, - int* const width, int* const height) { +static int ReadImageInfo(VP8LBitReader* const br, + int* const width, int* const height, + int* const has_alpha) { const uint8_t signature = VP8LReadBits(br, 8); if (!VP8LCheckSignature(&signature, 1)) { return 0; } *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; + *has_alpha = VP8LReadBits(br, 1); + VP8LReadBits(br, VP8L_VERSION_BITS); // Read/ignore the version number. return 1; } int VP8LGetInfo(const uint8_t* data, size_t data_size, - int* const width, int* const height) { + int* const width, int* const height, int* const has_alpha) { if (data == NULL || data_size < kHeaderBytes) { return 0; // not enough data } else { - int w, h; + int w, h, a; VP8LBitReader br; VP8LInitBitReader(&br, data, data_size); - if (!ReadImageSize(&br, &w, &h)) { + if (!ReadImageInfo(&br, &w, &h, &a)) { return 0; } if (width != NULL) *width = w; if (height != NULL) *height = h; + if (has_alpha != NULL) *has_alpha = a; return 1; } } @@ -308,7 +311,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int num_htree_groups = 1; if (VP8LReadBits(br, 1)) { // use meta Huffman codes - const int huffman_precision = VP8LReadBits(br, 4); + const int huffman_precision = VP8LReadBits(br, 3) + 2; const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_pixs = huffman_xsize * huffman_ysize; @@ -735,7 +738,7 @@ static int ReadTransform(int* const xsize, int const* ysize, switch (type) { case PREDICTOR_TRANSFORM: case CROSS_COLOR_TRANSFORM: - transform->bits_ = VP8LReadBits(br, 4); + transform->bits_ = VP8LReadBits(br, 3) + 2; ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, transform->bits_), VP8LSubSampleSize(transform->ysize_, @@ -1006,7 +1009,7 @@ int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data, //------------------------------------------------------------------------------ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { - int width, height; + int width, height, has_alpha; if (dec == NULL) return 0; if (io == NULL) { @@ -1017,7 +1020,7 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { dec->io_ = io; dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, io->data, io->data_size); - if (!ReadImageSize(&dec->br_, &width, &height)) { + if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto Error; } diff --git a/src/dec/vp8li.h b/src/dec/vp8li.h index f275c862..605b85e3 100644 --- a/src/dec/vp8li.h +++ b/src/dec/vp8li.h @@ -91,11 +91,10 @@ typedef struct { int VP8LCheckSignature(const uint8_t* const data, size_t size); // Validates the VP8L data-header and retrieves basic header information viz -// width and height. Returns 0 in case of formatting error. width/height -// can be passed NULL. +// width, height and alpha. Returns 0 in case of formatting error. +// width/height/has_alpha can be passed NULL. int VP8LGetInfo(const uint8_t* data, size_t data_size, // data available so far - int* const width, int* const height); - + int* const width, int* const height, int* const has_alpha); // Decodes a raw image stream (without header) and store the alpha data // into *output, which must be of size width x height. Returns false in case diff --git a/src/dec/webp.c b/src/dec/webp.c index fd9cc7ab..22e998b4 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -577,11 +577,12 @@ static VP8StatusCode GetFeatures(const uint8_t* data, size_t data_size, return VP8_STATUS_BITSTREAM_ERROR; } } else { + int has_alpha; // Validates raw VP8L data. - if (!VP8LGetInfo(data, data_size, width, height)) { + if (!VP8LGetInfo(data, data_size, width, height, &has_alpha)) { return VP8_STATUS_BITSTREAM_ERROR; } - features->has_alpha = 1; + features->has_alpha = has_alpha; } return VP8_STATUS_OK; // Return features from VP8 header. diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index a9192b66..f8297a6d 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -30,6 +30,8 @@ extern "C" { #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. #define MAX_HUFF_IMAGE_SIZE (32 * 1024 * 1024) +#define MIN_HISTO_BITS 2 +#define MAX_HISTO_BITS 9 // ----------------------------------------------------------------------------- // Palette @@ -40,6 +42,15 @@ static int CompareColors(const void* p1, const void* p2) { return (a < b) ? -1 : (a > b) ? 1 : 0; } +static int HasRealAlpha(const uint32_t* const argb, int num_pix) { + int i; + for (i = 0; i < num_pix; ++i) { + // Checking for the presence of non-opaque alpha. + if (argb[i] < ARGB_BLACK) return 1; + } + return 0; +} + // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, // creates a palette and returns true, else returns false. static int AnalyzeAndCreatePalette(const uint32_t* const argb, int num_pix, @@ -329,6 +340,7 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw, const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; VP8LWriteBits(bw, 3, nbitpairs - 1); + assert(trimmed_length >= 2); VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); } StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code); @@ -501,7 +513,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, } histogram_image_size = max_index; - VP8LWriteBits(bw, 4, histogram_bits); + assert(histogram_bits >= 2); + VP8LWriteBits(bw, 3, histogram_bits - 2); ok = EncodeImageInternal(bw, histogram_argb, VP8LSubSampleSize(width, histogram_bits), VP8LSubSampleSize(height, histogram_bits), @@ -594,7 +607,8 @@ static int ApplyPredictFilter(const VP8LEncoder* const enc, enc->transform_data_); VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM); - VP8LWriteBits(bw, 4, pred_bits); + assert(pred_bits >= 2); + VP8LWriteBits(bw, 3, pred_bits - 2); if (!EncodeImageInternal(bw, enc->transform_data_, transform_width, transform_height, quality, 0, 0)) { return 0; @@ -614,7 +628,8 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc, enc->argb_, enc->transform_data_); VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM); - VP8LWriteBits(bw, 4, ccolor_transform_bits); + assert(ccolor_transform_bits >= 2); + VP8LWriteBits(bw, 3, ccolor_transform_bits - 2); if (!EncodeImageInternal(bw, enc->transform_data_, transform_width, transform_height, quality, 0, 0)) { return 0; @@ -656,6 +671,12 @@ static int WriteImageSize(const WebPPicture* const pic, return !bw->error_; } +static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) { + VP8LWriteBits(bw, 1, has_alpha); + VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION); + return !bw->error_; +} + static WebPEncodingError WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw, size_t* const coded_size) { @@ -769,6 +790,7 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, // Save palette to bitstream. VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); + assert(palette_size >= 2); VP8LWriteBits(bw, 8, palette_size - 1); for (i = palette_size - 1; i >= 1; --i) { palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); @@ -811,7 +833,8 @@ static int GetHistoBits(const WebPConfig* const config, if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; ++histo_bits; } - return (histo_bits < 3) ? 3 : (histo_bits > 10) ? 10 : histo_bits; + return (histo_bits < MIN_HISTO_BITS) ? MIN_HISTO_BITS : + (histo_bits > MAX_HISTO_BITS) ? MAX_HISTO_BITS : histo_bits; } static void InitEncParams(VP8LEncoder* const enc) { @@ -938,6 +961,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, int VP8LEncodeImage(const WebPConfig* const config, const WebPPicture* const picture) { int width, height; + int has_alpha; size_t coded_size; WebPEncodingError err = VP8_ENC_OK; VP8LBitWriter bw; @@ -954,7 +978,17 @@ int VP8LEncodeImage(const WebPConfig* const config, // Write image size. VP8LBitWriterInit(&bw, (width * height) >> 1); - if (!WriteImageSize(picture, &bw)) goto Error; + if (!WriteImageSize(picture, &bw)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + has_alpha = HasRealAlpha(picture->argb, width * height); + // Write the non-trivial Alpha flag and lossless version. + if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } // Encode main image stream. err = VP8LEncodeStream(config, picture, &bw); diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 836e940c..ba2127d9 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -123,7 +123,7 @@ static WebPImageInfo* CreateImageInfo(uint32_t x_offset, uint32_t y_offset, WebPImageInfo* image_info = NULL; const int ok = is_lossless ? - VP8LGetInfo(data, size, &width, &height) : + VP8LGetInfo(data, size, &width, &height, NULL) : VP8GetInfo(data, size, size, &width, &height); if (!ok) return NULL; @@ -500,7 +500,7 @@ static WebPMuxError GetImageWidthHeight(const WebPChunk* const image_chunk, ok = (tag == kChunks[IDX_VP8].tag) ? VP8GetInfo(image_chunk->data_, image_chunk->payload_size_, image_chunk->payload_size_, &w, &h) : - VP8LGetInfo(image_chunk->data_, image_chunk->payload_size_, &w, &h); + VP8LGetInfo(image_chunk->data_, image_chunk->payload_size_, &w, &h, NULL); if (ok) { *width = w; *height = h; diff --git a/src/webp/format_constants.h b/src/webp/format_constants.h index 2f7ddf5f..dadd4895 100644 --- a/src/webp/format_constants.h +++ b/src/webp/format_constants.h @@ -20,10 +20,11 @@ // VP8L related constants. #define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. -#define VP8L_MAGIC_BYTE 0x64 // VP8L signature byte. -#define VP8L_MAGIC_BYTE_RSVD 0x65 // VP8L reserved signature byte. +#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte. #define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store // width and height. +#define VP8L_VERSION_BITS 3 // 3 bits reserved for version. +#define VP8L_VERSION 0 // version 0 #define MAX_PALETTE_SIZE 256 #define MAX_CACHE_BITS 11