mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
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:
parent
16612ddd30
commit
9523f2a5de
@ -6,7 +6,7 @@ libwebpencode_la_SOURCES = analysis.c config.c cost.c cost.h filter.c \
|
|||||||
layer.c
|
layer.c
|
||||||
libwebpencode_la_LDFLAGS = -version-info 2:0:0 -lm
|
libwebpencode_la_LDFLAGS = -version-info 2:0:0 -lm
|
||||||
libwebpencode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
|
libwebpencode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
|
||||||
libwebpencodeinclude_HEADERS = ../webp/encode.h ../webp/types.h
|
libwebpencodeinclude_HEADERS = ../webp/encode.h ../webp/types.h ../webp/mux.h
|
||||||
libwebpencodeincludedir = $(includedir)/webp
|
libwebpencodeincludedir = $(includedir)/webp
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libwebpencode.la
|
noinst_LTLIBRARIES = libwebpencode.la
|
||||||
|
@ -12,92 +12,38 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "vp8enci.h"
|
#include "vp8enci.h"
|
||||||
|
#include "../utils/alpha.h"
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
#include "zlib.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
|
|
||||||
#define CHUNK_SIZE 8192
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
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;
|
|
||||||
unsigned char chunk[CHUNK_SIZE];
|
|
||||||
|
|
||||||
*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;
|
|
||||||
|
|
||||||
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 */
|
|
||||||
|
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc) {
|
void VP8EncInitAlpha(VP8Encoder* enc) {
|
||||||
enc->has_alpha_ = (enc->pic_->a != NULL);
|
enc->has_alpha_ = (enc->pic_->a != NULL);
|
||||||
enc->alpha_data_ = NULL;
|
enc->alpha_data_ = NULL;
|
||||||
enc->alpha_data_size_ = 0;
|
enc->alpha_data_size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8EncCodeAlphaBlock(VP8EncIterator* it) {
|
|
||||||
(void)it;
|
|
||||||
// Nothing for now. We just ZLIB-compress in the end.
|
|
||||||
}
|
|
||||||
|
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc) {
|
int VP8EncFinishAlpha(VP8Encoder* enc) {
|
||||||
if (enc->has_alpha_) {
|
if (enc->has_alpha_) {
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
const WebPConfig* config = enc->config_;
|
||||||
const WebPPicture* pic = enc->pic_;
|
const WebPPicture* pic = enc->pic_;
|
||||||
|
uint8_t* tmp_data = NULL;
|
||||||
|
size_t tmp_size = 0;
|
||||||
assert(pic->a);
|
assert(pic->a);
|
||||||
if (!CompressAlpha(pic->a, pic->width * pic->height,
|
if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride,
|
||||||
&enc->alpha_data_, &enc->alpha_data_size_,
|
config->alpha_quality, config->alpha_compression,
|
||||||
enc->config_->alpha_compression)) {
|
&tmp_data, &tmp_size)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
if (tmp_size != (uint32_t)tmp_size) { // Sanity check.
|
||||||
|
free(tmp_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
enc->alpha_data_size_ = (uint32_t)tmp_size;
|
||||||
|
enc->alpha_data_ = tmp_data;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,9 @@ int WebPConfigInitInternal(WebPConfig* const config,
|
|||||||
config->show_compressed = 0;
|
config->show_compressed = 0;
|
||||||
config->preprocessing = 0;
|
config->preprocessing = 0;
|
||||||
config->autofilter = 0;
|
config->autofilter = 0;
|
||||||
config->alpha_compression = 0;
|
|
||||||
config->partition_limit = 0;
|
config->partition_limit = 0;
|
||||||
|
config->alpha_compression = 1;
|
||||||
|
config->alpha_quality = 100;
|
||||||
|
|
||||||
// TODO(skal): tune.
|
// TODO(skal): tune.
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
@ -111,6 +112,8 @@ int WebPValidateConfig(const WebPConfig* const config) {
|
|||||||
return 0;
|
return 0;
|
||||||
if (config->alpha_compression < 0)
|
if (config->alpha_compression < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (config->alpha_quality < 0 || config->alpha_quality > 100)
|
||||||
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,9 +588,6 @@ int VP8EncLoop(VP8Encoder* const enc) {
|
|||||||
ResetAfterSkip(&it);
|
ResetAfterSkip(&it);
|
||||||
}
|
}
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
if (enc->has_alpha_) {
|
|
||||||
VP8EncCodeAlphaBlock(&it);
|
|
||||||
}
|
|
||||||
if (enc->use_layer_) {
|
if (enc->use_layer_) {
|
||||||
VP8EncCodeLayerBlock(&it);
|
VP8EncCodeLayerBlock(&it);
|
||||||
}
|
}
|
||||||
|
@ -147,11 +147,11 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) {
|
|||||||
dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||||
CopyPlane(src->v, src->uv_stride,
|
CopyPlane(src->v, src->uv_stride,
|
||||||
dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
if (dst->a != NULL) {
|
if (dst->a != NULL) {
|
||||||
CopyPlane(src->a, src->a_stride,
|
CopyPlane(src->a, src->a_stride,
|
||||||
dst->a, dst->a_stride, dst->width, dst->height);
|
dst->a, dst->a_stride, dst->width, dst->height);
|
||||||
}
|
}
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
if (dst->u0 != NULL) {
|
if (dst->u0 != NULL) {
|
||||||
int uv0_width = src->width;
|
int uv0_width = src->width;
|
||||||
if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
||||||
@ -194,12 +194,12 @@ int WebPPictureCrop(WebPPicture* const pic,
|
|||||||
tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
|
tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
if (tmp.a) {
|
if (tmp.a) {
|
||||||
const int a_offset = top * pic->a_stride + left;
|
const int a_offset = top * pic->a_stride + left;
|
||||||
CopyPlane(pic->a + a_offset, pic->a_stride,
|
CopyPlane(pic->a + a_offset, pic->a_stride,
|
||||||
tmp.a, tmp.a_stride, width, height);
|
tmp.a, tmp.a_stride, width, height);
|
||||||
}
|
}
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
if (tmp.u0) {
|
if (tmp.u0) {
|
||||||
int w = width;
|
int w = width;
|
||||||
int l = left;
|
int l = left;
|
||||||
@ -347,11 +347,11 @@ int WebPPictureRescale(WebPPicture* const pic, int width, int height) {
|
|||||||
tmp.v,
|
tmp.v,
|
||||||
HALVE(width), HALVE(height), tmp.uv_stride, work);
|
HALVE(width), HALVE(height), tmp.uv_stride, work);
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
if (tmp.a) {
|
if (tmp.a) {
|
||||||
RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
|
RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
|
||||||
tmp.a, width, height, tmp.a_stride, work);
|
tmp.a, width, height, tmp.a_stride, work);
|
||||||
}
|
}
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
if (tmp.u0) {
|
if (tmp.u0) {
|
||||||
int s = 1;
|
int s = 1;
|
||||||
if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
||||||
@ -547,7 +547,6 @@ static int Import(WebPPicture* const picture,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (import_alpha) {
|
if (import_alpha) {
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
const uint8_t* const a_ptr = rgb + 3;
|
const uint8_t* const a_ptr = rgb + 3;
|
||||||
assert(step >= 4);
|
assert(step >= 4);
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
@ -556,7 +555,6 @@ static int Import(WebPPicture* const picture,
|
|||||||
a_ptr[step * x + y * rgb_stride];
|
a_ptr[step * x + y * rgb_stride];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
230
src/enc/syntax.c
230
src/enc/syntax.c
@ -12,24 +12,21 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.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)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define KSIGNATURE 0x9d012a
|
#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_PARTITION0_SIZE (1 << 19) // max size of mode partition
|
||||||
#define MAX_PARTITION_SIZE (1 << 24) // max size for token 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) {
|
static void PutLE32(uint8_t* const data, uint32_t val) {
|
||||||
data[0] = (val >> 0) & 0xff;
|
data[0] = (val >> 0) & 0xff;
|
||||||
@ -38,46 +35,162 @@ static void PutLE32(uint8_t* const data, uint32_t val) {
|
|||||||
data[3] = (val >> 24) & 0xff;
|
data[3] = (val >> 24) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PutHeader(int profile, size_t size0, size_t total_size,
|
static int IsVP8XNeeded(const VP8Encoder* const enc) {
|
||||||
WebPPicture* const pic) {
|
return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
|
||||||
uint8_t buf[KHEADER_SIZE];
|
// This could change in the future.
|
||||||
uint8_t RIFF[KRIFF_SIZE] = {
|
}
|
||||||
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '
|
|
||||||
|
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;
|
uint32_t bits;
|
||||||
|
|
||||||
if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
|
if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
|
||||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW);
|
return 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bits = 0 // keyframe (1b)
|
bits = 0 // keyframe (1b)
|
||||||
| (profile << 1) // profile (3b)
|
| (profile << 1) // profile (3b)
|
||||||
| (1 << 4) // visible (1b)
|
| (1 << 4) // visible (1b)
|
||||||
| ((uint32_t)size0 << 5); // partition length (19b)
|
| ((uint32_t)size0 << 5); // partition length (19b)
|
||||||
buf[0] = bits & 0xff;
|
vp8_frm_hdr[0] = bits & 0xff;
|
||||||
buf[1] = (bits >> 8) & 0xff;
|
vp8_frm_hdr[1] = (bits >> 8) & 0xff;
|
||||||
buf[2] = (bits >> 16) & 0xff;
|
vp8_frm_hdr[2] = (bits >> 16) & 0xff;
|
||||||
// signature
|
// signature
|
||||||
buf[3] = (KSIGNATURE >> 16) & 0xff;
|
vp8_frm_hdr[3] = (KSIGNATURE >> 16) & 0xff;
|
||||||
buf[4] = (KSIGNATURE >> 8) & 0xff;
|
vp8_frm_hdr[4] = (KSIGNATURE >> 8) & 0xff;
|
||||||
buf[5] = (KSIGNATURE >> 0) & 0xff;
|
vp8_frm_hdr[5] = (KSIGNATURE >> 0) & 0xff;
|
||||||
// dimensions
|
// dimensions
|
||||||
buf[6] = pic->width & 0xff;
|
vp8_frm_hdr[6] = pic->width & 0xff;
|
||||||
buf[7] = pic->width >> 8;
|
vp8_frm_hdr[7] = pic->width >> 8;
|
||||||
buf[8] = pic->height & 0xff;
|
vp8_frm_hdr[8] = pic->height & 0xff;
|
||||||
buf[9] = pic->height >> 8;
|
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
|
// Segmentation header
|
||||||
@ -186,14 +299,6 @@ static int WriteExtensions(VP8Encoder* const enc) {
|
|||||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
|
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
|
buffer[KTRAILER_SIZE - 1] = 0x01; // marker
|
||||||
if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
|
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_;
|
const int mb_size = enc->mb_w_ * enc->mb_h_;
|
||||||
uint64_t pos1, pos2, pos3;
|
uint64_t pos1, pos2, pos3;
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
const int need_extensions = enc->has_alpha_ || enc->use_layer_;
|
const int need_extensions = enc->use_layer_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pos1 = VP8BitWriterPos(bw);
|
pos1 = VP8BitWriterPos(bw);
|
||||||
@ -254,25 +359,43 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
|||||||
WebPPicture* const pic = enc->pic_;
|
WebPPicture* const pic = enc->pic_;
|
||||||
VP8BitWriter* const bw = &enc->bw_;
|
VP8BitWriter* const bw = &enc->bw_;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
size_t coded_size, pad;
|
size_t vp8_size, pad, riff_size;
|
||||||
int p;
|
int p;
|
||||||
|
|
||||||
// Partition #0 with header and partition sizes
|
// Partition #0 with header and partition sizes
|
||||||
ok = !!GeneratePartition0(enc);
|
ok = !!GeneratePartition0(enc);
|
||||||
|
|
||||||
// Compute total size (for the RIFF header)
|
// Compute VP8 size
|
||||||
coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1);
|
vp8_size = VP8_FRAME_HEADER_SIZE +
|
||||||
|
VP8BitWriterSize(bw) +
|
||||||
|
3 * (enc->num_parts_ - 1);
|
||||||
for (p = 0; p < enc->num_parts_; ++p) {
|
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
|
// Emit headers and partition #0
|
||||||
{
|
{
|
||||||
const uint8_t* const part0 = VP8BitWriterBuf(bw);
|
const uint8_t* const part0 = VP8BitWriterBuf(bw);
|
||||||
const size_t size0 = VP8BitWriterSize(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)
|
&& pic->writer(part0, size0, pic)
|
||||||
&& EmitPartitionsSize(enc, pic);
|
&& EmitPartitionsSize(enc, pic);
|
||||||
free((void*)part0);
|
free((void*)part0);
|
||||||
@ -289,11 +412,10 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
|||||||
|
|
||||||
// Padding byte
|
// Padding byte
|
||||||
if (ok && pad) {
|
if (ok && pad) {
|
||||||
const uint8_t pad_byte[1] = { 0 };
|
ok = PutPaddingByte(pic);
|
||||||
ok = pic->writer(pad_byte, 1, pic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enc->coded_size_ = (int)coded_size + KRIFF_SIZE;
|
enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ struct VP8Encoder {
|
|||||||
// transparency blob
|
// transparency blob
|
||||||
int has_alpha_;
|
int has_alpha_;
|
||||||
uint8_t* alpha_data_; // non-NULL if transparency is present
|
uint8_t* alpha_data_; // non-NULL if transparency is present
|
||||||
size_t alpha_data_size_;
|
uint32_t alpha_data_size_;
|
||||||
|
|
||||||
// enhancement layer
|
// enhancement layer
|
||||||
int use_layer_;
|
int use_layer_;
|
||||||
@ -436,7 +436,6 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
|||||||
|
|
||||||
// in alpha.c
|
// in alpha.c
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression
|
void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression
|
||||||
void VP8EncCodeAlphaBlock(VP8EncIterator* it); // analyze or code a macroblock
|
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data
|
int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data
|
||||||
void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data
|
void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data
|
||||||
|
|
||||||
|
@ -250,8 +250,8 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
|
|||||||
ResetFilterHeader(enc);
|
ResetFilterHeader(enc);
|
||||||
ResetBoundaryPredictions(enc);
|
ResetBoundaryPredictions(enc);
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
VP8EncInitAlpha(enc);
|
VP8EncInitAlpha(enc);
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
VP8EncInitLayer(enc);
|
VP8EncInitLayer(enc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -260,8 +260,8 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
|
|||||||
|
|
||||||
static void DeleteEncoder(VP8Encoder* enc) {
|
static void DeleteEncoder(VP8Encoder* enc) {
|
||||||
if (enc) {
|
if (enc) {
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
VP8EncDeleteAlpha(enc);
|
VP8EncDeleteAlpha(enc);
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
VP8EncDeleteLayer(enc);
|
VP8EncDeleteLayer(enc);
|
||||||
#endif
|
#endif
|
||||||
free(enc);
|
free(enc);
|
||||||
@ -335,8 +335,8 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
|||||||
ok = VP8EncAnalyze(enc)
|
ok = VP8EncAnalyze(enc)
|
||||||
&& VP8StatLoop(enc)
|
&& VP8StatLoop(enc)
|
||||||
&& VP8EncLoop(enc)
|
&& VP8EncLoop(enc)
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
&& VP8EncFinishAlpha(enc)
|
&& VP8EncFinishAlpha(enc)
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
&& VP8EncFinishLayer(enc)
|
&& VP8EncFinishLayer(enc)
|
||||||
#endif
|
#endif
|
||||||
&& VP8EncWrite(enc);
|
&& VP8EncWrite(enc);
|
||||||
|
@ -71,7 +71,11 @@ typedef struct {
|
|||||||
// Default is set to 0 for easier progressive decoding.
|
// Default is set to 0 for easier progressive decoding.
|
||||||
int partition_limit; // quality degradation allowed to fit the 512k limit on
|
int partition_limit; // quality degradation allowed to fit the 512k limit on
|
||||||
// prediction modes coding (0=no degradation, 100=full)
|
// prediction modes coding (0=no degradation, 100=full)
|
||||||
int alpha_compression; // Algorithm for optimizing the alpha plane (0 = none)
|
int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
|
||||||
|
// 1 = Backward reference counts encoded with
|
||||||
|
// Arithmetic encoder). Default is 1.
|
||||||
|
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
||||||
|
// Default is 100.
|
||||||
} WebPConfig;
|
} WebPConfig;
|
||||||
|
|
||||||
// Enumerate some predefined settings for WebPConfig, depending on the type
|
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||||
|
Loading…
Reference in New Issue
Block a user