mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-15 21:39:59 +02:00
WEBP_REDUCE_SIZE was introduced to bring down the library size by removing cropping and scaling support. Previously WebPPictureView() was only used with these two, but inec178f2c
Add progress hook granularity in lossless an additional use was added in VP8LEncodeStream() when extra side configurations are used in crunch mode (-mt, quality == 100 & method == 6 or quality >= 75 & method == 5 with a palette present currently). WebPPictureView() and, for coherency, WebPPictureIsView() are restored in this configuration to avoid affecting the general encode path. Previously WebPPictureView() was assumed to always succeed in these cases which could result in crashes with WEBP_REDUCE_SIZE defined. Bug: chromium:1345547 Bug: chromium:1345595 Bug: chromium:1345772 Bug: chromium:1345804 Change-Id: Ifecde36a726a434510478a764514b1469942c684 (cherry picked from commit84163d9d02
)
301 lines
9.8 KiB
C
301 lines
9.8 KiB
C
// 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 "src/webp/encode.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "src/enc/vp8i_enc.h"
|
|
|
|
#if !defined(WEBP_REDUCE_SIZE)
|
|
#include "src/utils/rescaler_utils.h"
|
|
#include "src/utils/utils.h"
|
|
#endif // !defined(WEBP_REDUCE_SIZE)
|
|
|
|
#define HALVE(x) (((x) + 1) >> 1)
|
|
|
|
// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
|
|
// into 'dst'. Mark 'dst' as not owning any memory.
|
|
static void PictureGrabSpecs(const WebPPicture* const src,
|
|
WebPPicture* const dst) {
|
|
assert(src != NULL && dst != NULL);
|
|
*dst = *src;
|
|
WebPPictureResetBuffers(dst);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// 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;
|
|
}
|
|
|
|
#if !defined(WEBP_REDUCE_SIZE)
|
|
int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
|
|
if (src == NULL || dst == NULL) return 0;
|
|
if (src == dst) return 1;
|
|
|
|
PictureGrabSpecs(src, dst);
|
|
if (!WebPPictureAlloc(dst)) return 0;
|
|
|
|
if (!src->use_argb) {
|
|
WebPCopyPlane(src->y, src->y_stride,
|
|
dst->y, dst->y_stride, dst->width, dst->height);
|
|
WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride,
|
|
HALVE(dst->width), HALVE(dst->height));
|
|
WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride,
|
|
HALVE(dst->width), HALVE(dst->height));
|
|
if (dst->a != NULL) {
|
|
WebPCopyPlane(src->a, src->a_stride,
|
|
dst->a, dst->a_stride, dst->width, dst->height);
|
|
}
|
|
} else {
|
|
WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
|
|
(uint8_t*)dst->argb, 4 * dst->argb_stride,
|
|
4 * dst->width, dst->height);
|
|
}
|
|
return 1;
|
|
}
|
|
#endif // !defined(WEBP_REDUCE_SIZE)
|
|
|
|
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_'.
|
|
PictureGrabSpecs(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;
|
|
}
|
|
|
|
#if !defined(WEBP_REDUCE_SIZE)
|
|
//------------------------------------------------------------------------------
|
|
// 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;
|
|
|
|
PictureGrabSpecs(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;
|
|
WebPCopyPlane(pic->y + y_offset, pic->y_stride,
|
|
tmp.y, tmp.y_stride, width, height);
|
|
WebPCopyPlane(pic->u + uv_offset, pic->uv_stride,
|
|
tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
|
|
WebPCopyPlane(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;
|
|
WebPCopyPlane(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);
|
|
WebPCopyPlane(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 int 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,
|
|
rescaler_t* const work,
|
|
int num_channels) {
|
|
WebPRescaler rescaler;
|
|
int y = 0;
|
|
if (!WebPRescalerInit(&rescaler, src_width, src_height,
|
|
dst, dst_width, dst_height, dst_stride,
|
|
num_channels, work)) {
|
|
return 0;
|
|
}
|
|
while (y < src_height) {
|
|
y += WebPRescalerImport(&rescaler, src_height - y,
|
|
src + y * src_stride, src_stride);
|
|
WebPRescalerExport(&rescaler);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
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* picture, int width, int height) {
|
|
WebPPicture tmp;
|
|
int prev_width, prev_height;
|
|
rescaler_t* work;
|
|
|
|
if (picture == NULL) return 0;
|
|
prev_width = picture->width;
|
|
prev_height = picture->height;
|
|
if (!WebPRescalerGetScaledDimensions(
|
|
prev_width, prev_height, &width, &height)) {
|
|
return 0;
|
|
}
|
|
|
|
PictureGrabSpecs(picture, &tmp);
|
|
tmp.width = width;
|
|
tmp.height = height;
|
|
if (!WebPPictureAlloc(&tmp)) return 0;
|
|
|
|
if (!picture->use_argb) {
|
|
work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
|
|
if (work == NULL) {
|
|
WebPPictureFree(&tmp);
|
|
return 0;
|
|
}
|
|
// If present, we need to rescale alpha first (for AlphaMultiplyY).
|
|
if (picture->a != NULL) {
|
|
WebPInitAlphaProcessing();
|
|
if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
|
|
tmp.a, width, height, tmp.a_stride, work, 1)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// We take transparency into account on the luma plane only. That's not
|
|
// totally exact blending, but still is a good approximation.
|
|
AlphaMultiplyY(picture, 0);
|
|
if (!RescalePlane(picture->y, prev_width, prev_height, picture->y_stride,
|
|
tmp.y, width, height, tmp.y_stride, work, 1) ||
|
|
!RescalePlane(picture->u, HALVE(prev_width), HALVE(prev_height),
|
|
picture->uv_stride, tmp.u, HALVE(width), HALVE(height),
|
|
tmp.uv_stride, work, 1) ||
|
|
!RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
|
|
picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
|
|
tmp.uv_stride, work, 1)) {
|
|
return 0;
|
|
}
|
|
AlphaMultiplyY(&tmp, 1);
|
|
} else {
|
|
work = (rescaler_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(picture, 0);
|
|
if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
|
|
picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
|
|
height, tmp.argb_stride * 4, work, 4)) {
|
|
return 0;
|
|
}
|
|
AlphaMultiplyARGB(&tmp, 1);
|
|
}
|
|
WebPPictureFree(picture);
|
|
WebPSafeFree(work);
|
|
*picture = tmp;
|
|
return 1;
|
|
}
|
|
|
|
#else // defined(WEBP_REDUCE_SIZE)
|
|
|
|
int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
|
|
(void)src;
|
|
(void)dst;
|
|
return 0;
|
|
}
|
|
|
|
int WebPPictureCrop(WebPPicture* pic,
|
|
int left, int top, int width, int height) {
|
|
(void)pic;
|
|
(void)left;
|
|
(void)top;
|
|
(void)width;
|
|
(void)height;
|
|
return 0;
|
|
}
|
|
|
|
int WebPPictureRescale(WebPPicture* pic, int width, int height) {
|
|
(void)pic;
|
|
(void)width;
|
|
(void)height;
|
|
return 0;
|
|
}
|
|
#endif // !defined(WEBP_REDUCE_SIZE)
|