Add Alpha Encode support from WebPEncode.

Extend WebP Encode functionality to encode Alpha data and produce
bit-stream (RIFF+VP8X+ALPH+VP8) corresponding to WebP-Alpha.

Change-Id: I983b4cd97be94a86a8e6d03b3b9c728db851bf48
This commit is contained in:
Vikas Arora
2011-12-01 12:14:15 +05:30
parent 16612ddd30
commit 9523f2a5de
9 changed files with 207 additions and 138 deletions

View File

@ -12,24 +12,21 @@
#include <assert.h>
#include <math.h>
#include "vp8enci.h"
#include "./vp8enci.h"
#include "../dec/webpi.h" // For chunk-size constants.
#include "../webp/mux.h" // For 'ALPHA_FLAG' constant.
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#define KSIGNATURE 0x9d012a
#define KHEADER_SIZE 10
#define KRIFF_SIZE 20
#define KSIZE_OFFSET (KRIFF_SIZE - 8)
#define MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
#define MAX_PARTITION_SIZE (1 << 24) // max size for token partition
//------------------------------------------------------------------------------
// Writers for header's various pieces (in order of appearance)
// Main keyframe header
//------------------------------------------------------------------------------
// Helper functions
static void PutLE32(uint8_t* const data, uint32_t val) {
data[0] = (val >> 0) & 0xff;
@ -38,46 +35,162 @@ static void PutLE32(uint8_t* const data, uint32_t val) {
data[3] = (val >> 24) & 0xff;
}
static int PutHeader(int profile, size_t size0, size_t total_size,
WebPPicture* const pic) {
uint8_t buf[KHEADER_SIZE];
uint8_t RIFF[KRIFF_SIZE] = {
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '
static int IsVP8XNeeded(const VP8Encoder* const enc) {
return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
// This could change in the future.
}
static int PutPaddingByte(const WebPPicture* const pic) {
const uint8_t pad_byte[1] = { 0 };
return !!pic->writer(pad_byte, 1, pic);
}
//------------------------------------------------------------------------------
// Writers for header's various pieces (in order of appearance)
static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
size_t riff_size) {
const WebPPicture* const pic = enc->pic_;
uint8_t riff[RIFF_HEADER_SIZE] = {
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
};
PutLE32(riff + TAG_SIZE, riff_size);
if (!pic->writer(riff, sizeof(riff), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
const WebPPicture* const pic = enc->pic_;
uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
'V', 'P', '8', 'X'
};
uint32_t flags = 0;
assert(IsVP8XNeeded(enc));
if (enc->has_alpha_) {
flags |= ALPHA_FLAG;
}
PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
PutLE32(vp8x + CHUNK_HEADER_SIZE + 4 , pic->width);
PutLE32(vp8x + CHUNK_HEADER_SIZE + 8 , pic->height);
if(!pic->writer(vp8x, sizeof(vp8x), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
const WebPPicture* const pic = enc->pic_;
uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
'A', 'L', 'P', 'H'
};
assert(enc->has_alpha_);
// Alpha chunk header.
PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_);
if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
// Alpha chunk data.
if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
// Padding.
if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
size_t vp8_size) {
uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
'V', 'P', '8', ' '
};
PutLE32(vp8_chunk_hdr + TAG_SIZE, vp8_size);
if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
int profile, size_t size0) {
uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
uint32_t bits;
if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW);
}
if (total_size > 0xfffffffeU - KRIFF_SIZE) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
}
PutLE32(RIFF + 4, (uint32_t)(total_size + KSIZE_OFFSET));
PutLE32(RIFF + 16, (uint32_t)total_size);
if (!pic->writer(RIFF, sizeof(RIFF), pic)) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
}
bits = 0 // keyframe (1b)
| (profile << 1) // profile (3b)
| (1 << 4) // visible (1b)
| ((uint32_t)size0 << 5); // partition length (19b)
buf[0] = bits & 0xff;
buf[1] = (bits >> 8) & 0xff;
buf[2] = (bits >> 16) & 0xff;
| ((uint32_t)size0 << 5); // partition length (19b)
vp8_frm_hdr[0] = bits & 0xff;
vp8_frm_hdr[1] = (bits >> 8) & 0xff;
vp8_frm_hdr[2] = (bits >> 16) & 0xff;
// signature
buf[3] = (KSIGNATURE >> 16) & 0xff;
buf[4] = (KSIGNATURE >> 8) & 0xff;
buf[5] = (KSIGNATURE >> 0) & 0xff;
vp8_frm_hdr[3] = (KSIGNATURE >> 16) & 0xff;
vp8_frm_hdr[4] = (KSIGNATURE >> 8) & 0xff;
vp8_frm_hdr[5] = (KSIGNATURE >> 0) & 0xff;
// dimensions
buf[6] = pic->width & 0xff;
buf[7] = pic->width >> 8;
buf[8] = pic->height & 0xff;
buf[9] = pic->height >> 8;
vp8_frm_hdr[6] = pic->width & 0xff;
vp8_frm_hdr[7] = pic->width >> 8;
vp8_frm_hdr[8] = pic->height & 0xff;
vp8_frm_hdr[9] = pic->height >> 8;
return pic->writer(buf, sizeof(buf), pic);
if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
return VP8_ENC_ERROR_BAD_WRITE;
}
return VP8_ENC_OK;
}
// WebP Headers.
static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
size_t vp8_size, size_t riff_size) {
WebPPicture* const pic = enc->pic_;
WebPEncodingError err = VP8_ENC_OK;
// RIFF header.
err = PutRIFFHeader(enc, riff_size);
if (err != VP8_ENC_OK) goto Error;
// VP8X.
if (IsVP8XNeeded(enc)) {
err = PutVP8XHeader(enc);
if (err != VP8_ENC_OK) goto Error;
}
// Alpha.
if (enc->has_alpha_) {
err = PutAlphaChunk(enc);
if (err != VP8_ENC_OK) goto Error;
}
// VP8 header.
err = PutVP8Header(pic, vp8_size);
if (err != VP8_ENC_OK) goto Error;
// VP8 frame header.
err = PutVP8FrameHeader(pic, enc->profile_, size0);
if (err != VP8_ENC_OK) goto Error;
// All OK.
return 1;
// Error.
Error:
return WebPEncodingSetError(pic, err);
}
// Segmentation header
@ -186,14 +299,6 @@ static int WriteExtensions(VP8Encoder* const enc) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
}
}
// Alpha (bytes 4..6)
PutLE24(buffer + 4, enc->alpha_data_size_);
if (enc->alpha_data_size_ > 0) {
assert(enc->has_alpha_);
if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
}
}
buffer[KTRAILER_SIZE - 1] = 0x01; // marker
if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
@ -211,7 +316,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) {
const int mb_size = enc->mb_w_ * enc->mb_h_;
uint64_t pos1, pos2, pos3;
#ifdef WEBP_EXPERIMENTAL_FEATURES
const int need_extensions = enc->has_alpha_ || enc->use_layer_;
const int need_extensions = enc->use_layer_;
#endif
pos1 = VP8BitWriterPos(bw);
@ -254,25 +359,43 @@ int VP8EncWrite(VP8Encoder* const enc) {
WebPPicture* const pic = enc->pic_;
VP8BitWriter* const bw = &enc->bw_;
int ok = 0;
size_t coded_size, pad;
size_t vp8_size, pad, riff_size;
int p;
// Partition #0 with header and partition sizes
ok = !!GeneratePartition0(enc);
// Compute total size (for the RIFF header)
coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1);
// Compute VP8 size
vp8_size = VP8_FRAME_HEADER_SIZE +
VP8BitWriterSize(bw) +
3 * (enc->num_parts_ - 1);
for (p = 0; p < enc->num_parts_; ++p) {
coded_size += VP8BitWriterSize(enc->parts_ + p);
vp8_size += VP8BitWriterSize(enc->parts_ + p);
}
pad = vp8_size & 1;
vp8_size += pad;
// Computer RIFF size
// At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data.
riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
}
if (enc->has_alpha_) { // Add size for: ALPH header + data.
const uint32_t padded_alpha_size = enc->alpha_data_size_ +
(enc->alpha_data_size_ & 1);
riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
}
// Sanity check.
if (riff_size > 0xfffffffeU) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
}
pad = coded_size & 1;
coded_size += pad;
// Emit headers and partition #0
{
const uint8_t* const part0 = VP8BitWriterBuf(bw);
const size_t size0 = VP8BitWriterSize(bw);
ok = ok && PutHeader(enc->profile_, size0, coded_size, pic)
ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
&& pic->writer(part0, size0, pic)
&& EmitPartitionsSize(enc, pic);
free((void*)part0);
@ -289,11 +412,10 @@ int VP8EncWrite(VP8Encoder* const enc) {
// Padding byte
if (ok && pad) {
const uint8_t pad_byte[1] = { 0 };
ok = pic->writer(pad_byte, 1, pic);
ok = PutPaddingByte(pic);
}
enc->coded_size_ = (int)coded_size + KRIFF_SIZE;
enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
return ok;
}