reduce number of copies and mallocs in alpha plane enc/dec

Change-Id: Ice48d2dd403c18870567ee6e1b210b1eb3d5b44f
This commit is contained in:
Pascal Massimino 2011-12-21 16:24:25 -08:00
parent b1662b0530
commit a90cb2be6e

View File

@ -23,29 +23,6 @@ extern "C" {
#define MAX_SYMBOLS 255
#define ALPHA_HEADER_LEN 2
// -----------------------------------------------------------------------------
// Alpha Encode.
static int EncodeIdent(const uint8_t* data, int width, int height,
uint8_t** output, size_t* output_size) {
const size_t data_size = height * width;
uint8_t* alpha = NULL;
assert((output != NULL) && (output_size != NULL));
if (data == NULL) {
return 0;
}
alpha = (uint8_t*)malloc(data_size);
if (alpha == NULL) {
return 0;
}
memcpy(alpha, data, data_size);
*output_size = data_size;
*output = alpha;
return 1;
}
// -----------------------------------------------------------------------------
// Zlib-like encoding using TCoder
@ -80,7 +57,7 @@ static size_t GetLongestMatch(const uint8_t* const data,
}
static int EncodeZlibTCoder(const uint8_t* data, int width, int height,
uint8_t** output, size_t* output_size) {
VP8BitWriter* const bw) {
int ok = 0;
const size_t data_size = width * height;
const size_t MAX_DIST = 3 * width;
@ -201,26 +178,19 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height,
// Final bitstream assembly.
{
int n;
VP8BitWriter bw;
VP8BitWriterInit(&bw, 0);
TCoderInit(coder);
TCoderInit(coderd);
TCoderInit(coderl);
for (n = 0; n < num_tokens; ++n) {
const Token* const t = &msg[n];
const int is_literal = (t->dist == 0);
TCoderEncode(coderd, t->dist, &bw);
TCoderEncode(coderd, t->dist, bw);
if (is_literal) { // literal
TCoderEncode(coder, t->literal, &bw);
TCoderEncode(coder, t->literal, bw);
} else {
TCoderEncode(coderl, t->len - MIN_LEN, &bw);
TCoderEncode(coderl, t->len - MIN_LEN, bw);
}
}
// clean up
VP8BitWriterFinish(&bw);
*output = VP8BitWriterBuf(&bw);
*output_size = VP8BitWriterSize(&bw);
ok = 1;
}
@ -229,32 +199,45 @@ static int EncodeZlibTCoder(const uint8_t* data, int width, int height,
if (coderl) TCoderDelete(coderl);
if (coderd) TCoderDelete(coderd);
free(msg);
return ok && !bw->error_;
}
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
int method, VP8BitWriter* const bw) {
int ok = 0;
if (method == 0) {
ok = VP8BitWriterAppend(bw, data, width * height);
ok = ok && !bw->error_;
} else if (method == 1) {
ok = EncodeZlibTCoder(data, width, height, bw);
VP8BitWriterFinish(bw);
}
return ok;
}
// -----------------------------------------------------------------------------
// TODO(skal): move to dsp/ ?
static void CopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
while (height-- > 0) {
memcpy(dst, src, width);
src += src_stride;
dst += dst_stride;
}
}
int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
int quality, int method,
uint8_t** output, size_t* output_size) {
const int kMaxImageDim = (1 << 14) - 1;
uint8_t* compressed_alpha = NULL;
uint8_t* quant_alpha = NULL;
uint8_t* out = NULL;
size_t compressed_size = 0;
const size_t data_size = height * width;
float mse = 0.0;
int ok = 0;
int h;
int ok = 1;
if ((data == NULL) || (output == NULL) || (output_size == NULL)) {
return 0;
}
if (width <= 0 || width > kMaxImageDim ||
height <= 0 || height > kMaxImageDim || stride < width) {
return 0;
}
// quick sanity checks
assert(data != NULL && output != NULL && output_size != NULL);
assert(width > 0 && height > 0);
assert(stride >= width);
if (quality < 0 || quality > 100) {
return 0;
@ -269,74 +252,45 @@ int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
return 0;
}
// Extract the alpha data (WidthXHeight) from raw_data (StrideXHeight).
for (h = 0; h < height; ++h) {
memcpy(quant_alpha + h * width, data + h * stride, width);
}
// Extract alpha data (width x height) from raw_data (stride x height).
CopyPlane(data, stride, quant_alpha, width, width, height);
if (quality < 100) { // No Quantization required for 'quality = 100'.
// 16 Alpha levels gives quite a low MSE w.r.t Original Alpha plane hence
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
// mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
// and Quality:]70, 100] -> Levels:]16, 256].
const int alpha_levels = (quality <= 70) ?
2 + quality / 5 :
16 + (quality - 70) * 8;
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
: (16 + (quality - 70) * 8);
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL);
}
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &mse);
if (ok) {
uint8_t header[ALPHA_HEADER_LEN];
VP8BitWriter bw;
VP8BitWriterInit(&bw,
(method == 0) ? (2 + data_size)
: (data_size >> 5) /* rough estimate of final size */);
header[0] = method & 0xff; // Compression Method.
header[1] = 0; // reserved byte for later use
VP8BitWriterAppend(&bw, header, sizeof(header));
ok = EncodeAlphaInternal(quant_alpha, width, height, method, &bw);
if (!ok) {
free(quant_alpha);
return 0;
}
}
if (method == 0) {
ok = EncodeIdent(quant_alpha, width, height,
&compressed_alpha, &compressed_size);
} else if (method == 1) {
ok = EncodeZlibTCoder(quant_alpha, width, height,
&compressed_alpha, &compressed_size);
}
free(quant_alpha);
if (!ok) {
return 0;
}
out = (uint8_t*)malloc(compressed_size + ALPHA_HEADER_LEN);
if (out == NULL) {
free(compressed_alpha);
return 0;
VP8BitWriterWipeOut(&bw);
} else {
*output = out;
*output = VP8BitWriterBuf(&bw);
*output_size = VP8BitWriterSize(&bw);
}
}
// Alpha bit-stream Header:
// Byte0: Compression Method.
// Byte1: Reserved for later extension.
out[0] = method & 0xff;
out[1] = 0; // Reserved Byte.
out += ALPHA_HEADER_LEN;
memcpy(out, compressed_alpha, compressed_size);
free(compressed_alpha);
out += compressed_size;
*output_size = out - *output;
return 1;
free(quant_alpha);
return ok;
}
// -----------------------------------------------------------------------------
// Alpha Decode.
static int DecodeIdent(const uint8_t* data, size_t data_size,
uint8_t* output) {
assert((data != NULL) && (output != NULL));
memcpy(output, data, data_size);
return 1;
}
static int DecompressZlibTCoder(const uint8_t* data, size_t data_size,
int width, int height,
static int DecompressZlibTCoder(VP8BitReader* const br, int width,
uint8_t* output, size_t output_size) {
int ok = 1;
const size_t MAX_DIST = 3 * width;
@ -348,20 +302,17 @@ static int DecompressZlibTCoder(const uint8_t* data, size_t data_size,
if (coder == NULL || coderd == NULL || coderl == NULL) {
goto End;
}
(void)height; // unused parameter
{
size_t pos = 0;
VP8BitReader br;
VP8InitBitReader(&br, data, data + data_size);
while (pos < output_size && !br.eof_) {
const size_t dist = TCoderDecode(coderd, &br);
assert(br != NULL);
while (pos < output_size && !br->eof_) {
const size_t dist = TCoderDecode(coderd, br);
if (dist == 0) {
const int literal = TCoderDecode(coder, &br);
output[pos] = literal;
output[pos] = TCoderDecode(coder, br);
++pos;
} else {
const size_t len = MIN_LEN + TCoderDecode(coderl, &br);
const size_t len = MIN_LEN + TCoderDecode(coderl, br);
size_t k;
if (pos + len > output_size || pos < dist) goto End;
for (k = 0; k < len; ++k) {
@ -370,7 +321,7 @@ static int DecompressZlibTCoder(const uint8_t* data, size_t data_size,
pos += len;
}
}
ok = !br.eof_;
ok = !br->eof_;
}
End:
@ -386,51 +337,42 @@ int DecodeAlpha(const uint8_t* data, size_t data_size,
int width, int height, int stride,
uint8_t* output) {
uint8_t* decoded_data = NULL;
const size_t decoded_size = height * width;
int ok = 0;
int method;
size_t decoded_size = height * width;
if (data == NULL || output == NULL) {
return 0;
}
assert(width > 0 && height > 0 && stride >= width);
assert(data != NULL && output != NULL);
if (data_size <= ALPHA_HEADER_LEN) {
return 0;
}
if (width <= 0 || height <= 0 || stride < width) {
return 0;
}
method = data[0];
if (method < 0 || method > 1) {
ok = (data[1] == 0);
if (method < 0 || method > 1 || !ok) {
return 0;
}
if (method == 0) {
ok = (data_size >= decoded_size);
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
} else if (method == 1) {
VP8BitReader br;
decoded_data = (uint8_t*)malloc(decoded_size);
if (decoded_data == NULL) {
return 0;
}
data_size -= ALPHA_HEADER_LEN;
data += ALPHA_HEADER_LEN;
if (method == 0) {
ok = DecodeIdent(data, data_size, decoded_data);
} else if (method == 1) {
ok = DecompressZlibTCoder(data, data_size, width, height,
decoded_data, decoded_size);
VP8InitBitReader(&br, data + ALPHA_HEADER_LEN, data + data_size);
ok = DecompressZlibTCoder(&br, width, decoded_data, decoded_size);
}
// Construct raw_data (height x stride) from alpha data (height x width).
if (ok) {
// Construct raw_data (HeightXStride) from the alpha data (HeightXWidth).
int h;
for (h = 0; h < height; ++h) {
memcpy(output + h * stride, decoded_data + h * width, width);
}
CopyPlane(decoded_data, width, output, stride, width, height);
}
if (method == 1) {
free(decoded_data);
}
return ok;
}