libwebp: Sync with upstream 1.2.1

Changes: https://chromium.googlesource.com/webm/libwebp/+/1.2.1/NEWS
(cherry picked from commit 41ce417847)
This commit is contained in:
Rémi Verschelde 2021-11-19 13:54:40 +01:00
parent d085385e60
commit 724c207005
No known key found for this signature in database
GPG key ID: C3336907360768E1
71 changed files with 1649 additions and 1138 deletions

View file

@ -67,6 +67,7 @@ if env["builtin_libwebp"]:
"dsp/lossless_msa.c",
"dsp/lossless_neon.c",
"dsp/lossless_sse2.c",
"dsp/lossless_sse41.c",
"dsp/rescaler.c",
"dsp/rescaler_mips32.c",
"dsp/rescaler_mips_dsp_r2.c",

View file

@ -246,13 +246,13 @@ from the Android NDK r18.
## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/
- Version: 1.1.0 (d7844e9762b61c9638c263657bd49e1690184832, 2020)
- Version: 1.2.1 (9ce5843dbabcfd3f7c39ec7ceba9cbeb213cbfdf, 2021)
- License: BSD-3-Clause
Files extracted from upstream source:
- `src/*` except from: .am, .rc and .in files
- AUTHORS, COPYING, PATENTS
- `src/*` except from: `.am`, `.rc` and `.in` files
- `AUTHORS`, `COPYING`, `PATENTS`
Important: The files `utils/bit_reader_utils.{c,h}` have Godot-made
changes to ensure they build for Javascript/HTML5. Those

View file

@ -1,9 +1,15 @@
Contributors:
- Aidan O'Loan (aidanol at gmail dot com)
- Alan Browning (browning at google dot com)
- Charles Munger (clm at google dot com)
- Cheng Yi (cyi at google dot com)
- Christian Duvivier (cduvivier at google dot com)
- Christopher Degawa (ccom at randomderp dot com)
- Clement Courbet (courbet at google dot com)
- Djordje Pesut (djordje dot pesut at imgtec dot com)
- Hui Su (huisu at google dot com)
- Ilya Kurdyukov (jpegqs at gmail dot com)
- Ingvar Stepanyan (rreverser at google dot com)
- James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de)
- Jehan (jehan at girinstud dot io)
@ -20,6 +26,7 @@ Contributors:
- Mislav Bradac (mislavm at google dot com)
- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org)
- Oliver Wolff (oliver dot wolff at qt dot io)
- Owen Rodley (orodley at google dot com)
- Parag Salasakar (img dot mips1 at gmail dot com)
- Pascal Massimino (pascal dot massimino at gmail dot com)
@ -38,5 +45,7 @@ Contributors:
- Vikas Arora (vikasa at google dot com)
- Vincent Rabaud (vrabaud at google dot com)
- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
- Wan-Teh Chang (wtc at google dot com)
- Yang Zhang (yang dot zhang at arm dot com)
- Yannis Guyon (yguyon at google dot com)
- Zhi An Ng (zhin at chromium dot org)

View file

@ -183,7 +183,7 @@ const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
assert(dec != NULL && io != NULL);
if (row < 0 || num_rows <= 0 || row + num_rows > height) {
return NULL; // sanity check.
return NULL;
}
if (!dec->is_alpha_decoded_) {

View file

@ -102,7 +102,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
int stride;
uint64_t size;
if ((uint64_t)w * kModeBpp[mode] >= (1ull << 32)) {
if ((uint64_t)w * kModeBpp[mode] >= (1ull << 31)) {
return VP8_STATUS_INVALID_PARAM;
}
stride = w * kModeBpp[mode];
@ -117,7 +117,6 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
}
total_size = size + 2 * uv_size + a_size;
// Security/sanity checks
output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
if (output == NULL) {
return VP8_STATUS_OUT_OF_MEMORY;
@ -156,11 +155,11 @@ VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
}
if (WebPIsRGBMode(buffer->colorspace)) {
WebPRGBABuffer* const buf = &buffer->u.RGBA;
buf->rgba += (buffer->height - 1) * buf->stride;
buf->rgba += (int64_t)(buffer->height - 1) * buf->stride;
buf->stride = -buf->stride;
} else {
WebPYUVABuffer* const buf = &buffer->u.YUVA;
const int H = buffer->height;
const int64_t H = buffer->height;
buf->y += (H - 1) * buf->y_stride;
buf->y_stride = -buf->y_stride;
buf->u += ((H - 1) >> 1) * buf->u_stride;
@ -188,8 +187,7 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
const int ch = options->crop_height;
const int x = options->crop_left & ~1;
const int y = options->crop_top & ~1;
if (x < 0 || y < 0 || cw <= 0 || ch <= 0 ||
x + cw > width || y + ch > height) {
if (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) {
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
}
width = cw;

View file

@ -705,7 +705,7 @@ static int AllocateMemory(VP8Decoder* const dec) {
+ cache_size + alpha_size + WEBP_ALIGN_CST;
uint8_t* mem;
if (needed != (size_t)needed) return 0; // check for overflow
if (!CheckSizeOverflow(needed)) return 0; // check for overflow
if (needed > dec->mem_size_) {
WebPSafeFree(dec->mem_);
dec->mem_size_ = 0;

View file

@ -25,21 +25,16 @@
static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
WebPDecBuffer* output = p->output;
const WebPYUVABuffer* const buf = &output->u.YUVA;
uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
uint8_t* const y_dst = buf->y + (size_t)io->mb_y * buf->y_stride;
uint8_t* const u_dst = buf->u + (size_t)(io->mb_y >> 1) * buf->u_stride;
uint8_t* const v_dst = buf->v + (size_t)(io->mb_y >> 1) * buf->v_stride;
const int mb_w = io->mb_w;
const int mb_h = io->mb_h;
const int uv_w = (mb_w + 1) / 2;
const int uv_h = (mb_h + 1) / 2;
int j;
for (j = 0; j < mb_h; ++j) {
memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
}
for (j = 0; j < uv_h; ++j) {
memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
}
WebPCopyPlane(io->y, io->y_stride, y_dst, buf->y_stride, mb_w, mb_h);
WebPCopyPlane(io->u, io->uv_stride, u_dst, buf->u_stride, uv_w, uv_h);
WebPCopyPlane(io->v, io->uv_stride, v_dst, buf->v_stride, uv_w, uv_h);
return io->mb_h;
}
@ -47,7 +42,7 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
WebPDecBuffer* const output = p->output;
WebPRGBABuffer* const buf = &output->u.RGBA;
uint8_t* const dst = buf->rgba + io->mb_y * buf->stride;
uint8_t* const dst = buf->rgba + (size_t)io->mb_y * buf->stride;
WebPSamplerProcessPlane(io->y, io->y_stride,
io->u, io->v, io->uv_stride,
dst, buf->stride, io->mb_w, io->mb_h,
@ -62,7 +57,7 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
int num_lines_out = io->mb_h; // a priori guess
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
uint8_t* dst = buf->rgba + (size_t)io->mb_y * buf->stride;
WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
const uint8_t* cur_y = io->y;
const uint8_t* cur_u = io->u;
@ -133,7 +128,7 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
const int mb_w = io->mb_w;
const int mb_h = io->mb_h;
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
uint8_t* dst = buf->a + (size_t)io->mb_y * buf->a_stride;
int j;
(void)expected_num_lines_out;
assert(expected_num_lines_out == mb_h);
@ -186,7 +181,7 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
@ -210,7 +205,7 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
#if (WEBP_SWAP_16BIT_CSP == 1)
uint8_t* alpha_dst = base_rgba;
@ -276,9 +271,9 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
int expected_num_lines_out) {
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride;
uint8_t* const dst_a = buf->a + (size_t)p->last_y * buf->a_stride;
if (io->a != NULL) {
uint8_t* const dst_y = buf->y + p->last_y * buf->y_stride;
uint8_t* const dst_y = buf->y + (size_t)p->last_y * buf->y_stride;
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a);
assert(expected_num_lines_out == num_lines_out);
if (num_lines_out > 0) { // unmultiply the Y
@ -303,46 +298,57 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int uv_out_height = (out_height + 1) >> 1;
const int uv_in_width = (io->mb_w + 1) >> 1;
const int uv_in_height = (io->mb_h + 1) >> 1;
const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
// scratch memory for luma rescaler
const size_t work_size = 2 * (size_t)out_width;
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
size_t tmp_size, rescaler_size;
uint64_t total_size;
size_t rescaler_size;
rescaler_t* work;
WebPRescaler* scalers;
const int num_rescalers = has_alpha ? 4 : 3;
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
total_size = ((uint64_t)work_size + 2 * uv_work_size) * sizeof(*work);
if (has_alpha) {
tmp_size += work_size * sizeof(*work);
total_size += (uint64_t)work_size * sizeof(*work);
}
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
total_size += rescaler_size;
if (!CheckSizeOverflow(total_size)) {
return 0;
}
p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size);
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
if (p->memory == NULL) {
return 0; // memory error
}
work = (rescaler_t*)p->memory;
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size);
scalers = (WebPRescaler*)WEBP_ALIGN(
(const uint8_t*)work + total_size - rescaler_size);
p->scaler_y = &scalers[0];
p->scaler_u = &scalers[1];
p->scaler_v = &scalers[2];
p->scaler_a = has_alpha ? &scalers[3] : NULL;
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
buf->y, out_width, out_height, buf->y_stride, 1,
work);
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
work) ||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
work + work_size);
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
work + work_size) ||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
work + work_size + uv_work_size);
work + work_size + uv_work_size)) {
return 0;
}
p->emit = EmitRescaledYUV;
if (has_alpha) {
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
buf->a, out_width, out_height, buf->a_stride, 1,
work + work_size + 2 * uv_work_size);
work + work_size + 2 * uv_work_size)) {
return 0;
}
p->emit_alpha = EmitRescaledAlphaYUV;
WebPInitAlphaProcessing();
}
@ -356,7 +362,7 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
const WebPYUV444Converter convert =
WebPYUV444Converters[p->output->colorspace];
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + y_pos * buf->stride;
uint8_t* dst = buf->rgba + (size_t)y_pos * buf->stride;
int num_lines_out = 0;
// For RGB rescaling, because of the YUV420, current scan position
// U/V can be +1/-1 line from the Y one. Hence the double test.
@ -383,15 +389,15 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
while (j < mb_h) {
const int y_lines_in =
WebPRescalerImport(p->scaler_y, mb_h - j,
io->y + j * io->y_stride, io->y_stride);
io->y + (size_t)j * io->y_stride, io->y_stride);
j += y_lines_in;
if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) {
const int u_lines_in =
WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j,
io->u + uv_j * io->uv_stride, io->uv_stride);
const int v_lines_in =
WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j,
io->v + uv_j * io->uv_stride, io->uv_stride);
const int u_lines_in = WebPRescalerImport(
p->scaler_u, uv_mb_h - uv_j, io->u + (size_t)uv_j * io->uv_stride,
io->uv_stride);
const int v_lines_in = WebPRescalerImport(
p->scaler_v, uv_mb_h - uv_j, io->v + (size_t)uv_j * io->uv_stride,
io->uv_stride);
(void)v_lines_in; // remove a gcc warning
assert(u_lines_in == v_lines_in);
uv_j += u_lines_in;
@ -403,7 +409,7 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int alpha_first =
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
@ -431,7 +437,7 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
int max_lines_out) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
#if (WEBP_SWAP_16BIT_CSP == 1)
uint8_t* alpha_dst = base_rgba;
#else
@ -470,7 +476,7 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
int lines_left = expected_num_out_lines;
const int y_end = p->last_y + lines_left;
while (lines_left > 0) {
const int row_offset = scaler->src_y - io->mb_y;
const int64_t row_offset = (int64_t)scaler->src_y - io->mb_y;
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
io->a + row_offset * io->width, io->width);
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
@ -485,51 +491,58 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int out_height = io->scaled_height;
const int uv_in_width = (io->mb_w + 1) >> 1;
const int uv_in_height = (io->mb_h + 1) >> 1;
const size_t work_size = 2 * out_width; // scratch memory for one rescaler
// scratch memory for one rescaler
const size_t work_size = 2 * (size_t)out_width;
rescaler_t* work; // rescalers work area
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
size_t tmp_size1, tmp_size2, total_size, rescaler_size;
uint64_t tmp_size1, tmp_size2, total_size;
size_t rescaler_size;
WebPRescaler* scalers;
const int num_rescalers = has_alpha ? 4 : 3;
tmp_size1 = 3 * work_size;
tmp_size2 = 3 * out_width;
if (has_alpha) {
tmp_size1 += work_size;
tmp_size2 += out_width;
}
tmp_size1 = (uint64_t)num_rescalers * work_size;
tmp_size2 = (uint64_t)num_rescalers * out_width;
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
total_size += rescaler_size;
if (!CheckSizeOverflow(total_size)) {
return 0;
}
p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size);
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
if (p->memory == NULL) {
return 0; // memory error
}
work = (rescaler_t*)p->memory;
tmp = (uint8_t*)(work + tmp_size1);
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size);
scalers = (WebPRescaler*)WEBP_ALIGN(
(const uint8_t*)work + total_size - rescaler_size);
p->scaler_y = &scalers[0];
p->scaler_u = &scalers[1];
p->scaler_v = &scalers[2];
p->scaler_a = has_alpha ? &scalers[3] : NULL;
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
tmp + 0 * out_width, out_width, out_height, 0, 1,
work + 0 * work_size);
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
work + 0 * work_size) ||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
tmp + 1 * out_width, out_width, out_height, 0, 1,
work + 1 * work_size);
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
work + 1 * work_size) ||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
tmp + 2 * out_width, out_width, out_height, 0, 1,
work + 2 * work_size);
work + 2 * work_size)) {
return 0;
}
p->emit = EmitRescaledRGB;
WebPInitYUV444Converters();
if (has_alpha) {
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
tmp + 3 * out_width, out_width, out_height, 0, 1,
work + 3 * work_size);
work + 3 * work_size)) {
return 0;
}
p->emit_alpha = EmitRescaledAlphaRGB;
if (p->output->colorspace == MODE_RGBA_4444 ||
p->output->colorspace == MODE_rgbA_4444) {

View file

@ -335,7 +335,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
io->scaled_width = io->width;
io->scaled_height = io->height;
io->mb_w = io->width; // sanity check
io->mb_w = io->width; // for soundness
io->mb_h = io->height; // ditto
VP8ResetProba(&dec->proba_);
@ -494,14 +494,12 @@ static int GetCoeffsAlt(VP8BitReader* const br,
return 16;
}
static WEBP_TSAN_IGNORE_FUNCTION void InitGetCoeffs(void) {
if (GetCoeffs == NULL) {
WEBP_DSP_INIT_FUNC(InitGetCoeffs) {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
GetCoeffs = GetCoeffsAlt;
} else {
GetCoeffs = GetCoeffsFast;
}
}
}
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {

View file

@ -31,8 +31,8 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
#define DEC_MIN_VERSION 1
#define DEC_REV_VERSION 0
#define DEC_MIN_VERSION 2
#define DEC_REV_VERSION 1
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),

View file

@ -559,8 +559,11 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
memory += work_size * sizeof(*work);
scaled_data = (uint32_t*)memory;
WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data,
out_width, out_height, 0, num_channels, work);
if (!WebPRescalerInit(dec->rescaler, in_width, in_height,
(uint8_t*)scaled_data, out_width, out_height,
0, num_channels, work)) {
return 0;
}
return 1;
}
#endif // WEBP_REDUCE_SIZE
@ -574,13 +577,14 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace,
int rgba_stride, uint8_t* const rgba) {
uint32_t* const src = (uint32_t*)rescaler->dst;
uint8_t* dst = rgba;
const int dst_width = rescaler->dst_width;
int num_lines_out = 0;
while (WebPRescalerHasPendingOutput(rescaler)) {
uint8_t* const dst = rgba + num_lines_out * rgba_stride;
WebPRescalerExportRow(rescaler);
WebPMultARGBRow(src, dst_width, 1);
VP8LConvertFromBGRA(src, dst_width, colorspace, dst);
dst += rgba_stride;
++num_lines_out;
}
return num_lines_out;
@ -594,8 +598,8 @@ static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec,
int num_lines_in = 0;
int num_lines_out = 0;
while (num_lines_in < mb_h) {
uint8_t* const row_in = in + num_lines_in * in_stride;
uint8_t* const row_out = out + num_lines_out * out_stride;
uint8_t* const row_in = in + (uint64_t)num_lines_in * in_stride;
uint8_t* const row_out = out + (uint64_t)num_lines_out * out_stride;
const int lines_left = mb_h - num_lines_in;
const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left);
int lines_imported;
@ -796,7 +800,8 @@ static void ProcessRows(VP8LDecoder* const dec, int row) {
const WebPDecBuffer* const output = dec->output_;
if (WebPIsRGBMode(output->colorspace)) { // convert to RGBA
const WebPRGBABuffer* const buf = &output->u.RGBA;
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
uint8_t* const rgba =
buf->rgba + (int64_t)dec->last_out_row_ * buf->stride;
const int num_rows_out =
#if !defined(WEBP_REDUCE_SIZE)
io->use_scaling ?
@ -947,7 +952,6 @@ static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) {
break;
default:
goto Copy;
break;
}
CopySmallPattern8b(src, dst, length, pattern);
return;
@ -1515,7 +1519,7 @@ static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) {
assert(dec->width_ <= final_width);
dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t));
if (dec->pixels_ == NULL) {
dec->argb_cache_ = NULL; // for sanity check
dec->argb_cache_ = NULL; // for soundness
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
return 0;
}
@ -1525,7 +1529,7 @@ static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) {
static int AllocateInternalBuffers8b(VP8LDecoder* const dec) {
const uint64_t total_num_pixels = (uint64_t)dec->width_ * dec->height_;
dec->argb_cache_ = NULL; // for sanity check
dec->argb_cache_ = NULL; // for soundness
dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint8_t));
if (dec->pixels_ == NULL) {
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
@ -1667,7 +1671,6 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
VP8Io* io = NULL;
WebPDecParams* params = NULL;
// Sanity checks.
if (dec == NULL) return 0;
assert(dec->hdr_.huffman_tables_ != NULL);

View file

@ -785,6 +785,13 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
//------------------------------------------------------------------------------
// Cropping and rescaling.
int WebPCheckCropDimensions(int image_width, int image_height,
int x, int y, int w, int h) {
return !(x < 0 || y < 0 || w <= 0 || h <= 0 ||
x >= image_width || w > image_width || w > image_width - x ||
y >= image_height || h > image_height || h > image_height - y);
}
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
const int W = io->width;
@ -792,7 +799,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
int x = 0, y = 0, w = W, h = H;
// Cropping
io->use_cropping = (options != NULL) && (options->use_cropping > 0);
io->use_cropping = (options != NULL) && options->use_cropping;
if (io->use_cropping) {
w = options->crop_width;
h = options->crop_height;
@ -802,7 +809,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
x &= ~1;
y &= ~1;
}
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
if (!WebPCheckCropDimensions(W, H, x, y, w, h)) {
return 0; // out of frame boundary error
}
}
@ -814,7 +821,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
io->mb_h = h;
// Scaling
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
io->use_scaling = (options != NULL) && options->use_scaling;
if (io->use_scaling) {
int scaled_width = options->scaled_width;
int scaled_height = options->scaled_height;
@ -835,7 +842,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
if (io->use_scaling) {
// disable filter (only for large downscaling ratio).
io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
io->bypass_filtering |= (io->scaled_width < W * 3 / 4) &&
(io->scaled_height < H * 3 / 4);
io->fancy_upsampling = 0;
}

View file

@ -77,6 +77,10 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
//------------------------------------------------------------------------------
// Misc utils
// Returns true if crop dimensions are within image bounds.
int WebPCheckCropDimensions(int image_width, int image_height,
int x, int y, int w, int h);
// Initializes VP8Io with custom setup, io and teardown functions. The default
// hooks will use the supplied 'params' as io->opaque handle.
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);

View file

@ -87,11 +87,19 @@ WebPAnimDecoder* WebPAnimDecoderNewInternal(
int abi_version) {
WebPAnimDecoderOptions options;
WebPAnimDecoder* dec = NULL;
WebPBitstreamFeatures features;
if (webp_data == NULL ||
WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
return NULL;
}
// Validate the bitstream before doing expensive allocations. The demuxer may
// be more tolerant than the decoder.
if (WebPGetFeatures(webp_data->bytes, webp_data->size, &features) !=
VP8_STATUS_OK) {
return NULL;
}
// Note: calloc() so that the pointer members are initialized to NULL.
dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
if (dec == NULL) goto Error;
@ -145,7 +153,7 @@ static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
uint32_t canvas_height) {
const uint64_t size =
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
if (size != (size_t)size) return 0;
if (!CheckSizeOverflow(size)) return 0;
memset(buf, 0, (size_t)size);
return 1;
}
@ -166,7 +174,7 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
static int CopyCanvas(const uint8_t* src, uint8_t* dst,
uint32_t width, uint32_t height) {
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
if (size != (size_t)size) return 0;
if (!CheckSizeOverflow(size)) return 0;
assert(src != NULL && dst != NULL);
memcpy(dst, src, (size_t)size);
return 1;
@ -346,12 +354,15 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
{
const uint8_t* in = iter.fragment.bytes;
const size_t in_size = iter.fragment.size;
const size_t out_offset =
(iter.y_offset * width + iter.x_offset) * NUM_CHANNELS;
const uint32_t stride = width * NUM_CHANNELS; // at most 25 + 2 bits
const uint64_t out_offset = (uint64_t)iter.y_offset * stride +
(uint64_t)iter.x_offset * NUM_CHANNELS; // 53b
const uint64_t size = (uint64_t)iter.height * stride; // at most 25 + 27b
WebPDecoderConfig* const config = &dec->config_;
WebPRGBABuffer* const buf = &config->output.u.RGBA;
buf->stride = NUM_CHANNELS * width;
buf->size = buf->stride * iter.height;
if ((size_t)size != size) goto Error;
buf->stride = (int)stride;
buf->size = (size_t)size;
buf->rgba = dec->curr_frame_ + out_offset;
if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {

View file

@ -24,8 +24,8 @@
#include "src/webp/format_constants.h"
#define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 1
#define DMUX_REV_VERSION 0
#define DMUX_MIN_VERSION 2
#define DMUX_REV_VERSION 1
typedef struct {
size_t start_; // start location of the data
@ -221,12 +221,16 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
const size_t chunk_start_offset = mem->start_;
const uint32_t fourcc = ReadLE32(mem);
const uint32_t payload_size = ReadLE32(mem);
const uint32_t payload_size_padded = payload_size + (payload_size & 1);
const size_t payload_available = (payload_size_padded > MemDataSize(mem))
? MemDataSize(mem) : payload_size_padded;
const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
uint32_t payload_size_padded;
size_t payload_available;
size_t chunk_size;
if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
payload_size_padded = payload_size + (payload_size & 1);
payload_available = (payload_size_padded > MemDataSize(mem))
? MemDataSize(mem) : payload_size_padded;
chunk_size = CHUNK_HEADER_SIZE + payload_available;
if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
@ -312,6 +316,7 @@ static ParseStatus ParseAnimationFrame(
int bits;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
size_t start_offset;
ParseStatus status =
NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
if (status != PARSE_OK) return status;
@ -332,7 +337,11 @@ static ParseStatus ParseAnimationFrame(
// Store a frame only if the animation flag is set there is some data for
// this frame is available.
start_offset = mem->start_;
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
if (status != PARSE_ERROR && mem->start_ - start_offset > anmf_payload_size) {
status = PARSE_ERROR;
}
if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
added_frame = AddFrame(dmux, frame);
if (added_frame) {
@ -446,9 +455,11 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
const size_t chunk_start_offset = mem->start_;
const uint32_t fourcc = ReadLE32(mem);
const uint32_t chunk_size = ReadLE32(mem);
const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
uint32_t chunk_size_padded;
if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
chunk_size_padded = chunk_size + (chunk_size & 1);
if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
switch (fourcc) {

View file

@ -157,7 +157,8 @@ void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse) {
}
}
void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr,
const uint8_t* WEBP_RESTRICT const alpha,
int width, int inverse) {
int x;
for (x = 0; x < width; ++x) {
@ -178,7 +179,8 @@ void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
#undef MFIX
void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
void (*WebPMultRow)(uint8_t* WEBP_RESTRICT const ptr,
const uint8_t* WEBP_RESTRICT const alpha,
int width, int inverse);
//------------------------------------------------------------------------------
@ -193,8 +195,8 @@ void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
}
}
void WebPMultRows(uint8_t* ptr, int stride,
const uint8_t* alpha, int alpha_stride,
void WebPMultRows(uint8_t* WEBP_RESTRICT ptr, int stride,
const uint8_t* WEBP_RESTRICT alpha, int alpha_stride,
int width, int num_rows, int inverse) {
int n;
for (n = 0; n < num_rows; ++n) {
@ -290,9 +292,9 @@ static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444,
}
#if !WEBP_NEON_OMIT_C_CODE
static int DispatchAlpha_C(const uint8_t* alpha, int alpha_stride,
static int DispatchAlpha_C(const uint8_t* WEBP_RESTRICT alpha, int alpha_stride,
int width, int height,
uint8_t* dst, int dst_stride) {
uint8_t* WEBP_RESTRICT dst, int dst_stride) {
uint32_t alpha_mask = 0xff;
int i, j;
@ -309,9 +311,10 @@ static int DispatchAlpha_C(const uint8_t* alpha, int alpha_stride,
return (alpha_mask != 0xff);
}
static void DispatchAlphaToGreen_C(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint32_t* dst, int dst_stride) {
static void DispatchAlphaToGreen_C(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint32_t* WEBP_RESTRICT dst,
int dst_stride) {
int i, j;
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
@ -322,9 +325,9 @@ static void DispatchAlphaToGreen_C(const uint8_t* alpha, int alpha_stride,
}
}
static int ExtractAlpha_C(const uint8_t* argb, int argb_stride,
static int ExtractAlpha_C(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
int width, int height,
uint8_t* alpha, int alpha_stride) {
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
uint8_t alpha_mask = 0xff;
int i, j;
@ -340,7 +343,8 @@ static int ExtractAlpha_C(const uint8_t* argb, int argb_stride,
return (alpha_mask == 0xff);
}
static void ExtractGreen_C(const uint32_t* argb, uint8_t* alpha, int size) {
static void ExtractGreen_C(const uint32_t* WEBP_RESTRICT argb,
uint8_t* WEBP_RESTRICT alpha, int size) {
int i;
for (i = 0; i < size; ++i) alpha[i] = argb[i] >> 8;
}
@ -359,6 +363,11 @@ static int HasAlpha32b_C(const uint8_t* src, int length) {
return 0;
}
static void AlphaReplace_C(uint32_t* src, int length, uint32_t color) {
int x;
for (x = 0; x < length; ++x) if ((src[x] >> 24) == 0) src[x] = color;
}
//------------------------------------------------------------------------------
// Simple channel manipulations.
@ -367,8 +376,11 @@ static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
}
#ifdef WORDS_BIGENDIAN
static void PackARGB_C(const uint8_t* a, const uint8_t* r, const uint8_t* g,
const uint8_t* b, int len, uint32_t* out) {
static void PackARGB_C(const uint8_t* WEBP_RESTRICT a,
const uint8_t* WEBP_RESTRICT r,
const uint8_t* WEBP_RESTRICT g,
const uint8_t* WEBP_RESTRICT b,
int len, uint32_t* WEBP_RESTRICT out) {
int i;
for (i = 0; i < len; ++i) {
out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]);
@ -376,8 +388,10 @@ static void PackARGB_C(const uint8_t* a, const uint8_t* r, const uint8_t* g,
}
#endif
static void PackRGB_C(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out) {
static void PackRGB_C(const uint8_t* WEBP_RESTRICT r,
const uint8_t* WEBP_RESTRICT g,
const uint8_t* WEBP_RESTRICT b,
int len, int step, uint32_t* WEBP_RESTRICT out) {
int i, offset = 0;
for (i = 0; i < len; ++i) {
out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]);
@ -387,19 +401,26 @@ static void PackRGB_C(const uint8_t* r, const uint8_t* g, const uint8_t* b,
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int);
void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int);
int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int);
int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
int (*WebPDispatchAlpha)(const uint8_t* WEBP_RESTRICT, int, int, int,
uint8_t* WEBP_RESTRICT, int);
void (*WebPDispatchAlphaToGreen)(const uint8_t* WEBP_RESTRICT, int, int, int,
uint32_t* WEBP_RESTRICT, int);
int (*WebPExtractAlpha)(const uint8_t* WEBP_RESTRICT, int, int, int,
uint8_t* WEBP_RESTRICT, int);
void (*WebPExtractGreen)(const uint32_t* WEBP_RESTRICT argb,
uint8_t* WEBP_RESTRICT alpha, int size);
#ifdef WORDS_BIGENDIAN
void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g,
const uint8_t* b, int, uint32_t*);
#endif
void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out);
void (*WebPPackRGB)(const uint8_t* WEBP_RESTRICT r,
const uint8_t* WEBP_RESTRICT g,
const uint8_t* WEBP_RESTRICT b,
int len, int step, uint32_t* WEBP_RESTRICT out);
int (*WebPHasAlpha8b)(const uint8_t* src, int length);
int (*WebPHasAlpha32b)(const uint8_t* src, int length);
void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color);
//------------------------------------------------------------------------------
// Init function
@ -428,13 +449,14 @@ WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) {
WebPHasAlpha8b = HasAlpha8b_C;
WebPHasAlpha32b = HasAlpha32b_C;
WebPAlphaReplace = AlphaReplace_C;
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitAlphaProcessingSSE2();
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
WebPInitAlphaProcessingSSE41();
}
@ -448,7 +470,7 @@ WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
WebPInitAlphaProcessingNEON();
@ -469,4 +491,5 @@ WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) {
assert(WebPPackRGB != NULL);
assert(WebPHasAlpha8b != NULL);
assert(WebPHasAlpha32b != NULL);
assert(WebPAlphaReplace != NULL);
}

View file

@ -80,9 +80,9 @@ static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first,
//------------------------------------------------------------------------------
static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint8_t* dst, int dst_stride) {
static int DispatchAlpha_NEON(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint8_t* WEBP_RESTRICT dst, int dst_stride) {
uint32_t alpha_mask = 0xffffffffu;
uint8x8_t mask8 = vdup_n_u8(0xff);
uint32_t tmp[2];
@ -112,9 +112,10 @@ static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
return (alpha_mask != 0xffffffffu);
}
static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint32_t* dst, int dst_stride) {
static void DispatchAlphaToGreen_NEON(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint32_t* WEBP_RESTRICT dst,
int dst_stride) {
int i, j;
uint8x8x4_t greens; // leave A/R/B channels zero'd.
greens.val[0] = vdup_n_u8(0);
@ -131,9 +132,9 @@ static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride,
}
}
static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
static int ExtractAlpha_NEON(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
int width, int height,
uint8_t* alpha, int alpha_stride) {
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
uint32_t alpha_mask = 0xffffffffu;
uint8x8_t mask8 = vdup_n_u8(0xff);
uint32_t tmp[2];
@ -161,8 +162,8 @@ static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
return (alpha_mask == 0xffffffffu);
}
static void ExtractGreen_NEON(const uint32_t* argb,
uint8_t* alpha, int size) {
static void ExtractGreen_NEON(const uint32_t* WEBP_RESTRICT argb,
uint8_t* WEBP_RESTRICT alpha, int size) {
int i;
for (i = 0; i + 16 <= size; i += 16) {
const uint8x16x4_t rgbX = vld4q_u8((const uint8_t*)(argb + i));

View file

@ -18,9 +18,9 @@
//------------------------------------------------------------------------------
static int DispatchAlpha_SSE2(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint8_t* dst, int dst_stride) {
static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint8_t* WEBP_RESTRICT dst, int dst_stride) {
// alpha_and stores an 'and' operation of all the alpha[] values. The final
// value is not 0xff if any of the alpha[] is not equal to 0xff.
uint32_t alpha_and = 0xff;
@ -72,9 +72,10 @@ static int DispatchAlpha_SSE2(const uint8_t* alpha, int alpha_stride,
return (alpha_and != 0xff);
}
static void DispatchAlphaToGreen_SSE2(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint32_t* dst, int dst_stride) {
static void DispatchAlphaToGreen_SSE2(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint32_t* WEBP_RESTRICT dst,
int dst_stride) {
int i, j;
const __m128i zero = _mm_setzero_si128();
const int limit = width & ~15;
@ -98,9 +99,9 @@ static void DispatchAlphaToGreen_SSE2(const uint8_t* alpha, int alpha_stride,
}
}
static int ExtractAlpha_SSE2(const uint8_t* argb, int argb_stride,
static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
int width, int height,
uint8_t* alpha, int alpha_stride) {
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
// alpha_and stores an 'and' operation of all the alpha[] values. The final
// value is not 0xff if any of the alpha[] is not equal to 0xff.
uint32_t alpha_and = 0xff;
@ -265,6 +266,27 @@ static int HasAlpha32b_SSE2(const uint8_t* src, int length) {
return 0;
}
static void AlphaReplace_SSE2(uint32_t* src, int length, uint32_t color) {
const __m128i m_color = _mm_set1_epi32(color);
const __m128i zero = _mm_setzero_si128();
int i = 0;
for (; i + 8 <= length; i += 8) {
const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));
const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 4));
const __m128i b0 = _mm_srai_epi32(a0, 24);
const __m128i b1 = _mm_srai_epi32(a1, 24);
const __m128i c0 = _mm_cmpeq_epi32(b0, zero);
const __m128i c1 = _mm_cmpeq_epi32(b1, zero);
const __m128i d0 = _mm_and_si128(c0, m_color);
const __m128i d1 = _mm_and_si128(c1, m_color);
const __m128i e0 = _mm_andnot_si128(c0, a0);
const __m128i e1 = _mm_andnot_si128(c1, a1);
_mm_storeu_si128((__m128i*)(src + i + 0), _mm_or_si128(d0, e0));
_mm_storeu_si128((__m128i*)(src + i + 4), _mm_or_si128(d1, e1));
}
for (; i < length; ++i) if ((src[i] >> 24) == 0) src[i] = color;
}
// -----------------------------------------------------------------------------
// Apply alpha value to rows
@ -296,7 +318,8 @@ static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) {
if (width > 0) WebPMultARGBRow_C(ptr + x, width, inverse);
}
static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha,
static void MultRow_SSE2(uint8_t* WEBP_RESTRICT const ptr,
const uint8_t* WEBP_RESTRICT const alpha,
int width, int inverse) {
int x = 0;
if (!inverse) {
@ -334,6 +357,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {
WebPHasAlpha8b = HasAlpha8b_SSE2;
WebPHasAlpha32b = HasAlpha32b_SSE2;
WebPAlphaReplace = AlphaReplace_SSE2;
}
#else // !WEBP_USE_SSE2

View file

@ -19,9 +19,9 @@
//------------------------------------------------------------------------------
static int ExtractAlpha_SSE41(const uint8_t* argb, int argb_stride,
int width, int height,
uint8_t* alpha, int alpha_stride) {
static int ExtractAlpha_SSE41(const uint8_t* WEBP_RESTRICT argb,
int argb_stride, int width, int height,
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
// alpha_and stores an 'and' operation of all the alpha[] values. The final
// value is not 0xff if any of the alpha[] is not equal to 0xff.
uint32_t alpha_and = 0xff;

View file

@ -395,12 +395,12 @@ WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
VP8EncDspCostInitMIPSdspR2();
}
#endif
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8EncDspCostInitSSE2();
}
#endif
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (VP8GetCPUInfo(kNEON)) {
VP8EncDspCostInitNEON();
}

View file

@ -55,12 +55,18 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type), "c"(0));
}
#elif (defined(_M_X64) || defined(_M_IX86)) && \
defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1
#include <intrin.h>
#define GetCPUInfo(info, type) __cpuidex(info, type, 0) // set ecx=0
#elif defined(WEBP_MSC_SSE2)
#define WEBP_HAVE_MSC_CPUID
#elif _MSC_VER > 1310
#include <intrin.h>
#define GetCPUInfo __cpuid
#define WEBP_HAVE_MSC_CPUID
#endif
#endif
// NaCl has no support for xgetbv or the raw opcode.
@ -94,7 +100,7 @@ static WEBP_INLINE uint64_t xgetbv(void) {
#define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains.
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_HAVE_MSC_CPUID)
// helper function for run-time detection of slow SSSE3 platforms
static int CheckSlowModel(int info) {
@ -179,9 +185,34 @@ static int AndroidCPUInfo(CPUFeature feature) {
return 0;
}
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
#elif defined(WEBP_USE_NEON)
// define a dummy function to enable turning off NEON at runtime by setting
// VP8DecGetCPUInfo = NULL
#elif defined(EMSCRIPTEN) // also needs to be before generic NEON test
// Use compile flags as an indicator of SIMD support instead of a runtime check.
static int wasmCPUInfo(CPUFeature feature) {
switch (feature) {
#ifdef WEBP_HAVE_SSE2
case kSSE2:
return 1;
#endif
#ifdef WEBP_HAVE_SSE41
case kSSE3:
case kSlowSSSE3:
case kSSE4_1:
return 1;
#endif
#ifdef WEBP_HAVE_NEON
case kNEON:
return 1;
#endif
default:
break;
}
return 0;
}
VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo;
#elif defined(WEBP_HAVE_NEON)
// In most cases this function doesn't check for NEON support (it's assumed by
// the configuration), but enables turning off NEON at runtime, for testing
// purposes, by setting VP8DecGetCPUInfo = NULL.
static int armCPUInfo(CPUFeature feature) {
if (feature != kNEON) return 0;
#if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD)

View file

@ -807,10 +807,10 @@ WEBP_DSP_INIT_FUNC(VP8DspInit) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8DspInitSSE2();
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
VP8DspInitSSE41();
}
@ -834,7 +834,7 @@ WEBP_DSP_INIT_FUNC(VP8DspInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
VP8DspInitNEON();

View file

@ -1283,12 +1283,12 @@ static void DC4_NEON(uint8_t* dst) { // DC
const uint8x8_t A = vld1_u8(dst - BPS); // top row
const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top
const uint16x4_t p1 = vpadd_u16(p0, p0);
const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + 0 * BPS - 1));
const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + 1 * BPS - 1));
const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + 2 * BPS - 1));
const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + 3 * BPS - 1));
const uint16x8_t s0 = vaddq_u16(L0, L1);
const uint16x8_t s1 = vaddq_u16(L2, L3);
const uint8x8_t L0 = vld1_u8(dst + 0 * BPS - 1);
const uint8x8_t L1 = vld1_u8(dst + 1 * BPS - 1);
const uint8x8_t L2 = vld1_u8(dst + 2 * BPS - 1);
const uint8x8_t L3 = vld1_u8(dst + 3 * BPS - 1);
const uint16x8_t s0 = vaddl_u8(L0, L1);
const uint16x8_t s1 = vaddl_u8(L2, L3);
const uint16x8_t s01 = vaddq_u16(s0, s1);
const uint16x8_t sum = vaddq_u16(s01, vcombine_u16(p1, p1));
const uint8x8_t dc0 = vrshrn_n_u16(sum, 3); // (sum + 4) >> 3
@ -1429,8 +1429,7 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x8_t A = vld1_u8(dst - BPS); // top row
#if defined(__aarch64__)
const uint16x8_t B = vmovl_u8(A);
const uint16_t p2 = vaddvq_u16(B);
const uint16_t p2 = vaddlv_u8(A);
sum_top = vdupq_n_u16(p2);
#else
const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top
@ -1441,18 +1440,18 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
}
if (do_left) {
const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + 0 * BPS - 1));
const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + 1 * BPS - 1));
const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + 2 * BPS - 1));
const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + 3 * BPS - 1));
const uint16x8_t L4 = vmovl_u8(vld1_u8(dst + 4 * BPS - 1));
const uint16x8_t L5 = vmovl_u8(vld1_u8(dst + 5 * BPS - 1));
const uint16x8_t L6 = vmovl_u8(vld1_u8(dst + 6 * BPS - 1));
const uint16x8_t L7 = vmovl_u8(vld1_u8(dst + 7 * BPS - 1));
const uint16x8_t s0 = vaddq_u16(L0, L1);
const uint16x8_t s1 = vaddq_u16(L2, L3);
const uint16x8_t s2 = vaddq_u16(L4, L5);
const uint16x8_t s3 = vaddq_u16(L6, L7);
const uint8x8_t L0 = vld1_u8(dst + 0 * BPS - 1);
const uint8x8_t L1 = vld1_u8(dst + 1 * BPS - 1);
const uint8x8_t L2 = vld1_u8(dst + 2 * BPS - 1);
const uint8x8_t L3 = vld1_u8(dst + 3 * BPS - 1);
const uint8x8_t L4 = vld1_u8(dst + 4 * BPS - 1);
const uint8x8_t L5 = vld1_u8(dst + 5 * BPS - 1);
const uint8x8_t L6 = vld1_u8(dst + 6 * BPS - 1);
const uint8x8_t L7 = vld1_u8(dst + 7 * BPS - 1);
const uint16x8_t s0 = vaddl_u8(L0, L1);
const uint16x8_t s1 = vaddl_u8(L2, L3);
const uint16x8_t s2 = vaddl_u8(L4, L5);
const uint16x8_t s3 = vaddl_u8(L6, L7);
const uint16x8_t s01 = vaddq_u16(s0, s1);
const uint16x8_t s23 = vaddq_u16(s2, s3);
sum_left = vaddq_u16(s01, s23);
@ -1512,29 +1511,34 @@ static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x16_t A = vld1q_u8(dst - BPS); // top row
#if defined(__aarch64__)
const uint16_t p3 = vaddlvq_u8(A);
sum_top = vdupq_n_u16(p3);
#else
const uint16x8_t p0 = vpaddlq_u8(A); // cascading summation of the top
const uint16x4_t p1 = vadd_u16(vget_low_u16(p0), vget_high_u16(p0));
const uint16x4_t p2 = vpadd_u16(p1, p1);
const uint16x4_t p3 = vpadd_u16(p2, p2);
sum_top = vcombine_u16(p3, p3);
#endif
}
if (do_left) {
int i;
sum_left = vdupq_n_u16(0);
for (i = 0; i < 16; i += 8) {
const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + (i + 0) * BPS - 1));
const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + (i + 1) * BPS - 1));
const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + (i + 2) * BPS - 1));
const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + (i + 3) * BPS - 1));
const uint16x8_t L4 = vmovl_u8(vld1_u8(dst + (i + 4) * BPS - 1));
const uint16x8_t L5 = vmovl_u8(vld1_u8(dst + (i + 5) * BPS - 1));
const uint16x8_t L6 = vmovl_u8(vld1_u8(dst + (i + 6) * BPS - 1));
const uint16x8_t L7 = vmovl_u8(vld1_u8(dst + (i + 7) * BPS - 1));
const uint16x8_t s0 = vaddq_u16(L0, L1);
const uint16x8_t s1 = vaddq_u16(L2, L3);
const uint16x8_t s2 = vaddq_u16(L4, L5);
const uint16x8_t s3 = vaddq_u16(L6, L7);
const uint8x8_t L0 = vld1_u8(dst + (i + 0) * BPS - 1);
const uint8x8_t L1 = vld1_u8(dst + (i + 1) * BPS - 1);
const uint8x8_t L2 = vld1_u8(dst + (i + 2) * BPS - 1);
const uint8x8_t L3 = vld1_u8(dst + (i + 3) * BPS - 1);
const uint8x8_t L4 = vld1_u8(dst + (i + 4) * BPS - 1);
const uint8x8_t L5 = vld1_u8(dst + (i + 5) * BPS - 1);
const uint8x8_t L6 = vld1_u8(dst + (i + 6) * BPS - 1);
const uint8x8_t L7 = vld1_u8(dst + (i + 7) * BPS - 1);
const uint16x8_t s0 = vaddl_u8(L0, L1);
const uint16x8_t s1 = vaddl_u8(L2, L3);
const uint16x8_t s2 = vaddl_u8(L4, L5);
const uint16x8_t s3 = vaddl_u8(L6, L7);
const uint16x8_t s01 = vaddq_u16(s0, s1);
const uint16x8_t s23 = vaddq_u16(s2, s3);
const uint16x8_t sum = vaddq_u16(s01, s23);

View file

@ -26,6 +26,23 @@ extern "C" {
#define BPS 32 // this is the common stride for enc/dec
//------------------------------------------------------------------------------
// WEBP_RESTRICT
// Declares a pointer with the restrict type qualifier if available.
// This allows code to hint to the compiler that only this pointer references a
// particular object or memory region within the scope of the block in which it
// is declared. This may allow for improved optimizations due to the lack of
// pointer aliasing. See also:
// https://en.cppreference.com/w/c/language/restrict
#if defined(__GNUC__)
#define WEBP_RESTRICT __restrict__
#elif defined(_MSC_VER)
#define WEBP_RESTRICT __restrict
#else
#define WEBP_RESTRICT
#endif
//------------------------------------------------------------------------------
// CPU detection
@ -51,9 +68,7 @@ extern "C" {
# define __has_builtin(x) 0
#endif
// for now, none of the optimizations below are available in emscripten
#if !defined(EMSCRIPTEN)
#if !defined(HAVE_CONFIG_H)
#if defined(_MSC_VER) && _MSC_VER > 1310 && \
(defined(_M_X64) || defined(_M_IX86))
#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
@ -63,23 +78,37 @@ extern "C" {
(defined(_M_X64) || defined(_M_IX86))
#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets
#endif
#endif
// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
// files without intrinsics, allowing the corresponding Init() to be called.
// Files containing intrinsics will need to be built targeting the instruction
// set so should succeed on one of the earlier tests.
#if defined(__SSE2__) || defined(WEBP_MSC_SSE2) || defined(WEBP_HAVE_SSE2)
#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \
(!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2))
#define WEBP_USE_SSE2
#endif
#if defined(__SSE4_1__) || defined(WEBP_MSC_SSE41) || defined(WEBP_HAVE_SSE41)
#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2)
#define WEBP_HAVE_SSE2
#endif
#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \
(!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41))
#define WEBP_USE_SSE41
#endif
#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41)
#define WEBP_HAVE_SSE41
#endif
#undef WEBP_MSC_SSE41
#undef WEBP_MSC_SSE2
// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
// inline assembly would need to be modified for use with Native Client.
#if (defined(__ARM_NEON__) || \
defined(__aarch64__) || defined(WEBP_HAVE_NEON)) && \
#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
(!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \
!defined(__native_client__)
#define WEBP_USE_NEON
#endif
@ -95,6 +124,10 @@ extern "C" {
#define WEBP_USE_INTRINSICS
#endif
#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
#define WEBP_HAVE_NEON
#endif
#if defined(__mips__) && !defined(__mips64) && \
defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
#define WEBP_USE_MIPS32
@ -110,13 +143,11 @@ extern "C" {
#define WEBP_USE_MSA
#endif
#endif /* EMSCRIPTEN */
#ifndef WEBP_DSP_OMIT_C_CODE
#define WEBP_DSP_OMIT_C_CODE 1
#endif
#if (defined(__aarch64__) || defined(__ARM_NEON__)) && WEBP_DSP_OMIT_C_CODE
#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE
#define WEBP_NEON_OMIT_C_CODE 1
#else
#define WEBP_NEON_OMIT_C_CODE 0
@ -193,6 +224,12 @@ extern "C" {
#endif
#endif
// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'.
// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning.
#if !defined(WEBP_OFFSET_PTR)
#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off)))
#endif
// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
#if !defined(WEBP_SWAP_16BIT_CSP)
#define WEBP_SWAP_16BIT_CSP 0
@ -572,26 +609,29 @@ extern void (*WebPApplyAlphaMultiply4444)(
// Dispatch the values from alpha[] plane to the ARGB destination 'dst'.
// Returns true if alpha[] plane has non-trivial values different from 0xff.
extern int (*WebPDispatchAlpha)(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint8_t* dst, int dst_stride);
extern int (*WebPDispatchAlpha)(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint8_t* WEBP_RESTRICT dst, int dst_stride);
// Transfer packed 8b alpha[] values to green channel in dst[], zero'ing the
// A/R/B values. 'dst_stride' is the stride for dst[] in uint32_t units.
extern void (*WebPDispatchAlphaToGreen)(const uint8_t* alpha, int alpha_stride,
int width, int height,
uint32_t* dst, int dst_stride);
extern void (*WebPDispatchAlphaToGreen)(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint32_t* WEBP_RESTRICT dst,
int dst_stride);
// Extract the alpha values from 32b values in argb[] and pack them into alpha[]
// (this is the opposite of WebPDispatchAlpha).
// Returns true if there's only trivial 0xff alpha values.
extern int (*WebPExtractAlpha)(const uint8_t* argb, int argb_stride,
int width, int height,
uint8_t* alpha, int alpha_stride);
extern int (*WebPExtractAlpha)(const uint8_t* WEBP_RESTRICT argb,
int argb_stride, int width, int height,
uint8_t* WEBP_RESTRICT alpha,
int alpha_stride);
// Extract the green values from 32b values in argb[] and pack them into alpha[]
// (this is the opposite of WebPDispatchAlphaToGreen).
extern void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
extern void (*WebPExtractGreen)(const uint32_t* WEBP_RESTRICT argb,
uint8_t* WEBP_RESTRICT alpha, int size);
// Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B).
// Un-Multiply operation transforms x into x * 255 / A.
@ -604,34 +644,42 @@ void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
int inverse);
// Same for a row of single values, with side alpha values.
extern void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
extern void (*WebPMultRow)(uint8_t* WEBP_RESTRICT const ptr,
const uint8_t* WEBP_RESTRICT const alpha,
int width, int inverse);
// Same a WebPMultRow(), but for several 'num_rows' rows.
void WebPMultRows(uint8_t* ptr, int stride,
const uint8_t* alpha, int alpha_stride,
void WebPMultRows(uint8_t* WEBP_RESTRICT ptr, int stride,
const uint8_t* WEBP_RESTRICT alpha, int alpha_stride,
int width, int num_rows, int inverse);
// Plain-C versions, used as fallback by some implementations.
void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr,
const uint8_t* WEBP_RESTRICT const alpha,
int width, int inverse);
void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse);
#ifdef WORDS_BIGENDIAN
// ARGB packing function: a/r/g/b input is rgba or bgra order.
extern void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r,
const uint8_t* g, const uint8_t* b, int len,
uint32_t* out);
extern void (*WebPPackARGB)(const uint8_t* WEBP_RESTRICT a,
const uint8_t* WEBP_RESTRICT r,
const uint8_t* WEBP_RESTRICT g,
const uint8_t* WEBP_RESTRICT b,
int len, uint32_t* WEBP_RESTRICT out);
#endif
// RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order.
extern void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
int len, int step, uint32_t* out);
extern void (*WebPPackRGB)(const uint8_t* WEBP_RESTRICT r,
const uint8_t* WEBP_RESTRICT g,
const uint8_t* WEBP_RESTRICT b,
int len, int step, uint32_t* WEBP_RESTRICT out);
// This function returns true if src[i] contains a value different from 0xff.
extern int (*WebPHasAlpha8b)(const uint8_t* src, int length);
// This function returns true if src[4*i] contains a value different from 0xff.
extern int (*WebPHasAlpha32b)(const uint8_t* src, int length);
// replaces transparent values in src[] by 'color'.
extern void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color);
// To be called first before using the above.
void WebPInitAlphaProcessing(void);

View file

@ -773,10 +773,10 @@ WEBP_DSP_INIT_FUNC(VP8EncDspInit) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8EncDspInitSSE2();
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
VP8EncDspInitSSE41();
}
@ -800,7 +800,7 @@ WEBP_DSP_INIT_FUNC(VP8EncDspInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
VP8EncDspInitNEON();

View file

@ -254,7 +254,7 @@ WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
#endif
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8FiltersInitSSE2();
}
@ -271,7 +271,7 @@ WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
VP8FiltersInitNEON();

View file

@ -320,7 +320,12 @@ extern void VP8FiltersInitSSE2(void);
WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitSSE2(void) {
WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_SSE2;
#if defined(CHROMIUM)
// TODO(crbug.com/654974)
(void)VerticalUnfilter_SSE2;
#else
WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_SSE2;
#endif
WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_SSE2;
WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_SSE2;

View file

@ -107,62 +107,62 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
//------------------------------------------------------------------------------
// Predictors
static uint32_t Predictor0_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor0_C(uint32_t left, const uint32_t* const top) {
(void)top;
(void)left;
return ARGB_BLACK;
}
static uint32_t Predictor1_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor1_C(uint32_t left, const uint32_t* const top) {
(void)top;
return left;
}
static uint32_t Predictor2_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor2_C(uint32_t left, const uint32_t* const top) {
(void)left;
return top[0];
}
static uint32_t Predictor3_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor3_C(uint32_t left, const uint32_t* const top) {
(void)left;
return top[1];
}
static uint32_t Predictor4_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor4_C(uint32_t left, const uint32_t* const top) {
(void)left;
return top[-1];
}
static uint32_t Predictor5_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor5_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average3(left, top[0], top[1]);
return pred;
}
static uint32_t Predictor6_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor6_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(left, top[-1]);
return pred;
}
static uint32_t Predictor7_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor7_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(left, top[0]);
return pred;
}
static uint32_t Predictor8_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor8_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(top[-1], top[0]);
(void)left;
return pred;
}
static uint32_t Predictor9_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor9_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(top[0], top[1]);
(void)left;
return pred;
}
static uint32_t Predictor10_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor10_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
return pred;
}
static uint32_t Predictor11_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor11_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Select(top[0], left, top[-1]);
return pred;
}
static uint32_t Predictor12_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor12_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
return pred;
}
static uint32_t Predictor13_C(uint32_t left, const uint32_t* const top) {
uint32_t VP8LPredictor13_C(uint32_t left, const uint32_t* const top) {
const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
return pred;
}
@ -182,18 +182,18 @@ static void PredictorAdd1_C(const uint32_t* in, const uint32_t* upper,
out[i] = left = VP8LAddPixels(in[i], left);
}
}
GENERATE_PREDICTOR_ADD(Predictor2_C, PredictorAdd2_C)
GENERATE_PREDICTOR_ADD(Predictor3_C, PredictorAdd3_C)
GENERATE_PREDICTOR_ADD(Predictor4_C, PredictorAdd4_C)
GENERATE_PREDICTOR_ADD(Predictor5_C, PredictorAdd5_C)
GENERATE_PREDICTOR_ADD(Predictor6_C, PredictorAdd6_C)
GENERATE_PREDICTOR_ADD(Predictor7_C, PredictorAdd7_C)
GENERATE_PREDICTOR_ADD(Predictor8_C, PredictorAdd8_C)
GENERATE_PREDICTOR_ADD(Predictor9_C, PredictorAdd9_C)
GENERATE_PREDICTOR_ADD(Predictor10_C, PredictorAdd10_C)
GENERATE_PREDICTOR_ADD(Predictor11_C, PredictorAdd11_C)
GENERATE_PREDICTOR_ADD(Predictor12_C, PredictorAdd12_C)
GENERATE_PREDICTOR_ADD(Predictor13_C, PredictorAdd13_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor2_C, PredictorAdd2_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor3_C, PredictorAdd3_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor4_C, PredictorAdd4_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor5_C, PredictorAdd5_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor6_C, PredictorAdd6_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor7_C, PredictorAdd7_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor8_C, PredictorAdd8_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor9_C, PredictorAdd9_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor10_C, PredictorAdd10_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor11_C, PredictorAdd11_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor12_C, PredictorAdd12_C)
GENERATE_PREDICTOR_ADD(VP8LPredictor13_C, PredictorAdd13_C)
//------------------------------------------------------------------------------
@ -562,7 +562,6 @@ VP8LPredictorFunc VP8LPredictors[16];
// exposed plain-C implementations
VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16];
VP8LPredictorFunc VP8LPredictors_C[16];
VP8LTransformColorInverseFunc VP8LTransformColorInverse;
@ -576,6 +575,7 @@ VP8LMapARGBFunc VP8LMapColor32b;
VP8LMapAlphaFunc VP8LMapColor8b;
extern void VP8LDspInitSSE2(void);
extern void VP8LDspInitSSE41(void);
extern void VP8LDspInitNEON(void);
extern void VP8LDspInitMIPSdspR2(void);
extern void VP8LDspInitMSA(void);
@ -600,8 +600,7 @@ extern void VP8LDspInitMSA(void);
} while (0);
WEBP_DSP_INIT_FUNC(VP8LDspInit) {
COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors)
COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors_C)
COPY_PREDICTOR_ARRAY(VP8LPredictor, VP8LPredictors)
COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd)
COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd_C)
@ -623,9 +622,14 @@ WEBP_DSP_INIT_FUNC(VP8LDspInit) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8LDspInitSSE2();
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
VP8LDspInitSSE41();
}
#endif
}
#endif
#if defined(WEBP_USE_MIPS_DSP_R2)
@ -640,7 +644,7 @@ WEBP_DSP_INIT_FUNC(VP8LDspInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
VP8LDspInitNEON();

View file

@ -30,7 +30,22 @@ extern "C" {
typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top);
extern VP8LPredictorFunc VP8LPredictors[16];
extern VP8LPredictorFunc VP8LPredictors_C[16];
uint32_t VP8LPredictor0_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor1_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor2_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor3_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor4_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor5_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor6_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor7_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor8_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor9_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor10_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor11_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor12_C(uint32_t left, const uint32_t* const top);
uint32_t VP8LPredictor13_C(uint32_t left, const uint32_t* const top);
// These Add/Sub function expects upper[-1] and out[-1] to be readable.
typedef void (*VP8LPredictorAddSubFunc)(const uint32_t* in,
const uint32_t* upper, int num_pixels,

View file

@ -184,19 +184,6 @@ static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \
} \
}
// It subtracts the prediction from the input pixel and stores the residual
// in the output pixel.
#define GENERATE_PREDICTOR_SUB(PREDICTOR, PREDICTOR_SUB) \
static void PREDICTOR_SUB(const uint32_t* in, const uint32_t* upper, \
int num_pixels, uint32_t* out) { \
int x; \
assert(upper != NULL); \
for (x = 0; x < num_pixels; ++x) { \
const uint32_t pred = (PREDICTOR)(in[x - 1], upper + x); \
out[x] = VP8LSubPixels(in[x], pred); \
} \
}
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -329,6 +329,15 @@ const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX] = {
static float FastSLog2Slow_C(uint32_t v) {
assert(v >= LOG_LOOKUP_IDX_MAX);
if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ)
// use clz if available
const int log_cnt = BitsLog2Floor(v) - 7;
const uint32_t y = 1 << log_cnt;
int correction = 0;
const float v_f = (float)v;
const uint32_t orig_v = v;
v >>= log_cnt;
#else
int log_cnt = 0;
uint32_t y = 1;
int correction = 0;
@ -339,6 +348,7 @@ static float FastSLog2Slow_C(uint32_t v) {
v = v >> 1;
y = y << 1;
} while (v >= LOG_LOOKUP_IDX_MAX);
#endif
// vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256
// Xf = floor(Xf) * (1 + (v % y) / v)
// log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v)
@ -355,6 +365,14 @@ static float FastSLog2Slow_C(uint32_t v) {
static float FastLog2Slow_C(uint32_t v) {
assert(v >= LOG_LOOKUP_IDX_MAX);
if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ)
// use clz if available
const int log_cnt = BitsLog2Floor(v) - 7;
const uint32_t y = 1 << log_cnt;
const uint32_t orig_v = v;
double log_2;
v >>= log_cnt;
#else
int log_cnt = 0;
uint32_t y = 1;
const uint32_t orig_v = v;
@ -364,6 +382,7 @@ static float FastLog2Slow_C(uint32_t v) {
v = v >> 1;
y = y << 1;
} while (v >= LOG_LOOKUP_IDX_MAX);
#endif
log_2 = kLog2Table[v] + log_cnt;
if (orig_v >= APPROX_LOG_MAX) {
// Since the division is still expensive, add this correction factor only
@ -702,140 +721,6 @@ void VP8LHistogramAdd(const VP8LHistogram* const a,
//------------------------------------------------------------------------------
// Image transforms.
static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) {
return (((a0 ^ a1) & 0xfefefefeu) >> 1) + (a0 & a1);
}
static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) {
return Average2(Average2(a0, a2), a1);
}
static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3) {
return Average2(Average2(a0, a1), Average2(a2, a3));
}
static WEBP_INLINE uint32_t Clip255(uint32_t a) {
if (a < 256) {
return a;
}
// return 0, when a is a negative integer.
// return 255, when a is positive.
return ~a >> 24;
}
static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
return Clip255(a + b - c);
}
static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
uint32_t c2) {
const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
const int r = AddSubtractComponentFull((c0 >> 16) & 0xff,
(c1 >> 16) & 0xff,
(c2 >> 16) & 0xff);
const int g = AddSubtractComponentFull((c0 >> 8) & 0xff,
(c1 >> 8) & 0xff,
(c2 >> 8) & 0xff);
const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
}
static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
return Clip255(a + (a - b) / 2);
}
static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
uint32_t c2) {
const uint32_t ave = Average2(c0, c1);
const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24);
const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff);
const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff);
const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff);
return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
}
// gcc-4.9 on ARM generates incorrect code in Select() when Sub3() is inlined.
#if defined(__arm__) && \
(LOCAL_GCC_VERSION == 0x409 || LOCAL_GCC_VERSION == 0x408)
# define LOCAL_INLINE __attribute__ ((noinline))
#else
# define LOCAL_INLINE WEBP_INLINE
#endif
static LOCAL_INLINE int Sub3(int a, int b, int c) {
const int pb = b - c;
const int pa = a - c;
return abs(pb) - abs(pa);
}
#undef LOCAL_INLINE
static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
const int pa_minus_pb =
Sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
return (pa_minus_pb <= 0) ? a : b;
}
//------------------------------------------------------------------------------
// Predictors
static uint32_t Predictor2(uint32_t left, const uint32_t* const top) {
(void)left;
return top[0];
}
static uint32_t Predictor3(uint32_t left, const uint32_t* const top) {
(void)left;
return top[1];
}
static uint32_t Predictor4(uint32_t left, const uint32_t* const top) {
(void)left;
return top[-1];
}
static uint32_t Predictor5(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average3(left, top[0], top[1]);
return pred;
}
static uint32_t Predictor6(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(left, top[-1]);
return pred;
}
static uint32_t Predictor7(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(left, top[0]);
return pred;
}
static uint32_t Predictor8(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(top[-1], top[0]);
(void)left;
return pred;
}
static uint32_t Predictor9(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average2(top[0], top[1]);
(void)left;
return pred;
}
static uint32_t Predictor10(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
return pred;
}
static uint32_t Predictor11(uint32_t left, const uint32_t* const top) {
const uint32_t pred = Select(top[0], left, top[-1]);
return pred;
}
static uint32_t Predictor12(uint32_t left, const uint32_t* const top) {
const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
return pred;
}
static uint32_t Predictor13(uint32_t left, const uint32_t* const top) {
const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
return pred;
}
//------------------------------------------------------------------------------
static void PredictorSub0_C(const uint32_t* in, const uint32_t* upper,
int num_pixels, uint32_t* out) {
int i;
@ -850,18 +735,33 @@ static void PredictorSub1_C(const uint32_t* in, const uint32_t* upper,
(void)upper;
}
GENERATE_PREDICTOR_SUB(Predictor2, PredictorSub2_C)
GENERATE_PREDICTOR_SUB(Predictor3, PredictorSub3_C)
GENERATE_PREDICTOR_SUB(Predictor4, PredictorSub4_C)
GENERATE_PREDICTOR_SUB(Predictor5, PredictorSub5_C)
GENERATE_PREDICTOR_SUB(Predictor6, PredictorSub6_C)
GENERATE_PREDICTOR_SUB(Predictor7, PredictorSub7_C)
GENERATE_PREDICTOR_SUB(Predictor8, PredictorSub8_C)
GENERATE_PREDICTOR_SUB(Predictor9, PredictorSub9_C)
GENERATE_PREDICTOR_SUB(Predictor10, PredictorSub10_C)
GENERATE_PREDICTOR_SUB(Predictor11, PredictorSub11_C)
GENERATE_PREDICTOR_SUB(Predictor12, PredictorSub12_C)
GENERATE_PREDICTOR_SUB(Predictor13, PredictorSub13_C)
// It subtracts the prediction from the input pixel and stores the residual
// in the output pixel.
#define GENERATE_PREDICTOR_SUB(PREDICTOR_I) \
static void PredictorSub##PREDICTOR_I##_C(const uint32_t* in, \
const uint32_t* upper, \
int num_pixels, uint32_t* out) { \
int x; \
assert(upper != NULL); \
for (x = 0; x < num_pixels; ++x) { \
const uint32_t pred = \
VP8LPredictor##PREDICTOR_I##_C(in[x - 1], upper + x); \
out[x] = VP8LSubPixels(in[x], pred); \
} \
}
GENERATE_PREDICTOR_SUB(2)
GENERATE_PREDICTOR_SUB(3)
GENERATE_PREDICTOR_SUB(4)
GENERATE_PREDICTOR_SUB(5)
GENERATE_PREDICTOR_SUB(6)
GENERATE_PREDICTOR_SUB(7)
GENERATE_PREDICTOR_SUB(8)
GENERATE_PREDICTOR_SUB(9)
GENERATE_PREDICTOR_SUB(10)
GENERATE_PREDICTOR_SUB(11)
GENERATE_PREDICTOR_SUB(12)
GENERATE_PREDICTOR_SUB(13)
//------------------------------------------------------------------------------
@ -962,10 +862,10 @@ WEBP_DSP_INIT_FUNC(VP8LEncDspInit) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8LEncDspInitSSE2();
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
VP8LEncDspInitSSE41();
}
@ -989,7 +889,7 @@ WEBP_DSP_INIT_FUNC(VP8LEncDspInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
VP8LEncDspInitNEON();

View file

@ -232,76 +232,55 @@ static void AddVectorEq_SSE2(const uint32_t* a, uint32_t* out, int size) {
//------------------------------------------------------------------------------
// Entropy
// Checks whether the X or Y contribution is worth computing and adding.
// Used in loop unrolling.
#define ANALYZE_X_OR_Y(x_or_y, j) \
do { \
if ((x_or_y)[i + (j)] != 0) retval -= VP8LFastSLog2((x_or_y)[i + (j)]); \
} while (0)
// Checks whether the X + Y contribution is worth computing and adding.
// Used in loop unrolling.
#define ANALYZE_XY(j) \
do { \
if (tmp[j] != 0) { \
retval -= VP8LFastSLog2(tmp[j]); \
ANALYZE_X_OR_Y(X, j); \
} \
} while (0)
// TODO(https://crbug.com/webp/499): this function produces different results
// from the C code due to use of double/float resulting in output differences
// when compared to -noasm.
#if !(defined(WEBP_HAVE_SLOW_CLZ_CTZ) || defined(__i386__) || defined(_M_IX86))
static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) {
int i;
double retval = 0.;
int sumX, sumXY;
int32_t tmp[4];
__m128i zero = _mm_setzero_si128();
// Sums up X + Y, 4 ints at a time (and will merge it at the end for sumXY).
__m128i sumXY_128 = zero;
__m128i sumX_128 = zero;
int sumX = 0, sumXY = 0;
const __m128i zero = _mm_setzero_si128();
for (i = 0; i < 256; i += 4) {
const __m128i x = _mm_loadu_si128((const __m128i*)(X + i));
const __m128i y = _mm_loadu_si128((const __m128i*)(Y + i));
// Check if any X is non-zero: this actually provides a speedup as X is
// usually sparse.
if (_mm_movemask_epi8(_mm_cmpeq_epi32(x, zero)) != 0xFFFF) {
const __m128i xy_128 = _mm_add_epi32(x, y);
sumXY_128 = _mm_add_epi32(sumXY_128, xy_128);
sumX_128 = _mm_add_epi32(sumX_128, x);
// Analyze the different X + Y.
_mm_storeu_si128((__m128i*)tmp, xy_128);
ANALYZE_XY(0);
ANALYZE_XY(1);
ANALYZE_XY(2);
ANALYZE_XY(3);
} else {
// X is fully 0, so only deal with Y.
sumXY_128 = _mm_add_epi32(sumXY_128, y);
ANALYZE_X_OR_Y(Y, 0);
ANALYZE_X_OR_Y(Y, 1);
ANALYZE_X_OR_Y(Y, 2);
ANALYZE_X_OR_Y(Y, 3);
for (i = 0; i < 256; i += 16) {
const __m128i x0 = _mm_loadu_si128((const __m128i*)(X + i + 0));
const __m128i y0 = _mm_loadu_si128((const __m128i*)(Y + i + 0));
const __m128i x1 = _mm_loadu_si128((const __m128i*)(X + i + 4));
const __m128i y1 = _mm_loadu_si128((const __m128i*)(Y + i + 4));
const __m128i x2 = _mm_loadu_si128((const __m128i*)(X + i + 8));
const __m128i y2 = _mm_loadu_si128((const __m128i*)(Y + i + 8));
const __m128i x3 = _mm_loadu_si128((const __m128i*)(X + i + 12));
const __m128i y3 = _mm_loadu_si128((const __m128i*)(Y + i + 12));
const __m128i x4 = _mm_packs_epi16(_mm_packs_epi32(x0, x1),
_mm_packs_epi32(x2, x3));
const __m128i y4 = _mm_packs_epi16(_mm_packs_epi32(y0, y1),
_mm_packs_epi32(y2, y3));
const int32_t mx = _mm_movemask_epi8(_mm_cmpgt_epi8(x4, zero));
int32_t my = _mm_movemask_epi8(_mm_cmpgt_epi8(y4, zero)) | mx;
while (my) {
const int32_t j = BitsCtz(my);
int xy;
if ((mx >> j) & 1) {
const int x = X[i + j];
sumXY += x;
retval -= VP8LFastSLog2(x);
}
xy = X[i + j] + Y[i + j];
sumX += xy;
retval -= VP8LFastSLog2(xy);
my &= my - 1;
}
}
// Sum up sumX_128 to get sumX.
_mm_storeu_si128((__m128i*)tmp, sumX_128);
sumX = tmp[3] + tmp[2] + tmp[1] + tmp[0];
// Sum up sumXY_128 to get sumXY.
_mm_storeu_si128((__m128i*)tmp, sumXY_128);
sumXY = tmp[3] + tmp[2] + tmp[1] + tmp[0];
retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
return (float)retval;
}
#undef ANALYZE_X_OR_Y
#undef ANALYZE_XY
#else
#define DONT_USE_COMBINED_SHANNON_ENTROPY_SSE2_FUNC // won't be faster
#endif
//------------------------------------------------------------------------------
@ -461,8 +440,9 @@ static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* upper,
}
#define GENERATE_PREDICTOR_1(X, IN) \
static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
int num_pixels, uint32_t* out) { \
static void PredictorSub##X##_SSE2(const uint32_t* const in, \
const uint32_t* const upper, \
int num_pixels, uint32_t* const out) { \
int i; \
for (i = 0; i + 4 <= num_pixels; i += 4) { \
const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \
@ -471,9 +451,10 @@ static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
_mm_storeu_si128((__m128i*)&out[i], res); \
} \
if (i != num_pixels) { \
VP8LPredictorsSub_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
VP8LPredictorsSub_C[(X)](in + i, WEBP_OFFSET_PTR(upper, i), \
num_pixels - i, out + i); \
} \
}
}
GENERATE_PREDICTOR_1(1, in[i - 1]) // Predictor1: L
GENERATE_PREDICTOR_1(2, upper[i]) // Predictor2: T
@ -657,7 +638,9 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) {
VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE2;
VP8LAddVector = AddVector_SSE2;
VP8LAddVectorEq = AddVectorEq_SSE2;
#if !defined(DONT_USE_COMBINED_SHANNON_ENTROPY_SSE2_FUNC)
VP8LCombinedShannonEntropy = CombinedShannonEntropy_SSE2;
#endif
VP8LVectorMismatch = VectorMismatch_SSE2;
VP8LBundleColorMap = BundleColorMap_SSE2;

View file

@ -44,46 +44,47 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data,
//------------------------------------------------------------------------------
// Color Transform
#define SPAN 8
#define MK_CST_16(HI, LO) \
_mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
int tile_width, int tile_height,
int green_to_blue, int red_to_blue,
int histo[]) {
const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue));
const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue));
const __m128i mask_g = _mm_set1_epi16((short)0xff00); // green mask
const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1,
-1, -1, -1, -1, -1, -1, -1);
const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1,
2, -1, 6, -1, 10, -1, 14);
const __m128i mult =
MK_CST_16(CST_5b(red_to_blue) + 256,CST_5b(green_to_blue));
const __m128i perm =
_mm_setr_epi8(-1, 1, -1, 2, -1, 5, -1, 6, -1, 9, -1, 10, -1, 13, -1, 14);
if (tile_width >= 4) {
int y;
for (y = 0; y < tile_height; ++y) {
const uint32_t* const src = argb + y * stride;
int i, x;
for (x = 0; x + SPAN <= tile_width; x += SPAN) {
uint16_t values[SPAN];
const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
const __m128i r0 = _mm_shuffle_epi8(in0, shuffler_lo);
const __m128i r1 = _mm_shuffle_epi8(in1, shuffler_hi);
const __m128i r = _mm_or_si128(r0, r1); // r 0
const __m128i gb0 = _mm_and_si128(in0, mask_gb);
const __m128i gb1 = _mm_and_si128(in1, mask_gb);
const __m128i gb = _mm_packus_epi32(gb0, gb1); // g b
const __m128i g = _mm_and_si128(gb, mask_g); // g 0
const __m128i A = _mm_mulhi_epi16(r, mults_r); // x dbr
const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dbg
const __m128i C = _mm_sub_epi8(gb, B); // x b'
const __m128i D = _mm_sub_epi8(C, A); // x b''
const __m128i E = _mm_and_si128(D, mask_b); // 0 b''
_mm_storeu_si128((__m128i*)values, E);
for (i = 0; i < SPAN; ++i) ++histo[values[i]];
const __m128i A1 = _mm_loadu_si128((const __m128i*)src);
const __m128i B1 = _mm_shuffle_epi8(A1, perm);
const __m128i C1 = _mm_mulhi_epi16(B1, mult);
const __m128i D1 = _mm_sub_epi16(A1, C1);
__m128i E = _mm_add_epi16(_mm_srli_epi32(D1, 16), D1);
int x;
for (x = 4; x + 4 <= tile_width; x += 4) {
const __m128i A2 = _mm_loadu_si128((const __m128i*)(src + x));
__m128i B2, C2, D2;
++histo[_mm_extract_epi8(E, 0)];
B2 = _mm_shuffle_epi8(A2, perm);
++histo[_mm_extract_epi8(E, 4)];
C2 = _mm_mulhi_epi16(B2, mult);
++histo[_mm_extract_epi8(E, 8)];
D2 = _mm_sub_epi16(A2, C2);
++histo[_mm_extract_epi8(E, 12)];
E = _mm_add_epi16(_mm_srli_epi32(D2, 16), D2);
}
++histo[_mm_extract_epi8(E, 0)];
++histo[_mm_extract_epi8(E, 4)];
++histo[_mm_extract_epi8(E, 8)];
++histo[_mm_extract_epi8(E, 12)];
}
}
{
const int left_over = tile_width & (SPAN - 1);
const int left_over = tile_width & 3;
if (left_over > 0) {
VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride,
left_over, tile_height,
@ -95,33 +96,37 @@ static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
int tile_width, int tile_height,
int green_to_red, int histo[]) {
const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_red));
const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
const __m128i mask = _mm_set1_epi16(0xff);
const __m128i mult = MK_CST_16(0, CST_5b(green_to_red));
const __m128i mask_g = _mm_set1_epi32(0x0000ff00);
if (tile_width >= 4) {
int y;
for (y = 0; y < tile_height; ++y) {
const uint32_t* const src = argb + y * stride;
int i, x;
for (x = 0; x + SPAN <= tile_width; x += SPAN) {
uint16_t values[SPAN];
const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
const __m128i g0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0
const __m128i g1 = _mm_and_si128(in1, mask_g);
const __m128i g = _mm_packus_epi32(g0, g1); // g 0
const __m128i A0 = _mm_srli_epi32(in0, 16); // 0 0 | x r
const __m128i A1 = _mm_srli_epi32(in1, 16);
const __m128i A = _mm_packus_epi32(A0, A1); // x r
const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dr
const __m128i C = _mm_sub_epi8(A, B); // x r'
const __m128i D = _mm_and_si128(C, mask); // 0 r'
_mm_storeu_si128((__m128i*)values, D);
for (i = 0; i < SPAN; ++i) ++histo[values[i]];
const __m128i A1 = _mm_loadu_si128((const __m128i*)src);
const __m128i B1 = _mm_and_si128(A1, mask_g);
const __m128i C1 = _mm_madd_epi16(B1, mult);
__m128i D = _mm_sub_epi16(A1, C1);
int x;
for (x = 4; x + 4 <= tile_width; x += 4) {
const __m128i A2 = _mm_loadu_si128((const __m128i*)(src + x));
__m128i B2, C2;
++histo[_mm_extract_epi8(D, 2)];
B2 = _mm_and_si128(A2, mask_g);
++histo[_mm_extract_epi8(D, 6)];
C2 = _mm_madd_epi16(B2, mult);
++histo[_mm_extract_epi8(D, 10)];
++histo[_mm_extract_epi8(D, 14)];
D = _mm_sub_epi16(A2, C2);
}
++histo[_mm_extract_epi8(D, 2)];
++histo[_mm_extract_epi8(D, 6)];
++histo[_mm_extract_epi8(D, 10)];
++histo[_mm_extract_epi8(D, 14)];
}
}
{
const int left_over = tile_width & (SPAN - 1);
const int left_over = tile_width & 3;
if (left_over > 0) {
VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride,
left_over, tile_height, green_to_red,
@ -130,6 +135,8 @@ static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
}
}
#undef MK_CST_16
//------------------------------------------------------------------------------
// Entry point

View file

@ -18,7 +18,6 @@
#include "src/dsp/common_sse2.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
#include <assert.h>
#include <emmintrin.h>
//------------------------------------------------------------------------------

View file

@ -0,0 +1,132 @@
// Copyright 2021 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.
// -----------------------------------------------------------------------------
//
// SSE41 variant of methods for lossless decoder
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
#include "src/dsp/common_sse41.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
//------------------------------------------------------------------------------
// Color-space conversion functions
static void TransformColorInverse_SSE41(const VP8LMultipliers* const m,
const uint32_t* const src,
int num_pixels, uint32_t* dst) {
// sign-extended multiplying constants, pre-shifted by 5.
#define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend
const __m128i mults_rb = _mm_set1_epi32((uint32_t)CST(green_to_red_) << 16 |
(CST(green_to_blue_) & 0xffff));
const __m128i mults_b2 = _mm_set1_epi32(CST(red_to_blue_));
#undef CST
const __m128i mask_ag = _mm_set1_epi32(0xff00ff00);
const __m128i perm1 = _mm_setr_epi8(-1, 1, -1, 1, -1, 5, -1, 5,
-1, 9, -1, 9, -1, 13, -1, 13);
const __m128i perm2 = _mm_setr_epi8(-1, 2, -1, -1, -1, 6, -1, -1,
-1, 10, -1, -1, -1, 14, -1, -1);
int i;
for (i = 0; i + 4 <= num_pixels; i += 4) {
const __m128i A = _mm_loadu_si128((const __m128i*)(src + i));
const __m128i B = _mm_shuffle_epi8(A, perm1); // argb -> g0g0
const __m128i C = _mm_mulhi_epi16(B, mults_rb);
const __m128i D = _mm_add_epi8(A, C);
const __m128i E = _mm_shuffle_epi8(D, perm2);
const __m128i F = _mm_mulhi_epi16(E, mults_b2);
const __m128i G = _mm_add_epi8(D, F);
const __m128i out = _mm_blendv_epi8(G, A, mask_ag);
_mm_storeu_si128((__m128i*)&dst[i], out);
}
// Fall-back to C-version for left-overs.
if (i != num_pixels) {
VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i);
}
}
//------------------------------------------------------------------------------
#define ARGB_TO_RGB_SSE41 do { \
while (num_pixels >= 16) { \
const __m128i in0 = _mm_loadu_si128(in + 0); \
const __m128i in1 = _mm_loadu_si128(in + 1); \
const __m128i in2 = _mm_loadu_si128(in + 2); \
const __m128i in3 = _mm_loadu_si128(in + 3); \
const __m128i a0 = _mm_shuffle_epi8(in0, perm0); \
const __m128i a1 = _mm_shuffle_epi8(in1, perm1); \
const __m128i a2 = _mm_shuffle_epi8(in2, perm2); \
const __m128i a3 = _mm_shuffle_epi8(in3, perm3); \
const __m128i b0 = _mm_blend_epi16(a0, a1, 0xc0); \
const __m128i b1 = _mm_blend_epi16(a1, a2, 0xf0); \
const __m128i b2 = _mm_blend_epi16(a2, a3, 0xfc); \
_mm_storeu_si128(out + 0, b0); \
_mm_storeu_si128(out + 1, b1); \
_mm_storeu_si128(out + 2, b2); \
in += 4; \
out += 3; \
num_pixels -= 16; \
} \
} while (0)
static void ConvertBGRAToRGB_SSE41(const uint32_t* src, int num_pixels,
uint8_t* dst) {
const __m128i* in = (const __m128i*)src;
__m128i* out = (__m128i*)dst;
const __m128i perm0 = _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9,
8, 14, 13, 12, -1, -1, -1, -1);
const __m128i perm1 = _mm_shuffle_epi32(perm0, 0x39);
const __m128i perm2 = _mm_shuffle_epi32(perm0, 0x4e);
const __m128i perm3 = _mm_shuffle_epi32(perm0, 0x93);
ARGB_TO_RGB_SSE41;
// left-overs
if (num_pixels > 0) {
VP8LConvertBGRAToRGB_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
}
}
static void ConvertBGRAToBGR_SSE41(const uint32_t* src,
int num_pixels, uint8_t* dst) {
const __m128i* in = (const __m128i*)src;
__m128i* out = (__m128i*)dst;
const __m128i perm0 = _mm_setr_epi8(0, 1, 2, 4, 5, 6, 8, 9, 10,
12, 13, 14, -1, -1, -1, -1);
const __m128i perm1 = _mm_shuffle_epi32(perm0, 0x39);
const __m128i perm2 = _mm_shuffle_epi32(perm0, 0x4e);
const __m128i perm3 = _mm_shuffle_epi32(perm0, 0x93);
ARGB_TO_RGB_SSE41;
// left-overs
if (num_pixels > 0) {
VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
}
}
#undef ARGB_TO_RGB_SSE41
//------------------------------------------------------------------------------
// Entry point
extern void VP8LDspInitSSE41(void);
WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE41(void) {
VP8LTransformColorInverse = TransformColorInverse_SSE41;
VP8LConvertBGRAToRGB = ConvertBGRAToRGB_SSE41;
VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE41;
}
#else // !WEBP_USE_SSE41
WEBP_DSP_INIT_STUB(VP8LDspInitSSE41)
#endif // WEBP_USE_SSE41

View file

@ -38,8 +38,9 @@ void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk,
int x_out = channel;
// simple bilinear interpolation
int accum = wrk->x_add;
int left = src[x_in];
int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
rescaler_t left = (rescaler_t)src[x_in];
rescaler_t right =
(wrk->src_width > 1) ? (rescaler_t)src[x_in + x_stride] : left;
x_in += x_stride;
while (1) {
wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
@ -50,7 +51,7 @@ void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk,
left = right;
x_in += x_stride;
assert(x_in < wrk->src_width * x_stride);
right = src[x_in];
right = (rescaler_t)src[x_in];
accum += wrk->x_add;
}
}
@ -213,7 +214,7 @@ WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPRescalerDspInitSSE2();
}
@ -235,7 +236,7 @@ WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
WebPRescalerDspInitNEON();

View file

@ -150,7 +150,7 @@ WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
#endif
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
VP8SSIMDspInitSSE2();
}

View file

@ -233,12 +233,12 @@ WEBP_DSP_INIT_FUNC(WebPInitYUV444Converters) {
WebPYUV444Converters[MODE_rgbA_4444] = WebPYuv444ToRgba4444_C;
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitYUV444ConvertersSSE2();
}
#endif
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
WebPInitYUV444ConvertersSSE41();
}
@ -278,12 +278,12 @@ WEBP_DSP_INIT_FUNC(WebPInitUpsamplers) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitUpsamplersSSE2();
}
#endif
#if defined(WEBP_USE_SSE41)
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
WebPInitUpsamplersSSE41();
}
@ -300,7 +300,7 @@ WEBP_DSP_INIT_FUNC(WebPInitUpsamplers) {
#endif
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
WebPInitUpsamplersNEON();

View file

@ -90,16 +90,16 @@ WEBP_DSP_INIT_FUNC(WebPInitSamplers) {
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitSamplersSSE2();
}
#endif // WEBP_USE_SSE2
#if defined(WEBP_USE_SSE41)
#endif // WEBP_HAVE_SSE2
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
WebPInitSamplersSSE41();
}
#endif // WEBP_USE_SSE41
#endif // WEBP_HAVE_SSE41
#if defined(WEBP_USE_MIPS32)
if (VP8GetCPUInfo(kMIPS32)) {
WebPInitSamplersMIPS32();
@ -276,26 +276,26 @@ WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
#endif
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_SSE2)
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitConvertARGBToYUVSSE2();
WebPInitSharpYUVSSE2();
}
#endif // WEBP_USE_SSE2
#if defined(WEBP_USE_SSE41)
#endif // WEBP_HAVE_SSE2
#if defined(WEBP_HAVE_SSE41)
if (VP8GetCPUInfo(kSSE4_1)) {
WebPInitConvertARGBToYUVSSE41();
}
#endif // WEBP_USE_SSE41
#endif // WEBP_HAVE_SSE41
}
#if defined(WEBP_USE_NEON)
#if defined(WEBP_HAVE_NEON)
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
WebPInitConvertARGBToYUVNEON();
WebPInitSharpYUVNEON();
}
#endif // WEBP_USE_NEON
#endif // WEBP_HAVE_NEON
assert(WebPConvertARGBToY != NULL);
assert(WebPConvertARGBToUV != NULL);

View file

@ -303,7 +303,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
int ok = 1;
const int reduce_levels = (quality < 100);
// quick sanity checks
// quick correctness checks
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
assert(enc != NULL && pic != NULL && pic->a != NULL);
assert(output != NULL && output_size != NULL);
@ -361,7 +361,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
//------------------------------------------------------------------------------
// Main calls
static int CompressAlphaJob(void* arg1, void* dummy) {
static int CompressAlphaJob(void* arg1, void* unused) {
VP8Encoder* const enc = (VP8Encoder*)arg1;
const WebPConfig* config = enc->config_;
uint8_t* alpha_data = NULL;
@ -375,13 +375,13 @@ static int CompressAlphaJob(void* arg1, void* dummy) {
filter, effort_level, &alpha_data, &alpha_size)) {
return 0;
}
if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
if (alpha_size != (uint32_t)alpha_size) { // Soundness check.
WebPSafeFree(alpha_data);
return 0;
}
enc->alpha_data_size_ = (uint32_t)alpha_size;
enc->alpha_data_ = alpha_data;
(void)dummy;
(void)unused;
return 1;
}

View file

@ -126,16 +126,6 @@ static void InitHistogram(VP8Histogram* const histo) {
histo->last_non_zero = 1;
}
static void MergeHistograms(const VP8Histogram* const in,
VP8Histogram* const out) {
if (in->max_value > out->max_value) {
out->max_value = in->max_value;
}
if (in->last_non_zero > out->last_non_zero) {
out->last_non_zero = in->last_non_zero;
}
}
//------------------------------------------------------------------------------
// Simplified k-Means, to assign Nb segments based on alpha-histogram
@ -285,49 +275,6 @@ static int FastMBAnalyze(VP8EncIterator* const it) {
return 0;
}
static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
int best_alpha) {
uint8_t modes[16];
const int max_mode = MAX_INTRA4_MODE;
int i4_alpha;
VP8Histogram total_histo;
int cur_histo = 0;
InitHistogram(&total_histo);
VP8IteratorStartI4(it);
do {
int mode;
int best_mode_alpha = DEFAULT_ALPHA;
VP8Histogram histos[2];
const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
VP8MakeIntra4Preds(it);
for (mode = 0; mode < max_mode; ++mode) {
int alpha;
InitHistogram(&histos[cur_histo]);
VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
0, 1, &histos[cur_histo]);
alpha = GetAlpha(&histos[cur_histo]);
if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
best_mode_alpha = alpha;
modes[it->i4_] = mode;
cur_histo ^= 1; // keep track of best histo so far.
}
}
// accumulate best histogram
MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
// Note: we reuse the original samples for predictors
} while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC));
i4_alpha = GetAlpha(&total_histo);
if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
VP8SetIntra4Mode(it, modes);
best_alpha = i4_alpha;
}
return best_alpha;
}
static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
int best_alpha = DEFAULT_ALPHA;
int smallest_alpha = 0;
@ -371,13 +318,6 @@ static void MBAnalyze(VP8EncIterator* const it,
best_alpha = FastMBAnalyze(it);
} else {
best_alpha = MBAnalyzeBestIntra16Mode(it);
if (enc->method_ >= 5) {
// We go and make a fast decision for intra4/intra16.
// It's usually not a good and definitive pick, but helps seeding the
// stats about level bit-cost.
// TODO(skal): improve criterion.
best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha);
}
}
best_uv_alpha = MBAnalyzeBestUVMode(it);

View file

@ -11,13 +11,14 @@
//
#include <assert.h>
#include <float.h>
#include <math.h>
#include "src/enc/backward_references_enc.h"
#include "src/enc/histogram_enc.h"
#include "src/dsp/dsp.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
#include "src/dsp/dsp.h"
#include "src/enc/backward_references_enc.h"
#include "src/enc/histogram_enc.h"
#include "src/utils/color_cache_utils.h"
#include "src/utils/utils.h"
@ -103,6 +104,20 @@ void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
}
}
// Swaps the content of two VP8LBackwardRefs.
static void BackwardRefsSwap(VP8LBackwardRefs* const refs1,
VP8LBackwardRefs* const refs2) {
const int point_to_refs1 =
(refs1->tail_ != NULL && refs1->tail_ == &refs1->refs_);
const int point_to_refs2 =
(refs2->tail_ != NULL && refs2->tail_ == &refs2->refs_);
const VP8LBackwardRefs tmp = *refs1;
*refs1 = *refs2;
*refs2 = tmp;
if (point_to_refs2) refs1->tail_ = &refs1->refs_;
if (point_to_refs1) refs2->tail_ = &refs2->refs_;
}
void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
assert(refs != NULL);
memset(refs, 0, sizeof(*refs));
@ -154,6 +169,22 @@ static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
return b;
}
// Return 1 on success, 0 on error.
static int BackwardRefsClone(const VP8LBackwardRefs* const from,
VP8LBackwardRefs* const to) {
const PixOrCopyBlock* block_from = from->refs_;
VP8LClearBackwardRefs(to);
while (block_from != NULL) {
PixOrCopyBlock* const block_to = BackwardRefsNewBlock(to);
if (block_to == NULL) return 0;
memcpy(block_to->start_, block_from->start_,
block_from->size_ * sizeof(PixOrCopy));
block_to->size_ = block_from->size_;
block_from = block_from->next_;
}
return 1;
}
extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
const PixOrCopy v);
void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
@ -753,12 +784,18 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality,
}
}
} else {
int code, extra_bits, extra_bits_value;
// We should compute the contribution of the (distance,length)
// histograms but those are the same independently from the cache size.
// As those constant contributions are in the end added to the other
// histogram contributions, we can safely ignore them.
// histogram contributions, we can ignore them, except for the length
// prefix that is part of the literal_ histogram.
int len = PixOrCopyLength(v);
uint32_t argb_prev = *argb ^ 0xffffffffu;
VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value);
for (i = 0; i <= cache_bits_max; ++i) {
++histos[i]->literal_[NUM_LITERAL_CODES + code];
}
// Update the color caches.
do {
if (*argb != argb_prev) {
@ -842,16 +879,21 @@ extern int VP8LBackwardReferencesTraceBackwards(
int xsize, int ysize, const uint32_t* const argb, int cache_bits,
const VP8LHashChain* const hash_chain,
const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
static VP8LBackwardRefs* GetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
int lz77_types_to_try, int* const cache_bits,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,
VP8LBackwardRefs* worst) {
const int cache_bits_initial = *cache_bits;
double bit_cost_best = -1;
static int GetBackwardReferences(int width, int height,
const uint32_t* const argb, int quality,
int lz77_types_to_try, int cache_bits_max,
int do_no_cache,
const VP8LHashChain* const hash_chain,
VP8LBackwardRefs* const refs,
int* const cache_bits_best) {
VP8LHistogram* histo = NULL;
int lz77_type, lz77_type_best = 0;
int i, lz77_type;
// Index 0 is for a color cache, index 1 for no cache (if needed).
int lz77_types_best[2] = {0, 0};
double bit_costs_best[2] = {DBL_MAX, DBL_MAX};
VP8LHashChain hash_chain_box;
VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1];
int status = 0;
memset(&hash_chain_box, 0, sizeof(hash_chain_box));
histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
@ -860,86 +902,129 @@ static VP8LBackwardRefs* GetBackwardReferences(
for (lz77_type = 1; lz77_types_to_try;
lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
int res = 0;
double bit_cost;
int cache_bits_tmp = cache_bits_initial;
double bit_cost = 0.;
if ((lz77_types_to_try & lz77_type) == 0) continue;
switch (lz77_type) {
case kLZ77RLE:
res = BackwardReferencesRle(width, height, argb, 0, worst);
res = BackwardReferencesRle(width, height, argb, 0, refs_tmp);
break;
case kLZ77Standard:
// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color
// cache is not that different in practice.
res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
res = BackwardReferencesLz77(width, height, argb, 0, hash_chain,
refs_tmp);
break;
case kLZ77Box:
if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;
res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,
&hash_chain_box, worst);
&hash_chain_box, refs_tmp);
break;
default:
assert(0);
}
if (!res) goto Error;
// Next, try with a color cache and update the references.
if (!CalculateBestCacheSize(argb, quality, worst, &cache_bits_tmp)) {
// Start with the no color cache case.
for (i = 1; i >= 0; --i) {
int cache_bits = (i == 1) ? 0 : cache_bits_max;
if (i == 1 && !do_no_cache) continue;
if (i == 0) {
// Try with a color cache.
if (!CalculateBestCacheSize(argb, quality, refs_tmp, &cache_bits)) {
goto Error;
}
if (cache_bits_tmp > 0) {
if (!BackwardRefsWithLocalCache(argb, cache_bits_tmp, worst)) {
if (cache_bits > 0) {
if (!BackwardRefsWithLocalCache(argb, cache_bits, refs_tmp)) {
goto Error;
}
}
}
// Keep the best backward references.
VP8LHistogramCreate(histo, worst, cache_bits_tmp);
if (i == 0 && do_no_cache && cache_bits == 0) {
// No need to re-compute bit_cost as it was computed at i == 1.
} else {
VP8LHistogramCreate(histo, refs_tmp, cache_bits);
bit_cost = VP8LHistogramEstimateBits(histo);
if (lz77_type_best == 0 || bit_cost < bit_cost_best) {
VP8LBackwardRefs* const tmp = worst;
worst = best;
best = tmp;
bit_cost_best = bit_cost;
*cache_bits = cache_bits_tmp;
lz77_type_best = lz77_type;
}
if (bit_cost < bit_costs_best[i]) {
if (i == 1) {
// Do not swap as the full cache analysis would have the wrong
// VP8LBackwardRefs to start with.
if (!BackwardRefsClone(refs_tmp, &refs[1])) goto Error;
} else {
BackwardRefsSwap(refs_tmp, &refs[0]);
}
bit_costs_best[i] = bit_cost;
lz77_types_best[i] = lz77_type;
if (i == 0) *cache_bits_best = cache_bits;
}
}
assert(lz77_type_best > 0);
}
assert(lz77_types_best[0] > 0);
assert(!do_no_cache || lz77_types_best[1] > 0);
// Improve on simple LZ77 but only for high quality (TraceBackwards is
// costly).
if ((lz77_type_best == kLZ77Standard || lz77_type_best == kLZ77Box) &&
for (i = 1; i >= 0; --i) {
if (i == 1 && !do_no_cache) continue;
if ((lz77_types_best[i] == kLZ77Standard ||
lz77_types_best[i] == kLZ77Box) &&
quality >= 25) {
const VP8LHashChain* const hash_chain_tmp =
(lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
hash_chain_tmp, best, worst)) {
(lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box;
const int cache_bits = (i == 1) ? 0 : *cache_bits_best;
if (VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,
hash_chain_tmp, &refs[i],
refs_tmp)) {
double bit_cost_trace;
VP8LHistogramCreate(histo, worst, *cache_bits);
VP8LHistogramCreate(histo, refs_tmp, cache_bits);
bit_cost_trace = VP8LHistogramEstimateBits(histo);
if (bit_cost_trace < bit_cost_best) best = worst;
if (bit_cost_trace < bit_costs_best[i]) {
BackwardRefsSwap(refs_tmp, &refs[i]);
}
}
}
BackwardReferences2DLocality(width, best);
BackwardReferences2DLocality(width, &refs[i]);
if (i == 1 && lz77_types_best[0] == lz77_types_best[1] &&
*cache_bits_best == 0) {
// If the best cache size is 0 and we have the same best LZ77, just copy
// the data over and stop here.
if (!BackwardRefsClone(&refs[1], &refs[0])) goto Error;
break;
}
}
status = 1;
Error:
VP8LHashChainClear(&hash_chain_box);
VP8LFreeHistogram(histo);
return best;
return status;
}
VP8LBackwardRefs* VP8LGetBackwardReferences(
WebPEncodingError VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
int low_effort, int lz77_types_to_try, int* const cache_bits,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
VP8LBackwardRefs* const refs_tmp2) {
int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
int* const cache_bits_best) {
if (low_effort) {
return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
hash_chain, refs_tmp1);
VP8LBackwardRefs* refs_best;
*cache_bits_best = cache_bits_max;
refs_best = GetBackwardReferencesLowEffort(
width, height, argb, cache_bits_best, hash_chain, refs);
if (refs_best == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
// Set it in first position.
BackwardRefsSwap(refs_best, &refs[0]);
} else {
return GetBackwardReferences(width, height, argb, quality,
lz77_types_to_try, cache_bits, hash_chain,
refs_tmp1, refs_tmp2);
if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
cache_bits_max, do_no_cache, hash_chain, refs,
cache_bits_best)) {
return VP8_ENC_ERROR_OUT_OF_MEMORY;
}
}
return VP8_ENC_OK;
}

View file

@ -16,6 +16,7 @@
#include <assert.h>
#include <stdlib.h>
#include "src/webp/types.h"
#include "src/webp/encode.h"
#include "src/webp/format_constants.h"
#ifdef __cplusplus
@ -218,14 +219,19 @@ enum VP8LLZ77Type {
// Evaluates best possible backward references for specified quality.
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
// bits to use (passing 0 implies disabling the local color cache).
// The optimal cache bits is evaluated and set for the *cache_bits parameter.
// The return value is the pointer to the best of the two backward refs viz,
// refs[0] or refs[1].
VP8LBackwardRefs* VP8LGetBackwardReferences(
// The optimal cache bits is evaluated and set for the *cache_bits_best
// parameter with the matching refs_best.
// If do_no_cache == 0, refs is an array of 2 values and the best
// VP8LBackwardRefs is put in the first element.
// If do_no_cache != 0, refs is an array of 3 values and the best
// VP8LBackwardRefs is put in the first element, the best value with no-cache in
// the second element.
// In both cases, the last element is used as temporary internally.
WebPEncodingError VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
int low_effort, int lz77_types_to_try, int* const cache_bits,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
VP8LBackwardRefs* const refs_tmp2);
int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
int* const cache_bits_best);
#ifdef __cplusplus
}

View file

@ -39,6 +39,8 @@ int WebPConfigInitInternal(WebPConfig* config,
config->partitions = 0;
config->segments = 4;
config->pass = 1;
config->qmin = 0;
config->qmax = 100;
config->show_compressed = 0;
config->preprocessing = 0;
config->autofilter = 0;
@ -106,6 +108,9 @@ int WebPValidateConfig(const WebPConfig* config) {
if (config->filter_type < 0 || config->filter_type > 1) return 0;
if (config->autofilter < 0 || config->autofilter > 1) return 0;
if (config->pass < 1 || config->pass > 10) return 0;
if (config->qmin < 0 || config->qmax > 100 || config->qmin > config->qmax) {
return 0;
}
if (config->show_compressed < 0 || config->show_compressed > 1) return 0;
if (config->preprocessing < 0 || config->preprocessing > 7) return 0;
if (config->partitions < 0 || config->partitions > 3) return 0;

View file

@ -31,10 +31,15 @@
// we allow 2k of extra head-room in PARTITION0 limit.
#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
static float Clamp(float v, float min, float max) {
return (v < min) ? min : (v > max) ? max : v;
}
typedef struct { // struct for organizing convergence in either size or PSNR
int is_first;
float dq;
float q, last_q;
float qmin, qmax;
double value, last_value; // PSNR or size
double target;
int do_size_search;
@ -47,7 +52,9 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
s->is_first = 1;
s->dq = 10.f;
s->q = s->last_q = enc->config_->quality;
s->qmin = 1.f * enc->config_->qmin;
s->qmax = 1.f * enc->config_->qmax;
s->q = s->last_q = Clamp(enc->config_->quality, s->qmin, s->qmax);
s->target = do_size_search ? (double)target_size
: (target_PSNR > 0.) ? target_PSNR
: 40.; // default, just in case
@ -56,10 +63,6 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
return do_size_search;
}
static float Clamp(float v, float min, float max) {
return (v < min) ? min : (v > max) ? max : v;
}
static float ComputeNextQ(PassStats* const s) {
float dq;
if (s->is_first) {
@ -75,7 +78,7 @@ static float ComputeNextQ(PassStats* const s) {
s->dq = Clamp(dq, -30.f, 30.f);
s->last_q = s->q;
s->last_value = s->value;
s->q = Clamp(s->q + s->dq, 0.f, 100.f);
s->q = Clamp(s->q + s->dq, s->qmin, s->qmax);
return s->q;
}
@ -848,9 +851,10 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
}
#if (DEBUG_SEARCH > 0)
printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n",
printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf "
" range:[%.1f, %.1f]\n",
num_pass_left, stats.last_value, stats.value,
stats.last_q, stats.q, stats.dq);
stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax);
#endif
if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
++num_pass_left;

View file

@ -208,6 +208,7 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
} else if (PixOrCopyIsCacheIdx(v)) {
const int literal_ix =
NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
assert(histo->palette_code_bits_ != 0);
++histo->literal_[literal_ix];
} else {
int code, extra_bits;
@ -1170,13 +1171,15 @@ static void RemoveEmptyHistograms(VP8LHistogramSet* const image_histo) {
int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int low_effort,
int histo_bits, int cache_bits,
int histogram_bits, int cache_bits,
VP8LHistogramSet* const image_histo,
VP8LHistogram* const tmp_histo,
uint16_t* const histogram_symbols) {
int ok = 0;
const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
const int histo_xsize =
histogram_bits ? VP8LSubSampleSize(xsize, histogram_bits) : 1;
const int histo_ysize =
histogram_bits ? VP8LSubSampleSize(ysize, histogram_bits) : 1;
const int image_histo_raw_size = histo_xsize * histo_ysize;
VP8LHistogramSet* const orig_histo =
VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits);
@ -1192,7 +1195,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
if (orig_histo == NULL || map_tmp == NULL) goto Error;
// Construct the histograms from backward references.
HistogramBuild(xsize, histo_bits, refs, orig_histo);
HistogramBuild(xsize, histogram_bits, refs, orig_histo);
// Copies the histograms and computes its bit_cost.
// histogram_symbols is optimized
HistogramCopyAndAnalyze(orig_histo, image_histo, &num_used,

View file

@ -64,8 +64,8 @@ void VP8LHistogramCreate(VP8LHistogram* const p,
const VP8LBackwardRefs* const refs,
int palette_code_bits);
// Return the size of the histogram for a given palette_code_bits.
int VP8LGetHistogramSize(int palette_code_bits);
// Return the size of the histogram for a given cache_bits.
int VP8LGetHistogramSize(int cache_bits);
// Set the palette_code_bits and reset the stats.
// If init_arrays is true, the arrays are also filled with 0's.
@ -110,7 +110,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int low_effort,
int histogram_bits, int cache_bits,
VP8LHistogramSet* const image_in,
VP8LHistogramSet* const image_histo,
VP8LHistogram* const tmp_histo,
uint16_t* const histogram_symbols);

View file

@ -61,16 +61,14 @@ static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
// Checking for the presence of non-opaque alpha.
int WebPPictureHasTransparency(const WebPPicture* picture) {
if (picture == NULL) return 0;
if (!picture->use_argb) {
return CheckNonOpaque(picture->a, picture->width, picture->height,
1, picture->a_stride);
} else {
if (picture->use_argb) {
const int alpha_offset = ALPHA_OFFSET;
return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
picture->width, picture->height,
4, picture->argb_stride * sizeof(*picture->argb));
}
return 0;
return CheckNonOpaque(picture->a, picture->width, picture->height,
1, picture->a_stride);
}
//------------------------------------------------------------------------------
@ -90,8 +88,9 @@ int WebPPictureHasTransparency(const WebPPicture* picture) {
static int kLinearToGammaTab[kGammaTabSize + 1];
static uint16_t kGammaToLinearTab[256];
static volatile int kGammaTablesOk = 0;
static void InitGammaTables(void);
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {
WEBP_DSP_INIT_FUNC(InitGammaTables) {
if (!kGammaTablesOk) {
int v;
const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
@ -181,8 +180,9 @@ static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
#define GAMMA_TO_LINEAR_BITS 14
static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
static volatile int kGammaTablesSOk = 0;
static void InitGammaTablesS(void);
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {
WEBP_DSP_INIT_FUNC(InitGammaTablesS) {
assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
if (!kGammaTablesSOk) {
int v;

View file

@ -164,7 +164,7 @@ int WebPPictureCrop(WebPPicture* pic,
//------------------------------------------------------------------------------
// Simple picture rescaler
static void RescalePlane(const uint8_t* src,
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,
@ -172,14 +172,17 @@ static void RescalePlane(const uint8_t* src,
int num_channels) {
WebPRescaler rescaler;
int y = 0;
WebPRescalerInit(&rescaler, src_width, src_height,
if (!WebPRescalerInit(&rescaler, src_width, src_height,
dst, dst_width, dst_height, dst_stride,
num_channels, work);
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) {
@ -222,25 +225,28 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
// 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);
if (!RescalePlane(pic->a, prev_width, prev_height, pic->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(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,
if (!RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
tmp.y, width, height, tmp.y_stride, work, 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(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);
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) {
@ -252,11 +258,12 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
// the premultiplication afterward (while preserving the alpha channel).
WebPInitAlphaProcessing();
AlphaMultiplyARGB(pic, 0);
RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
if (!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);
tmp.argb_stride * 4, work, 4)) {
return 0;
}
AlphaMultiplyARGB(&tmp, 1);
}
WebPPictureFree(pic);

View file

@ -83,6 +83,19 @@ static int SmoothenBlock(const uint8_t* a_ptr, int a_stride, uint8_t* y_ptr,
return (count == 0);
}
void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color) {
if (pic != NULL && pic->use_argb) {
int y = pic->height;
uint32_t* argb = pic->argb;
color &= 0xffffffu; // force alpha=0
WebPInitAlphaProcessing();
while (y-- > 0) {
WebPAlphaReplace(argb, pic->width, color);
argb += pic->argb_stride;
}
}
}
void WebPCleanupTransparentArea(WebPPicture* pic) {
int x, y, w, h;
if (pic == NULL) return;
@ -165,24 +178,6 @@ void WebPCleanupTransparentArea(WebPPicture* pic) {
#undef SIZE
#undef SIZE2
void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) {
int x, y, w, h;
uint32_t* argb;
assert(pic != NULL && pic->use_argb);
w = pic->width;
h = pic->height;
argb = pic->argb;
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
if ((argb[x] & 0xff000000) == 0) {
argb[x] = 0x00000000;
}
}
argb += pic->argb_stride;
}
}
//------------------------------------------------------------------------------
// Blend color and remove transparency info

View file

@ -349,7 +349,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
(enc->alpha_data_size_ & 1);
riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
}
// Sanity check.
// RIFF size should fit in 32-bits.
if (riff_size > 0xfffffffeU) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
}

View file

@ -31,8 +31,8 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 1
#define ENC_REV_VERSION 0
#define ENC_MIN_VERSION 2
#define ENC_REV_VERSION 1
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
@ -286,8 +286,7 @@ int VP8IteratorNext(VP8EncIterator* const it);
// save the yuv_out_ boundary values to top_/left_ arrays for next iterations.
void VP8IteratorSaveBoundary(VP8EncIterator* const it);
// Report progression based on macroblock rows. Return 0 for user-abort request.
int VP8IteratorProgress(const VP8EncIterator* const it,
int final_delta_percent);
int VP8IteratorProgress(const VP8EncIterator* const it, int delta);
// Intra4x4 iterations
void VP8IteratorStartI4(VP8EncIterator* const it);
// returns true if not done.
@ -505,9 +504,9 @@ int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
// Returns false in case of error (invalid param, out-of-memory).
int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
// Clean-up the RGB samples under fully transparent area, to help lossless
// compressibility (no guarantee, though). Assumes that pic->use_argb is true.
void WebPCleanupTransparentAreaLossless(WebPPicture* const pic);
// Replace samples that are fully transparent by 'color' to help compressibility
// (no guarantee, though). Assumes pic->use_argb is true.
void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color);
//------------------------------------------------------------------------------

View file

@ -65,25 +65,22 @@ static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
*col2 = tmp;
}
static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
// Find greedily always the closest color of the predicted color to minimize
// deltas in the palette. This reduces storage needs since the
// palette is stored with delta encoding.
uint32_t predict = 0x00000000;
int i, k;
for (i = 0; i < num_colors; ++i) {
int best_ix = i;
uint32_t best_score = ~0U;
for (k = i; k < num_colors; ++k) {
const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
if (best_score > cur_score) {
best_score = cur_score;
best_ix = k;
static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
int num_colors) {
int low = 0, hi = num_colors;
if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
while (1) {
const int mid = (low + hi) >> 1;
if (sorted[mid] == color) {
return mid;
} else if (sorted[mid] < color) {
low = mid;
} else {
hi = mid;
}
}
SwapColor(&palette[best_ix], &palette[i]);
predict = palette[i];
}
assert(0);
return 0;
}
// The palette has been sorted by alpha. This function checks if the other
@ -92,7 +89,8 @@ static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
// no benefit to re-organize them greedily. A monotonic development
// would be spotted in green-only situations (like lossy alpha) or gray-scale
// images.
static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
int num_colors) {
uint32_t predict = 0x000000;
int i;
uint8_t sign_found = 0x00;
@ -115,28 +113,215 @@ static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
}
static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
int num_colors, uint32_t* const palette) {
uint32_t predict = 0x00000000;
int i, k;
memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
// Find greedily always the closest color of the predicted color to minimize
// deltas in the palette. This reduces storage needs since the
// palette is stored with delta encoding.
for (i = 0; i < num_colors; ++i) {
int best_ix = i;
uint32_t best_score = ~0U;
for (k = i; k < num_colors; ++k) {
const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
if (best_score > cur_score) {
best_score = cur_score;
best_ix = k;
}
}
SwapColor(&palette[best_ix], &palette[i]);
predict = palette[i];
}
}
// Sort palette in increasing order and prepare an inverse mapping array.
static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
uint32_t sorted[], uint32_t idx_map[]) {
uint32_t i;
memcpy(sorted, palette, num_colors * sizeof(*sorted));
qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
for (i = 0; i < num_colors; ++i) {
idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
}
}
// -----------------------------------------------------------------------------
// Modified Zeng method from "A Survey on Palette Reordering
// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
// Pinho and Antonio J. R. Neves.
// Finds the biggest cooccurrence in the matrix.
static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
uint32_t num_colors, uint8_t* const c1,
uint8_t* const c2) {
// Find the index that is most frequently located adjacent to other
// (different) indexes.
uint32_t best_sum = 0u;
uint32_t i, j, best_cooccurrence;
*c1 = 0u;
for (i = 0; i < num_colors; ++i) {
uint32_t sum = 0;
for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
if (sum > best_sum) {
best_sum = sum;
*c1 = i;
}
}
// Find the index that is most frequently found adjacent to *c1.
*c2 = 0u;
best_cooccurrence = 0u;
for (i = 0; i < num_colors; ++i) {
if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
best_cooccurrence = cooccurrence[*c1 * num_colors + i];
*c2 = i;
}
}
assert(*c1 != *c2);
}
// Builds the cooccurrence matrix
static WebPEncodingError CoOccurrenceBuild(const WebPPicture* const pic,
const uint32_t* const palette,
uint32_t num_colors,
uint32_t* cooccurrence) {
uint32_t *lines, *line_top, *line_current, *line_tmp;
int x, y;
const uint32_t* src = pic->argb;
uint32_t prev_pix = ~src[0];
uint32_t prev_idx = 0u;
uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
uint32_t palette_sorted[MAX_PALETTE_SIZE];
lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
if (lines == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
line_top = &lines[0];
line_current = &lines[pic->width];
PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
for (y = 0; y < pic->height; ++y) {
for (x = 0; x < pic->width; ++x) {
const uint32_t pix = src[x];
if (pix != prev_pix) {
prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
prev_pix = pix;
}
line_current[x] = prev_idx;
// 4-connectivity is what works best as mentioned in "On the relation
// between Memon's and the modified Zeng's palette reordering methods".
if (x > 0 && prev_idx != line_current[x - 1]) {
const uint32_t left_idx = line_current[x - 1];
++cooccurrence[prev_idx * num_colors + left_idx];
++cooccurrence[left_idx * num_colors + prev_idx];
}
if (y > 0 && prev_idx != line_top[x]) {
const uint32_t top_idx = line_top[x];
++cooccurrence[prev_idx * num_colors + top_idx];
++cooccurrence[top_idx * num_colors + prev_idx];
}
}
line_tmp = line_top;
line_top = line_current;
line_current = line_tmp;
src += pic->argb_stride;
}
WebPSafeFree(lines);
return VP8_ENC_OK;
}
struct Sum {
uint8_t index;
uint32_t sum;
};
// Implements the modified Zeng method from "A Survey on Palette Reordering
// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
// Pinho and Antonio J. R. Neves.
static WebPEncodingError PaletteSortModifiedZeng(
const WebPPicture* const pic, const uint32_t* const palette_sorted,
uint32_t num_colors, uint32_t* const palette) {
uint32_t i, j, ind;
uint8_t remapping[MAX_PALETTE_SIZE];
uint32_t* cooccurrence;
struct Sum sums[MAX_PALETTE_SIZE];
uint32_t first, last;
uint32_t num_sums;
// TODO(vrabaud) check whether one color images should use palette or not.
if (num_colors <= 1) return VP8_ENC_OK;
// Build the co-occurrence matrix.
cooccurrence =
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
if (cooccurrence == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
if (CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence) !=
VP8_ENC_OK) {
WebPSafeFree(cooccurrence);
return VP8_ENC_ERROR_OUT_OF_MEMORY;
}
// Initialize the mapping list with the two best indices.
CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
// We need to append and prepend to the list of remapping. To this end, we
// actually define the next start/end of the list as indices in a vector (with
// a wrap around when the end is reached).
first = 0;
last = 1;
num_sums = num_colors - 2; // -2 because we know the first two values
if (num_sums > 0) {
// Initialize the sums with the first two remappings and find the best one
struct Sum* best_sum = &sums[0];
best_sum->index = 0u;
best_sum->sum = 0u;
for (i = 0, j = 0; i < num_colors; ++i) {
if (i == remapping[0] || i == remapping[1]) continue;
sums[j].index = i;
sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
cooccurrence[i * num_colors + remapping[1]];
if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
++j;
}
while (num_sums > 0) {
const uint8_t best_index = best_sum->index;
// Compute delta to know if we need to prepend or append the best index.
int32_t delta = 0;
const int32_t n = num_colors - num_sums;
for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
const uint16_t l_j = remapping[(ind + j) % num_colors];
delta += (n - 1 - 2 * (int32_t)j) *
(int32_t)cooccurrence[best_index * num_colors + l_j];
}
if (delta > 0) {
first = (first == 0) ? num_colors - 1 : first - 1;
remapping[first] = best_index;
} else {
++last;
remapping[last] = best_index;
}
// Remove best_sum from sums.
*best_sum = sums[num_sums - 1];
--num_sums;
// Update all the sums and find the best one.
best_sum = &sums[0];
for (i = 0; i < num_sums; ++i) {
sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
}
}
}
assert((last + 1) % num_colors == first);
WebPSafeFree(cooccurrence);
// Re-map the palette.
for (i = 0; i < num_colors; ++i) {
palette[i] = palette_sorted[remapping[(first + i) % num_colors]];
}
return VP8_ENC_OK;
}
// -----------------------------------------------------------------------------
// Palette
// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
// creates a palette and returns true, else returns false.
static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
int low_effort,
uint32_t palette[MAX_PALETTE_SIZE],
int* const palette_size) {
const int num_colors = WebPGetColorPalette(pic, palette);
if (num_colors > MAX_PALETTE_SIZE) {
*palette_size = 0;
return 0;
}
*palette_size = num_colors;
qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) {
GreedyMinimizeDeltas(palette, num_colors);
}
return 1;
}
// These five modes are evaluated and their respective entropy is computed.
typedef enum {
kDirect = 0,
@ -144,9 +329,17 @@ typedef enum {
kSubGreen = 2,
kSpatialSubGreen = 3,
kPalette = 4,
kNumEntropyIx = 5
kPaletteAndSpatial = 5,
kNumEntropyIx = 6
} EntropyIx;
typedef enum {
kSortedDefault = 0,
kMinimizeDelta = 1,
kModifiedZeng = 2,
kUnusedPalette = 3,
} PaletteSorting;
typedef enum {
kHistoAlpha = 0,
kHistoAlphaPred,
@ -354,14 +547,21 @@ static int GetTransformBits(int method, int histo_bits) {
}
// Set of parameters to be used in each iteration of the cruncher.
#define CRUNCH_CONFIGS_LZ77_MAX 2
#define CRUNCH_SUBCONFIGS_MAX 2
typedef struct {
int lz77_;
int do_no_cache_;
} CrunchSubConfig;
typedef struct {
int entropy_idx_;
int lz77s_types_to_try_[CRUNCH_CONFIGS_LZ77_MAX];
int lz77s_types_to_try_size_;
PaletteSorting palette_sorting_type_;
CrunchSubConfig sub_configs_[CRUNCH_SUBCONFIGS_MAX];
int sub_configs_size_;
} CrunchConfig;
#define CRUNCH_CONFIGS_MAX kNumEntropyIx
// +2 because we add a palette sorting configuration for kPalette and
// kPaletteAndSpatial.
#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2)
static int EncoderAnalyze(VP8LEncoder* const enc,
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
@ -376,11 +576,20 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
int i;
int use_palette;
int n_lz77s;
// If set to 0, analyze the cache with the computed cache value. If 1, also
// analyze with no-cache.
int do_no_cache = 0;
assert(pic != NULL && pic->argb != NULL);
use_palette =
AnalyzeAndCreatePalette(pic, low_effort,
enc->palette_, &enc->palette_size_);
// Check whether a palette is possible.
enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_);
use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE);
if (!use_palette) {
enc->palette_size_ = 0;
} else {
qsort(enc->palette_sorted_, enc->palette_size_,
sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort);
}
// Empirical bit sizes.
enc->histo_bits_ = GetHistoBits(method, use_palette,
@ -390,6 +599,8 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
if (low_effort) {
// AnalyzeEntropy is somewhat slow.
crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen;
crunch_configs[0].palette_sorting_type_ =
use_palette ? kSortedDefault : kUnusedPalette;
n_lz77s = 1;
*crunch_configs_size = 1;
} else {
@ -402,29 +613,59 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
return 0;
}
if (method == 6 && config->quality == 100) {
do_no_cache = 1;
// Go brute force on all transforms.
*crunch_configs_size = 0;
for (i = 0; i < kNumEntropyIx; ++i) {
if (i != kPalette || use_palette) {
// We can only apply kPalette or kPaletteAndSpatial if we can indeed use
// a palette.
if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) {
assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
crunch_configs[(*crunch_configs_size)++].entropy_idx_ = i;
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) {
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
kMinimizeDelta;
++*crunch_configs_size;
// Also add modified Zeng's method.
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
kModifiedZeng;
} else {
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
kUnusedPalette;
}
++*crunch_configs_size;
}
}
} else {
// Only choose the guessed best transform.
*crunch_configs_size = 1;
crunch_configs[0].entropy_idx_ = min_entropy_ix;
crunch_configs[0].palette_sorting_type_ =
use_palette ? kMinimizeDelta : kUnusedPalette;
if (config->quality >= 75 && method == 5) {
// Test with and without color cache.
do_no_cache = 1;
// If we have a palette, also check in combination with spatial.
if (min_entropy_ix == kPalette) {
*crunch_configs_size = 2;
crunch_configs[1].entropy_idx_ = kPaletteAndSpatial;
crunch_configs[1].palette_sorting_type_ = kMinimizeDelta;
}
}
}
}
// Fill in the different LZ77s.
assert(n_lz77s <= CRUNCH_CONFIGS_LZ77_MAX);
assert(n_lz77s <= CRUNCH_SUBCONFIGS_MAX);
for (i = 0; i < *crunch_configs_size; ++i) {
int j;
for (j = 0; j < n_lz77s; ++j) {
crunch_configs[i].lz77s_types_to_try_[j] =
assert(j < CRUNCH_SUBCONFIGS_MAX);
crunch_configs[i].sub_configs_[j].lz77_ =
(j == 0) ? kLZ77Standard | kLZ77RLE : kLZ77Box;
crunch_configs[i].sub_configs_[j].do_no_cache_ = do_no_cache;
}
crunch_configs[i].lz77s_types_to_try_size_ = n_lz77s;
crunch_configs[i].sub_configs_size_ = n_lz77s;
}
return 1;
}
@ -440,7 +681,7 @@ static int EncoderInit(VP8LEncoder* const enc) {
int i;
if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0;
for (i = 0; i < 3; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
for (i = 0; i < 4; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
return 1;
}
@ -769,13 +1010,10 @@ static WebPEncodingError StoreImageToBitMask(
}
// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
const uint32_t* const argb,
VP8LHashChain* const hash_chain,
VP8LBackwardRefs* const refs_tmp1,
VP8LBackwardRefs* const refs_tmp2,
int width, int height,
int quality, int low_effort) {
static WebPEncodingError EncodeImageNoHuffman(
VP8LBitWriter* const bw, const uint32_t* const argb,
VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_array,
int width, int height, int quality, int low_effort) {
int i;
int max_tokens = 0;
WebPEncodingError err = VP8_ENC_OK;
@ -798,13 +1036,11 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0,
kLZ77Standard | kLZ77RLE, &cache_bits,
hash_chain, refs_tmp1, refs_tmp2);
if (refs == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
err = VP8LGetBackwardReferences(
width, height, argb, quality, /*low_effort=*/0, kLZ77Standard | kLZ77RLE,
cache_bits, /*do_no_cache=*/0, hash_chain, refs_array, &cache_bits);
if (err != VP8_ENC_OK) goto Error;
refs = &refs_array[0];
histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
if (histogram_image == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
@ -860,11 +1096,11 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
static WebPEncodingError EncodeImageInternal(
VP8LBitWriter* const bw, const uint32_t* const argb,
VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width,
VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[4], int width,
int height, int quality, int low_effort, int use_cache,
const CrunchConfig* const config, int* cache_bits, int histogram_bits,
size_t init_byte_position, int* const hdr_size, int* const data_size) {
WebPEncodingError err = VP8_ENC_OK;
WebPEncodingError err = VP8_ENC_ERROR_OUT_OF_MEMORY;
const uint32_t histogram_image_xysize =
VP8LSubSampleSize(width, histogram_bits) *
VP8LSubSampleSize(height, histogram_bits);
@ -876,70 +1112,71 @@ static WebPEncodingError EncodeImageInternal(
3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
HuffmanTreeToken* tokens = NULL;
HuffmanTreeCode* huffman_codes = NULL;
VP8LBackwardRefs* refs_best;
VP8LBackwardRefs* refs_tmp;
uint16_t* const histogram_symbols =
(uint16_t*)WebPSafeMalloc(histogram_image_xysize,
sizeof(*histogram_symbols));
int lz77s_idx;
int sub_configs_idx;
int cache_bits_init, write_histogram_image;
VP8LBitWriter bw_init = *bw, bw_best;
int hdr_size_tmp;
VP8LHashChain hash_chain_histogram; // histogram image hash chain
size_t bw_size_best = ~(size_t)0;
assert(histogram_bits >= MIN_HUFFMAN_BITS);
assert(histogram_bits <= MAX_HUFFMAN_BITS);
assert(hdr_size != NULL);
assert(data_size != NULL);
if (histogram_symbols == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
// Make sure we can allocate the different objects.
memset(&hash_chain_histogram, 0, sizeof(hash_chain_histogram));
if (huff_tree == NULL || histogram_symbols == NULL ||
!VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize) ||
!VP8LHashChainFill(hash_chain, quality, argb, width, height,
low_effort)) {
goto Error;
}
if (use_cache) {
// If the value is different from zero, it has been set during the
// palette analysis.
if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS;
cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
} else {
*cache_bits = 0;
cache_bits_init = 0;
}
// 'best_refs' is the reference to the best backward refs and points to one
// of refs_array[0] or refs_array[1].
// Calculate backward references from ARGB image.
if (huff_tree == NULL ||
!VP8LHashChainFill(hash_chain, quality, argb, width, height,
low_effort) ||
!VP8LBitWriterInit(&bw_best, 0) ||
(config->lz77s_types_to_try_size_ > 1 &&
// If several iterations will happen, clone into bw_best.
if (!VP8LBitWriterInit(&bw_best, 0) ||
((config->sub_configs_size_ > 1 ||
config->sub_configs_[0].do_no_cache_) &&
!VP8LBitWriterClone(bw, &bw_best))) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
for (lz77s_idx = 0; lz77s_idx < config->lz77s_types_to_try_size_;
++lz77s_idx) {
refs_best = VP8LGetBackwardReferences(
width, height, argb, quality, low_effort,
config->lz77s_types_to_try_[lz77s_idx], cache_bits, hash_chain,
&refs_array[0], &refs_array[1]);
if (refs_best == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
// Keep the best references aside and use the other element from the first
// two as a temporary for later usage.
refs_tmp = &refs_array[refs_best == &refs_array[0] ? 1 : 0];
for (sub_configs_idx = 0; sub_configs_idx < config->sub_configs_size_;
++sub_configs_idx) {
const CrunchSubConfig* const sub_config =
&config->sub_configs_[sub_configs_idx];
int cache_bits_best, i_cache;
err = VP8LGetBackwardReferences(width, height, argb, quality, low_effort,
sub_config->lz77_, cache_bits_init,
sub_config->do_no_cache_, hash_chain,
&refs_array[0], &cache_bits_best);
if (err != VP8_ENC_OK) goto Error;
histogram_image =
VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits);
tmp_histo = VP8LAllocateHistogram(*cache_bits);
if (histogram_image == NULL || tmp_histo == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
for (i_cache = 0; i_cache < (sub_config->do_no_cache_ ? 2 : 1); ++i_cache) {
const int cache_bits_tmp = (i_cache == 0) ? cache_bits_best : 0;
// Speed-up: no need to study the no-cache case if it was already studied
// in i_cache == 0.
if (i_cache == 1 && cache_bits_best == 0) break;
// Reset the bit writer for this iteration.
VP8LBitWriterReset(&bw_init, bw);
// Build histogram image and symbols from backward references.
if (!VP8LGetHistoImageSymbols(width, height, refs_best, quality, low_effort,
histogram_bits, *cache_bits, histogram_image,
tmp_histo, histogram_symbols)) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
histogram_image =
VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits_tmp);
tmp_histo = VP8LAllocateHistogram(cache_bits_tmp);
if (histogram_image == NULL || tmp_histo == NULL ||
!VP8LGetHistoImageSymbols(width, height, &refs_array[i_cache],
quality, low_effort, histogram_bits,
cache_bits_tmp, histogram_image, tmp_histo,
histogram_symbols)) {
goto Error;
}
// Create Huffman bit lengths and codes for each histogram image.
@ -948,10 +1185,10 @@ static WebPEncodingError EncodeImageInternal(
huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
sizeof(*huffman_codes));
// Note: some histogram_image entries may point to tmp_histos[], so the
// latter need to outlive the following call to GetHuffBitLengthsAndCodes().
// latter need to outlive the following call to
// GetHuffBitLengthsAndCodes().
if (huffman_codes == NULL ||
!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
// Free combined histograms.
@ -963,16 +1200,15 @@ static WebPEncodingError EncodeImageInternal(
tmp_histo = NULL;
// Color Cache parameters.
if (*cache_bits > 0) {
if (cache_bits_tmp > 0) {
VP8LPutBits(bw, 1, 1);
VP8LPutBits(bw, *cache_bits, 4);
VP8LPutBits(bw, cache_bits_tmp, 4);
} else {
VP8LPutBits(bw, 0, 1);
}
// Huffman image + meta huffman.
{
const int write_histogram_image = (histogram_image_size > 1);
write_histogram_image = (histogram_image_size > 1);
VP8LPutBits(bw, write_histogram_image, 1);
if (write_histogram_image) {
uint32_t* const histogram_argb =
@ -980,10 +1216,7 @@ static WebPEncodingError EncodeImageInternal(
sizeof(*histogram_argb));
int max_index = 0;
uint32_t i;
if (histogram_argb == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
if (histogram_argb == NULL) goto Error;
for (i = 0; i < histogram_image_xysize; ++i) {
const int symbol_index = histogram_symbols[i] & 0xffff;
histogram_argb[i] = (symbol_index << 8);
@ -995,13 +1228,12 @@ static WebPEncodingError EncodeImageInternal(
VP8LPutBits(bw, histogram_bits - 2, 3);
err = EncodeImageNoHuffman(
bw, histogram_argb, hash_chain, refs_tmp, &refs_array[2],
bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
VP8LSubSampleSize(width, histogram_bits),
VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
WebPSafeFree(histogram_argb);
if (err != VP8_ENC_OK) goto Error;
}
}
// Store Huffman codes.
{
@ -1015,10 +1247,7 @@ static WebPEncodingError EncodeImageInternal(
}
}
tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
if (tokens == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
if (tokens == NULL) goto Error;
for (i = 0; i < 5 * histogram_image_size; ++i) {
HuffmanTreeCode* const codes = &huffman_codes[i];
StoreHuffmanCode(bw, huff_tree, tokens, codes);
@ -1027,18 +1256,18 @@ static WebPEncodingError EncodeImageInternal(
}
// Store actual literals.
hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
err = StoreImageToBitMask(bw, width, histogram_bits, refs_best,
err = StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
histogram_symbols, huffman_codes);
if (err != VP8_ENC_OK) goto Error;
// Keep track of the smallest image so far.
if (lz77s_idx == 0 ||
VP8LBitWriterNumBytes(bw) < VP8LBitWriterNumBytes(&bw_best)) {
if (VP8LBitWriterNumBytes(bw) < bw_size_best) {
bw_size_best = VP8LBitWriterNumBytes(bw);
*cache_bits = cache_bits_tmp;
*hdr_size = hdr_size_tmp;
*data_size =
(int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size);
VP8LBitWriterSwap(bw, &bw_best);
}
// Reset the bit writer for the following iteration if any.
if (config->lz77s_types_to_try_size_ > 1) VP8LBitWriterReset(&bw_init, bw);
WebPSafeFree(tokens);
tokens = NULL;
if (huffman_codes != NULL) {
@ -1047,13 +1276,16 @@ static WebPEncodingError EncodeImageInternal(
huffman_codes = NULL;
}
}
}
VP8LBitWriterSwap(bw, &bw_best);
err = VP8_ENC_OK;
Error:
WebPSafeFree(tokens);
WebPSafeFree(huff_tree);
VP8LFreeHistogramSet(histogram_image);
VP8LFreeHistogram(tmp_histo);
VP8LHashChainClear(&hash_chain_histogram);
if (huffman_codes != NULL) {
WebPSafeFree(huffman_codes->codes);
WebPSafeFree(huffman_codes);
@ -1095,8 +1327,7 @@ static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
VP8LPutBits(bw, pred_bits - 2, 3);
return EncodeImageNoHuffman(
bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
(VP8LBackwardRefs*)&enc->refs_[0], // cast const away
(VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
(VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
quality, low_effort);
}
@ -1116,8 +1347,7 @@ static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
VP8LPutBits(bw, ccolor_transform_bits - 2, 3);
return EncodeImageNoHuffman(
bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
(VP8LBackwardRefs*)&enc->refs_[0], // cast const away
(VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
(VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
quality, low_effort);
}
@ -1272,22 +1502,6 @@ static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
// -----------------------------------------------------------------------------
static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
int hi) {
int low = 0;
if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
while (1) {
const int mid = (low + hi) >> 1;
if (sorted[mid] == color) {
return mid;
} else if (sorted[mid] < color) {
low = mid;
} else {
hi = mid;
}
}
}
#define APPLY_PALETTE_GREEDY_MAX 4
static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[],
@ -1322,17 +1536,6 @@ static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
(32 - PALETTE_INV_SIZE_BITS);
}
// Sort palette in increasing order and prepare an inverse mapping array.
static void PrepareMapToPalette(const uint32_t palette[], int num_colors,
uint32_t sorted[], uint32_t idx_map[]) {
int i;
memcpy(sorted, palette, num_colors * sizeof(*sorted));
qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
for (i = 0; i < num_colors; ++i) {
idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
}
}
// Use 1 pixel cache for ARGB pixels.
#define APPLY_PALETTE_FOR(COLOR_INDEX) do { \
uint32_t prev_pix = palette[0]; \
@ -1464,8 +1667,8 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
}
tmp_palette[0] = palette[0];
return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_,
&enc->refs_[0], &enc->refs_[1], palette_size, 1,
20 /* quality */, low_effort);
&enc->refs_[0], palette_size, 1, /*quality=*/20,
low_effort);
}
// -----------------------------------------------------------------------------
@ -1491,7 +1694,7 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) {
if (enc != NULL) {
int i;
VP8LHashChainClear(&enc->hash_chain_);
for (i = 0; i < 3; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
for (i = 0; i < 4; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
ClearTransformBuffer(enc);
WebPSafeFree(enc);
}
@ -1541,7 +1744,7 @@ static int EncodeStreamHook(void* input, void* data2) {
int data_size = 0;
int use_delta_palette = 0;
int idx;
size_t best_size = 0;
size_t best_size = ~(size_t)0;
VP8LBitWriter bw_init = *bw, bw_best;
(void)data2;
@ -1553,12 +1756,15 @@ static int EncodeStreamHook(void* input, void* data2) {
for (idx = 0; idx < num_crunch_configs; ++idx) {
const int entropy_idx = crunch_configs[idx].entropy_idx_;
enc->use_palette_ = (entropy_idx == kPalette);
enc->use_palette_ =
(entropy_idx == kPalette) || (entropy_idx == kPaletteAndSpatial);
enc->use_subtract_green_ =
(entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
enc->use_predict_ =
(entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
if (low_effort) {
enc->use_predict_ = (entropy_idx == kSpatial) ||
(entropy_idx == kSpatialSubGreen) ||
(entropy_idx == kPaletteAndSpatial);
// When using a palette, R/B==0, hence no need to test for cross-color.
if (low_effort || enc->use_palette_) {
enc->use_cross_color_ = 0;
} else {
enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_;
@ -1590,6 +1796,19 @@ static int EncodeStreamHook(void* input, void* data2) {
// Encode palette
if (enc->use_palette_) {
if (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) {
// Nothing to do, we have already sorted the palette.
memcpy(enc->palette_, enc->palette_sorted_,
enc->palette_size_ * sizeof(*enc->palette_));
} else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) {
PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_,
enc->palette_);
} else {
assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
err = PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
enc->palette_size_, enc->palette_);
if (err != VP8_ENC_OK) goto Error;
}
err = EncodePalette(bw, low_effort, enc);
if (err != VP8_ENC_OK) goto Error;
err = MapImageFromPalette(enc, use_delta_palette);
@ -1640,7 +1859,7 @@ static int EncodeStreamHook(void* input, void* data2) {
if (err != VP8_ENC_OK) goto Error;
// If we are better than what we already have.
if (idx == 0 || VP8LBitWriterNumBytes(bw) < best_size) {
if (VP8LBitWriterNumBytes(bw) < best_size) {
best_size = VP8LBitWriterNumBytes(bw);
// Store the BitWriter.
VP8LBitWriterSwap(bw, &bw_best);
@ -1754,6 +1973,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
enc_side->palette_size_ = enc_main->palette_size_;
memcpy(enc_side->palette_, enc_main->palette_,
sizeof(enc_main->palette_));
memcpy(enc_side->palette_sorted_, enc_main->palette_sorted_,
sizeof(enc_main->palette_sorted_));
param->enc_ = enc_side;
}
// Create the workers.
@ -1816,7 +2037,7 @@ Error:
}
#undef CRUNCH_CONFIGS_MAX
#undef CRUNCH_CONFIGS_LZ77_MAX
#undef CRUNCH_SUBCONFIGS_MAX
int VP8LEncodeImage(const WebPConfig* const config,
const WebPPicture* const picture) {

View file

@ -69,9 +69,11 @@ typedef struct {
int use_palette_;
int palette_size_;
uint32_t palette_[MAX_PALETTE_SIZE];
// Sorted version of palette_ for cache purposes.
uint32_t palette_sorted_[MAX_PALETTE_SIZE];
// Some 'scratch' (potentially large) objects.
struct VP8LBackwardRefs refs_[3]; // Backward Refs array for temporaries.
struct VP8LBackwardRefs refs_[4]; // Backward Refs array for temporaries.
VP8LHashChain hash_chain_; // HashChain data for constructing
// backward references.
} VP8LEncoder;

View file

@ -400,7 +400,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
}
if (!config->exact) {
WebPCleanupTransparentAreaLossless(pic);
WebPReplaceTransparentPixels(pic, 0x000000);
}
ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.

View file

@ -248,9 +248,6 @@ WebPAnimEncoder* WebPAnimEncoderNewInternal(
enc = (WebPAnimEncoder*)WebPSafeCalloc(1, sizeof(*enc));
if (enc == NULL) return NULL;
// sanity inits, so we can call WebPAnimEncoderDelete():
enc->encoded_frames_ = NULL;
enc->mux_ = NULL;
MarkNoError(enc);
// Dimensions and options.
@ -421,7 +418,7 @@ static void MinimizeChangeRectangle(const WebPPicture* const src,
const int max_allowed_diff_lossy = QualityToMaxDiff(quality);
const int max_allowed_diff = is_lossless ? 0 : max_allowed_diff_lossy;
// Sanity checks.
// Assumption/correctness checks.
assert(src->width == dst->width && src->height == dst->height);
assert(rect->x_offset_ + rect->width_ <= dst->width);
assert(rect->y_offset_ + rect->height_ <= dst->height);
@ -949,7 +946,8 @@ static int IncreasePreviousDuration(WebPAnimEncoder* const enc, int duration) {
int new_duration;
assert(enc->count_ >= 1);
assert(prev_enc_frame->sub_frame_.duration ==
assert(!prev_enc_frame->is_key_frame_ ||
prev_enc_frame->sub_frame_.duration ==
prev_enc_frame->key_frame_.duration);
assert(prev_enc_frame->sub_frame_.duration ==
(prev_enc_frame->sub_frame_.duration & (MAX_DURATION - 1)));
@ -1358,6 +1356,12 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
if (!IncreasePreviousDuration(enc, (int)prev_frame_duration)) {
return 0;
}
// IncreasePreviousDuration() may add a frame to avoid exceeding
// MAX_DURATION which could cause CacheFrame() to over read encoded_frames_
// before the next flush.
if (enc->count_ == enc->size_ && !FlushFrames(enc)) {
return 0;
}
} else {
enc->first_timestamp_ = timestamp;
}

View file

@ -235,7 +235,6 @@ WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
WebPMuxImage wpi;
WebPMuxError err;
// Sanity checks.
if (mux == NULL || bitstream == NULL || bitstream->bytes == NULL ||
bitstream->size > MAX_CHUNK_PAYLOAD) {
return WEBP_MUX_INVALID_ARGUMENT;
@ -267,7 +266,6 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info,
WebPMuxImage wpi;
WebPMuxError err;
// Sanity checks.
if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT;
if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT;

View file

@ -28,8 +28,8 @@ extern "C" {
// Defines and constants.
#define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 1
#define MUX_REV_VERSION 0
#define MUX_MIN_VERSION 2
#define MUX_REV_VERSION 1
// Chunk object.
typedef struct WebPChunk WebPChunk;

View file

@ -56,7 +56,7 @@ static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
uint32_t chunk_size;
WebPData chunk_data;
// Sanity checks.
// Correctness checks.
if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
chunk_size = GetLE32(data + TAG_SIZE);
if (chunk_size > MAX_CHUNK_PAYLOAD) return WEBP_MUX_BAD_DATA;
@ -155,7 +155,6 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
break;
default:
goto Fail;
break;
}
subchunk_size = ChunkDiskSize(&subchunk);
bytes += subchunk_size;
@ -187,7 +186,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
WebPChunk** chunk_list_ends[WEBP_CHUNK_NIL + 1] = { NULL };
ChunkInit(&chunk);
// Sanity checks.
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
return NULL; // version mismatch
}
@ -264,7 +262,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
ChunkRelease(&chunk);
goto PushImage;
break;
default: // A non-image chunk.
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
@ -483,7 +480,6 @@ WebPMuxError WebPMuxGetFrame(
WebPMuxError err;
WebPMuxImage* wpi;
// Sanity checks.
if (mux == NULL || frame == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}

View file

@ -55,7 +55,7 @@ void VP8LoadFinalBytes(VP8BitReader* const br);
// makes sure br->value_ has at least BITS bits worth of data
static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE
void VP8LoadNewBytes(VP8BitReader* const br) {
void VP8LoadNewBytes(VP8BitReader* WEBP_RESTRICT const br) {
assert(br != NULL && br->buf_ != NULL);
// Read 'BITS' bits at a time if possible.
if (br->buf_ < br->buf_max_) {
@ -104,7 +104,7 @@ void VP8LoadNewBytes(VP8BitReader* const br) {
}
// Read a bit with proba 'prob'. Speed-critical function!
static WEBP_INLINE int VP8GetBit(VP8BitReader* const br,
static WEBP_INLINE int VP8GetBit(VP8BitReader* WEBP_RESTRICT const br,
int prob, const char label[]) {
// Don't move this declaration! It makes a big speed difference to store
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
@ -137,7 +137,8 @@ static WEBP_INLINE int VP8GetBit(VP8BitReader* const br,
// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here)
static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
int VP8GetSigned(VP8BitReader* const br, int v, const char label[]) {
int VP8GetSigned(VP8BitReader* WEBP_RESTRICT const br, int v,
const char label[]) {
if (br->bits_ < 0) {
VP8LoadNewBytes(br);
}
@ -155,7 +156,7 @@ int VP8GetSigned(VP8BitReader* const br, int v, const char label[]) {
}
}
static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br,
static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* WEBP_RESTRICT const br,
int prob, const char label[]) {
// Don't move this declaration! It makes a big speed difference to store
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't

View file

@ -41,14 +41,7 @@ void VP8InitBitReader(VP8BitReader* const br,
br->bits_ = -8; // to load the very first 8bits
br->eof_ = 0;
VP8BitReaderSetBuffer(br, start, size);
// -- GODOT -- begin
#ifdef JAVASCRIPT_ENABLED // html5 required aligned reads
while(((uintptr_t)br->buf_ & 1) != 0 && !br->eof_)
VP8LoadFinalBytes(br);
#else
VP8LoadNewBytes(br);
#endif
// -- GODOT -- end
}
void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {

View file

@ -58,12 +58,6 @@ extern "C" {
// BITS can be any multiple of 8 from 8 to 56 (inclusive).
// Pick values that fit natural register size.
// -- GODOT -- start
#ifdef JAVASCRIPT_ENABLED
#define BITS 16
#else
// -- GODOT -- end
#if defined(__i386__) || defined(_M_IX86) // x86 32bit
#define BITS 24
#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit
@ -78,10 +72,6 @@ extern "C" {
#define BITS 24
#endif
// -- GODOT -- start
#endif
// -- GODOT -- end
//------------------------------------------------------------------------------
// Derived types and constants:
// bit_t = natural register type for storing 'value_' (which is BITS+8 bits)

View file

@ -278,7 +278,7 @@ void VP8LPutBitsFlushBits(VP8LBitWriter* const bw) {
// If needed, make some room by flushing some bits out.
if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
if (extra_size != (size_t)extra_size ||
if (!CheckSizeOverflow(extra_size) ||
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
bw->cur_ = bw->buf_;
bw->error_ = 1;
@ -314,7 +314,7 @@ void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) {
while (used >= VP8L_WRITER_BITS) {
if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
if (extra_size != (size_t)extra_size ||
if (!CheckSizeOverflow(extra_size) ||
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
bw->cur_ = bw->buf_;
bw->error_ = 1;

View file

@ -20,22 +20,22 @@
//------------------------------------------------------------------------------
// VP8LColorCache.
int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits) {
const int hash_size = 1 << hash_bits;
assert(cc != NULL);
assert(color_cache != NULL);
assert(hash_bits > 0);
cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size,
sizeof(*cc->colors_));
if (cc->colors_ == NULL) return 0;
cc->hash_shift_ = 32 - hash_bits;
cc->hash_bits_ = hash_bits;
color_cache->colors_ = (uint32_t*)WebPSafeCalloc(
(uint64_t)hash_size, sizeof(*color_cache->colors_));
if (color_cache->colors_ == NULL) return 0;
color_cache->hash_shift_ = 32 - hash_bits;
color_cache->hash_bits_ = hash_bits;
return 1;
}
void VP8LColorCacheClear(VP8LColorCache* const cc) {
if (cc != NULL) {
WebPSafeFree(cc->colors_);
cc->colors_ = NULL;
void VP8LColorCacheClear(VP8LColorCache* const color_cache) {
if (color_cache != NULL) {
WebPSafeFree(color_cache->colors_);
color_cache->colors_ = NULL;
}
}

View file

@ -404,8 +404,7 @@ static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) {
// Main entry point
void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
uint8_t* const buf_rle,
HuffmanTree* const huff_tree,
uint8_t* const buf_rle, HuffmanTree* const huff_tree,
HuffmanTreeCode* const huff_code) {
const int num_symbols = huff_code->num_symbols;
memset(buf_rle, 0, num_symbols * sizeof(*buf_rle));

View file

@ -51,7 +51,7 @@ int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
// huffman code tree.
void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
uint8_t* const buf_rle, HuffmanTree* const huff_tree,
HuffmanTreeCode* const tree);
HuffmanTreeCode* const huff_code);
#ifdef __cplusplus
}

View file

@ -12,66 +12,74 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "src/dsp/dsp.h"
#include "src/utils/rescaler_utils.h"
#include "src/utils/utils.h"
//------------------------------------------------------------------------------
void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
int WebPRescalerInit(WebPRescaler* const rescaler,
int src_width, int src_height,
uint8_t* const dst,
int dst_width, int dst_height, int dst_stride,
int num_channels, rescaler_t* const work) {
const int x_add = src_width, x_sub = dst_width;
const int y_add = src_height, y_sub = dst_height;
wrk->x_expand = (src_width < dst_width);
wrk->y_expand = (src_height < dst_height);
wrk->src_width = src_width;
wrk->src_height = src_height;
wrk->dst_width = dst_width;
wrk->dst_height = dst_height;
wrk->src_y = 0;
wrk->dst_y = 0;
wrk->dst = dst;
wrk->dst_stride = dst_stride;
wrk->num_channels = num_channels;
const uint64_t total_size = 2ull * dst_width * num_channels * sizeof(*work);
if (!CheckSizeOverflow(total_size)) return 0;
rescaler->x_expand = (src_width < dst_width);
rescaler->y_expand = (src_height < dst_height);
rescaler->src_width = src_width;
rescaler->src_height = src_height;
rescaler->dst_width = dst_width;
rescaler->dst_height = dst_height;
rescaler->src_y = 0;
rescaler->dst_y = 0;
rescaler->dst = dst;
rescaler->dst_stride = dst_stride;
rescaler->num_channels = num_channels;
// for 'x_expand', we use bilinear interpolation
wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add;
wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
if (!wrk->x_expand) { // fx_scale is not used otherwise
wrk->fx_scale = WEBP_RESCALER_FRAC(1, wrk->x_sub);
rescaler->x_add = rescaler->x_expand ? (x_sub - 1) : x_add;
rescaler->x_sub = rescaler->x_expand ? (x_add - 1) : x_sub;
if (!rescaler->x_expand) { // fx_scale is not used otherwise
rescaler->fx_scale = WEBP_RESCALER_FRAC(1, rescaler->x_sub);
}
// vertical scaling parameters
wrk->y_add = wrk->y_expand ? y_add - 1 : y_add;
wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub;
wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add;
if (!wrk->y_expand) {
rescaler->y_add = rescaler->y_expand ? y_add - 1 : y_add;
rescaler->y_sub = rescaler->y_expand ? y_sub - 1 : y_sub;
rescaler->y_accum = rescaler->y_expand ? rescaler->y_sub : rescaler->y_add;
if (!rescaler->y_expand) {
// This is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast.
// Its value is <= WEBP_RESCALER_ONE, because dst_height <= wrk->y_add, and
// wrk->x_add >= 1;
const uint64_t ratio =
(uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add);
// Its value is <= WEBP_RESCALER_ONE, because dst_height <= rescaler->y_add
// and rescaler->x_add >= 1;
const uint64_t num = (uint64_t)dst_height * WEBP_RESCALER_ONE;
const uint64_t den = (uint64_t)rescaler->x_add * rescaler->y_add;
const uint64_t ratio = num / den;
if (ratio != (uint32_t)ratio) {
// When ratio == WEBP_RESCALER_ONE, we can't represent the ratio with the
// current fixed-point precision. This happens when src_height ==
// wrk->y_add (which == src_height), and wrk->x_add == 1.
// rescaler->y_add (which == src_height), and rescaler->x_add == 1.
// => We special-case fxy_scale = 0, in WebPRescalerExportRow().
wrk->fxy_scale = 0;
rescaler->fxy_scale = 0;
} else {
wrk->fxy_scale = (uint32_t)ratio;
rescaler->fxy_scale = (uint32_t)ratio;
}
wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->y_sub);
rescaler->fy_scale = WEBP_RESCALER_FRAC(1, rescaler->y_sub);
} else {
wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->x_add);
// wrk->fxy_scale is unused here.
rescaler->fy_scale = WEBP_RESCALER_FRAC(1, rescaler->x_add);
// rescaler->fxy_scale is unused here.
}
wrk->irow = work;
wrk->frow = work + num_channels * dst_width;
memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
rescaler->irow = work;
rescaler->frow = work + num_channels * dst_width;
memset(work, 0, (size_t)total_size);
WebPRescalerDspInit();
return 1;
}
int WebPRescalerGetScaledDimensions(int src_width, int src_height,
@ -82,6 +90,7 @@ int WebPRescalerGetScaledDimensions(int src_width, int src_height,
{
int width = *scaled_width;
int height = *scaled_height;
const int max_size = INT_MAX / 2;
// if width is unspecified, scale original proportionally to height ratio.
if (width == 0 && src_height > 0) {
@ -94,7 +103,7 @@ int WebPRescalerGetScaledDimensions(int src_width, int src_height,
(int)(((uint64_t)src_height * width + src_width - 1) / src_width);
}
// Check if the overall dimensions still make sense.
if (width <= 0 || height <= 0) {
if (width <= 0 || height <= 0 || width > max_size || height > max_size) {
return 0;
}
@ -107,31 +116,34 @@ int WebPRescalerGetScaledDimensions(int src_width, int src_height,
//------------------------------------------------------------------------------
// all-in-one calls
int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) {
const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub;
int WebPRescaleNeededLines(const WebPRescaler* const rescaler,
int max_num_lines) {
const int num_lines =
(rescaler->y_accum + rescaler->y_sub - 1) / rescaler->y_sub;
return (num_lines > max_num_lines) ? max_num_lines : num_lines;
}
int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
int WebPRescalerImport(WebPRescaler* const rescaler, int num_lines,
const uint8_t* src, int src_stride) {
int total_imported = 0;
while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) {
if (wrk->y_expand) {
rescaler_t* const tmp = wrk->irow;
wrk->irow = wrk->frow;
wrk->frow = tmp;
while (total_imported < num_lines &&
!WebPRescalerHasPendingOutput(rescaler)) {
if (rescaler->y_expand) {
rescaler_t* const tmp = rescaler->irow;
rescaler->irow = rescaler->frow;
rescaler->frow = tmp;
}
WebPRescalerImportRow(wrk, src);
if (!wrk->y_expand) { // Accumulate the contribution of the new row.
WebPRescalerImportRow(rescaler, src);
if (!rescaler->y_expand) { // Accumulate the contribution of the new row.
int x;
for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) {
wrk->irow[x] += wrk->frow[x];
for (x = 0; x < rescaler->num_channels * rescaler->dst_width; ++x) {
rescaler->irow[x] += rescaler->frow[x];
}
}
++wrk->src_y;
++rescaler->src_y;
src += src_stride;
++total_imported;
wrk->y_accum -= wrk->y_sub;
rescaler->y_accum -= rescaler->y_sub;
}
return total_imported;
}

View file

@ -47,7 +47,8 @@ struct WebPRescaler {
};
// Initialize a rescaler given scratch area 'work' and dimensions of src & dst.
void WebPRescalerInit(WebPRescaler* const rescaler,
// Returns false in case of error.
int WebPRescalerInit(WebPRescaler* const rescaler,
int src_width, int src_height,
uint8_t* const dst,
int dst_width, int dst_height, int dst_stride,

View file

@ -101,6 +101,9 @@ static void Increment(int* const v) {
#if defined(MALLOC_LIMIT)
{
const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
#if MALLOC_LIMIT > 1
mem_limit = (size_t)MALLOC_LIMIT;
#endif
if (malloc_limit_str != NULL) {
mem_limit = atoi(malloc_limit_str);
}
@ -169,16 +172,16 @@ static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
const uint64_t total_size = nmemb * size;
if (nmemb == 0) return 1;
if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
if (total_size != (size_t)total_size) return 0;
if (!CheckSizeOverflow(total_size)) return 0;
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
return 0; // fake fail!
}
#endif
#if defined(MALLOC_LIMIT)
#if defined(PRINT_MEM_INFO) && defined(MALLOC_LIMIT)
if (mem_limit > 0) {
const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
if (new_total_mem != (size_t)new_total_mem ||
if (!CheckSizeOverflow(new_total_mem) ||
new_total_mem > mem_limit) {
return 0; // fake fail!
}
@ -231,7 +234,7 @@ void WebPFree(void* ptr) {
void WebPCopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
assert(src != NULL && dst != NULL);
assert(src_stride >= width && dst_stride >= width);
assert(abs(src_stride) >= width && abs(dst_stride) >= width);
while (height-- > 0) {
memcpy(dst, src, width);
src += src_stride;

View file

@ -42,6 +42,10 @@ extern "C" {
#endif
#endif // WEBP_MAX_ALLOCABLE_MEMORY
static WEBP_INLINE int CheckSizeOverflow(uint64_t size) {
return size == (size_t)size;
}
// size-checking safe malloc/calloc: verify that the requested size is not too
// large, or return NULL. You don't need to call these for constructs like
// malloc(sizeof(foo)), but only if there's picture-dependent size involved
@ -107,24 +111,33 @@ static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
PutLE16(data + 2, (int)(val >> 16));
}
// Returns (int)floor(log2(n)). n must be > 0.
// use GNU builtins where available.
#if defined(__GNUC__) && \
((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
// Returns (int)floor(log2(n)). n must be > 0.
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
return 31 ^ __builtin_clz(n);
}
// counts the number of trailing zero
static WEBP_INLINE int BitsCtz(uint32_t n) { return __builtin_ctz(n); }
#elif defined(_MSC_VER) && _MSC_VER > 1310 && \
(defined(_M_X64) || defined(_M_IX86))
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanForward)
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
unsigned long first_set_bit;
unsigned long first_set_bit; // NOLINT (runtime/int)
_BitScanReverse(&first_set_bit, n);
return first_set_bit;
}
#else // default: use the C-version.
static WEBP_INLINE int BitsCtz(uint32_t n) {
unsigned long first_set_bit; // NOLINT (runtime/int)
_BitScanForward(&first_set_bit, n);
return first_set_bit;
}
#else // default: use the (slow) C-version.
#define WEBP_HAVE_SLOW_CLZ_CTZ // signal that the Clz/Ctz function are slow
// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
// based on table or not. Can be used as fallback if clz() is not available.
#define WEBP_NEED_LOG_TABLE_8BIT
@ -139,6 +152,15 @@ static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
}
static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); }
static WEBP_INLINE int BitsCtz(uint32_t n) {
int i;
for (i = 0; i < 32; ++i, n >>= 1) {
if (n & 1) return i;
}
return 32;
}
#endif
//------------------------------------------------------------------------------

View file

@ -453,7 +453,7 @@ struct WebPDecoderOptions {
int scaled_width, scaled_height; // final resolution
int use_threads; // if true, use multi-threaded decoding
int dithering_strength; // dithering strength (0=Off, 100=full)
int flip; // flip output vertically
int flip; // if true, flip output vertically
int alpha_dithering_strength; // alpha dithering strength in [0..100]
uint32_t pad[5]; // padding for later use

View file

@ -148,7 +148,8 @@ struct WebPConfig {
int use_delta_palette; // reserved for future lossless feature
int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion
uint32_t pad[2]; // padding for later use
int qmin; // minimum permissible quality factor
int qmax; // maximum permissible quality factor
};
// Enumerate some predefined settings for WebPConfig, depending on the type
@ -291,6 +292,11 @@ typedef enum WebPEncodingError {
#define WEBP_MAX_DIMENSION 16383
// Main exchange structure (input samples, output bytes, statistics)
//
// Once WebPPictureInit() has been called, it's ok to make all the INPUT fields
// (use_argb, y/u/v, argb, ...) point to user-owned data, even if
// WebPPictureAlloc() has been called. Depending on the value use_argb,
// it's guaranteed that either *argb or *y/*u/*v content will be kept untouched.
struct WebPPicture {
// INPUT
//////////////