mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
split monolithic picture.c into picture_{tools,psnr,rescale}.c
Change-Id: Ia5eb5496e4337e5bac8203872c5b014cad21c4f9
This commit is contained in:
parent
c76f07ecc2
commit
fbadb48026
@ -64,6 +64,9 @@ LOCAL_SRC_FILES := \
|
|||||||
src/enc/histogram.c \
|
src/enc/histogram.c \
|
||||||
src/enc/iterator.c \
|
src/enc/iterator.c \
|
||||||
src/enc/picture.c \
|
src/enc/picture.c \
|
||||||
|
src/enc/picture_psnr.c \
|
||||||
|
src/enc/picture_tools.c \
|
||||||
|
src/enc/picture_rescale.c \
|
||||||
src/enc/quant.c \
|
src/enc/quant.c \
|
||||||
src/enc/syntax.c \
|
src/enc/syntax.c \
|
||||||
src/enc/token.c \
|
src/enc/token.c \
|
||||||
|
@ -218,6 +218,9 @@ ENC_OBJS = \
|
|||||||
$(DIROBJ)\enc\histogram.obj \
|
$(DIROBJ)\enc\histogram.obj \
|
||||||
$(DIROBJ)\enc\iterator.obj \
|
$(DIROBJ)\enc\iterator.obj \
|
||||||
$(DIROBJ)\enc\picture.obj \
|
$(DIROBJ)\enc\picture.obj \
|
||||||
|
$(DIROBJ)\enc\picture_psnr.obj \
|
||||||
|
$(DIROBJ)\enc\picture_tools.obj \
|
||||||
|
$(DIROBJ)\enc\picture_rescale.obj \
|
||||||
$(DIROBJ)\enc\quant.obj \
|
$(DIROBJ)\enc\quant.obj \
|
||||||
$(DIROBJ)\enc\syntax.obj \
|
$(DIROBJ)\enc\syntax.obj \
|
||||||
$(DIROBJ)\enc\token.obj \
|
$(DIROBJ)\enc\token.obj \
|
||||||
|
@ -143,6 +143,9 @@ ENC_OBJS = \
|
|||||||
src/enc/histogram.o \
|
src/enc/histogram.o \
|
||||||
src/enc/iterator.o \
|
src/enc/iterator.o \
|
||||||
src/enc/picture.o \
|
src/enc/picture.o \
|
||||||
|
src/enc/picture_psnr.o \
|
||||||
|
src/enc/picture_rescale.o \
|
||||||
|
src/enc/picture_tools.o \
|
||||||
src/enc/quant.o \
|
src/enc/quant.o \
|
||||||
src/enc/syntax.o \
|
src/enc/syntax.o \
|
||||||
src/enc/token.o \
|
src/enc/token.o \
|
||||||
|
@ -12,6 +12,9 @@ libwebpencode_la_SOURCES += frame.c
|
|||||||
libwebpencode_la_SOURCES += histogram.c
|
libwebpencode_la_SOURCES += histogram.c
|
||||||
libwebpencode_la_SOURCES += iterator.c
|
libwebpencode_la_SOURCES += iterator.c
|
||||||
libwebpencode_la_SOURCES += picture.c
|
libwebpencode_la_SOURCES += picture.c
|
||||||
|
libwebpencode_la_SOURCES += picture_psnr.c
|
||||||
|
libwebpencode_la_SOURCES += picture_rescale.c
|
||||||
|
libwebpencode_la_SOURCES += picture_tools.c
|
||||||
libwebpencode_la_SOURCES += quant.c
|
libwebpencode_la_SOURCES += quant.c
|
||||||
libwebpencode_la_SOURCES += syntax.c
|
libwebpencode_la_SOURCES += syntax.c
|
||||||
libwebpencode_la_SOURCES += token.c
|
libwebpencode_la_SOURCES += token.c
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "../utils/random.h"
|
#include "../utils/random.h"
|
||||||
#include "../utils/rescaler.h"
|
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
#include "../dsp/dsp.h"
|
|
||||||
#include "../dsp/yuv.h"
|
#include "../dsp/yuv.h"
|
||||||
|
|
||||||
// Uncomment to disable gamma-compression during RGB->U/V averaging
|
// Uncomment to disable gamma-compression during RGB->U/V averaging
|
||||||
@ -141,7 +139,7 @@ static void PictureResetYUVA(WebPPicture* const picture) {
|
|||||||
|
|
||||||
// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
|
// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
|
||||||
// into 'dst'. Mark 'dst' as not owning any memory.
|
// into 'dst'. Mark 'dst' as not owning any memory.
|
||||||
static void WebPPictureGrabSpecs(const WebPPicture* const src,
|
void WebPPictureGrabSpecs(const WebPPicture* const src,
|
||||||
WebPPicture* const dst) {
|
WebPPicture* const dst) {
|
||||||
assert(src != NULL && dst != NULL);
|
assert(src != NULL && dst != NULL);
|
||||||
*dst = *src;
|
*dst = *src;
|
||||||
@ -176,260 +174,6 @@ void WebPPictureFree(WebPPicture* picture) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Picture copying
|
|
||||||
|
|
||||||
// Not worth moving to dsp/enc.c (only used here).
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust top-left corner to chroma sample position.
|
|
||||||
static void SnapTopLeftPosition(const WebPPicture* const pic,
|
|
||||||
int* const left, int* const top) {
|
|
||||||
if (!pic->use_argb) {
|
|
||||||
*left &= ~1;
|
|
||||||
*top &= ~1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust top-left corner and verify that the sub-rectangle is valid.
|
|
||||||
static int AdjustAndCheckRectangle(const WebPPicture* const pic,
|
|
||||||
int* const left, int* const top,
|
|
||||||
int width, int height) {
|
|
||||||
SnapTopLeftPosition(pic, left, top);
|
|
||||||
if ((*left) < 0 || (*top) < 0) return 0;
|
|
||||||
if (width <= 0 || height <= 0) return 0;
|
|
||||||
if ((*left) + width > pic->width) return 0;
|
|
||||||
if ((*top) + height > pic->height) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
|
|
||||||
if (src == NULL || dst == NULL) return 0;
|
|
||||||
if (src == dst) return 1;
|
|
||||||
|
|
||||||
WebPPictureGrabSpecs(src, dst);
|
|
||||||
if (!WebPPictureAlloc(dst)) return 0;
|
|
||||||
|
|
||||||
if (!src->use_argb) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
|
|
||||||
(uint8_t*)dst->argb, 4 * dst->argb_stride,
|
|
||||||
4 * dst->width, dst->height);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPPictureIsView(const WebPPicture* picture) {
|
|
||||||
if (picture == NULL) return 0;
|
|
||||||
if (picture->use_argb) {
|
|
||||||
return (picture->memory_argb_ == NULL);
|
|
||||||
}
|
|
||||||
return (picture->memory_ == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPPictureView(const WebPPicture* src,
|
|
||||||
int left, int top, int width, int height,
|
|
||||||
WebPPicture* dst) {
|
|
||||||
if (src == NULL || dst == NULL) return 0;
|
|
||||||
|
|
||||||
// verify rectangle position.
|
|
||||||
if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
|
|
||||||
|
|
||||||
if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
|
|
||||||
WebPPictureGrabSpecs(src, dst);
|
|
||||||
}
|
|
||||||
dst->width = width;
|
|
||||||
dst->height = height;
|
|
||||||
if (!src->use_argb) {
|
|
||||||
dst->y = src->y + top * src->y_stride + left;
|
|
||||||
dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
|
|
||||||
dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
|
|
||||||
dst->y_stride = src->y_stride;
|
|
||||||
dst->uv_stride = src->uv_stride;
|
|
||||||
if (src->a != NULL) {
|
|
||||||
dst->a = src->a + top * src->a_stride + left;
|
|
||||||
dst->a_stride = src->a_stride;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dst->argb = src->argb + top * src->argb_stride + left;
|
|
||||||
dst->argb_stride = src->argb_stride;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Picture cropping
|
|
||||||
|
|
||||||
int WebPPictureCrop(WebPPicture* pic,
|
|
||||||
int left, int top, int width, int height) {
|
|
||||||
WebPPicture tmp;
|
|
||||||
|
|
||||||
if (pic == NULL) return 0;
|
|
||||||
if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
|
|
||||||
|
|
||||||
WebPPictureGrabSpecs(pic, &tmp);
|
|
||||||
tmp.width = width;
|
|
||||||
tmp.height = height;
|
|
||||||
if (!WebPPictureAlloc(&tmp)) return 0;
|
|
||||||
|
|
||||||
if (!pic->use_argb) {
|
|
||||||
const int y_offset = top * pic->y_stride + left;
|
|
||||||
const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
|
|
||||||
CopyPlane(pic->y + y_offset, pic->y_stride,
|
|
||||||
tmp.y, tmp.y_stride, width, height);
|
|
||||||
CopyPlane(pic->u + uv_offset, pic->uv_stride,
|
|
||||||
tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
|
|
||||||
CopyPlane(pic->v + uv_offset, pic->uv_stride,
|
|
||||||
tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
|
|
||||||
|
|
||||||
if (tmp.a != NULL) {
|
|
||||||
const int a_offset = top * pic->a_stride + left;
|
|
||||||
CopyPlane(pic->a + a_offset, pic->a_stride,
|
|
||||||
tmp.a, tmp.a_stride, width, height);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const uint8_t* const src =
|
|
||||||
(const uint8_t*)(pic->argb + top * pic->argb_stride + left);
|
|
||||||
CopyPlane(src, pic->argb_stride * 4,
|
|
||||||
(uint8_t*)tmp.argb, tmp.argb_stride * 4,
|
|
||||||
width * 4, height);
|
|
||||||
}
|
|
||||||
WebPPictureFree(pic);
|
|
||||||
*pic = tmp;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Simple picture rescaler
|
|
||||||
|
|
||||||
static void RescalePlane(const uint8_t* src,
|
|
||||||
int src_width, int src_height, int src_stride,
|
|
||||||
uint8_t* dst,
|
|
||||||
int dst_width, int dst_height, int dst_stride,
|
|
||||||
int32_t* const work,
|
|
||||||
int num_channels) {
|
|
||||||
WebPRescaler rescaler;
|
|
||||||
int y = 0;
|
|
||||||
WebPRescalerInit(&rescaler, src_width, src_height,
|
|
||||||
dst, dst_width, dst_height, dst_stride,
|
|
||||||
num_channels,
|
|
||||||
src_width, dst_width,
|
|
||||||
src_height, dst_height,
|
|
||||||
work);
|
|
||||||
memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
|
|
||||||
while (y < src_height) {
|
|
||||||
y += WebPRescalerImport(&rescaler, src_height - y,
|
|
||||||
src + y * src_stride, src_stride);
|
|
||||||
WebPRescalerExport(&rescaler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
|
|
||||||
assert(pic->argb != NULL);
|
|
||||||
WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb),
|
|
||||||
pic->width, pic->height, inverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
|
|
||||||
if (pic->a != NULL) {
|
|
||||||
WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride,
|
|
||||||
pic->width, pic->height, inverse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPPictureRescale(WebPPicture* pic, int width, int height) {
|
|
||||||
WebPPicture tmp;
|
|
||||||
int prev_width, prev_height;
|
|
||||||
int32_t* work;
|
|
||||||
|
|
||||||
if (pic == NULL) return 0;
|
|
||||||
prev_width = pic->width;
|
|
||||||
prev_height = pic->height;
|
|
||||||
// if width is unspecified, scale original proportionally to height ratio.
|
|
||||||
if (width == 0) {
|
|
||||||
width = (prev_width * height + prev_height / 2) / prev_height;
|
|
||||||
}
|
|
||||||
// if height is unspecified, scale original proportionally to width ratio.
|
|
||||||
if (height == 0) {
|
|
||||||
height = (prev_height * width + prev_width / 2) / prev_width;
|
|
||||||
}
|
|
||||||
// Check if the overall dimensions still make sense.
|
|
||||||
if (width <= 0 || height <= 0) return 0;
|
|
||||||
|
|
||||||
WebPPictureGrabSpecs(pic, &tmp);
|
|
||||||
tmp.width = width;
|
|
||||||
tmp.height = height;
|
|
||||||
if (!WebPPictureAlloc(&tmp)) return 0;
|
|
||||||
|
|
||||||
if (!pic->use_argb) {
|
|
||||||
work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
|
|
||||||
if (work == NULL) {
|
|
||||||
WebPPictureFree(&tmp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// If present, we need to rescale alpha first (for AlphaMultiplyY).
|
|
||||||
if (pic->a != NULL) {
|
|
||||||
WebPInitAlphaProcessing();
|
|
||||||
RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
|
|
||||||
tmp.a, width, height, tmp.a_stride, work, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We take transparency into account on the luma plane only. That's not
|
|
||||||
// totally exact blending, but still is a good approximation.
|
|
||||||
AlphaMultiplyY(pic, 0);
|
|
||||||
RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
|
|
||||||
tmp.y, width, height, tmp.y_stride, work, 1);
|
|
||||||
AlphaMultiplyY(&tmp, 1);
|
|
||||||
|
|
||||||
RescalePlane(pic->u,
|
|
||||||
HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
|
|
||||||
tmp.u,
|
|
||||||
HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
|
|
||||||
RescalePlane(pic->v,
|
|
||||||
HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
|
|
||||||
tmp.v,
|
|
||||||
HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
|
|
||||||
} else {
|
|
||||||
work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
|
|
||||||
if (work == NULL) {
|
|
||||||
WebPPictureFree(&tmp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// In order to correctly interpolate colors, we need to apply the alpha
|
|
||||||
// weighting first (black-matting), scale the RGB values, and remove
|
|
||||||
// the premultiplication afterward (while preserving the alpha channel).
|
|
||||||
WebPInitAlphaProcessing();
|
|
||||||
AlphaMultiplyARGB(pic, 0);
|
|
||||||
RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
|
|
||||||
pic->argb_stride * 4,
|
|
||||||
(uint8_t*)tmp.argb, width, height,
|
|
||||||
tmp.argb_stride * 4,
|
|
||||||
work, 4);
|
|
||||||
AlphaMultiplyARGB(&tmp, 1);
|
|
||||||
}
|
|
||||||
WebPPictureFree(pic);
|
|
||||||
WebPSafeFree(work);
|
|
||||||
*pic = tmp;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// WebPMemoryWriter: Write-to-memory
|
// WebPMemoryWriter: Write-to-memory
|
||||||
|
|
||||||
@ -734,6 +478,8 @@ static int Import(WebPPicture* const picture,
|
|||||||
#undef SUM1
|
#undef SUM1
|
||||||
#undef RGB_TO_UV
|
#undef RGB_TO_UV
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
int WebPPictureImportRGB(WebPPicture* picture,
|
int WebPPictureImportRGB(WebPPicture* picture,
|
||||||
const uint8_t* rgb, int rgb_stride) {
|
const uint8_t* rgb, int rgb_stride) {
|
||||||
return Import(picture, rgb, rgb_stride, 3, 0, 0);
|
return Import(picture, rgb, rgb_stride, 3, 0, 0);
|
||||||
@ -859,326 +605,6 @@ int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
|
|||||||
return WebPPictureARGBToYUVADithered(picture, colorspace, 0.f);
|
return WebPPictureARGBToYUVADithered(picture, colorspace, 0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Helper: clean up fully transparent area to help compressibility.
|
|
||||||
|
|
||||||
#define SIZE 8
|
|
||||||
#define SIZE2 (SIZE / 2)
|
|
||||||
static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
|
|
||||||
int y, x;
|
|
||||||
for (y = 0; y < size; ++y) {
|
|
||||||
for (x = 0; x < size; ++x) {
|
|
||||||
if (ptr[x]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ptr += stride;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) {
|
|
||||||
int y, x;
|
|
||||||
for (y = 0; y < size; ++y) {
|
|
||||||
for (x = 0; x < size; ++x) {
|
|
||||||
if (ptr[x] & 0xff000000u) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ptr += stride;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void flatten(uint8_t* ptr, int v, int stride, int size) {
|
|
||||||
int y;
|
|
||||||
for (y = 0; y < size; ++y) {
|
|
||||||
memset(ptr, v, size);
|
|
||||||
ptr += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) {
|
|
||||||
int x, y;
|
|
||||||
for (y = 0; y < size; ++y) {
|
|
||||||
for (x = 0; x < size; ++x) ptr[x] = v;
|
|
||||||
ptr += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebPCleanupTransparentArea(WebPPicture* pic) {
|
|
||||||
int x, y, w, h;
|
|
||||||
if (pic == NULL) return;
|
|
||||||
w = pic->width / SIZE;
|
|
||||||
h = pic->height / SIZE;
|
|
||||||
|
|
||||||
// note: we ignore the left-overs on right/bottom
|
|
||||||
if (pic->use_argb) {
|
|
||||||
uint32_t argb_value = 0;
|
|
||||||
for (y = 0; y < h; ++y) {
|
|
||||||
int need_reset = 1;
|
|
||||||
for (x = 0; x < w; ++x) {
|
|
||||||
const int off = (y * pic->argb_stride + x) * SIZE;
|
|
||||||
if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) {
|
|
||||||
if (need_reset) {
|
|
||||||
argb_value = pic->argb[off];
|
|
||||||
need_reset = 0;
|
|
||||||
}
|
|
||||||
flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE);
|
|
||||||
} else {
|
|
||||||
need_reset = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const uint8_t* const a_ptr = pic->a;
|
|
||||||
int values[3] = { 0 };
|
|
||||||
if (a_ptr == NULL) return; // nothing to do
|
|
||||||
for (y = 0; y < h; ++y) {
|
|
||||||
int need_reset = 1;
|
|
||||||
for (x = 0; x < w; ++x) {
|
|
||||||
const int off_a = (y * pic->a_stride + x) * SIZE;
|
|
||||||
const int off_y = (y * pic->y_stride + x) * SIZE;
|
|
||||||
const int off_uv = (y * pic->uv_stride + x) * SIZE2;
|
|
||||||
if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
|
|
||||||
if (need_reset) {
|
|
||||||
values[0] = pic->y[off_y];
|
|
||||||
values[1] = pic->u[off_uv];
|
|
||||||
values[2] = pic->v[off_uv];
|
|
||||||
need_reset = 0;
|
|
||||||
}
|
|
||||||
flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
|
|
||||||
flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
|
|
||||||
flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
|
|
||||||
} else {
|
|
||||||
need_reset = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef SIZE
|
|
||||||
#undef SIZE2
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Blend color and remove transparency info
|
|
||||||
|
|
||||||
#define BLEND(V0, V1, ALPHA) \
|
|
||||||
((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
|
|
||||||
#define BLEND_10BIT(V0, V1, ALPHA) \
|
|
||||||
((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
|
|
||||||
|
|
||||||
void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
|
|
||||||
const int red = (background_rgb >> 16) & 0xff;
|
|
||||||
const int green = (background_rgb >> 8) & 0xff;
|
|
||||||
const int blue = (background_rgb >> 0) & 0xff;
|
|
||||||
VP8Random rg;
|
|
||||||
int x, y;
|
|
||||||
if (pic == NULL) return;
|
|
||||||
VP8InitRandom(&rg, 0.f);
|
|
||||||
if (!pic->use_argb) {
|
|
||||||
const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
|
|
||||||
const int Y0 = RGBToY(red, green, blue, &rg);
|
|
||||||
// VP8RGBToU/V expects the u/v values summed over four pixels
|
|
||||||
const int U0 = RGBToU(4 * red, 4 * green, 4 * blue, &rg);
|
|
||||||
const int V0 = RGBToV(4 * red, 4 * green, 4 * blue, &rg);
|
|
||||||
const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
|
|
||||||
if (!has_alpha || pic->a == NULL) return; // nothing to do
|
|
||||||
for (y = 0; y < pic->height; ++y) {
|
|
||||||
// Luma blending
|
|
||||||
uint8_t* const y_ptr = pic->y + y * pic->y_stride;
|
|
||||||
uint8_t* const a_ptr = pic->a + y * pic->a_stride;
|
|
||||||
for (x = 0; x < pic->width; ++x) {
|
|
||||||
const int alpha = a_ptr[x];
|
|
||||||
if (alpha < 0xff) {
|
|
||||||
y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Chroma blending every even line
|
|
||||||
if ((y & 1) == 0) {
|
|
||||||
uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
|
|
||||||
uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
|
|
||||||
uint8_t* const a_ptr2 =
|
|
||||||
(y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
|
|
||||||
for (x = 0; x < uv_width; ++x) {
|
|
||||||
// Average four alpha values into a single blending weight.
|
|
||||||
// TODO(skal): might lead to visible contouring. Can we do better?
|
|
||||||
const int alpha =
|
|
||||||
a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
|
|
||||||
a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
|
|
||||||
u[x] = BLEND_10BIT(U0, u[x], alpha);
|
|
||||||
v[x] = BLEND_10BIT(V0, v[x], alpha);
|
|
||||||
}
|
|
||||||
if (pic->width & 1) { // rightmost pixel
|
|
||||||
const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
|
|
||||||
u[x] = BLEND_10BIT(U0, u[x], alpha);
|
|
||||||
v[x] = BLEND_10BIT(V0, v[x], alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memset(a_ptr, 0xff, pic->width);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint32_t* argb = pic->argb;
|
|
||||||
const uint32_t background = MakeARGB32(red, green, blue);
|
|
||||||
for (y = 0; y < pic->height; ++y) {
|
|
||||||
for (x = 0; x < pic->width; ++x) {
|
|
||||||
const int alpha = (argb[x] >> 24) & 0xff;
|
|
||||||
if (alpha != 0xff) {
|
|
||||||
if (alpha > 0) {
|
|
||||||
int r = (argb[x] >> 16) & 0xff;
|
|
||||||
int g = (argb[x] >> 8) & 0xff;
|
|
||||||
int b = (argb[x] >> 0) & 0xff;
|
|
||||||
r = BLEND(red, r, alpha);
|
|
||||||
g = BLEND(green, g, alpha);
|
|
||||||
b = BLEND(blue, b, alpha);
|
|
||||||
argb[x] = MakeARGB32(r, g, b);
|
|
||||||
} else {
|
|
||||||
argb[x] = background;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argb += pic->argb_stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef BLEND
|
|
||||||
#undef BLEND_10BIT
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// local-min distortion
|
|
||||||
//
|
|
||||||
// For every pixel in the *reference* picture, we search for the local best
|
|
||||||
// match in the compressed image. This is not a symmetrical measure.
|
|
||||||
|
|
||||||
// search radius. Shouldn't be too large.
|
|
||||||
#define RADIUS 2
|
|
||||||
|
|
||||||
static float AccumulateLSIM(const uint8_t* src, int src_stride,
|
|
||||||
const uint8_t* ref, int ref_stride,
|
|
||||||
int w, int h) {
|
|
||||||
int x, y;
|
|
||||||
double total_sse = 0.;
|
|
||||||
for (y = 0; y < h; ++y) {
|
|
||||||
const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
|
|
||||||
const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
|
|
||||||
for (x = 0; x < w; ++x) {
|
|
||||||
const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
|
|
||||||
const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
|
|
||||||
double best_sse = 255. * 255.;
|
|
||||||
const double value = (double)ref[y * ref_stride + x];
|
|
||||||
int i, j;
|
|
||||||
for (j = y_0; j < y_1; ++j) {
|
|
||||||
const uint8_t* s = src + j * src_stride;
|
|
||||||
for (i = x_0; i < x_1; ++i) {
|
|
||||||
const double sse = (double)(s[i] - value) * (s[i] - value);
|
|
||||||
if (sse < best_sse) best_sse = sse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
total_sse += best_sse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (float)total_sse;
|
|
||||||
}
|
|
||||||
#undef RADIUS
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Distortion
|
|
||||||
|
|
||||||
// Max value returned in case of exact similarity.
|
|
||||||
static const double kMinDistortion_dB = 99.;
|
|
||||||
static float GetPSNR(const double v) {
|
|
||||||
return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
|
|
||||||
: kMinDistortion_dB);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
|
|
||||||
int type, float result[5]) {
|
|
||||||
DistoStats stats[5];
|
|
||||||
int has_alpha;
|
|
||||||
int uv_w, uv_h;
|
|
||||||
|
|
||||||
if (src == NULL || ref == NULL ||
|
|
||||||
src->width != ref->width || src->height != ref->height ||
|
|
||||||
src->y == NULL || ref->y == NULL ||
|
|
||||||
src->u == NULL || ref->u == NULL ||
|
|
||||||
src->v == NULL || ref->v == NULL ||
|
|
||||||
result == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// TODO(skal): provide distortion for ARGB too.
|
|
||||||
if (src->use_argb == 1 || src->use_argb != ref->use_argb) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT);
|
|
||||||
if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) ||
|
|
||||||
(has_alpha && (src->a == NULL || ref->a == NULL))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(stats, 0, sizeof(stats));
|
|
||||||
|
|
||||||
uv_w = HALVE(src->width);
|
|
||||||
uv_h = HALVE(src->height);
|
|
||||||
if (type >= 2) {
|
|
||||||
float sse[4];
|
|
||||||
sse[0] = AccumulateLSIM(src->y, src->y_stride,
|
|
||||||
ref->y, ref->y_stride, src->width, src->height);
|
|
||||||
sse[1] = AccumulateLSIM(src->u, src->uv_stride,
|
|
||||||
ref->u, ref->uv_stride, uv_w, uv_h);
|
|
||||||
sse[2] = AccumulateLSIM(src->v, src->uv_stride,
|
|
||||||
ref->v, ref->uv_stride, uv_w, uv_h);
|
|
||||||
sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride,
|
|
||||||
ref->a, ref->a_stride,
|
|
||||||
src->width, src->height)
|
|
||||||
: 0.f;
|
|
||||||
result[0] = GetPSNR(sse[0] / (src->width * src->height));
|
|
||||||
result[1] = GetPSNR(sse[1] / (uv_w * uv_h));
|
|
||||||
result[2] = GetPSNR(sse[2] / (uv_w * uv_h));
|
|
||||||
result[3] = GetPSNR(sse[3] / (src->width * src->height));
|
|
||||||
{
|
|
||||||
double total_sse = sse[0] + sse[1] + sse[2];
|
|
||||||
int total_pixels = src->width * src->height + 2 * uv_w * uv_h;
|
|
||||||
if (has_alpha) {
|
|
||||||
total_pixels += src->width * src->height;
|
|
||||||
total_sse += sse[3];
|
|
||||||
}
|
|
||||||
result[4] = GetPSNR(total_sse / total_pixels);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int c;
|
|
||||||
VP8SSIMAccumulatePlane(src->y, src->y_stride,
|
|
||||||
ref->y, ref->y_stride,
|
|
||||||
src->width, src->height, &stats[0]);
|
|
||||||
VP8SSIMAccumulatePlane(src->u, src->uv_stride,
|
|
||||||
ref->u, ref->uv_stride,
|
|
||||||
uv_w, uv_h, &stats[1]);
|
|
||||||
VP8SSIMAccumulatePlane(src->v, src->uv_stride,
|
|
||||||
ref->v, ref->uv_stride,
|
|
||||||
uv_w, uv_h, &stats[2]);
|
|
||||||
if (has_alpha) {
|
|
||||||
VP8SSIMAccumulatePlane(src->a, src->a_stride,
|
|
||||||
ref->a, ref->a_stride,
|
|
||||||
src->width, src->height, &stats[3]);
|
|
||||||
}
|
|
||||||
for (c = 0; c <= 4; ++c) {
|
|
||||||
if (type == 1) {
|
|
||||||
const double v = VP8SSIMGet(&stats[c]);
|
|
||||||
result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
|
|
||||||
: kMinDistortion_dB);
|
|
||||||
} else {
|
|
||||||
const double v = VP8SSIMGetSquaredError(&stats[c]);
|
|
||||||
result[c] = GetPSNR(v);
|
|
||||||
}
|
|
||||||
// Accumulate forward
|
|
||||||
if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Simplest high-level calls:
|
// Simplest high-level calls:
|
||||||
|
|
||||||
|
150
src/enc/picture_psnr.c
Normal file
150
src/enc/picture_psnr.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the COPYING file in the root of the source
|
||||||
|
// tree. An additional intellectual property rights grant can be found
|
||||||
|
// in the file PATENTS. All contributing project authors may
|
||||||
|
// be found in the AUTHORS file in the root of the source tree.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// WebPPicture tools for measuring distortion
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "./vp8enci.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// local-min distortion
|
||||||
|
//
|
||||||
|
// For every pixel in the *reference* picture, we search for the local best
|
||||||
|
// match in the compressed image. This is not a symmetrical measure.
|
||||||
|
|
||||||
|
#define RADIUS 2 // search radius. Shouldn't be too large.
|
||||||
|
|
||||||
|
static float AccumulateLSIM(const uint8_t* src, int src_stride,
|
||||||
|
const uint8_t* ref, int ref_stride,
|
||||||
|
int w, int h) {
|
||||||
|
int x, y;
|
||||||
|
double total_sse = 0.;
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
|
||||||
|
const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
|
||||||
|
for (x = 0; x < w; ++x) {
|
||||||
|
const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
|
||||||
|
const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
|
||||||
|
double best_sse = 255. * 255.;
|
||||||
|
const double value = (double)ref[y * ref_stride + x];
|
||||||
|
int i, j;
|
||||||
|
for (j = y_0; j < y_1; ++j) {
|
||||||
|
const uint8_t* s = src + j * src_stride;
|
||||||
|
for (i = x_0; i < x_1; ++i) {
|
||||||
|
const double sse = (double)(s[i] - value) * (s[i] - value);
|
||||||
|
if (sse < best_sse) best_sse = sse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_sse += best_sse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (float)total_sse;
|
||||||
|
}
|
||||||
|
#undef RADIUS
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Distortion
|
||||||
|
|
||||||
|
// Max value returned in case of exact similarity.
|
||||||
|
static const double kMinDistortion_dB = 99.;
|
||||||
|
static float GetPSNR(const double v) {
|
||||||
|
return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
|
||||||
|
: kMinDistortion_dB);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
|
||||||
|
int type, float result[5]) {
|
||||||
|
DistoStats stats[5];
|
||||||
|
int has_alpha;
|
||||||
|
int uv_w, uv_h;
|
||||||
|
|
||||||
|
if (src == NULL || ref == NULL ||
|
||||||
|
src->width != ref->width || src->height != ref->height ||
|
||||||
|
src->y == NULL || ref->y == NULL ||
|
||||||
|
src->u == NULL || ref->u == NULL ||
|
||||||
|
src->v == NULL || ref->v == NULL ||
|
||||||
|
result == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// TODO(skal): provide distortion for ARGB too.
|
||||||
|
if (src->use_argb == 1 || src->use_argb != ref->use_argb) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT);
|
||||||
|
if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) ||
|
||||||
|
(has_alpha && (src->a == NULL || ref->a == NULL))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(stats, 0, sizeof(stats));
|
||||||
|
|
||||||
|
uv_w = (src->width + 1) >> 1;
|
||||||
|
uv_h = (src->height + 1) >> 1;
|
||||||
|
if (type >= 2) {
|
||||||
|
float sse[4];
|
||||||
|
sse[0] = AccumulateLSIM(src->y, src->y_stride,
|
||||||
|
ref->y, ref->y_stride, src->width, src->height);
|
||||||
|
sse[1] = AccumulateLSIM(src->u, src->uv_stride,
|
||||||
|
ref->u, ref->uv_stride, uv_w, uv_h);
|
||||||
|
sse[2] = AccumulateLSIM(src->v, src->uv_stride,
|
||||||
|
ref->v, ref->uv_stride, uv_w, uv_h);
|
||||||
|
sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride,
|
||||||
|
ref->a, ref->a_stride,
|
||||||
|
src->width, src->height)
|
||||||
|
: 0.f;
|
||||||
|
result[0] = GetPSNR(sse[0] / (src->width * src->height));
|
||||||
|
result[1] = GetPSNR(sse[1] / (uv_w * uv_h));
|
||||||
|
result[2] = GetPSNR(sse[2] / (uv_w * uv_h));
|
||||||
|
result[3] = GetPSNR(sse[3] / (src->width * src->height));
|
||||||
|
{
|
||||||
|
double total_sse = sse[0] + sse[1] + sse[2];
|
||||||
|
int total_pixels = src->width * src->height + 2 * uv_w * uv_h;
|
||||||
|
if (has_alpha) {
|
||||||
|
total_pixels += src->width * src->height;
|
||||||
|
total_sse += sse[3];
|
||||||
|
}
|
||||||
|
result[4] = GetPSNR(total_sse / total_pixels);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int c;
|
||||||
|
VP8SSIMAccumulatePlane(src->y, src->y_stride,
|
||||||
|
ref->y, ref->y_stride,
|
||||||
|
src->width, src->height, &stats[0]);
|
||||||
|
VP8SSIMAccumulatePlane(src->u, src->uv_stride,
|
||||||
|
ref->u, ref->uv_stride,
|
||||||
|
uv_w, uv_h, &stats[1]);
|
||||||
|
VP8SSIMAccumulatePlane(src->v, src->uv_stride,
|
||||||
|
ref->v, ref->uv_stride,
|
||||||
|
uv_w, uv_h, &stats[2]);
|
||||||
|
if (has_alpha) {
|
||||||
|
VP8SSIMAccumulatePlane(src->a, src->a_stride,
|
||||||
|
ref->a, ref->a_stride,
|
||||||
|
src->width, src->height, &stats[3]);
|
||||||
|
}
|
||||||
|
for (c = 0; c <= 4; ++c) {
|
||||||
|
if (type == 1) {
|
||||||
|
const double v = VP8SSIMGet(&stats[c]);
|
||||||
|
result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
|
||||||
|
: kMinDistortion_dB);
|
||||||
|
} else {
|
||||||
|
const double v = VP8SSIMGetSquaredError(&stats[c]);
|
||||||
|
result[c] = GetPSNR(v);
|
||||||
|
}
|
||||||
|
// Accumulate forward
|
||||||
|
if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
276
src/enc/picture_rescale.c
Normal file
276
src/enc/picture_rescale.c
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the COPYING file in the root of the source
|
||||||
|
// tree. An additional intellectual property rights grant can be found
|
||||||
|
// in the file PATENTS. All contributing project authors may
|
||||||
|
// be found in the AUTHORS file in the root of the source tree.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// WebPPicture tools: copy, crop, rescaling and view.
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "./vp8enci.h"
|
||||||
|
#include "../utils/rescaler.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
|
#define HALVE(x) (((x) + 1) >> 1)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Picture copying
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust top-left corner to chroma sample position.
|
||||||
|
static void SnapTopLeftPosition(const WebPPicture* const pic,
|
||||||
|
int* const left, int* const top) {
|
||||||
|
if (!pic->use_argb) {
|
||||||
|
*left &= ~1;
|
||||||
|
*top &= ~1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust top-left corner and verify that the sub-rectangle is valid.
|
||||||
|
static int AdjustAndCheckRectangle(const WebPPicture* const pic,
|
||||||
|
int* const left, int* const top,
|
||||||
|
int width, int height) {
|
||||||
|
SnapTopLeftPosition(pic, left, top);
|
||||||
|
if ((*left) < 0 || (*top) < 0) return 0;
|
||||||
|
if (width <= 0 || height <= 0) return 0;
|
||||||
|
if ((*left) + width > pic->width) return 0;
|
||||||
|
if ((*top) + height > pic->height) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
|
||||||
|
if (src == NULL || dst == NULL) return 0;
|
||||||
|
if (src == dst) return 1;
|
||||||
|
|
||||||
|
WebPPictureGrabSpecs(src, dst);
|
||||||
|
if (!WebPPictureAlloc(dst)) return 0;
|
||||||
|
|
||||||
|
if (!src->use_argb) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
|
||||||
|
(uint8_t*)dst->argb, 4 * dst->argb_stride,
|
||||||
|
4 * dst->width, dst->height);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPPictureIsView(const WebPPicture* picture) {
|
||||||
|
if (picture == NULL) return 0;
|
||||||
|
if (picture->use_argb) {
|
||||||
|
return (picture->memory_argb_ == NULL);
|
||||||
|
}
|
||||||
|
return (picture->memory_ == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPPictureView(const WebPPicture* src,
|
||||||
|
int left, int top, int width, int height,
|
||||||
|
WebPPicture* dst) {
|
||||||
|
if (src == NULL || dst == NULL) return 0;
|
||||||
|
|
||||||
|
// verify rectangle position.
|
||||||
|
if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
|
||||||
|
|
||||||
|
if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
|
||||||
|
WebPPictureGrabSpecs(src, dst);
|
||||||
|
}
|
||||||
|
dst->width = width;
|
||||||
|
dst->height = height;
|
||||||
|
if (!src->use_argb) {
|
||||||
|
dst->y = src->y + top * src->y_stride + left;
|
||||||
|
dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
|
||||||
|
dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
|
||||||
|
dst->y_stride = src->y_stride;
|
||||||
|
dst->uv_stride = src->uv_stride;
|
||||||
|
if (src->a != NULL) {
|
||||||
|
dst->a = src->a + top * src->a_stride + left;
|
||||||
|
dst->a_stride = src->a_stride;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dst->argb = src->argb + top * src->argb_stride + left;
|
||||||
|
dst->argb_stride = src->argb_stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Picture cropping
|
||||||
|
|
||||||
|
int WebPPictureCrop(WebPPicture* pic,
|
||||||
|
int left, int top, int width, int height) {
|
||||||
|
WebPPicture tmp;
|
||||||
|
|
||||||
|
if (pic == NULL) return 0;
|
||||||
|
if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
|
||||||
|
|
||||||
|
WebPPictureGrabSpecs(pic, &tmp);
|
||||||
|
tmp.width = width;
|
||||||
|
tmp.height = height;
|
||||||
|
if (!WebPPictureAlloc(&tmp)) return 0;
|
||||||
|
|
||||||
|
if (!pic->use_argb) {
|
||||||
|
const int y_offset = top * pic->y_stride + left;
|
||||||
|
const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
|
||||||
|
CopyPlane(pic->y + y_offset, pic->y_stride,
|
||||||
|
tmp.y, tmp.y_stride, width, height);
|
||||||
|
CopyPlane(pic->u + uv_offset, pic->uv_stride,
|
||||||
|
tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
|
||||||
|
CopyPlane(pic->v + uv_offset, pic->uv_stride,
|
||||||
|
tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
|
||||||
|
|
||||||
|
if (tmp.a != NULL) {
|
||||||
|
const int a_offset = top * pic->a_stride + left;
|
||||||
|
CopyPlane(pic->a + a_offset, pic->a_stride,
|
||||||
|
tmp.a, tmp.a_stride, width, height);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uint8_t* const src =
|
||||||
|
(const uint8_t*)(pic->argb + top * pic->argb_stride + left);
|
||||||
|
CopyPlane(src, pic->argb_stride * 4,
|
||||||
|
(uint8_t*)tmp.argb, tmp.argb_stride * 4,
|
||||||
|
width * 4, height);
|
||||||
|
}
|
||||||
|
WebPPictureFree(pic);
|
||||||
|
*pic = tmp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Simple picture rescaler
|
||||||
|
|
||||||
|
static void RescalePlane(const uint8_t* src,
|
||||||
|
int src_width, int src_height, int src_stride,
|
||||||
|
uint8_t* dst,
|
||||||
|
int dst_width, int dst_height, int dst_stride,
|
||||||
|
int32_t* const work,
|
||||||
|
int num_channels) {
|
||||||
|
WebPRescaler rescaler;
|
||||||
|
int y = 0;
|
||||||
|
WebPRescalerInit(&rescaler, src_width, src_height,
|
||||||
|
dst, dst_width, dst_height, dst_stride,
|
||||||
|
num_channels,
|
||||||
|
src_width, dst_width,
|
||||||
|
src_height, dst_height,
|
||||||
|
work);
|
||||||
|
memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
|
||||||
|
while (y < src_height) {
|
||||||
|
y += WebPRescalerImport(&rescaler, src_height - y,
|
||||||
|
src + y * src_stride, src_stride);
|
||||||
|
WebPRescalerExport(&rescaler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
|
||||||
|
assert(pic->argb != NULL);
|
||||||
|
WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb),
|
||||||
|
pic->width, pic->height, inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
|
||||||
|
if (pic->a != NULL) {
|
||||||
|
WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride,
|
||||||
|
pic->width, pic->height, inverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPPictureRescale(WebPPicture* pic, int width, int height) {
|
||||||
|
WebPPicture tmp;
|
||||||
|
int prev_width, prev_height;
|
||||||
|
int32_t* work;
|
||||||
|
|
||||||
|
if (pic == NULL) return 0;
|
||||||
|
prev_width = pic->width;
|
||||||
|
prev_height = pic->height;
|
||||||
|
// if width is unspecified, scale original proportionally to height ratio.
|
||||||
|
if (width == 0) {
|
||||||
|
width = (prev_width * height + prev_height / 2) / prev_height;
|
||||||
|
}
|
||||||
|
// if height is unspecified, scale original proportionally to width ratio.
|
||||||
|
if (height == 0) {
|
||||||
|
height = (prev_height * width + prev_width / 2) / prev_width;
|
||||||
|
}
|
||||||
|
// Check if the overall dimensions still make sense.
|
||||||
|
if (width <= 0 || height <= 0) return 0;
|
||||||
|
|
||||||
|
WebPPictureGrabSpecs(pic, &tmp);
|
||||||
|
tmp.width = width;
|
||||||
|
tmp.height = height;
|
||||||
|
if (!WebPPictureAlloc(&tmp)) return 0;
|
||||||
|
|
||||||
|
if (!pic->use_argb) {
|
||||||
|
work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
|
||||||
|
if (work == NULL) {
|
||||||
|
WebPPictureFree(&tmp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// If present, we need to rescale alpha first (for AlphaMultiplyY).
|
||||||
|
if (pic->a != NULL) {
|
||||||
|
WebPInitAlphaProcessing();
|
||||||
|
RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
|
||||||
|
tmp.a, width, height, tmp.a_stride, work, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We take transparency into account on the luma plane only. That's not
|
||||||
|
// totally exact blending, but still is a good approximation.
|
||||||
|
AlphaMultiplyY(pic, 0);
|
||||||
|
RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
|
||||||
|
tmp.y, width, height, tmp.y_stride, work, 1);
|
||||||
|
AlphaMultiplyY(&tmp, 1);
|
||||||
|
|
||||||
|
RescalePlane(pic->u,
|
||||||
|
HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
|
||||||
|
tmp.u,
|
||||||
|
HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
|
||||||
|
RescalePlane(pic->v,
|
||||||
|
HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
|
||||||
|
tmp.v,
|
||||||
|
HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
|
||||||
|
} else {
|
||||||
|
work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
|
||||||
|
if (work == NULL) {
|
||||||
|
WebPPictureFree(&tmp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// In order to correctly interpolate colors, we need to apply the alpha
|
||||||
|
// weighting first (black-matting), scale the RGB values, and remove
|
||||||
|
// the premultiplication afterward (while preserving the alpha channel).
|
||||||
|
WebPInitAlphaProcessing();
|
||||||
|
AlphaMultiplyARGB(pic, 0);
|
||||||
|
RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
|
||||||
|
pic->argb_stride * 4,
|
||||||
|
(uint8_t*)tmp.argb, width, height,
|
||||||
|
tmp.argb_stride * 4,
|
||||||
|
work, 4);
|
||||||
|
AlphaMultiplyARGB(&tmp, 1);
|
||||||
|
}
|
||||||
|
WebPPictureFree(pic);
|
||||||
|
WebPSafeFree(work);
|
||||||
|
*pic = tmp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
206
src/enc/picture_tools.c
Normal file
206
src/enc/picture_tools.c
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the COPYING file in the root of the source
|
||||||
|
// tree. An additional intellectual property rights grant can be found
|
||||||
|
// in the file PATENTS. All contributing project authors may
|
||||||
|
// be found in the AUTHORS file in the root of the source tree.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// WebPPicture tools: alpha handling, etc.
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include "./vp8enci.h"
|
||||||
|
#include "../dsp/yuv.h"
|
||||||
|
|
||||||
|
static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
|
||||||
|
return (0xff000000u | (r << 16) | (g << 8) | b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Helper: clean up fully transparent area to help compressibility.
|
||||||
|
|
||||||
|
#define SIZE 8
|
||||||
|
#define SIZE2 (SIZE / 2)
|
||||||
|
static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
|
||||||
|
int y, x;
|
||||||
|
for (y = 0; y < size; ++y) {
|
||||||
|
for (x = 0; x < size; ++x) {
|
||||||
|
if (ptr[x]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_transparent_argb_area(const uint32_t* ptr, int stride, int size) {
|
||||||
|
int y, x;
|
||||||
|
for (y = 0; y < size; ++y) {
|
||||||
|
for (x = 0; x < size; ++x) {
|
||||||
|
if (ptr[x] & 0xff000000u) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flatten(uint8_t* ptr, int v, int stride, int size) {
|
||||||
|
int y;
|
||||||
|
for (y = 0; y < size; ++y) {
|
||||||
|
memset(ptr, v, size);
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flatten_argb(uint32_t* ptr, uint32_t v, int stride, int size) {
|
||||||
|
int x, y;
|
||||||
|
for (y = 0; y < size; ++y) {
|
||||||
|
for (x = 0; x < size; ++x) ptr[x] = v;
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPCleanupTransparentArea(WebPPicture* pic) {
|
||||||
|
int x, y, w, h;
|
||||||
|
if (pic == NULL) return;
|
||||||
|
w = pic->width / SIZE;
|
||||||
|
h = pic->height / SIZE;
|
||||||
|
|
||||||
|
// note: we ignore the left-overs on right/bottom
|
||||||
|
if (pic->use_argb) {
|
||||||
|
uint32_t argb_value = 0;
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
int need_reset = 1;
|
||||||
|
for (x = 0; x < w; ++x) {
|
||||||
|
const int off = (y * pic->argb_stride + x) * SIZE;
|
||||||
|
if (is_transparent_argb_area(pic->argb + off, pic->argb_stride, SIZE)) {
|
||||||
|
if (need_reset) {
|
||||||
|
argb_value = pic->argb[off];
|
||||||
|
need_reset = 0;
|
||||||
|
}
|
||||||
|
flatten_argb(pic->argb + off, argb_value, pic->argb_stride, SIZE);
|
||||||
|
} else {
|
||||||
|
need_reset = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uint8_t* const a_ptr = pic->a;
|
||||||
|
int values[3] = { 0 };
|
||||||
|
if (a_ptr == NULL) return; // nothing to do
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
int need_reset = 1;
|
||||||
|
for (x = 0; x < w; ++x) {
|
||||||
|
const int off_a = (y * pic->a_stride + x) * SIZE;
|
||||||
|
const int off_y = (y * pic->y_stride + x) * SIZE;
|
||||||
|
const int off_uv = (y * pic->uv_stride + x) * SIZE2;
|
||||||
|
if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
|
||||||
|
if (need_reset) {
|
||||||
|
values[0] = pic->y[off_y];
|
||||||
|
values[1] = pic->u[off_uv];
|
||||||
|
values[2] = pic->v[off_uv];
|
||||||
|
need_reset = 0;
|
||||||
|
}
|
||||||
|
flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
|
||||||
|
flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
|
||||||
|
flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
|
||||||
|
} else {
|
||||||
|
need_reset = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SIZE
|
||||||
|
#undef SIZE2
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Blend color and remove transparency info
|
||||||
|
|
||||||
|
#define BLEND(V0, V1, ALPHA) \
|
||||||
|
((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
|
||||||
|
#define BLEND_10BIT(V0, V1, ALPHA) \
|
||||||
|
((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
|
||||||
|
|
||||||
|
void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
|
||||||
|
const int red = (background_rgb >> 16) & 0xff;
|
||||||
|
const int green = (background_rgb >> 8) & 0xff;
|
||||||
|
const int blue = (background_rgb >> 0) & 0xff;
|
||||||
|
int x, y;
|
||||||
|
if (pic == NULL) return;
|
||||||
|
if (!pic->use_argb) {
|
||||||
|
const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
|
||||||
|
const int Y0 = VP8RGBToY(red, green, blue, 1 << (YUV_FIX - 1));
|
||||||
|
// VP8RGBToU/V expects the u/v values summed over four pixels
|
||||||
|
const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 1 << (YUV_FIX + 1));
|
||||||
|
const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 1 << (YUV_FIX + 1));
|
||||||
|
const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
|
||||||
|
if (!has_alpha || pic->a == NULL) return; // nothing to do
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
// Luma blending
|
||||||
|
uint8_t* const y_ptr = pic->y + y * pic->y_stride;
|
||||||
|
uint8_t* const a_ptr = pic->a + y * pic->a_stride;
|
||||||
|
for (x = 0; x < pic->width; ++x) {
|
||||||
|
const int alpha = a_ptr[x];
|
||||||
|
if (alpha < 0xff) {
|
||||||
|
y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Chroma blending every even line
|
||||||
|
if ((y & 1) == 0) {
|
||||||
|
uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
|
||||||
|
uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
|
||||||
|
uint8_t* const a_ptr2 =
|
||||||
|
(y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
|
||||||
|
for (x = 0; x < uv_width; ++x) {
|
||||||
|
// Average four alpha values into a single blending weight.
|
||||||
|
// TODO(skal): might lead to visible contouring. Can we do better?
|
||||||
|
const int alpha =
|
||||||
|
a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
|
||||||
|
a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
|
||||||
|
u[x] = BLEND_10BIT(U0, u[x], alpha);
|
||||||
|
v[x] = BLEND_10BIT(V0, v[x], alpha);
|
||||||
|
}
|
||||||
|
if (pic->width & 1) { // rightmost pixel
|
||||||
|
const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
|
||||||
|
u[x] = BLEND_10BIT(U0, u[x], alpha);
|
||||||
|
v[x] = BLEND_10BIT(V0, v[x], alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memset(a_ptr, 0xff, pic->width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t* argb = pic->argb;
|
||||||
|
const uint32_t background = MakeARGB32(red, green, blue);
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
for (x = 0; x < pic->width; ++x) {
|
||||||
|
const int alpha = (argb[x] >> 24) & 0xff;
|
||||||
|
if (alpha != 0xff) {
|
||||||
|
if (alpha > 0) {
|
||||||
|
int r = (argb[x] >> 16) & 0xff;
|
||||||
|
int g = (argb[x] >> 8) & 0xff;
|
||||||
|
int b = (argb[x] >> 0) & 0xff;
|
||||||
|
r = BLEND(red, r, alpha);
|
||||||
|
g = BLEND(green, g, alpha);
|
||||||
|
b = BLEND(blue, b, alpha);
|
||||||
|
argb[x] = MakeARGB32(r, g, b);
|
||||||
|
} else {
|
||||||
|
argb[x] = background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argb += pic->argb_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef BLEND
|
||||||
|
#undef BLEND_10BIT
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
@ -553,6 +553,13 @@ void VP8AdjustFilterStrength(VP8EncIterator* const it);
|
|||||||
// step of 'delta', given a sharpness parameter 'sharpness'.
|
// step of 'delta', given a sharpness parameter 'sharpness'.
|
||||||
int VP8FilterStrengthFromDelta(int sharpness, int delta);
|
int VP8FilterStrengthFromDelta(int sharpness, int delta);
|
||||||
|
|
||||||
|
// misc utils for picture_*.c:
|
||||||
|
|
||||||
|
// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
|
||||||
|
// into 'dst'. Mark 'dst' as not owning any memory.
|
||||||
|
void WebPPictureGrabSpecs(const WebPPicture* const src,
|
||||||
|
WebPPicture* const dst);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
Loading…
Reference in New Issue
Block a user