mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
libwebp interface changes for lossless encoding.
Change-Id: I703a1a18347acf78378cb23fddc6e5ca6dc6a0bb
This commit is contained in:
parent
c04eb7be9d
commit
227110c4c3
@ -44,6 +44,7 @@ int WebPConfigInitInternal(WebPConfig* const config,
|
||||
config->alpha_compression = 1;
|
||||
config->alpha_filtering = 1;
|
||||
config->alpha_quality = 100;
|
||||
config->lossless = 0;
|
||||
|
||||
// TODO(skal): tune.
|
||||
switch (preset) {
|
||||
@ -116,6 +117,8 @@ int WebPValidateConfig(const WebPConfig* const config) {
|
||||
return 0;
|
||||
if (config->alpha_quality < 0 || config->alpha_quality > 100)
|
||||
return 0;
|
||||
if (config->lossless < 0 || config->lossless > 1)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -32,75 +32,88 @@ int WebPPictureAlloc(WebPPicture* const picture) {
|
||||
const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
|
||||
const int width = picture->width;
|
||||
const int height = picture->height;
|
||||
const int y_stride = width;
|
||||
const int uv_width = HALVE(width);
|
||||
const int uv_height = HALVE(height);
|
||||
const int uv_stride = uv_width;
|
||||
int uv0_stride = 0;
|
||||
int a_width, a_stride;
|
||||
uint64_t y_size, uv_size, uv0_size, a_size, total_size;
|
||||
uint8_t* mem;
|
||||
|
||||
// U/V
|
||||
switch (uv_csp) {
|
||||
case WEBP_YUV420:
|
||||
break;
|
||||
if (!picture->use_argb_input) {
|
||||
const int y_stride = width;
|
||||
const int uv_width = HALVE(width);
|
||||
const int uv_height = HALVE(height);
|
||||
const int uv_stride = uv_width;
|
||||
int uv0_stride = 0;
|
||||
int a_width, a_stride;
|
||||
uint64_t y_size, uv_size, uv0_size, a_size, total_size;
|
||||
uint8_t* mem;
|
||||
|
||||
// U/V
|
||||
switch (uv_csp) {
|
||||
case WEBP_YUV420:
|
||||
break;
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
case WEBP_YUV400: // for now, we'll just reset the U/V samples
|
||||
break;
|
||||
case WEBP_YUV422:
|
||||
uv0_stride = uv_width;
|
||||
break;
|
||||
case WEBP_YUV444:
|
||||
uv0_stride = width;
|
||||
break;
|
||||
case WEBP_YUV400: // for now, we'll just reset the U/V samples
|
||||
break;
|
||||
case WEBP_YUV422:
|
||||
uv0_stride = uv_width;
|
||||
break;
|
||||
case WEBP_YUV444:
|
||||
uv0_stride = width;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
uv0_size = height * uv0_stride;
|
||||
|
||||
// alpha
|
||||
a_width = has_alpha ? width : 0;
|
||||
a_stride = a_width;
|
||||
y_size = (uint64_t)y_stride * height;
|
||||
uv_size = (uint64_t)uv_stride * uv_height;
|
||||
a_size = (uint64_t)a_stride * height;
|
||||
|
||||
total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
|
||||
|
||||
// Security and validation checks
|
||||
if (width <= 0 || height <= 0 || // check for luma/alpha param error
|
||||
uv_width < 0 || uv_height < 0 || // check for u/v param error
|
||||
y_size >= (1ULL << 40) || // check for reasonable global size
|
||||
(size_t)total_size != total_size) { // check for overflow on 32bit
|
||||
return 0;
|
||||
}
|
||||
uv0_size = height * uv0_stride;
|
||||
}
|
||||
picture->y_stride = y_stride;
|
||||
picture->uv_stride = uv_stride;
|
||||
picture->a_stride = a_stride;
|
||||
picture->uv0_stride = uv0_stride;
|
||||
WebPPictureFree(picture); // erase previous buffer
|
||||
mem = (uint8_t*)malloc((size_t)total_size);
|
||||
if (mem == NULL) return 0;
|
||||
|
||||
// alpha
|
||||
a_width = has_alpha ? width : 0;
|
||||
a_stride = a_width;
|
||||
y_size = (uint64_t)y_stride * height;
|
||||
uv_size = (uint64_t)uv_stride * uv_height;
|
||||
a_size = (uint64_t)a_stride * height;
|
||||
picture->y = mem;
|
||||
mem += y_size;
|
||||
|
||||
total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
|
||||
picture->u = mem;
|
||||
mem += uv_size;
|
||||
picture->v = mem;
|
||||
mem += uv_size;
|
||||
|
||||
// Security and validation checks
|
||||
if (width <= 0 || height <= 0 || // check for luma/alpha param error
|
||||
uv_width < 0 || uv_height < 0 || // check for u/v param error
|
||||
y_size >= (1ULL << 40) || // check for reasonable global size
|
||||
(size_t)total_size != total_size) { // check for overflow on 32bit
|
||||
return 0;
|
||||
}
|
||||
picture->y_stride = y_stride;
|
||||
picture->uv_stride = uv_stride;
|
||||
picture->a_stride = a_stride;
|
||||
picture->uv0_stride = uv0_stride;
|
||||
WebPPictureFree(picture); // erase previous buffer
|
||||
mem = (uint8_t*)malloc((size_t)total_size);
|
||||
if (mem == NULL) return 0;
|
||||
|
||||
picture->y = mem;
|
||||
mem += y_size;
|
||||
|
||||
picture->u = mem;
|
||||
mem += uv_size;
|
||||
picture->v = mem;
|
||||
mem += uv_size;
|
||||
|
||||
if (a_size) {
|
||||
picture->a = mem;
|
||||
mem += a_size;
|
||||
}
|
||||
if (uv0_size) {
|
||||
picture->u0 = mem;
|
||||
mem += uv0_size;
|
||||
picture->v0 = mem;
|
||||
mem += uv0_size;
|
||||
if (a_size) {
|
||||
picture->a = mem;
|
||||
mem += a_size;
|
||||
}
|
||||
if (uv0_size) {
|
||||
picture->u0 = mem;
|
||||
mem += uv0_size;
|
||||
picture->v0 = mem;
|
||||
mem += uv0_size;
|
||||
}
|
||||
} else {
|
||||
if (width <= 0 || height <= 0 ||
|
||||
width >= 0x4000 || height >= 0x4000) {
|
||||
return 0;
|
||||
}
|
||||
WebPPictureFree(picture); // erase previous buffer
|
||||
picture->argb = (uint32_t*)malloc(width * height *
|
||||
sizeof(*picture->argb));
|
||||
if (picture->argb == NULL) return 0;
|
||||
picture->argb_stride = width;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@ -114,12 +127,14 @@ static void WebPPictureGrabSpecs(const WebPPicture* const src,
|
||||
dst->y = dst->u = dst->v = NULL;
|
||||
dst->u0 = dst->v0 = NULL;
|
||||
dst->a = NULL;
|
||||
dst->argb = NULL;
|
||||
}
|
||||
|
||||
// Release memory owned by 'picture'.
|
||||
void WebPPictureFree(WebPPicture* const picture) {
|
||||
if (picture != NULL) {
|
||||
free(picture->y);
|
||||
free(picture->argb);
|
||||
WebPPictureGrabSpecs(NULL, picture);
|
||||
}
|
||||
}
|
||||
@ -144,28 +159,34 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) {
|
||||
WebPPictureGrabSpecs(src, dst);
|
||||
if (!WebPPictureAlloc(dst)) return 0;
|
||||
|
||||
CopyPlane(src->y, src->y_stride,
|
||||
dst->y, dst->y_stride, dst->width, dst->height);
|
||||
CopyPlane(src->u, src->uv_stride,
|
||||
dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||
CopyPlane(src->v, src->uv_stride,
|
||||
dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||
if (dst->a != NULL) {
|
||||
CopyPlane(src->a, src->a_stride,
|
||||
dst->a, dst->a_stride, dst->width, dst->height);
|
||||
}
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (dst->u0 != NULL) {
|
||||
int uv0_width = src->width;
|
||||
if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
||||
uv0_width = HALVE(uv0_width);
|
||||
if (!src->use_argb_input) {
|
||||
CopyPlane(src->y, src->y_stride,
|
||||
dst->y, dst->y_stride, dst->width, dst->height);
|
||||
CopyPlane(src->u, src->uv_stride,
|
||||
dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||
CopyPlane(src->v, src->uv_stride,
|
||||
dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
|
||||
if (dst->a != NULL) {
|
||||
CopyPlane(src->a, src->a_stride,
|
||||
dst->a, dst->a_stride, dst->width, dst->height);
|
||||
}
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (dst->u0 != NULL) {
|
||||
int uv0_width = src->width;
|
||||
if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) {
|
||||
uv0_width = HALVE(uv0_width);
|
||||
}
|
||||
CopyPlane(src->u0, src->uv0_stride,
|
||||
dst->u0, dst->uv0_stride, uv0_width, dst->height);
|
||||
CopyPlane(src->v0, src->uv0_stride,
|
||||
dst->v0, dst->uv0_stride, uv0_width, dst->height);
|
||||
}
|
||||
CopyPlane(src->u0, src->uv0_stride,
|
||||
dst->u0, dst->uv0_stride, uv0_width, dst->height);
|
||||
CopyPlane(src->v0, src->uv0_stride,
|
||||
dst->v0, dst->uv0_stride, uv0_width, dst->height);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride,
|
||||
(uint8_t*)dst->argb, 4 * dst->argb_stride,
|
||||
4 * dst->width, dst->height);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -438,64 +459,88 @@ static int Import(WebPPicture* const picture,
|
||||
const int width = picture->width;
|
||||
const int height = picture->height;
|
||||
|
||||
// Import luma plane
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
const int offset = step * x + y * rgb_stride;
|
||||
picture->y[x + y * picture->y_stride] =
|
||||
rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
|
||||
}
|
||||
}
|
||||
|
||||
// Downsample U/V plane
|
||||
if (uv_csp != WEBP_YUV400) {
|
||||
for (y = 0; y < (height >> 1); ++y) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV(x, y, SUM4);
|
||||
}
|
||||
if (picture->width & 1) {
|
||||
RGB_TO_UV(x, y, SUM2V);
|
||||
}
|
||||
}
|
||||
if (height & 1) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV(x, y, SUM2H);
|
||||
}
|
||||
if (width & 1) {
|
||||
RGB_TO_UV(x, y, SUM1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
// Store original U/V samples too
|
||||
if (uv_csp == WEBP_YUV422) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV0(2 * x, x, y, SUM2H);
|
||||
}
|
||||
if (width & 1) {
|
||||
RGB_TO_UV0(2 * x, x, y, SUM1);
|
||||
}
|
||||
}
|
||||
} else if (uv_csp == WEBP_YUV444) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
RGB_TO_UV0(x, x, y, SUM1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
MakeGray(picture);
|
||||
}
|
||||
|
||||
if (import_alpha) {
|
||||
const uint8_t* const a_ptr = rgb + 3;
|
||||
assert(step >= 4);
|
||||
if (!picture->use_argb_input) {
|
||||
// Import luma plane
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
picture->a[x + y * picture->a_stride] =
|
||||
a_ptr[step * x + y * rgb_stride];
|
||||
const int offset = step * x + y * rgb_stride;
|
||||
picture->y[x + y * picture->y_stride] =
|
||||
rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
|
||||
}
|
||||
}
|
||||
|
||||
// Downsample U/V plane
|
||||
if (uv_csp != WEBP_YUV400) {
|
||||
for (y = 0; y < (height >> 1); ++y) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV(x, y, SUM4);
|
||||
}
|
||||
if (picture->width & 1) {
|
||||
RGB_TO_UV(x, y, SUM2V);
|
||||
}
|
||||
}
|
||||
if (height & 1) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV(x, y, SUM2H);
|
||||
}
|
||||
if (width & 1) {
|
||||
RGB_TO_UV(x, y, SUM1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
// Store original U/V samples too
|
||||
if (uv_csp == WEBP_YUV422) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < (width >> 1); ++x) {
|
||||
RGB_TO_UV0(2 * x, x, y, SUM2H);
|
||||
}
|
||||
if (width & 1) {
|
||||
RGB_TO_UV0(2 * x, x, y, SUM1);
|
||||
}
|
||||
}
|
||||
} else if (uv_csp == WEBP_YUV444) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
RGB_TO_UV0(x, x, y, SUM1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
MakeGray(picture);
|
||||
}
|
||||
|
||||
if (import_alpha) {
|
||||
const uint8_t* const a_ptr = rgb + 3;
|
||||
assert(step >= 4);
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
picture->a[x + y * picture->a_stride] =
|
||||
a_ptr[step * x + y * rgb_stride];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!import_alpha) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
const int offset = step * x + y * rgb_stride;
|
||||
const uint32_t argb = 0xff000000 | (r_ptr[offset] << 16) |
|
||||
(g_ptr[offset] << 8) | (b_ptr[offset]);
|
||||
picture->argb[x + y * picture->argb_stride] = argb;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const uint8_t* const a_ptr = rgb + 3;
|
||||
assert(step >= 4);
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
const int offset = step * x + y * rgb_stride;
|
||||
const uint32_t argb = (a_ptr[offset] << 24) | (r_ptr[offset] << 16) |
|
||||
(g_ptr[offset] << 8) | (b_ptr[offset]);
|
||||
picture->argb[x + y * picture->argb_stride] = argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
230
src/enc/vp8l.c
Normal file
230
src/enc/vp8l.c
Normal file
@ -0,0 +1,230 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// main entry for the decoder
|
||||
//
|
||||
// Author: Vikas Arora (vikaas.arora@gmail.com)
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "./vp8enci.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/bit_writer.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static const uint32_t kImageSizeBits = 14;
|
||||
|
||||
static int VP8LEncAnalyze(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int EncodeImageInternal(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CreatePalette(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void EvalSubtractGreen(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
}
|
||||
|
||||
static int ApplyPredictFilter(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ApplyCrossColorFilter(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void EvalEmergingPalette(VP8LEncoder* const enc) {
|
||||
(void)enc;
|
||||
}
|
||||
|
||||
static void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
data[0] = (val >> 0) & 0xff;
|
||||
data[1] = (val >> 8) & 0xff;
|
||||
data[2] = (val >> 16) & 0xff;
|
||||
data[3] = (val >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc,
|
||||
size_t riff_size, size_t vp8l_size) {
|
||||
const WebPPicture* const pic = enc->pic_;
|
||||
uint8_t riff[HEADER_SIZE + SIGNATURE_SIZE] = {
|
||||
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
|
||||
'V', 'P', '8', 'L', 0, 0, 0, 0, LOSSLESS_MAGIC_BYTE,
|
||||
};
|
||||
if (riff_size < (vp8l_size + TAG_SIZE + CHUNK_HEADER_SIZE)) {
|
||||
return VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
||||
}
|
||||
PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
|
||||
PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
|
||||
if (!pic->writer(riff, sizeof(riff), pic)) {
|
||||
return VP8_ENC_ERROR_BAD_WRITE;
|
||||
}
|
||||
return VP8_ENC_OK;
|
||||
}
|
||||
|
||||
static WebPEncodingError WriteImage(VP8LEncoder* const enc) {
|
||||
size_t riff_size, vp8l_size, webpll_size, pad;
|
||||
WebPPicture* const pic = enc->pic_;
|
||||
WebPEncodingError err = VP8_ENC_OK;
|
||||
const uint8_t* const webpll_data = VP8LBitWriterFinish(&enc->bw_);
|
||||
|
||||
webpll_size = VP8LBitWriterNumBytes(&enc->bw_);
|
||||
vp8l_size = SIGNATURE_SIZE + webpll_size;
|
||||
pad = vp8l_size & 1;
|
||||
vp8l_size += pad;
|
||||
|
||||
riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size;
|
||||
err = WriteRiffHeader(enc, riff_size, vp8l_size);
|
||||
if (err != VP8_ENC_OK) goto Error;
|
||||
|
||||
if (!pic->writer(webpll_data, webpll_size, pic)) {
|
||||
err = VP8_ENC_ERROR_BAD_WRITE;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
if (pad) {
|
||||
const uint8_t pad_byte[1] = { 0 };
|
||||
if (!pic->writer(pad_byte, 1, pic)) {
|
||||
err = VP8_ENC_ERROR_BAD_WRITE;
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
return VP8_ENC_OK;
|
||||
|
||||
Error:
|
||||
WebPEncodingSetError(pic, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config,
|
||||
WebPPicture* const picture) {
|
||||
VP8LEncoder* enc;
|
||||
const int kEstmatedEncodeSize = (picture->width * picture->height) >> 1;
|
||||
(void)config;
|
||||
|
||||
enc = (VP8LEncoder*)malloc(sizeof(*enc));
|
||||
if (enc == NULL) {
|
||||
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
enc->pic_ = picture;
|
||||
enc->use_lz77_ = 1;
|
||||
enc->palette_bits_ = 7;
|
||||
|
||||
// TODO: Use config.quality to initialize histo_bits_ and transform_bits_.
|
||||
enc->histo_bits_ = 4;
|
||||
enc->transform_bits_ = 4;
|
||||
VP8LBitWriterInit(&enc->bw_, kEstmatedEncodeSize);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
static WebPEncodingError WriteImageSize(VP8LEncoder* const enc) {
|
||||
WebPEncodingError status = VP8_ENC_OK;
|
||||
WebPPicture* const pic = enc->pic_;
|
||||
const int width = pic->width - 1;
|
||||
const int height = pic->height - 1;
|
||||
if (width < 0x4000 && height < 0x4000) {
|
||||
VP8LWriteBits(&enc->bw_, kImageSizeBits, width);
|
||||
VP8LWriteBits(&enc->bw_, kImageSizeBits, height);
|
||||
} else {
|
||||
status = VP8_ENC_ERROR_BAD_DIMENSION;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void DeleteVP8LEncoder(VP8LEncoder* enc) {
|
||||
free(enc);
|
||||
}
|
||||
|
||||
int VP8LEncodeImage(const WebPConfig* const config,
|
||||
WebPPicture* const picture) {
|
||||
int ok = 0;
|
||||
VP8LEncoder* enc = NULL;
|
||||
WebPEncodingError err = VP8_ENC_OK;
|
||||
|
||||
if (config == NULL || picture == NULL) return 0;
|
||||
if (picture->argb == NULL) return 0;
|
||||
enc = InitVP8LEncoder(config, picture);
|
||||
if (enc == NULL) goto Error;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Analyze image (entropy, num_palettes etc)
|
||||
|
||||
if (!VP8LEncAnalyze(enc)) goto Error;
|
||||
|
||||
if (enc->use_palette_) {
|
||||
CreatePalette(enc);
|
||||
}
|
||||
|
||||
// Write image size.
|
||||
err = WriteImageSize(enc);
|
||||
if (err != VP8_ENC_OK) {
|
||||
ok = 0;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Apply transforms and write transform data.
|
||||
|
||||
EvalSubtractGreen(enc);
|
||||
|
||||
if (enc->use_predict_) {
|
||||
if (!ApplyPredictFilter(enc)) goto Error;
|
||||
}
|
||||
|
||||
if (enc->use_cross_color_) {
|
||||
if (!ApplyCrossColorFilter(enc)) goto Error;
|
||||
}
|
||||
|
||||
if (enc->use_emerging_palette_) {
|
||||
EvalEmergingPalette(enc);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Encode and write the transformed image.
|
||||
|
||||
ok = EncodeImageInternal(enc);
|
||||
if (!ok) goto Error;
|
||||
|
||||
err = WriteImage(enc);
|
||||
if (err != VP8_ENC_OK) {
|
||||
ok = 0;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
Error:
|
||||
VP8LBitWriterDestroy(&enc->bw_);
|
||||
DeleteVP8LEncoder(enc);
|
||||
if (!ok) {
|
||||
// TODO(vikasa): err is not set for all error paths. Set default err.
|
||||
if (err == VP8_ENC_OK) err = VP8_ENC_ERROR_BAD_WRITE;
|
||||
WebPEncodingSetError(picture, err);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
69
src/enc/vp8li.h
Normal file
69
src/enc/vp8li.h
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Lossless encoder: internal header.
|
||||
//
|
||||
// Author: Vikas Arora(vikaas.arora@gmail.com)
|
||||
|
||||
#ifndef WEBP_ENC_VP8LI_H_
|
||||
#define WEBP_ENC_VP8LI_H_
|
||||
|
||||
#include "../webp/encode.h"
|
||||
#include "../utils/bit_writer.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// TODO(vikasa): factorize these with ones used in lossless decoder.
|
||||
#define TAG_SIZE 4
|
||||
#define CHUNK_HEADER_SIZE 8
|
||||
#define RIFF_HEADER_SIZE 12
|
||||
#define HEADER_SIZE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE)
|
||||
#define SIGNATURE_SIZE 1
|
||||
#define LOSSLESS_MAGIC_BYTE 0x64
|
||||
|
||||
typedef struct {
|
||||
const WebPConfig* config_; // user configuration and parameters
|
||||
WebPPicture* pic_; // input / output picture
|
||||
|
||||
// Encoding parameters derived from quality parameter.
|
||||
int use_lz77_;
|
||||
int palette_bits_;
|
||||
int histo_bits_;
|
||||
int transform_bits_;
|
||||
|
||||
// Encoding parameters derived from image characteristics.
|
||||
int predicted_bits_;
|
||||
int non_predicted_bits_;
|
||||
int use_palette_;
|
||||
int num_palette_colors;
|
||||
int use_predict_;
|
||||
int use_cross_color_;
|
||||
int use_emerging_palette_;
|
||||
|
||||
VP8LBitWriter bw_;
|
||||
} VP8LEncoder;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// in vp8l.c
|
||||
|
||||
// Encodes the picture.
|
||||
// Returns 0 if config or picture is NULL or picture doesn't have valid argb
|
||||
// input.
|
||||
int VP8LEncodeImage(const WebPConfig* const config,
|
||||
WebPPicture* const picture);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_ENC_VP8LI_H_ */
|
@ -15,6 +15,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "./vp8enci.h"
|
||||
#include "./vp8li.h"
|
||||
|
||||
// #define PRINT_MEMORY_INFO
|
||||
|
||||
@ -142,8 +143,8 @@ static void MapConfigToTools(VP8Encoder* const enc) {
|
||||
// LFStats: 2048
|
||||
// Picture size (yuv): 589824
|
||||
|
||||
static VP8Encoder* InitEncoder(const WebPConfig* const config,
|
||||
WebPPicture* const picture) {
|
||||
static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
WebPPicture* const picture) {
|
||||
const int use_filter =
|
||||
(config->filter_strength > 0) || (config->autofilter > 0);
|
||||
const int mb_w = (picture->width + 15) >> 4;
|
||||
@ -259,7 +260,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
|
||||
return enc;
|
||||
}
|
||||
|
||||
static void DeleteEncoder(VP8Encoder* enc) {
|
||||
static void DeleteVP8Encoder(VP8Encoder* enc) {
|
||||
if (enc) {
|
||||
VP8EncDeleteAlpha(enc);
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
@ -327,7 +328,6 @@ int WebPReportProgress(VP8Encoder* const enc, int percent) {
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
||||
VP8Encoder* enc;
|
||||
int ok;
|
||||
|
||||
if (pic == NULL)
|
||||
@ -339,27 +339,36 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
|
||||
if (pic->width <= 0 || pic->height <= 0)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
||||
if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
||||
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
||||
|
||||
enc = InitEncoder(config, pic);
|
||||
if (enc == NULL) return 0; // pic->error is already set.
|
||||
// Note: each of the tasks below account for 20% in the progress report.
|
||||
ok = VP8EncAnalyze(enc)
|
||||
&& VP8StatLoop(enc)
|
||||
&& VP8EncLoop(enc)
|
||||
&& VP8EncFinishAlpha(enc)
|
||||
if (!config->lossless) {
|
||||
VP8Encoder* enc = NULL;
|
||||
if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
||||
|
||||
enc = InitVP8Encoder(config, pic);
|
||||
if (enc == NULL) return 0; // pic->error is already set.
|
||||
// Note: each of the tasks below account for 20% in the progress report.
|
||||
ok = VP8EncAnalyze(enc)
|
||||
&& VP8StatLoop(enc)
|
||||
&& VP8EncLoop(enc)
|
||||
&& VP8EncFinishAlpha(enc)
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
&& VP8EncFinishLayer(enc)
|
||||
&& VP8EncFinishLayer(enc)
|
||||
#endif
|
||||
&& VP8EncWrite(enc);
|
||||
StoreStats(enc);
|
||||
if (!ok) {
|
||||
VP8EncFreeBitWriters(enc);
|
||||
&& VP8EncWrite(enc);
|
||||
StoreStats(enc);
|
||||
if (!ok) {
|
||||
VP8EncFreeBitWriters(enc);
|
||||
}
|
||||
DeleteVP8Encoder(enc);
|
||||
} else {
|
||||
if (pic->argb == NULL)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
||||
|
||||
ok = VP8LEncodeImage(config, pic);
|
||||
}
|
||||
DeleteEncoder(enc);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
// Bit writing and boolean coder
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
// Vikas Arora (vikaas.arora@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // for memcpy()
|
||||
@ -186,6 +187,84 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LBitWriter
|
||||
|
||||
// Returns 1 on success.
|
||||
static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
|
||||
uint8_t* allocated_buf;
|
||||
size_t allocated_size;
|
||||
const size_t size_required = VP8LBitWriterNumBytes(bw) + extra_size;
|
||||
if ((bw->max_bytes_ > 0) && (size_required <= bw->max_bytes_)) return 1;
|
||||
allocated_size = (3 * bw->max_bytes_) >> 1;
|
||||
if (allocated_size < size_required) {
|
||||
allocated_size = size_required;
|
||||
}
|
||||
// Make Allocated size multiple of KBs
|
||||
allocated_size = (((allocated_size >> 10) + 1) << 10);
|
||||
allocated_buf = (uint8_t*)malloc(allocated_size);
|
||||
if (allocated_buf == NULL) return 0;
|
||||
memset(allocated_buf, 0, allocated_size);
|
||||
if (bw->bit_pos_ > 0) {
|
||||
memcpy(allocated_buf, bw->buf_, VP8LBitWriterNumBytes(bw));
|
||||
}
|
||||
free(bw->buf_);
|
||||
bw->buf_ = allocated_buf;
|
||||
bw->max_bytes_ = allocated_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) {
|
||||
memset(bw, 0, sizeof(*bw));
|
||||
return VP8LBitWriterResize(bw, expected_size);
|
||||
}
|
||||
|
||||
void VP8LBitWriterDestroy(VP8LBitWriter* const bw) {
|
||||
if (bw != NULL) {
|
||||
free(bw->buf_);
|
||||
memset(bw, 0, sizeof(*bw));
|
||||
}
|
||||
}
|
||||
|
||||
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) {
|
||||
if (n_bits < 1) return;
|
||||
#if !defined(__BIG_ENDIAN__)
|
||||
// Technically, this branch of the code can write up to 25 bits at a time,
|
||||
// but in deflate, the maximum number of bits written is 16 at a time.
|
||||
{
|
||||
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||
uint32_t v = *(const uint32_t*)(p);
|
||||
v |= bits << (bw->bit_pos_ & 7);
|
||||
*(uint32_t*)(p) = v;
|
||||
bw->bit_pos_ += n_bits;
|
||||
}
|
||||
#else // LITTLE_ENDIAN
|
||||
// implicit & 0xff is assumed for uint8_t arithmetics
|
||||
{
|
||||
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||
const int bits_reserved_in_first_byte = (bw->bit_pos_ & 7);
|
||||
*p++ |= (bits << bits_reserved_in_first_byte);
|
||||
const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
|
||||
if (bits_left_to_write >= 1) {
|
||||
*p++ = bits >> (8 - bits_reserved_in_first_byte);
|
||||
if (bits_left_to_write >= 9) {
|
||||
*p++ = bits >> (16 - bits_reserved_in_first_byte);
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
bw->bit_pos_ += n_bits;
|
||||
}
|
||||
#endif // BIG_ENDIAN
|
||||
if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
|
||||
const size_t kAdditionalBuffer = 32768 + bw->max_bytes_;
|
||||
if (!VP8LBitWriterResize(bw, kAdditionalBuffer)) {
|
||||
bw->bit_pos_ = 0;
|
||||
bw->error_ = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
@ -64,6 +64,56 @@ static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
|
||||
return bw->pos_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LBitWriter
|
||||
// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope
|
||||
// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is
|
||||
// implemented.
|
||||
|
||||
typedef struct {
|
||||
uint8_t* buf_;
|
||||
size_t bit_pos_;
|
||||
size_t max_bytes_;
|
||||
|
||||
// After all bits are written, the caller must observe the state of
|
||||
// error_. A value of 1 indicates that a memory allocation failure
|
||||
// has happened during bit writing. A value of 0 indicates successful
|
||||
// writing of bits.
|
||||
int error_;
|
||||
} VP8LBitWriter;
|
||||
|
||||
static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) {
|
||||
return (bw->bit_pos_ + 7) >> 3;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
|
||||
return bw->buf_;
|
||||
}
|
||||
|
||||
// Returns 1 on success.
|
||||
int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size);
|
||||
|
||||
void VP8LBitWriterDestroy(VP8LBitWriter* const bw);
|
||||
|
||||
// This function writes bits into bytes in increasing addresses, and within
|
||||
// a byte least-significant-bit first.
|
||||
//
|
||||
// The function can write up to 16 bits in one go with WriteBits
|
||||
// Example: let's assume that 3 bits (Rs below) have been written already:
|
||||
//
|
||||
// BYTE-0 BYTE+1 BYTE+2
|
||||
//
|
||||
// 0000 0RRR 0000 0000 0000 0000
|
||||
//
|
||||
// Now, we could write 5 or less bits in MSB by just sifting by 3
|
||||
// and OR'ing to BYTE-0.
|
||||
//
|
||||
// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0,
|
||||
// and locate the rest in BYTE+1 and BYTE+2.
|
||||
//
|
||||
// returns 1 on success.
|
||||
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
@ -76,6 +76,7 @@ typedef struct {
|
||||
// 0: none, 1: fast, 2: best. Default if 1.
|
||||
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
||||
// Default is 100.
|
||||
int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
|
||||
} WebPConfig;
|
||||
|
||||
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||
@ -189,7 +190,7 @@ struct WebPPicture {
|
||||
int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION)
|
||||
uint8_t *y, *u, *v; // pointers to luma/chroma planes.
|
||||
int y_stride, uv_stride; // luma/chroma strides.
|
||||
uint8_t *a; // pointer to the alpha plane
|
||||
uint8_t* a; // pointer to the alpha plane
|
||||
int a_stride; // stride of the alpha plane
|
||||
|
||||
// output
|
||||
@ -216,6 +217,10 @@ struct WebPPicture {
|
||||
WebPEncodingError error_code; // error code in case of problem.
|
||||
|
||||
WebPProgressHook progress_hook; // if not NULL, called while encoding.
|
||||
|
||||
int use_argb_input; // Flag for encoder to read argb pixels as input.
|
||||
uint32_t* argb; // Pointer to argb (32 bit) plane.
|
||||
int argb_stride; // This is stride in pixels units, not bytes.
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
|
Loading…
Reference in New Issue
Block a user