Change in lossless bit-stream.

Change the lossless signature to 0x2f
Add 1 bit indicator for 'droppable (or trivial) alpha)'.
Add 3 bit lossless version (for future extension like yuv support).
Change the sub-resolution information to 3 bits implying range [2 .. 9]

Change-Id: Ic7b8c069240bbcd326cf5d5d4cd2dde8667851e2
This commit is contained in:
Vikas Arora 2012-06-04 12:46:01 +05:30
parent 5f7bb3f53a
commit 069f903a08
6 changed files with 64 additions and 26 deletions

View File

@ -79,34 +79,37 @@ static int DecodeImageStream(int xsize, int ysize,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
int VP8LCheckSignature(const uint8_t* const data, size_t size) { int VP8LCheckSignature(const uint8_t* const data, size_t size) {
return (size >= 1) && return (size >= 1) && (data[0] == VP8L_MAGIC_BYTE);
(data[0] == VP8L_MAGIC_BYTE || data[0] == VP8L_MAGIC_BYTE_RSVD);
} }
static int ReadImageSize(VP8LBitReader* const br, static int ReadImageInfo(VP8LBitReader* const br,
int* const width, int* const height) { int* const width, int* const height,
int* const has_alpha) {
const uint8_t signature = VP8LReadBits(br, 8); const uint8_t signature = VP8LReadBits(br, 8);
if (!VP8LCheckSignature(&signature, 1)) { if (!VP8LCheckSignature(&signature, 1)) {
return 0; return 0;
} }
*width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1;
*height = 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; return 1;
} }
int VP8LGetInfo(const uint8_t* data, size_t data_size, 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) { if (data == NULL || data_size < kHeaderBytes) {
return 0; // not enough data return 0; // not enough data
} else { } else {
int w, h; int w, h, a;
VP8LBitReader br; VP8LBitReader br;
VP8LInitBitReader(&br, data, data_size); VP8LInitBitReader(&br, data, data_size);
if (!ReadImageSize(&br, &w, &h)) { if (!ReadImageInfo(&br, &w, &h, &a)) {
return 0; return 0;
} }
if (width != NULL) *width = w; if (width != NULL) *width = w;
if (height != NULL) *height = h; if (height != NULL) *height = h;
if (has_alpha != NULL) *has_alpha = a;
return 1; return 1;
} }
} }
@ -308,7 +311,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
int num_htree_groups = 1; int num_htree_groups = 1;
if (VP8LReadBits(br, 1)) { // use meta Huffman codes 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_xsize = VP8LSubSampleSize(xsize, huffman_precision);
const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision);
const int huffman_pixs = huffman_xsize * huffman_ysize; const int huffman_pixs = huffman_xsize * huffman_ysize;
@ -735,7 +738,7 @@ static int ReadTransform(int* const xsize, int const* ysize,
switch (type) { switch (type) {
case PREDICTOR_TRANSFORM: case PREDICTOR_TRANSFORM:
case CROSS_COLOR_TRANSFORM: case CROSS_COLOR_TRANSFORM:
transform->bits_ = VP8LReadBits(br, 4); transform->bits_ = VP8LReadBits(br, 3) + 2;
ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_,
transform->bits_), transform->bits_),
VP8LSubSampleSize(transform->ysize_, 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 VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
int width, height; int width, height, has_alpha;
if (dec == NULL) return 0; if (dec == NULL) return 0;
if (io == NULL) { if (io == NULL) {
@ -1017,7 +1020,7 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
dec->io_ = io; dec->io_ = io;
dec->status_ = VP8_STATUS_OK; dec->status_ = VP8_STATUS_OK;
VP8LInitBitReader(&dec->br_, io->data, io->data_size); 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; dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
goto Error; goto Error;
} }

View File

@ -91,11 +91,10 @@ typedef struct {
int VP8LCheckSignature(const uint8_t* const data, size_t size); int VP8LCheckSignature(const uint8_t* const data, size_t size);
// Validates the VP8L data-header and retrieves basic header information viz // Validates the VP8L data-header and retrieves basic header information viz
// width and height. Returns 0 in case of formatting error. width/height // width, height and alpha. Returns 0 in case of formatting error.
// can be passed NULL. // width/height/has_alpha can be passed NULL.
int VP8LGetInfo(const uint8_t* data, size_t data_size, // data available so far 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 // 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 // into *output, which must be of size width x height. Returns false in case

View File

@ -577,11 +577,12 @@ static VP8StatusCode GetFeatures(const uint8_t* data, size_t data_size,
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
} else { } else {
int has_alpha;
// Validates raw VP8L data. // 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; return VP8_STATUS_BITSTREAM_ERROR;
} }
features->has_alpha = 1; features->has_alpha = has_alpha;
} }
return VP8_STATUS_OK; // Return features from VP8 header. return VP8_STATUS_OK; // Return features from VP8 header.

View File

@ -30,6 +30,8 @@ extern "C" {
#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
#define MAX_HUFF_IMAGE_SIZE (32 * 1024 * 1024) #define MAX_HUFF_IMAGE_SIZE (32 * 1024 * 1024)
#define MIN_HISTO_BITS 2
#define MAX_HISTO_BITS 9
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Palette // Palette
@ -40,6 +42,15 @@ static int CompareColors(const void* p1, const void* p2) {
return (a < b) ? -1 : (a > b) ? 1 : 0; 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, // 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. // creates a palette and returns true, else returns false.
static int AnalyzeAndCreatePalette(const uint32_t* const argb, int num_pix, 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 nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2;
VP8LWriteBits(bw, 3, nbitpairs - 1); VP8LWriteBits(bw, 3, nbitpairs - 1);
assert(trimmed_length >= 2);
VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
} }
StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code); StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
@ -501,7 +513,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
} }
histogram_image_size = max_index; 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, ok = EncodeImageInternal(bw, histogram_argb,
VP8LSubSampleSize(width, histogram_bits), VP8LSubSampleSize(width, histogram_bits),
VP8LSubSampleSize(height, histogram_bits), VP8LSubSampleSize(height, histogram_bits),
@ -594,7 +607,8 @@ static int ApplyPredictFilter(const VP8LEncoder* const enc,
enc->transform_data_); enc->transform_data_);
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM); 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_, if (!EncodeImageInternal(bw, enc->transform_data_,
transform_width, transform_height, quality, 0, 0)) { transform_width, transform_height, quality, 0, 0)) {
return 0; return 0;
@ -614,7 +628,8 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc,
enc->argb_, enc->transform_data_); enc->argb_, enc->transform_data_);
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM); 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_, if (!EncodeImageInternal(bw, enc->transform_data_,
transform_width, transform_height, quality, 0, 0)) { transform_width, transform_height, quality, 0, 0)) {
return 0; return 0;
@ -656,6 +671,12 @@ static int WriteImageSize(const WebPPicture* const pic,
return !bw->error_; 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, static WebPEncodingError WriteImage(const WebPPicture* const pic,
VP8LBitWriter* const bw, VP8LBitWriter* const bw,
size_t* const coded_size) { size_t* const coded_size) {
@ -769,6 +790,7 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw,
// Save palette to bitstream. // Save palette to bitstream.
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM);
assert(palette_size >= 2);
VP8LWriteBits(bw, 8, palette_size - 1); VP8LWriteBits(bw, 8, palette_size - 1);
for (i = palette_size - 1; i >= 1; --i) { for (i = palette_size - 1; i >= 1; --i) {
palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); 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; if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
++histo_bits; ++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) { static void InitEncParams(VP8LEncoder* const enc) {
@ -938,6 +961,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
int VP8LEncodeImage(const WebPConfig* const config, int VP8LEncodeImage(const WebPConfig* const config,
const WebPPicture* const picture) { const WebPPicture* const picture) {
int width, height; int width, height;
int has_alpha;
size_t coded_size; size_t coded_size;
WebPEncodingError err = VP8_ENC_OK; WebPEncodingError err = VP8_ENC_OK;
VP8LBitWriter bw; VP8LBitWriter bw;
@ -954,7 +978,17 @@ int VP8LEncodeImage(const WebPConfig* const config,
// Write image size. // Write image size.
VP8LBitWriterInit(&bw, (width * height) >> 1); 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. // Encode main image stream.
err = VP8LEncodeStream(config, picture, &bw); err = VP8LEncodeStream(config, picture, &bw);

View File

@ -123,7 +123,7 @@ static WebPImageInfo* CreateImageInfo(uint32_t x_offset, uint32_t y_offset,
WebPImageInfo* image_info = NULL; WebPImageInfo* image_info = NULL;
const int ok = is_lossless ? const int ok = is_lossless ?
VP8LGetInfo(data, size, &width, &height) : VP8LGetInfo(data, size, &width, &height, NULL) :
VP8GetInfo(data, size, size, &width, &height); VP8GetInfo(data, size, size, &width, &height);
if (!ok) return NULL; if (!ok) return NULL;
@ -500,7 +500,7 @@ static WebPMuxError GetImageWidthHeight(const WebPChunk* const image_chunk,
ok = (tag == kChunks[IDX_VP8].tag) ? ok = (tag == kChunks[IDX_VP8].tag) ?
VP8GetInfo(image_chunk->data_, image_chunk->payload_size_, VP8GetInfo(image_chunk->data_, image_chunk->payload_size_,
image_chunk->payload_size_, &w, &h) : 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) { if (ok) {
*width = w; *width = w;
*height = h; *height = h;

View File

@ -20,10 +20,11 @@
// VP8L related constants. // VP8L related constants.
#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. #define VP8L_SIGNATURE_SIZE 1 // VP8L signature size.
#define VP8L_MAGIC_BYTE 0x64 // VP8L signature byte. #define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte.
#define VP8L_MAGIC_BYTE_RSVD 0x65 // VP8L reserved signature byte.
#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store #define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store
// width and height. // 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_PALETTE_SIZE 256
#define MAX_CACHE_BITS 11 #define MAX_CACHE_BITS 11