mirror of https://github.com/axmolengine/axmol.git
Update astcenc: 4.7.0 ==> 4.8.0, libwebp: 1.3.2 ==> 1.4.0
This commit is contained in:
parent
893ccab0bb
commit
a50eb06322
|
@ -6,7 +6,7 @@
|
|||
|
||||
## astcenc
|
||||
- [![Upstream](https://img.shields.io/github/v/release/ARM-software/astc-encoder?label=Upstream)](https://github.com/ARM-software/astc-encoder)
|
||||
- Version: 4.7.0
|
||||
- Version: 4.8.0
|
||||
- License: Apache-2.0
|
||||
|
||||
## Box2D
|
||||
|
@ -218,7 +218,7 @@
|
|||
|
||||
## simdjson
|
||||
- [![Upstream](https://img.shields.io/github/v/tag/simdjson/simdjson?label=Upstream)](https://github.com/simdjson/simdjson)
|
||||
- Version: 3.9.1
|
||||
- Version: 3.9.2
|
||||
- License: Apache-2.0
|
||||
|
||||
## stb (stb_image)
|
||||
|
@ -233,7 +233,7 @@
|
|||
|
||||
## webp
|
||||
- [![Upstream](https://img.shields.io/github/v/tag/webmproject/libwebp?label=Upstream)](https://github.com/webmproject/libwebp)
|
||||
- Version: 1.3.2
|
||||
- Version: 1.4.0
|
||||
- License: Google Inc
|
||||
|
||||
## xsxml
|
||||
|
|
|
@ -1167,7 +1167,7 @@ astcenc_error astcenc_decompress_image(
|
|||
return ASTCENC_ERR_OUT_OF_MEM;
|
||||
}
|
||||
|
||||
image_block blk;
|
||||
image_block blk {};
|
||||
blk.texel_count = static_cast<uint8_t>(block_x * block_y * block_z);
|
||||
|
||||
// Decode mode inferred from the output data type
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2011-2021 Arm Limited
|
||||
// Copyright 2011-2024 Arm Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
|
@ -464,10 +464,10 @@ static inline void write_bits(
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Read up to 8 bits at an arbitrary bit offset.
|
||||
* @brief Read up to 16 bits from two bytes.
|
||||
*
|
||||
* The stored value is at most 8 bits, but can be stored at an offset of between 0 and 7 bits so may
|
||||
* span two separate bytes in memory.
|
||||
* This function reads a packed N-bit field from two bytes in memory. The stored value must exist
|
||||
* within the two bytes, but can start at an arbitary bit offset and span the two bytes in memory.
|
||||
*
|
||||
* @param bitcount The number of bits to read.
|
||||
* @param bitoffset The bit offset to read from, between 0 and 7.
|
||||
|
|
|
@ -326,10 +326,10 @@ struct partition_info
|
|||
uint8_t partition_texel_count[BLOCK_MAX_PARTITIONS];
|
||||
|
||||
/** @brief The partition of each texel in the block. */
|
||||
uint8_t partition_of_texel[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS uint8_t partition_of_texel[BLOCK_MAX_TEXELS];
|
||||
|
||||
/** @brief The list of texels in each partition. */
|
||||
uint8_t texels_of_partition[BLOCK_MAX_PARTITIONS][BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS uint8_t texels_of_partition[BLOCK_MAX_PARTITIONS][BLOCK_MAX_TEXELS];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -367,19 +367,19 @@ struct decimation_info
|
|||
* @brief The number of weights that contribute to each texel.
|
||||
* Value is between 1 and 4.
|
||||
*/
|
||||
uint8_t texel_weight_count[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS uint8_t texel_weight_count[BLOCK_MAX_TEXELS];
|
||||
|
||||
/**
|
||||
* @brief The weight index of the N weights that are interpolated for each texel.
|
||||
* Stored transposed to improve vectorization.
|
||||
*/
|
||||
uint8_t texel_weights_tr[4][BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS uint8_t texel_weights_tr[4][BLOCK_MAX_TEXELS];
|
||||
|
||||
/**
|
||||
* @brief The bilinear contribution of the N weights that are interpolated for each texel.
|
||||
* Value is between 0 and 16, stored transposed to improve vectorization.
|
||||
*/
|
||||
uint8_t texel_weight_contribs_int_tr[4][BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS uint8_t texel_weight_contribs_int_tr[4][BLOCK_MAX_TEXELS];
|
||||
|
||||
/**
|
||||
* @brief The bilinear contribution of the N weights that are interpolated for each texel.
|
||||
|
@ -388,13 +388,13 @@ struct decimation_info
|
|||
ASTCENC_ALIGNAS float texel_weight_contribs_float_tr[4][BLOCK_MAX_TEXELS];
|
||||
|
||||
/** @brief The number of texels that each stored weight contributes to. */
|
||||
uint8_t weight_texel_count[BLOCK_MAX_WEIGHTS];
|
||||
ASTCENC_ALIGNAS uint8_t weight_texel_count[BLOCK_MAX_WEIGHTS];
|
||||
|
||||
/**
|
||||
* @brief The list of texels that use a specific weight index.
|
||||
* Stored transposed to improve vectorization.
|
||||
*/
|
||||
uint8_t weight_texels_tr[BLOCK_MAX_TEXELS][BLOCK_MAX_WEIGHTS];
|
||||
ASTCENC_ALIGNAS uint8_t weight_texels_tr[BLOCK_MAX_TEXELS][BLOCK_MAX_WEIGHTS];
|
||||
|
||||
/**
|
||||
* @brief The bilinear contribution to the N texels that use each weight.
|
||||
|
@ -732,7 +732,11 @@ struct block_size_descriptor
|
|||
*
|
||||
* The @c data_[rgba] fields store the image data in an encoded SoA float form designed for easy
|
||||
* vectorization. Input data is converted to float and stored as values between 0 and 65535. LDR
|
||||
* data is stored as direct UNORM data, HDR data is stored as LNS data.
|
||||
* data is stored as direct UNORM data, HDR data is stored as LNS data. They are allocated SIMD
|
||||
* elements over-size to allow vectorized stores of unaligned and partial SIMD lanes (e.g. in a
|
||||
* 6x6x6 block the final row write will read elements 210-217 (vec8) or 214-217 (vec4), which is
|
||||
* two elements above the last real data element). The overspill values are never written to memory,
|
||||
* and would be benign, but the padding avoids hitting undefined behavior.
|
||||
*
|
||||
* The @c rgb_lns and @c alpha_lns fields that assigned a per-texel use of HDR are only used during
|
||||
* decompression. The current compressor will always use HDR endpoint formats when in HDR mode.
|
||||
|
@ -740,16 +744,16 @@ struct block_size_descriptor
|
|||
struct image_block
|
||||
{
|
||||
/** @brief The input (compress) or output (decompress) data for the red color component. */
|
||||
ASTCENC_ALIGNAS float data_r[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS float data_r[BLOCK_MAX_TEXELS + ASTCENC_SIMD_WIDTH - 1];
|
||||
|
||||
/** @brief The input (compress) or output (decompress) data for the green color component. */
|
||||
ASTCENC_ALIGNAS float data_g[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS float data_g[BLOCK_MAX_TEXELS + ASTCENC_SIMD_WIDTH - 1];
|
||||
|
||||
/** @brief The input (compress) or output (decompress) data for the blue color component. */
|
||||
ASTCENC_ALIGNAS float data_b[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS float data_b[BLOCK_MAX_TEXELS + ASTCENC_SIMD_WIDTH - 1];
|
||||
|
||||
/** @brief The input (compress) or output (decompress) data for the alpha color component. */
|
||||
ASTCENC_ALIGNAS float data_a[BLOCK_MAX_TEXELS];
|
||||
ASTCENC_ALIGNAS float data_a[BLOCK_MAX_TEXELS + ASTCENC_SIMD_WIDTH - 1];
|
||||
|
||||
/** @brief The number of texels in the block. */
|
||||
uint8_t texel_count;
|
||||
|
@ -957,7 +961,7 @@ struct ASTCENC_ALIGNAS compression_working_buffers
|
|||
*
|
||||
* For two planes, second plane starts at @c WEIGHTS_PLANE2_OFFSET offsets.
|
||||
*/
|
||||
uint8_t dec_weights_uquant[WEIGHTS_MAX_BLOCK_MODES * BLOCK_MAX_WEIGHTS];
|
||||
ASTCENC_ALIGNAS uint8_t dec_weights_uquant[WEIGHTS_MAX_BLOCK_MODES * BLOCK_MAX_WEIGHTS];
|
||||
|
||||
/** @brief Error of the best encoding combination for each block mode. */
|
||||
ASTCENC_ALIGNAS float errors_of_best_combination[WEIGHTS_MAX_BLOCK_MODES];
|
||||
|
@ -1111,7 +1115,7 @@ struct symbolic_compressed_block
|
|||
*
|
||||
* If dual plane, the second plane starts at @c weights[WEIGHTS_PLANE2_OFFSET].
|
||||
*/
|
||||
uint8_t weights[BLOCK_MAX_WEIGHTS];
|
||||
ASTCENC_ALIGNAS uint8_t weights[BLOCK_MAX_WEIGHTS];
|
||||
|
||||
/**
|
||||
* @brief Get the weight quantization used by this block mode.
|
||||
|
|
|
@ -150,6 +150,7 @@ public:
|
|||
m_start_count = 0;
|
||||
m_done_count = 0;
|
||||
m_task_count = 0;
|
||||
m_callback = nullptr;
|
||||
m_callback_last_value = 0.0f;
|
||||
m_callback_min_diff = 1.0f;
|
||||
}
|
||||
|
|
|
@ -330,12 +330,14 @@ void physical_to_symbolic(
|
|||
return;
|
||||
}
|
||||
|
||||
// Low values span 3 bytes so need two read_bits calls
|
||||
int vx_low_s = read_bits(8, 12, pcb) | (read_bits(5, 12 + 8, pcb) << 8);
|
||||
int vx_high_s = read_bits(8, 25, pcb) | (read_bits(5, 25 + 8, pcb) << 8);
|
||||
int vx_high_s = read_bits(13, 25, pcb);
|
||||
int vx_low_t = read_bits(8, 38, pcb) | (read_bits(5, 38 + 8, pcb) << 8);
|
||||
int vx_high_t = read_bits(8, 51, pcb) | (read_bits(5, 51 + 8, pcb) << 8);
|
||||
int vx_high_t = read_bits(13, 51, pcb);
|
||||
|
||||
int all_ones = vx_low_s == 0x1FFF && vx_high_s == 0x1FFF && vx_low_t == 0x1FFF && vx_high_t == 0x1FFF;
|
||||
int all_ones = vx_low_s == 0x1FFF && vx_high_s == 0x1FFF &&
|
||||
vx_low_t == 0x1FFF && vx_high_t == 0x1FFF;
|
||||
|
||||
if ((vx_low_s >= vx_high_s || vx_low_t >= vx_high_t) && !all_ones)
|
||||
{
|
||||
|
@ -350,12 +352,14 @@ void physical_to_symbolic(
|
|||
int vx_high_s = read_bits(9, 19, pcb);
|
||||
int vx_low_t = read_bits(9, 28, pcb);
|
||||
int vx_high_t = read_bits(9, 37, pcb);
|
||||
int vx_low_p = read_bits(9, 46, pcb);
|
||||
int vx_high_p = read_bits(9, 55, pcb);
|
||||
int vx_low_r = read_bits(9, 46, pcb);
|
||||
int vx_high_r = read_bits(9, 55, pcb);
|
||||
|
||||
int all_ones = vx_low_s == 0x1FF && vx_high_s == 0x1FF && vx_low_t == 0x1FF && vx_high_t == 0x1FF && vx_low_p == 0x1FF && vx_high_p == 0x1FF;
|
||||
int all_ones = vx_low_s == 0x1FF && vx_high_s == 0x1FF &&
|
||||
vx_low_t == 0x1FF && vx_high_t == 0x1FF &&
|
||||
vx_low_r == 0x1FF && vx_high_r == 0x1FF;
|
||||
|
||||
if ((vx_low_s >= vx_high_s || vx_low_t >= vx_high_t || vx_low_p >= vx_high_p) && !all_ones)
|
||||
if ((vx_low_s >= vx_high_s || vx_low_t >= vx_high_t || vx_low_r >= vx_high_r) && !all_ones)
|
||||
{
|
||||
scb.block_type = SYM_BTYPE_ERROR;
|
||||
return;
|
||||
|
@ -470,8 +474,7 @@ void physical_to_symbolic(
|
|||
bitpos += 2;
|
||||
}
|
||||
}
|
||||
scb.partition_index = static_cast<uint16_t>(read_bits(6, 13, pcb) |
|
||||
(read_bits(PARTITION_INDEX_BITS - 6, 19, pcb) << 6));
|
||||
scb.partition_index = static_cast<uint16_t>(read_bits(10, 13, pcb));
|
||||
}
|
||||
|
||||
for (int i = 0; i < partition_count; i++)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2019-2023 Arm Limited
|
||||
// Copyright 2019-2024 Arm Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
|
@ -556,10 +556,16 @@ ASTCENC_SIMD_INLINE vmask4 operator>(vint4 a, vint4 b)
|
|||
*/
|
||||
template <int s> ASTCENC_SIMD_INLINE vint4 lsl(vint4 a)
|
||||
{
|
||||
return vint4(a.m[0] << s,
|
||||
a.m[1] << s,
|
||||
a.m[2] << s,
|
||||
a.m[3] << s);
|
||||
// Cast to unsigned to avoid shift in/out of sign bit undefined behavior
|
||||
unsigned int as0 = static_cast<unsigned int>(a.m[0]) << s;
|
||||
unsigned int as1 = static_cast<unsigned int>(a.m[1]) << s;
|
||||
unsigned int as2 = static_cast<unsigned int>(a.m[2]) << s;
|
||||
unsigned int as3 = static_cast<unsigned int>(a.m[3]) << s;
|
||||
|
||||
return vint4(static_cast<int>(as0),
|
||||
static_cast<int>(as1),
|
||||
static_cast<int>(as2),
|
||||
static_cast<int>(as3));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,6 +573,7 @@ template <int s> ASTCENC_SIMD_INLINE vint4 lsl(vint4 a)
|
|||
*/
|
||||
template <int s> ASTCENC_SIMD_INLINE vint4 lsr(vint4 a)
|
||||
{
|
||||
// Cast to unsigned to avoid shift in/out of sign bit undefined behavior
|
||||
unsigned int as0 = static_cast<unsigned int>(a.m[0]) >> s;
|
||||
unsigned int as1 = static_cast<unsigned int>(a.m[1]) >> s;
|
||||
unsigned int as2 = static_cast<unsigned int>(a.m[2]) >> s;
|
||||
|
|
|
@ -75,41 +75,48 @@ static int RGBToGray(int64_t r, int64_t g, int64_t b) {
|
|||
}
|
||||
|
||||
static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||
int rgb_bit_depth) {
|
||||
int rgb_bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth);
|
||||
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth);
|
||||
const uint32_t D = SharpYuvGammaToLinear(d, bit_depth);
|
||||
return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
|
||||
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type);
|
||||
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type);
|
||||
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type);
|
||||
const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type);
|
||||
return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth,
|
||||
transfer_type);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
|
||||
int rgb_bit_depth) {
|
||||
int rgb_bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth);
|
||||
const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth);
|
||||
int i = 0;
|
||||
do {
|
||||
const uint32_t R =
|
||||
SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type);
|
||||
const uint32_t G =
|
||||
SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type);
|
||||
const uint32_t B =
|
||||
SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type);
|
||||
const uint32_t Y = RGBToGray(R, G, B);
|
||||
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth);
|
||||
}
|
||||
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type);
|
||||
} while (++i < w);
|
||||
}
|
||||
|
||||
static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
||||
fixed_t* dst, int uv_w, int rgb_bit_depth) {
|
||||
int i;
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
fixed_t* dst, int uv_w, int rgb_bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
int i = 0;
|
||||
do {
|
||||
const int r =
|
||||
ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
|
||||
src2[0 * uv_w + 1], rgb_bit_depth);
|
||||
src2[0 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
const int g =
|
||||
ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
|
||||
src2[2 * uv_w + 1], rgb_bit_depth);
|
||||
src2[2 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
const int b =
|
||||
ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
|
||||
src2[4 * uv_w + 1], rgb_bit_depth);
|
||||
src2[4 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
const int W = RGBToGray(r, g, b);
|
||||
dst[0 * uv_w] = (fixed_t)(r - W);
|
||||
dst[1 * uv_w] = (fixed_t)(g - W);
|
||||
|
@ -117,15 +124,15 @@ static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
|||
dst += 1;
|
||||
src1 += 2;
|
||||
src2 += 2;
|
||||
}
|
||||
} while (++i < uv_w);
|
||||
}
|
||||
|
||||
static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
|
||||
int i;
|
||||
int i = 0;
|
||||
assert(w > 0);
|
||||
for (i = 0; i < w; ++i) {
|
||||
do {
|
||||
y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
|
||||
}
|
||||
} while (++i < w);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -151,9 +158,9 @@ static void ImportOneRow(const uint8_t* const r_ptr,
|
|||
// Convert the rgb_step from a number of bytes to a number of uint8_t or
|
||||
// uint16_t values depending the bit depth.
|
||||
const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;
|
||||
int i;
|
||||
int i = 0;
|
||||
const int w = (pic_width + 1) & ~1;
|
||||
for (i = 0; i < pic_width; ++i) {
|
||||
do {
|
||||
const int off = i * step;
|
||||
const int shift = GetPrecisionShift(rgb_bit_depth);
|
||||
if (rgb_bit_depth == 8) {
|
||||
|
@ -165,7 +172,7 @@ static void ImportOneRow(const uint8_t* const r_ptr,
|
|||
dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
|
||||
dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
|
||||
}
|
||||
}
|
||||
} while (++i < pic_width);
|
||||
if (pic_width & 1) { // replicate rightmost pixel
|
||||
dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
|
||||
dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
|
||||
|
@ -233,8 +240,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
|||
const int sfix = GetPrecisionShift(rgb_bit_depth);
|
||||
const int yuv_max = (1 << yuv_bit_depth) - 1;
|
||||
|
||||
for (best_uv = best_uv_base, j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
best_uv = best_uv_base;
|
||||
j = 0;
|
||||
do {
|
||||
i = 0;
|
||||
do {
|
||||
const int off = (i >> 1);
|
||||
const int W = best_y[i];
|
||||
const int r = best_uv[off + 0 * uv_w] + W;
|
||||
|
@ -246,19 +256,22 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
|||
} else {
|
||||
((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
|
||||
}
|
||||
}
|
||||
} while (++i < width);
|
||||
best_y += w;
|
||||
best_uv += (j & 1) * 3 * uv_w;
|
||||
y_ptr += y_stride;
|
||||
}
|
||||
for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int off = i;
|
||||
} while (++j < height);
|
||||
|
||||
best_uv = best_uv_base;
|
||||
j = 0;
|
||||
do {
|
||||
i = 0;
|
||||
do {
|
||||
// Note r, g and b values here are off by W, but a constant offset on all
|
||||
// 3 components doesn't change the value of u and v with a YCbCr matrix.
|
||||
const int r = best_uv[off + 0 * uv_w];
|
||||
const int g = best_uv[off + 1 * uv_w];
|
||||
const int b = best_uv[off + 2 * uv_w];
|
||||
const int r = best_uv[i + 0 * uv_w];
|
||||
const int g = best_uv[i + 1 * uv_w];
|
||||
const int b = best_uv[i + 2 * uv_w];
|
||||
const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);
|
||||
const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);
|
||||
if (yuv_bit_depth <= 8) {
|
||||
|
@ -268,11 +281,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
|||
((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
|
||||
((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
|
||||
}
|
||||
}
|
||||
} while (++i < uv_w);
|
||||
best_uv += 3 * uv_w;
|
||||
u_ptr += u_stride;
|
||||
v_ptr += v_stride;
|
||||
}
|
||||
} while (++j < uv_h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -285,7 +298,7 @@ static void* SafeMalloc(uint64_t nmemb, size_t size) {
|
|||
return malloc((size_t)total_size);
|
||||
}
|
||||
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((W) * (H), sizeof(T)))
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T)))
|
||||
|
||||
static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
const uint8_t* b_ptr, int rgb_step, int rgb_stride,
|
||||
|
@ -293,12 +306,14 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
|||
uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,
|
||||
int v_stride, int yuv_bit_depth, int width,
|
||||
int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
const SharpYuvConversionMatrix* yuv_matrix,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
// we expand the right/bottom border if needed
|
||||
const int w = (width + 1) & ~1;
|
||||
const int h = (height + 1) & ~1;
|
||||
const int uv_w = w >> 1;
|
||||
const int uv_h = h >> 1;
|
||||
const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
uint64_t prev_diff_y_sum = ~0;
|
||||
int j, iter;
|
||||
|
||||
|
@ -346,9 +361,9 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
|||
StoreGray(src1, best_y + 0, w);
|
||||
StoreGray(src2, best_y + w, w);
|
||||
|
||||
UpdateW(src1, target_y, w, rgb_bit_depth);
|
||||
UpdateW(src2, target_y + w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth);
|
||||
UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type);
|
||||
memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
|
@ -369,7 +384,8 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
|||
best_uv = best_uv_base;
|
||||
target_y = target_y_base;
|
||||
target_uv = target_uv_base;
|
||||
for (j = 0; j < h; j += 2) {
|
||||
j = 0;
|
||||
do {
|
||||
fixed_y_t* const src1 = tmp_buffer + 0 * w;
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
{
|
||||
|
@ -380,21 +396,21 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
|||
cur_uv = next_uv;
|
||||
}
|
||||
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth);
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type);
|
||||
|
||||
// update two rows of Y and one row of RGB
|
||||
diff_y_sum +=
|
||||
SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w,
|
||||
rgb_bit_depth + GetPrecisionShift(rgb_bit_depth));
|
||||
SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth);
|
||||
SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
|
||||
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
target_y += 2 * w;
|
||||
target_uv += 3 * uv_w;
|
||||
}
|
||||
j += 2;
|
||||
} while (j < h);
|
||||
// test exit condition
|
||||
if (iter > 0) {
|
||||
if (diff_y_sum < diff_y_threshold) break;
|
||||
|
@ -418,6 +434,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
|||
free(tmp_buffer);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#undef SAFE_ALLOC
|
||||
|
||||
#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
|
||||
|
@ -462,12 +479,42 @@ void SharpYuvInit(VP8CPUInfo cpu_info_func) {
|
|||
UNLOCK_ACCESS_AND_RETURN;
|
||||
}
|
||||
|
||||
int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step, int rgb_stride,
|
||||
int rgb_bit_depth, void* y_ptr, int y_stride,
|
||||
void* u_ptr, int u_stride, void* v_ptr,
|
||||
int v_stride, int yuv_bit_depth, int width,
|
||||
int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr,
|
||||
int rgb_step, int rgb_stride, int rgb_bit_depth,
|
||||
void* y_ptr, int y_stride, void* u_ptr, int u_stride,
|
||||
void* v_ptr, int v_stride, int yuv_bit_depth, int width,
|
||||
int height, const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
SharpYuvOptions options;
|
||||
options.yuv_matrix = yuv_matrix;
|
||||
options.transfer_type = kSharpYuvTransferFunctionSrgb;
|
||||
return SharpYuvConvertWithOptions(
|
||||
r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride,
|
||||
u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options);
|
||||
}
|
||||
|
||||
int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix,
|
||||
SharpYuvOptions* options, int version) {
|
||||
const int major = (version >> 24);
|
||||
const int minor = (version >> 16) & 0xff;
|
||||
if (options == NULL || yuv_matrix == NULL ||
|
||||
(major == SHARPYUV_VERSION_MAJOR && major == 0 &&
|
||||
minor != SHARPYUV_VERSION_MINOR) ||
|
||||
(major != SHARPYUV_VERSION_MAJOR)) {
|
||||
return 0;
|
||||
}
|
||||
options->yuv_matrix = yuv_matrix;
|
||||
options->transfer_type = kSharpYuvTransferFunctionSrgb;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step, int rgb_stride,
|
||||
int rgb_bit_depth, void* y_ptr, int y_stride,
|
||||
void* u_ptr, int u_stride, void* v_ptr,
|
||||
int v_stride, int yuv_bit_depth, int width,
|
||||
int height, const SharpYuvOptions* options) {
|
||||
const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix;
|
||||
SharpYuvTransferFunctionType transfer_type = options->transfer_type;
|
||||
SharpYuvConversionMatrix scaled_matrix;
|
||||
const int rgb_max = (1 << rgb_bit_depth) - 1;
|
||||
const int rgb_round = 1 << (rgb_bit_depth - 1);
|
||||
|
@ -486,7 +533,7 @@ int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
|||
if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {
|
||||
return 0;
|
||||
}
|
||||
if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride %2 != 0)) {
|
||||
if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) {
|
||||
// Step/stride should be even for uint16_t buffers.
|
||||
return 0;
|
||||
}
|
||||
|
@ -521,7 +568,7 @@ int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
|||
return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride,
|
||||
rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride,
|
||||
v_ptr, v_stride, yuv_bit_depth, width, height,
|
||||
&scaled_matrix);
|
||||
&scaled_matrix, transfer_type);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -22,22 +22,37 @@ extern "C" {
|
|||
#else
|
||||
// This explicitly marks library functions and allows for changing the
|
||||
// signature for e.g., Windows DLL builds.
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#if defined(_WIN32) && defined(WEBP_DLL)
|
||||
#define SHARPYUV_EXTERN __declspec(dllexport)
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define SHARPYUV_EXTERN extern __attribute__((visibility("default")))
|
||||
#else
|
||||
#if defined(_MSC_VER) && defined(WEBP_DLL)
|
||||
#define SHARPYUV_EXTERN __declspec(dllexport)
|
||||
#else
|
||||
#define SHARPYUV_EXTERN extern
|
||||
#endif /* _MSC_VER && WEBP_DLL */
|
||||
#endif /* __GNUC__ >= 4 */
|
||||
#endif /* defined(_WIN32) && defined(WEBP_DLL) */
|
||||
#endif /* WEBP_EXTERN */
|
||||
#endif /* SHARPYUV_EXTERN */
|
||||
|
||||
#ifndef SHARPYUV_INLINE
|
||||
#ifdef WEBP_INLINE
|
||||
#define SHARPYUV_INLINE WEBP_INLINE
|
||||
#else
|
||||
#ifndef _MSC_VER
|
||||
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
|
||||
#define SHARPYUV_INLINE inline
|
||||
#else
|
||||
#define SHARPYUV_INLINE
|
||||
#endif
|
||||
#else
|
||||
#define SHARPYUV_INLINE __forceinline
|
||||
#endif /* _MSC_VER */
|
||||
#endif /* WEBP_INLINE */
|
||||
#endif /* SHARPYUV_INLINE */
|
||||
|
||||
// SharpYUV API version following the convention from semver.org
|
||||
#define SHARPYUV_VERSION_MAJOR 0
|
||||
#define SHARPYUV_VERSION_MINOR 2
|
||||
#define SHARPYUV_VERSION_PATCH 1
|
||||
#define SHARPYUV_VERSION_MINOR 4
|
||||
#define SHARPYUV_VERSION_PATCH 0
|
||||
// Version as a uint32_t. The major number is the high 8 bits.
|
||||
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
|
||||
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
|
||||
|
@ -61,6 +76,33 @@ typedef struct {
|
|||
int rgb_to_v[4];
|
||||
} SharpYuvConversionMatrix;
|
||||
|
||||
typedef struct SharpYuvOptions SharpYuvOptions;
|
||||
|
||||
// Enums for transfer functions, as defined in H.273,
|
||||
// https://www.itu.int/rec/T-REC-H.273-202107-I/en
|
||||
typedef enum SharpYuvTransferFunctionType {
|
||||
// 0 is reserved
|
||||
kSharpYuvTransferFunctionBt709 = 1,
|
||||
// 2 is unspecified
|
||||
// 3 is reserved
|
||||
kSharpYuvTransferFunctionBt470M = 4,
|
||||
kSharpYuvTransferFunctionBt470Bg = 5,
|
||||
kSharpYuvTransferFunctionBt601 = 6,
|
||||
kSharpYuvTransferFunctionSmpte240 = 7,
|
||||
kSharpYuvTransferFunctionLinear = 8,
|
||||
kSharpYuvTransferFunctionLog100 = 9,
|
||||
kSharpYuvTransferFunctionLog100_Sqrt10 = 10,
|
||||
kSharpYuvTransferFunctionIec61966 = 11,
|
||||
kSharpYuvTransferFunctionBt1361 = 12,
|
||||
kSharpYuvTransferFunctionSrgb = 13,
|
||||
kSharpYuvTransferFunctionBt2020_10Bit = 14,
|
||||
kSharpYuvTransferFunctionBt2020_12Bit = 15,
|
||||
kSharpYuvTransferFunctionSmpte2084 = 16, // PQ
|
||||
kSharpYuvTransferFunctionSmpte428 = 17,
|
||||
kSharpYuvTransferFunctionHlg = 18,
|
||||
kSharpYuvTransferFunctionNum
|
||||
} SharpYuvTransferFunctionType;
|
||||
|
||||
// Converts RGB to YUV420 using a downsampling algorithm that minimizes
|
||||
// artefacts caused by chroma subsampling.
|
||||
// This is slower than standard downsampling (averaging of 4 UV values).
|
||||
|
@ -85,6 +127,8 @@ typedef struct {
|
|||
// adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they
|
||||
// should be multiples of 2.
|
||||
// width, height: width and height of the image in pixels
|
||||
// This function calls SharpYuvConvertWithOptions with a default transfer
|
||||
// function of kSharpYuvTransferFunctionSrgb.
|
||||
SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth,
|
||||
|
@ -93,6 +137,31 @@ SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
|||
int yuv_bit_depth, int width, int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix);
|
||||
|
||||
struct SharpYuvOptions {
|
||||
// This matrix cannot be NULL and can be initialized by
|
||||
// SharpYuvComputeConversionMatrix.
|
||||
const SharpYuvConversionMatrix* yuv_matrix;
|
||||
SharpYuvTransferFunctionType transfer_type;
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
SHARPYUV_EXTERN int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix*,
|
||||
SharpYuvOptions*, int);
|
||||
|
||||
// Should always be called, to initialize a fresh SharpYuvOptions
|
||||
// structure before modification. SharpYuvOptionsInit() must have succeeded
|
||||
// before using the 'options' object.
|
||||
static SHARPYUV_INLINE int SharpYuvOptionsInit(
|
||||
const SharpYuvConversionMatrix* yuv_matrix, SharpYuvOptions* options) {
|
||||
return SharpYuvOptionsInitInternal(yuv_matrix, options, SHARPYUV_VERSION);
|
||||
}
|
||||
|
||||
SHARPYUV_EXTERN int SharpYuvConvertWithOptions(
|
||||
const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth, void* y_ptr, int y_stride, void* u_ptr,
|
||||
int u_stride, void* v_ptr, int v_stride, int yuv_bit_depth, int width,
|
||||
int height, const SharpYuvOptions* options);
|
||||
|
||||
// TODO(b/194336375): Add YUV444 to YUV420 conversion. Maybe also add 422
|
||||
// support (it's rarely used in practice, especially for images).
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
@ -69,8 +70,7 @@ uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
|
|||
void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref, int16_t* dst,
|
||||
int len);
|
||||
void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth);
|
||||
const uint16_t* best_y, uint16_t* out, int bit_depth);
|
||||
|
||||
extern VP8CPUInfo SharpYuvGetCPUInfo;
|
||||
extern void InitSharpYuvSSE2(void);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "sharpyuv/sharpyuv_gamma.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "src/webp/types.h"
|
||||
|
@ -97,7 +98,7 @@ static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
|
|||
return result;
|
||||
}
|
||||
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
|
||||
static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {
|
||||
const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
|
||||
if (shift > 0) {
|
||||
return kGammaToLinearTabS[v << shift];
|
||||
|
@ -105,9 +106,314 @@ uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
|
|||
return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
|
||||
}
|
||||
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth) {
|
||||
static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {
|
||||
return FixedPointInterpolation(
|
||||
value, kLinearToGammaTabS,
|
||||
(GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
|
||||
bit_depth - GAMMA_TO_LINEAR_BITS);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define CLAMP(x, low, high) \
|
||||
(((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x)))
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
static WEBP_INLINE float Roundf(float x) {
|
||||
if (x < 0)
|
||||
return (float)ceil((double)(x - 0.5f));
|
||||
else
|
||||
return (float)floor((double)(x + 0.5f));
|
||||
}
|
||||
|
||||
static WEBP_INLINE float Powf(float base, float exp) {
|
||||
return (float)pow((double)base, (double)exp);
|
||||
}
|
||||
|
||||
static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); }
|
||||
|
||||
static float ToLinear709(float gamma) {
|
||||
if (gamma < 0.f) {
|
||||
return 0.f;
|
||||
} else if (gamma < 4.5f * 0.018053968510807f) {
|
||||
return gamma / 4.5f;
|
||||
} else if (gamma < 1.f) {
|
||||
return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float FromLinear709(float linear) {
|
||||
if (linear < 0.f) {
|
||||
return 0.f;
|
||||
} else if (linear < 0.018053968510807f) {
|
||||
return linear * 4.5f;
|
||||
} else if (linear < 1.f) {
|
||||
return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float ToLinear470M(float gamma) {
|
||||
return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f);
|
||||
}
|
||||
|
||||
static float FromLinear470M(float linear) {
|
||||
return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f);
|
||||
}
|
||||
|
||||
static float ToLinear470Bg(float gamma) {
|
||||
return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f);
|
||||
}
|
||||
|
||||
static float FromLinear470Bg(float linear) {
|
||||
return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f);
|
||||
}
|
||||
|
||||
static float ToLinearSmpte240(float gamma) {
|
||||
if (gamma < 0.f) {
|
||||
return 0.f;
|
||||
} else if (gamma < 4.f * 0.022821585529445f) {
|
||||
return gamma / 4.f;
|
||||
} else if (gamma < 1.f) {
|
||||
return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f);
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float FromLinearSmpte240(float linear) {
|
||||
if (linear < 0.f) {
|
||||
return 0.f;
|
||||
} else if (linear < 0.022821585529445f) {
|
||||
return linear * 4.f;
|
||||
} else if (linear < 1.f) {
|
||||
return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f;
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float ToLinearLog100(float gamma) {
|
||||
// The function is non-bijective so choose the middle of [0, 0.01].
|
||||
const float mid_interval = 0.01f / 2.f;
|
||||
return (gamma <= 0.0f) ? mid_interval
|
||||
: Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));
|
||||
}
|
||||
|
||||
static float FromLinearLog100(float linear) {
|
||||
return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f;
|
||||
}
|
||||
|
||||
static float ToLinearLog100Sqrt10(float gamma) {
|
||||
// The function is non-bijective so choose the middle of [0, 0.00316227766f[.
|
||||
const float mid_interval = 0.00316227766f / 2.f;
|
||||
return (gamma <= 0.0f) ? mid_interval
|
||||
: Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));
|
||||
}
|
||||
|
||||
static float FromLinearLog100Sqrt10(float linear) {
|
||||
return (linear < 0.00316227766f) ? 0.0f
|
||||
: 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;
|
||||
}
|
||||
|
||||
static float ToLinearIec61966(float gamma) {
|
||||
if (gamma <= -4.5f * 0.018053968510807f) {
|
||||
return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f);
|
||||
} else if (gamma < 4.5f * 0.018053968510807f) {
|
||||
return gamma / 4.5f;
|
||||
}
|
||||
return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
|
||||
}
|
||||
|
||||
static float FromLinearIec61966(float linear) {
|
||||
if (linear <= -0.018053968510807f) {
|
||||
return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f;
|
||||
} else if (linear < 0.018053968510807f) {
|
||||
return linear * 4.5f;
|
||||
}
|
||||
return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
|
||||
}
|
||||
|
||||
static float ToLinearBt1361(float gamma) {
|
||||
if (gamma < -0.25f) {
|
||||
return -0.25f;
|
||||
} else if (gamma < 0.f) {
|
||||
return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /
|
||||
-4.f;
|
||||
} else if (gamma < 4.5f * 0.018053968510807f) {
|
||||
return gamma / 4.5f;
|
||||
} else if (gamma < 1.f) {
|
||||
return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float FromLinearBt1361(float linear) {
|
||||
if (linear < -0.25f) {
|
||||
return -0.25f;
|
||||
} else if (linear < 0.f) {
|
||||
return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f;
|
||||
} else if (linear < 0.018053968510807f) {
|
||||
return linear * 4.5f;
|
||||
} else if (linear < 1.f) {
|
||||
return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
|
||||
}
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
static float ToLinearPq(float gamma) {
|
||||
if (gamma > 0.f) {
|
||||
const float pow_gamma = Powf(gamma, 32.f / 2523.f);
|
||||
const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f);
|
||||
const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN);
|
||||
return Powf(num / den, 4096.f / 653.f);
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
static float FromLinearPq(float linear) {
|
||||
if (linear > 0.f) {
|
||||
const float pow_linear = Powf(linear, 653.f / 4096.f);
|
||||
const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear;
|
||||
const float den = 1.0f + 2392.f / 128.f * pow_linear;
|
||||
return Powf(num / den, 2523.f / 32.f);
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
static float ToLinearSmpte428(float gamma) {
|
||||
return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f;
|
||||
}
|
||||
|
||||
static float FromLinearSmpte428(float linear) {
|
||||
return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f);
|
||||
}
|
||||
|
||||
// Conversion in BT.2100 requires RGB info. Simplify to gamma correction here.
|
||||
static float ToLinearHlg(float gamma) {
|
||||
if (gamma < 0.f) {
|
||||
return 0.f;
|
||||
} else if (gamma <= 0.5f) {
|
||||
return Powf((gamma * gamma) * (1.f / 3.f), 1.2f);
|
||||
}
|
||||
return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f,
|
||||
1.2f);
|
||||
}
|
||||
|
||||
static float FromLinearHlg(float linear) {
|
||||
linear = Powf(linear, 1.f / 1.2f);
|
||||
if (linear < 0.f) {
|
||||
return 0.f;
|
||||
} else if (linear <= (1.f / 12.f)) {
|
||||
return sqrtf(3.f * linear);
|
||||
}
|
||||
return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f;
|
||||
}
|
||||
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
float v_float, linear;
|
||||
if (transfer_type == kSharpYuvTransferFunctionSrgb) {
|
||||
return ToLinearSrgb(v, bit_depth);
|
||||
}
|
||||
v_float = (float)v / ((1 << bit_depth) - 1);
|
||||
switch (transfer_type) {
|
||||
case kSharpYuvTransferFunctionBt709:
|
||||
case kSharpYuvTransferFunctionBt601:
|
||||
case kSharpYuvTransferFunctionBt2020_10Bit:
|
||||
case kSharpYuvTransferFunctionBt2020_12Bit:
|
||||
linear = ToLinear709(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt470M:
|
||||
linear = ToLinear470M(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt470Bg:
|
||||
linear = ToLinear470Bg(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte240:
|
||||
linear = ToLinearSmpte240(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionLinear:
|
||||
return v;
|
||||
case kSharpYuvTransferFunctionLog100:
|
||||
linear = ToLinearLog100(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionLog100_Sqrt10:
|
||||
linear = ToLinearLog100Sqrt10(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionIec61966:
|
||||
linear = ToLinearIec61966(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt1361:
|
||||
linear = ToLinearBt1361(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte2084:
|
||||
linear = ToLinearPq(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte428:
|
||||
linear = ToLinearSmpte428(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionHlg:
|
||||
linear = ToLinearHlg(v_float);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
linear = 0;
|
||||
break;
|
||||
}
|
||||
return (uint32_t)Roundf(linear * ((1 << 16) - 1));
|
||||
}
|
||||
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
float v_float, linear;
|
||||
if (transfer_type == kSharpYuvTransferFunctionSrgb) {
|
||||
return FromLinearSrgb(v, bit_depth);
|
||||
}
|
||||
v_float = (float)v / ((1 << 16) - 1);
|
||||
switch (transfer_type) {
|
||||
case kSharpYuvTransferFunctionBt709:
|
||||
case kSharpYuvTransferFunctionBt601:
|
||||
case kSharpYuvTransferFunctionBt2020_10Bit:
|
||||
case kSharpYuvTransferFunctionBt2020_12Bit:
|
||||
linear = FromLinear709(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt470M:
|
||||
linear = FromLinear470M(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt470Bg:
|
||||
linear = FromLinear470Bg(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte240:
|
||||
linear = FromLinearSmpte240(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionLinear:
|
||||
return v;
|
||||
case kSharpYuvTransferFunctionLog100:
|
||||
linear = FromLinearLog100(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionLog100_Sqrt10:
|
||||
linear = FromLinearLog100Sqrt10(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionIec61966:
|
||||
linear = FromLinearIec61966(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionBt1361:
|
||||
linear = FromLinearBt1361(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte2084:
|
||||
linear = FromLinearPq(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionSmpte428:
|
||||
linear = FromLinearSmpte428(v_float);
|
||||
break;
|
||||
case kSharpYuvTransferFunctionHlg:
|
||||
linear = FromLinearHlg(v_float);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
linear = 0;
|
||||
break;
|
||||
}
|
||||
return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1));
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -22,11 +23,13 @@ extern "C" {
|
|||
// SharpYuvGammaToLinear or SharpYuvLinearToGamma.
|
||||
void SharpYuvInitGammaTables(void);
|
||||
|
||||
// Converts a gamma color value on 'bit_depth' bits to a 16 bit linear value.
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth);
|
||||
// Converts a 'bit_depth'-bit gamma color value to a 16-bit linear value.
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type);
|
||||
|
||||
// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits.
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth);
|
||||
// Converts a 16-bit linear color value to a 'bit_depth'-bit gamma value.
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
@ -13,18 +13,20 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/quant_levels_dec_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ALPHDecoder object.
|
||||
|
||||
// Allocates a new alpha decoder instance.
|
||||
static ALPHDecoder* ALPHNew(void) {
|
||||
WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
|
||||
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
return dec;
|
||||
}
|
||||
|
@ -45,9 +47,9 @@ static void ALPHDelete(ALPHDecoder* const dec) {
|
|||
// header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error in alpha header (data too short, invalid
|
||||
// compression method or filter, error in lossless header data etc).
|
||||
static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
size_t data_size, const VP8Io* const src_io,
|
||||
uint8_t* output) {
|
||||
WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
size_t data_size, const VP8Io* const src_io,
|
||||
uint8_t* output) {
|
||||
int ok = 0;
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
|
@ -79,7 +81,9 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
|||
}
|
||||
|
||||
// Copy the necessary parameters from src_io to io
|
||||
VP8InitIo(io);
|
||||
if (!VP8InitIo(io)) {
|
||||
return 0;
|
||||
}
|
||||
WebPInitCustomIo(NULL, io);
|
||||
io->opaque = dec;
|
||||
io->width = src_io->width;
|
||||
|
@ -107,7 +111,8 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
|||
// starting from row number 'row'. It assumes that rows up to (row - 1) have
|
||||
// already been decoded.
|
||||
// Returns false in case of bitstream error.
|
||||
static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
|
||||
WEBP_NODISCARD static int ALPHDecode(VP8Decoder* const dec, int row,
|
||||
int num_rows) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
const int width = alph_dec->width_;
|
||||
const int height = alph_dec->io_.crop_bottom;
|
||||
|
@ -117,21 +122,12 @@ static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
|
|||
const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
|
||||
uint8_t* dst = dec->alpha_plane_ + row * width;
|
||||
assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
|
||||
if (alph_dec->filter_ != WEBP_FILTER_NONE) {
|
||||
assert(WebPUnfilters[alph_dec->filter_] != NULL);
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
memcpy(dst, deltas, width * sizeof(*dst));
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
assert(WebPUnfilters[alph_dec->filter_] != NULL);
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
dec->alpha_prev_line_ = prev_line;
|
||||
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
|
||||
|
@ -147,7 +143,8 @@ static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
|
||||
WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec,
|
||||
const VP8Io* const io) {
|
||||
const int stride = io->width;
|
||||
const int height = io->crop_bottom;
|
||||
const uint64_t alpha_size = (uint64_t)stride * height;
|
||||
|
@ -155,7 +152,8 @@ static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
|
|||
dec->alpha_plane_mem_ =
|
||||
(uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
|
||||
if (dec->alpha_plane_mem_ == NULL) {
|
||||
return 0;
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
}
|
||||
dec->alpha_plane_ = dec->alpha_plane_mem_;
|
||||
dec->alpha_prev_line_ = NULL;
|
||||
|
@ -174,9 +172,9 @@ void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
|
|||
//------------------------------------------------------------------------------
|
||||
// Main entry point.
|
||||
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows) {
|
||||
WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows) {
|
||||
const int width = io->width;
|
||||
const int height = io->crop_bottom;
|
||||
|
||||
|
@ -189,10 +187,19 @@ const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
|||
if (!dec->is_alpha_decoded_) {
|
||||
if (dec->alph_dec_ == NULL) { // Initialize decoder.
|
||||
dec->alph_dec_ = ALPHNew();
|
||||
if (dec->alph_dec_ == NULL) return NULL;
|
||||
if (dec->alph_dec_ == NULL) {
|
||||
VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
return NULL;
|
||||
}
|
||||
if (!AllocateAlphaPlane(dec, io)) goto Error;
|
||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
||||
io, dec->alpha_plane_)) {
|
||||
VP8LDecoder* const vp8l_dec = dec->alph_dec_->vp8l_dec_;
|
||||
VP8SetError(dec,
|
||||
(vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY
|
||||
: vp8l_dec->status_,
|
||||
"Alpha decoder initialization failed.");
|
||||
goto Error;
|
||||
}
|
||||
// if we allowed use of alpha dithering, check whether it's needed at all
|
||||
|
|
|
@ -75,7 +75,7 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
|||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size =
|
||||
MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride);
|
||||
MIN_BUFFER_SIZE((uint64_t)width * kModeBpp[mode], height, stride);
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
|
||||
// In append mode, buffer allocations increase as multiples of this value.
|
||||
// Needs to be a power of 2.
|
||||
|
@ -161,8 +163,9 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
|||
|
||||
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
|
||||
// size if required and also updates VP8BitReader's if new memory is allocated.
|
||||
static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data,
|
||||
size_t data_size) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const int need_compressed_alpha = NeedCompressedAlpha(idec);
|
||||
|
@ -203,8 +206,9 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
WEBP_NODISCARD static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data,
|
||||
size_t data_size) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_buf = mem->buf_;
|
||||
const uint8_t* const old_start =
|
||||
|
@ -237,7 +241,8 @@ static void ClearMemBuffer(MemBuffer* const mem) {
|
|||
}
|
||||
}
|
||||
|
||||
static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
||||
WEBP_NODISCARD static int CheckMemBufferMode(MemBuffer* const mem,
|
||||
MemBufferMode expected) {
|
||||
if (mem->mode_ == MEM_MODE_NONE) {
|
||||
mem->mode_ = expected; // switch to the expected mode
|
||||
} else if (mem->mode_ != expected) {
|
||||
|
@ -248,7 +253,7 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
|||
}
|
||||
|
||||
// To be called last.
|
||||
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||
WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||
const WebPDecoderOptions* const options = idec->params_.options;
|
||||
WebPDecBuffer* const output = idec->params_.output;
|
||||
|
||||
|
@ -258,8 +263,10 @@ static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
|||
if (status != VP8_STATUS_OK) return status;
|
||||
}
|
||||
if (idec->final_output_ != NULL) {
|
||||
WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
|
||||
const VP8StatusCode status = WebPCopyDecBufferPixels(
|
||||
output, idec->final_output_); // do the slow-copy
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
*output = *idec->final_output_;
|
||||
idec->final_output_ = NULL;
|
||||
}
|
||||
|
@ -288,7 +295,7 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
|||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
(void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
idec->state_ = STATE_ERROR;
|
||||
return error;
|
||||
|
@ -329,6 +336,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
|||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
dec->incremental_ = 1;
|
||||
idec->dec_ = dec;
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
|
@ -601,8 +609,9 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
|||
//------------------------------------------------------------------------------
|
||||
// Internal constructor
|
||||
|
||||
static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
|
||||
const WebPBitstreamFeatures* const features) {
|
||||
WEBP_NODISCARD static WebPIDecoder* NewDecoder(
|
||||
WebPDecBuffer* const output_buffer,
|
||||
const WebPBitstreamFeatures* const features) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
|
@ -614,8 +623,10 @@ static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
|
|||
idec->last_mb_y_ = -1;
|
||||
|
||||
InitMemBuffer(&idec->mem_);
|
||||
WebPInitDecBuffer(&idec->output_);
|
||||
VP8InitIo(&idec->io_);
|
||||
if (!WebPInitDecBuffer(&idec->output_) || !VP8InitIo(&idec->io_)) {
|
||||
WebPSafeFree(idec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WebPResetDecParams(&idec->params_);
|
||||
if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
|
||||
|
@ -674,7 +685,8 @@ void WebPIDelete(WebPIDecoder* idec) {
|
|||
if (!idec->is_lossless_) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
// TODO(vrabaud) do we care about the return result?
|
||||
(void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
VP8Delete((VP8Decoder*)idec->dec_);
|
||||
} else {
|
||||
|
@ -851,8 +863,8 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
|||
return src;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride) {
|
||||
WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace >= MODE_YUV) {
|
||||
|
@ -867,10 +879,10 @@ uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
|||
return src->u.RGBA.rgba;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height,
|
||||
int* stride, int* uv_stride, int* a_stride) {
|
||||
WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height, int* stride,
|
||||
int* uv_stride, int* a_stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace < MODE_YUV) {
|
||||
|
|
|
@ -86,6 +86,8 @@ void VP8Delete(VP8Decoder* const dec) {
|
|||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
// VP8_STATUS_SUSPENDED is only meaningful in incremental decoding.
|
||||
assert(dec->incremental_ || error != VP8_STATUS_SUSPENDED);
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
|
@ -190,12 +192,12 @@ static int ParseSegmentHeader(VP8BitReader* br,
|
|||
}
|
||||
|
||||
// Paragraph 9.5
|
||||
// This function returns VP8_STATUS_SUSPENDED if we don't have all the
|
||||
// necessary data in 'buf'.
|
||||
// This case is not necessarily an error (for incremental decoding).
|
||||
// Still, no bitreader is ever initialized to make it possible to read
|
||||
// unavailable memory.
|
||||
// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
|
||||
// If we don't have all the necessary data in 'buf', this function returns
|
||||
// VP8_STATUS_SUSPENDED in incremental decoding, VP8_STATUS_NOT_ENOUGH_DATA
|
||||
// otherwise.
|
||||
// In incremental decoding, this case is not necessarily an error. Still, no
|
||||
// bitreader is ever initialized to make it possible to read unavailable memory.
|
||||
// If we don't even have the partitions' sizes, then VP8_STATUS_NOT_ENOUGH_DATA
|
||||
// is returned, and this is an unrecoverable error.
|
||||
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
|
||||
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
|
@ -225,8 +227,10 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
|||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||
if (part_start < buf_end) return VP8_STATUS_OK;
|
||||
return dec->incremental_
|
||||
? VP8_STATUS_SUSPENDED // Init is ok, but there's not enough data
|
||||
: VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
// Paragraph 9.4
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define WEBP_DEC_VP8_DEC_H_
|
||||
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -108,16 +109,14 @@ struct VP8Io {
|
|||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
int VP8InitIoInternal(VP8Io* const, int);
|
||||
WEBP_NODISCARD int VP8InitIoInternal(VP8Io* const, int);
|
||||
|
||||
// Set the custom IO function pointers and user-data. The setter for IO hooks
|
||||
// should be called before initiating incremental decoding. Returns true if
|
||||
// WebPIDecoder object is successfully modified, false otherwise.
|
||||
int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoPutHook put,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data);
|
||||
WEBP_NODISCARD int WebPISetIOHooks(WebPIDecoder* const idec, VP8IoPutHook put,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown, void* user_data);
|
||||
|
||||
// Main decoding object. This is an opaque structure.
|
||||
typedef struct VP8Decoder VP8Decoder;
|
||||
|
@ -128,17 +127,17 @@ VP8Decoder* VP8New(void);
|
|||
// Must be called to make sure 'io' is initialized properly.
|
||||
// Returns false in case of version mismatch. Upon such failure, no other
|
||||
// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
|
||||
static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
|
||||
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Decode the VP8 frame header. Returns true if ok.
|
||||
// Note: 'io->data' must be pointing to the start of the VP8 frame header.
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
|
||||
// Returns false in case of error.
|
||||
int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Return current status of the decoder:
|
||||
VP8StatusCode VP8Status(VP8Decoder* const dec);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "src/utils/random_utils.h"
|
||||
#include "src/utils/thread_utils.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -31,8 +32,8 @@ extern "C" {
|
|||
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 1
|
||||
#define DEC_MIN_VERSION 3
|
||||
#define DEC_REV_VERSION 2
|
||||
#define DEC_MIN_VERSION 4
|
||||
#define DEC_REV_VERSION 0
|
||||
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
|
@ -186,6 +187,7 @@ struct VP8Decoder {
|
|||
|
||||
// Main data source
|
||||
VP8BitReader br_;
|
||||
int incremental_; // if true, incremental decoding is expected
|
||||
|
||||
// headers
|
||||
VP8FrameHeader frm_hdr_;
|
||||
|
@ -281,7 +283,7 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
|
|||
void VP8ParseQuant(VP8Decoder* const dec);
|
||||
|
||||
// in frame.c
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Call io->setup() and finish setting up scan parameters.
|
||||
// After this call returns, one must always call VP8ExitCritical() with the
|
||||
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
||||
|
@ -289,7 +291,7 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
|||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Must always be called in pair with VP8EnterCritical().
|
||||
// Returns false in case of error.
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Return the multi-threading method to use (0=off), depending
|
||||
// on options and bitstream size. Only for lossy decoding.
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
|
@ -299,11 +301,12 @@ int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
|||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec);
|
||||
// Process the last decoded row (filtering + output).
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||
// To be called at the start of a new scanline, to initialize predictors.
|
||||
void VP8InitScanline(VP8Decoder* const dec);
|
||||
// Decode one macroblock. Returns false if there is not enough data.
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
|
||||
WEBP_NODISCARD int VP8DecodeMB(VP8Decoder* const dec,
|
||||
VP8BitReader* const token_br);
|
||||
|
||||
// in alpha.c
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// Authors: Vikas Arora (vikaas.arora@gmail.com)
|
||||
// Jyrki Alakuijala (jyrki@google.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
|
@ -101,6 +102,14 @@ static const uint16_t kTableSize[12] = {
|
|||
FIXED_TABLE_SIZE + 2704
|
||||
};
|
||||
|
||||
static int VP8LSetError(VP8LDecoder* const dec, VP8StatusCode error) {
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED) {
|
||||
dec->status_ = error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DecodeImageStream(int xsize, int ysize,
|
||||
int is_level0,
|
||||
VP8LDecoder* const dec,
|
||||
|
@ -301,7 +310,7 @@ static int ReadHuffmanCodeLengths(
|
|||
|
||||
End:
|
||||
VP8LHuffmanTablesDeallocate(&tables);
|
||||
if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
if (!ok) return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -333,10 +342,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
|
|||
int i;
|
||||
int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
|
||||
const int num_codes = VP8LReadBits(br, 4) + 4;
|
||||
if (num_codes > NUM_CODE_LENGTH_CODES) {
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
assert(num_codes <= NUM_CODE_LENGTH_CODES);
|
||||
|
||||
for (i = 0; i < num_codes; ++i) {
|
||||
code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3);
|
||||
|
@ -351,15 +357,14 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
|
|||
code_lengths, alphabet_size);
|
||||
}
|
||||
if (!ok || size == 0) {
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
int color_cache_bits, int allow_recursion) {
|
||||
int i, j;
|
||||
int i;
|
||||
VP8LBitReader* const br = &dec->br_;
|
||||
VP8LMetadata* const hdr = &dec->hdr_;
|
||||
uint32_t* huffman_image = NULL;
|
||||
|
@ -367,9 +372,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
HuffmanTables* huffman_tables = &hdr->huffman_tables_;
|
||||
int num_htree_groups = 1;
|
||||
int num_htree_groups_max = 1;
|
||||
int max_alphabet_size = 0;
|
||||
int* code_lengths = NULL;
|
||||
const int table_size = kTableSize[color_cache_bits];
|
||||
int* mapping = NULL;
|
||||
int ok = 0;
|
||||
|
||||
|
@ -383,7 +385,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision);
|
||||
const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision);
|
||||
const int huffman_pixs = huffman_xsize * huffman_ysize;
|
||||
if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec,
|
||||
if (!DecodeImageStream(huffman_xsize, huffman_ysize, /*is_level0=*/0, dec,
|
||||
&huffman_image)) {
|
||||
goto Error;
|
||||
}
|
||||
|
@ -407,7 +409,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
// values [0, num_htree_groups)
|
||||
mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping));
|
||||
if (mapping == NULL) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
goto Error;
|
||||
}
|
||||
// -1 means a value is unmapped, and therefore unused in the Huffman
|
||||
|
@ -426,25 +428,52 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
|
||||
if (br->eos_) goto Error;
|
||||
|
||||
// Find maximum alphabet size for the htree group.
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += 1 << color_cache_bits;
|
||||
}
|
||||
if (max_alphabet_size < alphabet_size) {
|
||||
max_alphabet_size = alphabet_size;
|
||||
}
|
||||
if (!ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups,
|
||||
num_htree_groups_max, mapping, dec,
|
||||
huffman_tables, &htree_groups)) {
|
||||
goto Error;
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
// All OK. Finalize pointers.
|
||||
hdr->huffman_image_ = huffman_image;
|
||||
hdr->num_htree_groups_ = num_htree_groups;
|
||||
hdr->htree_groups_ = htree_groups;
|
||||
|
||||
Error:
|
||||
WebPSafeFree(mapping);
|
||||
if (!ok) {
|
||||
WebPSafeFree(huffman_image);
|
||||
VP8LHuffmanTablesDeallocate(huffman_tables);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int ReadHuffmanCodesHelper(int color_cache_bits, int num_htree_groups,
|
||||
int num_htree_groups_max, const int* const mapping,
|
||||
VP8LDecoder* const dec,
|
||||
HuffmanTables* const huffman_tables,
|
||||
HTreeGroup** const htree_groups) {
|
||||
int i, j, ok = 0;
|
||||
const int max_alphabet_size =
|
||||
kAlphabetSize[0] + ((color_cache_bits > 0) ? 1 << color_cache_bits : 0);
|
||||
const int table_size = kTableSize[color_cache_bits];
|
||||
int* code_lengths = NULL;
|
||||
|
||||
if ((mapping == NULL && num_htree_groups != num_htree_groups_max) ||
|
||||
num_htree_groups > num_htree_groups_max) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
|
||||
sizeof(*code_lengths));
|
||||
htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
|
||||
code_lengths =
|
||||
(int*)WebPSafeCalloc((uint64_t)max_alphabet_size, sizeof(*code_lengths));
|
||||
*htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
|
||||
|
||||
if (htree_groups == NULL || code_lengths == NULL ||
|
||||
if (*htree_groups == NULL || code_lengths == NULL ||
|
||||
!VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
|
||||
huffman_tables)) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
goto Error;
|
||||
}
|
||||
|
||||
|
@ -464,7 +493,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
}
|
||||
} else {
|
||||
HTreeGroup* const htree_group =
|
||||
&htree_groups[(mapping == NULL) ? i : mapping[i]];
|
||||
&(*htree_groups)[(mapping == NULL) ? i : mapping[i]];
|
||||
HuffmanCode** const htrees = htree_group->htrees;
|
||||
int size;
|
||||
int total_size = 0;
|
||||
|
@ -516,18 +545,12 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||
}
|
||||
ok = 1;
|
||||
|
||||
// All OK. Finalize pointers.
|
||||
hdr->huffman_image_ = huffman_image;
|
||||
hdr->num_htree_groups_ = num_htree_groups;
|
||||
hdr->htree_groups_ = htree_groups;
|
||||
|
||||
Error:
|
||||
WebPSafeFree(code_lengths);
|
||||
WebPSafeFree(mapping);
|
||||
if (!ok) {
|
||||
WebPSafeFree(huffman_image);
|
||||
VP8LHuffmanTablesDeallocate(huffman_tables);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
VP8LHtreeGroupsFree(*htree_groups);
|
||||
*htree_groups = NULL;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -551,8 +574,7 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
|
|||
scaled_data_size * sizeof(*scaled_data);
|
||||
uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory));
|
||||
if (memory == NULL) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
}
|
||||
assert(dec->rescaler_memory == NULL);
|
||||
dec->rescaler_memory = memory;
|
||||
|
@ -1086,12 +1108,10 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data,
|
|||
End:
|
||||
br->eos_ = VP8LIsEndOfStream(br);
|
||||
if (!ok || (br->eos_ && pos < end)) {
|
||||
ok = 0;
|
||||
dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED
|
||||
: VP8_STATUS_BITSTREAM_ERROR;
|
||||
} else {
|
||||
dec->last_pixel_ = pos;
|
||||
return VP8LSetError(
|
||||
dec, br->eos_ ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
dec->last_pixel_ = pos;
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -1241,9 +1261,20 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
|
|||
}
|
||||
|
||||
br->eos_ = VP8LIsEndOfStream(br);
|
||||
if (dec->incremental_ && br->eos_ && src < src_end) {
|
||||
// In incremental decoding:
|
||||
// br->eos_ && src < src_last: if 'br' reached the end of the buffer and
|
||||
// 'src_last' has not been reached yet, there is not enough data. 'dec' has to
|
||||
// be reset until there is more data.
|
||||
// !br->eos_ && src < src_last: this cannot happen as either the buffer is
|
||||
// fully read, either enough has been read to reach 'src_last'.
|
||||
// src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go
|
||||
// beyond 'src_last' in case the image is cropped and an LZ77 goes further.
|
||||
// The buffer might have been enough or there is some left. 'br->eos_' does
|
||||
// not matter.
|
||||
assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last);
|
||||
if (dec->incremental_ && br->eos_ && src < src_last) {
|
||||
RestoreState(dec);
|
||||
} else if (!br->eos_) {
|
||||
} else if ((dec->incremental_ && src >= src_last) || !br->eos_) {
|
||||
// Process the remaining rows corresponding to last row-block.
|
||||
if (process_func != NULL) {
|
||||
process_func(dec, row > last_row ? last_row : row);
|
||||
|
@ -1258,8 +1289,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
|
|||
return 1;
|
||||
|
||||
Error:
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -1326,7 +1356,7 @@ static int ReadTransform(int* const xsize, int const* ysize,
|
|||
transform->bits_),
|
||||
VP8LSubSampleSize(transform->ysize_,
|
||||
transform->bits_),
|
||||
0, dec, &transform->data_);
|
||||
/*is_level0=*/0, dec, &transform->data_);
|
||||
break;
|
||||
case COLOR_INDEXING_TRANSFORM: {
|
||||
const int num_colors = VP8LReadBits(br, 8) + 1;
|
||||
|
@ -1336,8 +1366,11 @@ static int ReadTransform(int* const xsize, int const* ysize,
|
|||
: 3;
|
||||
*xsize = VP8LSubSampleSize(transform->xsize_, bits);
|
||||
transform->bits_ = bits;
|
||||
ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_);
|
||||
ok = ok && ExpandColorMap(num_colors, transform);
|
||||
ok = DecodeImageStream(num_colors, /*ysize=*/1, /*is_level0=*/0, dec,
|
||||
&transform->data_);
|
||||
if (ok && !ExpandColorMap(num_colors, transform)) {
|
||||
return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SUBTRACT_GREEN_TRANSFORM:
|
||||
|
@ -1443,7 +1476,7 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||
color_cache_bits = VP8LReadBits(br, 4);
|
||||
ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS);
|
||||
if (!ok) {
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
|
@ -1452,7 +1485,7 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||
ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize,
|
||||
color_cache_bits, is_level0);
|
||||
if (!ok) {
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
goto End;
|
||||
}
|
||||
|
||||
|
@ -1460,8 +1493,7 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||
if (color_cache_bits > 0) {
|
||||
hdr->color_cache_size_ = 1 << color_cache_bits;
|
||||
if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
ok = 0;
|
||||
ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
goto End;
|
||||
}
|
||||
} else {
|
||||
|
@ -1478,8 +1510,7 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||
const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize;
|
||||
data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data));
|
||||
if (data == NULL) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
ok = 0;
|
||||
ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
|
@ -1524,8 +1555,7 @@ static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) {
|
|||
dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t));
|
||||
if (dec->pixels_ == NULL) {
|
||||
dec->argb_cache_ = NULL; // for soundness
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
}
|
||||
dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels;
|
||||
return 1;
|
||||
|
@ -1536,8 +1566,7 @@ static int AllocateInternalBuffers8b(VP8LDecoder* const dec) {
|
|||
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;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -1592,7 +1621,8 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec,
|
|||
dec->status_ = VP8_STATUS_OK;
|
||||
VP8LInitBitReader(&dec->br_, data, data_size);
|
||||
|
||||
if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) {
|
||||
if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, /*is_level0=*/1,
|
||||
dec, /*decoded_data=*/NULL)) {
|
||||
goto Err;
|
||||
}
|
||||
|
||||
|
@ -1647,22 +1677,24 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
|
|||
|
||||
if (dec == NULL) return 0;
|
||||
if (io == NULL) {
|
||||
dec->status_ = VP8_STATUS_INVALID_PARAM;
|
||||
return 0;
|
||||
return VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
|
||||
}
|
||||
|
||||
dec->io_ = io;
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
VP8LInitBitReader(&dec->br_, io->data, io->data_size);
|
||||
if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) {
|
||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
goto Error;
|
||||
}
|
||||
dec->state_ = READ_DIM;
|
||||
io->width = width;
|
||||
io->height = height;
|
||||
|
||||
if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error;
|
||||
if (!DecodeImageStream(width, height, /*is_level0=*/1, dec,
|
||||
/*decoded_data=*/NULL)) {
|
||||
goto Error;
|
||||
}
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
|
@ -1692,7 +1724,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
|
|||
assert(dec->output_ != NULL);
|
||||
|
||||
if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
|
||||
dec->status_ = VP8_STATUS_INVALID_PARAM;
|
||||
VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
|
@ -1702,7 +1734,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
|
|||
if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
|
||||
#else
|
||||
if (io->use_scaling) {
|
||||
dec->status_ = VP8_STATUS_INVALID_PARAM;
|
||||
VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
|
||||
goto Err;
|
||||
}
|
||||
#endif
|
||||
|
@ -1720,7 +1752,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
|
|||
dec->hdr_.saved_color_cache_.colors_ == NULL) {
|
||||
if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_,
|
||||
dec->hdr_.color_cache_.hash_bits_)) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
goto Err;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/utils/color_cache_utils.h"
|
||||
#include "src/utils/huffman_utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -99,25 +100,26 @@ struct ALPHDecoder; // Defined in dec/alphai.h.
|
|||
|
||||
// Decodes image header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error.
|
||||
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const data, size_t data_size);
|
||||
WEBP_NODISCARD int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const data,
|
||||
size_t data_size);
|
||||
|
||||
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
|
||||
// already decoded in previous call(s), it will resume decoding from where it
|
||||
// was paused.
|
||||
// Returns false in case of bitstream error.
|
||||
int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
|
||||
int last_row);
|
||||
WEBP_NODISCARD int VP8LDecodeAlphaImageStream(
|
||||
struct ALPHDecoder* const alph_dec, int last_row);
|
||||
|
||||
// Allocates and initialize a new lossless decoder instance.
|
||||
VP8LDecoder* VP8LNew(void);
|
||||
WEBP_NODISCARD VP8LDecoder* VP8LNew(void);
|
||||
|
||||
// Decodes the image header. Returns false in case of error.
|
||||
int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
|
||||
WEBP_NODISCARD int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decodes an image. It's required to decode the lossless header before calling
|
||||
// this function. Returns false in case of error, with updated dec->status_.
|
||||
int VP8LDecodeImage(VP8LDecoder* const dec);
|
||||
WEBP_NODISCARD int VP8LDecodeImage(VP8LDecoder* const dec);
|
||||
|
||||
// Resets the decoder in its initial state, reclaiming memory.
|
||||
// Preserves the dec->status_ value.
|
||||
|
@ -126,6 +128,18 @@ void VP8LClear(VP8LDecoder* const dec);
|
|||
// Clears and deallocate a lossless decoder instance.
|
||||
void VP8LDelete(VP8LDecoder* const dec);
|
||||
|
||||
// Helper function for reading the different Huffman codes and storing them in
|
||||
// 'huffman_tables' and 'htree_groups'.
|
||||
// If mapping is NULL 'num_htree_groups_max' must equal 'num_htree_groups'.
|
||||
// If it is not NULL, it maps 'num_htree_groups_max' indices to the
|
||||
// 'num_htree_groups' groups. If 'num_htree_groups_max' > 'num_htree_groups',
|
||||
// some of those indices map to -1. This is used for non-balanced codes to
|
||||
// limit memory usage.
|
||||
WEBP_NODISCARD int ReadHuffmanCodesHelper(
|
||||
int color_cache_bits, int num_htree_groups, int num_htree_groups_max,
|
||||
const int* const mapping, VP8LDecoder* const dec,
|
||||
HuffmanTables* const huffman_tables, HTreeGroup** const htree_groups);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/mux_types.h" // ALPHA_FLAG
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RIFF layout is:
|
||||
|
@ -444,8 +447,9 @@ void WebPResetDecParams(WebPDecParams* const params) {
|
|||
// "Into" decoding variants
|
||||
|
||||
// Main flow
|
||||
static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
WebPDecParams* const params) {
|
||||
WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
size_t data_size,
|
||||
WebPDecParams* const params) {
|
||||
VP8StatusCode status;
|
||||
VP8Io io;
|
||||
WebPHeaderStructure headers;
|
||||
|
@ -459,7 +463,9 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
|||
}
|
||||
|
||||
assert(params != NULL);
|
||||
VP8InitIo(&io);
|
||||
if (!VP8InitIo(&io)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
io.data = headers.data + headers.offset;
|
||||
io.data_size = headers.data_size - headers.offset;
|
||||
WebPInitCustomIo(params, &io); // Plug the I/O functions.
|
||||
|
@ -523,17 +529,16 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
|||
}
|
||||
|
||||
// Helpers
|
||||
static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint8_t* const rgba,
|
||||
int stride, size_t size) {
|
||||
WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint8_t* const rgba,
|
||||
int stride, size_t size) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer buf;
|
||||
if (rgba == NULL) {
|
||||
if (rgba == NULL || !WebPInitDecBuffer(&buf)) {
|
||||
return NULL;
|
||||
}
|
||||
WebPInitDecBuffer(&buf);
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &buf;
|
||||
buf.colorspace = colorspace;
|
||||
|
@ -578,8 +583,7 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
|
|||
uint8_t* v, size_t v_size, int v_stride) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer output;
|
||||
if (luma == NULL) return NULL;
|
||||
WebPInitDecBuffer(&output);
|
||||
if (luma == NULL || !WebPInitDecBuffer(&output)) return NULL;
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &output;
|
||||
output.colorspace = MODE_YUV;
|
||||
|
@ -601,13 +605,17 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
|
||||
size_t data_size, int* const width, int* const height,
|
||||
WebPDecBuffer* const keep_info) {
|
||||
WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode,
|
||||
const uint8_t* const data,
|
||||
size_t data_size, int* const width,
|
||||
int* const height,
|
||||
WebPDecBuffer* const keep_info) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer output;
|
||||
|
||||
WebPInitDecBuffer(&output);
|
||||
if (!WebPInitDecBuffer(&output)) {
|
||||
return NULL;
|
||||
}
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &output;
|
||||
output.colorspace = mode;
|
||||
|
@ -733,7 +741,9 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
|
|||
}
|
||||
memset(config, 0, sizeof(*config));
|
||||
DefaultFeatures(&config->input);
|
||||
WebPInitDecBuffer(&config->output);
|
||||
if (!WebPInitDecBuffer(&config->output)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -772,7 +782,9 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
|||
if (WebPAvoidSlowMemory(params.output, &config->input)) {
|
||||
// decoding to slow memory: use a temporary in-mem buffer to decode into.
|
||||
WebPDecBuffer in_mem_buffer;
|
||||
WebPInitDecBuffer(&in_mem_buffer);
|
||||
if (!WebPInitDecBuffer(&in_mem_buffer)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
in_mem_buffer.colorspace = config->output.colorspace;
|
||||
in_mem_buffer.width = config->input.width;
|
||||
in_mem_buffer.height = config->input.height;
|
||||
|
|
|
@ -20,6 +20,7 @@ extern "C" {
|
|||
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/webp/decode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams: Decoding output parameters. Transient internal object.
|
||||
|
@ -87,8 +88,9 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
|
|||
|
||||
// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
|
||||
// to the *compressed* format, not the output one.
|
||||
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
VP8Io* const io, WEBP_CSP_MODE src_colorspace);
|
||||
WEBP_NODISCARD int WebPIoInitFromOptions(
|
||||
const WebPDecoderOptions* const options, VP8Io* const io,
|
||||
WEBP_CSP_MODE src_colorspace);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Internal functions regarding WebPDecBuffer memory (in buffer.c).
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#define NUM_CHANNELS 4
|
||||
|
||||
|
@ -68,8 +69,9 @@ int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
|
||||
WebPAnimDecoder* const dec) {
|
||||
WEBP_NODISCARD static int ApplyDecoderOptions(
|
||||
const WebPAnimDecoderOptions* const dec_options,
|
||||
WebPAnimDecoder* const dec) {
|
||||
WEBP_CSP_MODE mode;
|
||||
WebPDecoderConfig* config = &dec->config_;
|
||||
assert(dec_options != NULL);
|
||||
|
@ -82,7 +84,9 @@ static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
|
|||
dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
|
||||
? &BlendPixelRowNonPremult
|
||||
: &BlendPixelRowPremult;
|
||||
WebPInitDecoderConfig(config);
|
||||
if (!WebPInitDecoderConfig(config)) {
|
||||
return 0;
|
||||
}
|
||||
config->output.colorspace = mode;
|
||||
config->output.is_external_memory = 1;
|
||||
config->options.use_threads = dec_options->use_threads;
|
||||
|
@ -157,8 +161,8 @@ static int IsFullFrame(int width, int height, int canvas_width,
|
|||
}
|
||||
|
||||
// Clear the canvas to transparent.
|
||||
static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
WEBP_NODISCARD 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 (!CheckSizeOverflow(size)) return 0;
|
||||
|
@ -179,8 +183,8 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
|
|||
}
|
||||
|
||||
// Copy width * height pixels from 'src' to 'dst'.
|
||||
static int CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||
uint32_t width, uint32_t height) {
|
||||
WEBP_NODISCARD 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 (!CheckSizeOverflow(size)) return 0;
|
||||
assert(src != NULL && dst != NULL);
|
||||
|
@ -424,7 +428,9 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
|||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
dec->prev_iter_ = iter;
|
||||
dec->prev_frame_was_keyframe_ = is_key_frame;
|
||||
CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
|
||||
if (!CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
|
||||
dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "src/webp/format_constants.h"
|
||||
|
||||
#define DMUX_MAJ_VERSION 1
|
||||
#define DMUX_MIN_VERSION 3
|
||||
#define DMUX_REV_VERSION 2
|
||||
#define DMUX_MIN_VERSION 4
|
||||
#define DMUX_REV_VERSION 0
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
|
|
|
@ -144,6 +144,46 @@ static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
|
|||
return (alpha_and == 0xff);
|
||||
}
|
||||
|
||||
static void ExtractGreen_SSE2(const uint32_t* WEBP_RESTRICT argb,
|
||||
uint8_t* WEBP_RESTRICT alpha, int size) {
|
||||
int i;
|
||||
const __m128i mask = _mm_set1_epi32(0xff);
|
||||
const __m128i* src = (const __m128i*)argb;
|
||||
|
||||
for (i = 0; i + 16 <= size; i += 16, src += 4) {
|
||||
const __m128i a0 = _mm_loadu_si128(src + 0);
|
||||
const __m128i a1 = _mm_loadu_si128(src + 1);
|
||||
const __m128i a2 = _mm_loadu_si128(src + 2);
|
||||
const __m128i a3 = _mm_loadu_si128(src + 3);
|
||||
const __m128i b0 = _mm_srli_epi32(a0, 8);
|
||||
const __m128i b1 = _mm_srli_epi32(a1, 8);
|
||||
const __m128i b2 = _mm_srli_epi32(a2, 8);
|
||||
const __m128i b3 = _mm_srli_epi32(a3, 8);
|
||||
const __m128i c0 = _mm_and_si128(b0, mask);
|
||||
const __m128i c1 = _mm_and_si128(b1, mask);
|
||||
const __m128i c2 = _mm_and_si128(b2, mask);
|
||||
const __m128i c3 = _mm_and_si128(b3, mask);
|
||||
const __m128i d0 = _mm_packs_epi32(c0, c1);
|
||||
const __m128i d1 = _mm_packs_epi32(c2, c3);
|
||||
const __m128i e = _mm_packus_epi16(d0, d1);
|
||||
// store
|
||||
_mm_storeu_si128((__m128i*)&alpha[i], e);
|
||||
}
|
||||
if (i + 8 <= size) {
|
||||
const __m128i a0 = _mm_loadu_si128(src + 0);
|
||||
const __m128i a1 = _mm_loadu_si128(src + 1);
|
||||
const __m128i b0 = _mm_srli_epi32(a0, 8);
|
||||
const __m128i b1 = _mm_srli_epi32(a1, 8);
|
||||
const __m128i c0 = _mm_and_si128(b0, mask);
|
||||
const __m128i c1 = _mm_and_si128(b1, mask);
|
||||
const __m128i d = _mm_packs_epi32(c0, c1);
|
||||
const __m128i e = _mm_packus_epi16(d, d);
|
||||
_mm_storel_epi64((__m128i*)&alpha[i], e);
|
||||
i += 8;
|
||||
}
|
||||
for (; i < size; ++i) alpha[i] = argb[i] >> 8;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Non-dither premultiplied modes
|
||||
|
||||
|
@ -354,6 +394,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {
|
|||
WebPDispatchAlpha = DispatchAlpha_SSE2;
|
||||
WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2;
|
||||
WebPExtractAlpha = ExtractAlpha_SSE2;
|
||||
WebPExtractGreen = ExtractGreen_SSE2;
|
||||
|
||||
WebPHasAlpha8b = HasAlpha8b_SSE2;
|
||||
WebPHasAlpha32b = HasAlpha32b_SSE2;
|
||||
|
|
|
@ -36,18 +36,6 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
|||
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
|
||||
: "a"(info_type), "c"(0));
|
||||
}
|
||||
#elif defined(__x86_64__) && \
|
||||
(defined(__code_model_medium__) || defined(__code_model_large__)) && \
|
||||
defined(__PIC__)
|
||||
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile (
|
||||
"xchg{q}\t{%%rbx}, %q1\n"
|
||||
"cpuid\n"
|
||||
"xchg{q}\t{%%rbx}, %q1\n"
|
||||
: "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]),
|
||||
"=d"(cpu_info[3])
|
||||
: "a"(info_type), "c"(0));
|
||||
}
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile (
|
||||
|
|
|
@ -37,9 +37,6 @@ static WEBP_INLINE uint8_t clip_8b(int v) {
|
|||
STORE(3, y, DC - (d)); \
|
||||
} while (0)
|
||||
|
||||
#define MUL1(a) ((((a) * 20091) >> 16) + (a))
|
||||
#define MUL2(a) (((a) * 35468) >> 16)
|
||||
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
static void TransformOne_C(const int16_t* in, uint8_t* dst) {
|
||||
int C[4 * 4], *tmp;
|
||||
|
@ -48,8 +45,10 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) {
|
|||
for (i = 0; i < 4; ++i) { // vertical pass
|
||||
const int a = in[0] + in[8]; // [-4096, 4094]
|
||||
const int b = in[0] - in[8]; // [-4095, 4095]
|
||||
const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783]
|
||||
const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781]
|
||||
const int c = WEBP_TRANSFORM_AC3_MUL2(in[4]) -
|
||||
WEBP_TRANSFORM_AC3_MUL1(in[12]); // [-3783, 3783]
|
||||
const int d = WEBP_TRANSFORM_AC3_MUL1(in[4]) +
|
||||
WEBP_TRANSFORM_AC3_MUL2(in[12]); // [-3785, 3781]
|
||||
tmp[0] = a + d; // [-7881, 7875]
|
||||
tmp[1] = b + c; // [-7878, 7878]
|
||||
tmp[2] = b - c; // [-7878, 7878]
|
||||
|
@ -69,8 +68,10 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) {
|
|||
const int dc = tmp[0] + 4;
|
||||
const int a = dc + tmp[8];
|
||||
const int b = dc - tmp[8];
|
||||
const int c = MUL2(tmp[4]) - MUL1(tmp[12]);
|
||||
const int d = MUL1(tmp[4]) + MUL2(tmp[12]);
|
||||
const int c =
|
||||
WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]);
|
||||
const int d =
|
||||
WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]);
|
||||
STORE(0, 0, a + d);
|
||||
STORE(1, 0, b + c);
|
||||
STORE(2, 0, b - c);
|
||||
|
@ -83,17 +84,15 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) {
|
|||
// Simplified transform when only in[0], in[1] and in[4] are non-zero
|
||||
static void TransformAC3_C(const int16_t* in, uint8_t* dst) {
|
||||
const int a = in[0] + 4;
|
||||
const int c4 = MUL2(in[4]);
|
||||
const int d4 = MUL1(in[4]);
|
||||
const int c1 = MUL2(in[1]);
|
||||
const int d1 = MUL1(in[1]);
|
||||
const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
|
||||
const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
|
||||
const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
|
||||
const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
|
||||
STORE2(0, a + d4, d1, c1);
|
||||
STORE2(1, a + c4, d1, c1);
|
||||
STORE2(2, a - c4, d1, c1);
|
||||
STORE2(3, a - d4, d1, c1);
|
||||
}
|
||||
#undef MUL1
|
||||
#undef MUL2
|
||||
#undef STORE2
|
||||
|
||||
static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) {
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
#include "src/dsp/mips_macro.h"
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
static const int kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int kC2 = WEBP_TRANSFORM_AC3_C2;
|
||||
|
||||
static WEBP_INLINE int abs_mips32(int x) {
|
||||
const int sign = x >> 31;
|
||||
|
@ -219,7 +219,7 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
|||
int temp0, temp1, temp2, temp3, temp4;
|
||||
int temp5, temp6, temp7, temp8, temp9;
|
||||
int temp10, temp11, temp12, temp13, temp14;
|
||||
int temp15, temp16, temp17, temp18;
|
||||
int temp15, temp16, temp17, temp18, temp19;
|
||||
int16_t* p_in = (int16_t*)in;
|
||||
|
||||
// loops unrolled and merged to avoid usage of tmp buffer
|
||||
|
@ -233,16 +233,14 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
|||
"addu %[temp16], %[temp0], %[temp8] \n\t"
|
||||
"subu %[temp0], %[temp0], %[temp8] \n\t"
|
||||
"mul %[temp8], %[temp4], %[kC2] \n\t"
|
||||
"mul %[temp17], %[temp12], %[kC1] \n\t"
|
||||
"mul %[temp4], %[temp4], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp17, temp12)
|
||||
MUL_SHIFT_C1_IO(temp4, temp19)
|
||||
"mul %[temp12], %[temp12], %[kC2] \n\t"
|
||||
"lh %[temp1], 2(%[in]) \n\t"
|
||||
"lh %[temp5], 10(%[in]) \n\t"
|
||||
"lh %[temp9], 18(%[in]) \n\t"
|
||||
"lh %[temp13], 26(%[in]) \n\t"
|
||||
"sra %[temp8], %[temp8], 16 \n\t"
|
||||
"sra %[temp17], %[temp17], 16 \n\t"
|
||||
"sra %[temp4], %[temp4], 16 \n\t"
|
||||
"sra %[temp12], %[temp12], 16 \n\t"
|
||||
"lh %[temp2], 4(%[in]) \n\t"
|
||||
"lh %[temp6], 12(%[in]) \n\t"
|
||||
|
@ -261,49 +259,43 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
|||
"addu %[temp12], %[temp0], %[temp17] \n\t"
|
||||
"subu %[temp0], %[temp0], %[temp17] \n\t"
|
||||
"mul %[temp9], %[temp5], %[kC2] \n\t"
|
||||
"mul %[temp17], %[temp13], %[kC1] \n\t"
|
||||
"mul %[temp5], %[temp5], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp17, temp13)
|
||||
MUL_SHIFT_C1_IO(temp5, temp19)
|
||||
"mul %[temp13], %[temp13], %[kC2] \n\t"
|
||||
"sra %[temp9], %[temp9], 16 \n\t"
|
||||
"sra %[temp17], %[temp17], 16 \n\t"
|
||||
"subu %[temp17], %[temp9], %[temp17] \n\t"
|
||||
"sra %[temp5], %[temp5], 16 \n\t"
|
||||
"sra %[temp13], %[temp13], 16 \n\t"
|
||||
"addu %[temp5], %[temp5], %[temp13] \n\t"
|
||||
"addu %[temp13], %[temp1], %[temp17] \n\t"
|
||||
"subu %[temp1], %[temp1], %[temp17] \n\t"
|
||||
"mul %[temp17], %[temp14], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp17, temp14)
|
||||
"mul %[temp14], %[temp14], %[kC2] \n\t"
|
||||
"addu %[temp9], %[temp16], %[temp5] \n\t"
|
||||
"subu %[temp5], %[temp16], %[temp5] \n\t"
|
||||
"addu %[temp16], %[temp2], %[temp10] \n\t"
|
||||
"subu %[temp2], %[temp2], %[temp10] \n\t"
|
||||
"mul %[temp10], %[temp6], %[kC2] \n\t"
|
||||
"mul %[temp6], %[temp6], %[kC1] \n\t"
|
||||
"sra %[temp17], %[temp17], 16 \n\t"
|
||||
MUL_SHIFT_C1_IO(temp6, temp19)
|
||||
"sra %[temp14], %[temp14], 16 \n\t"
|
||||
"sra %[temp10], %[temp10], 16 \n\t"
|
||||
"sra %[temp6], %[temp6], 16 \n\t"
|
||||
"subu %[temp17], %[temp10], %[temp17] \n\t"
|
||||
"addu %[temp6], %[temp6], %[temp14] \n\t"
|
||||
"addu %[temp10], %[temp16], %[temp6] \n\t"
|
||||
"subu %[temp6], %[temp16], %[temp6] \n\t"
|
||||
"addu %[temp14], %[temp2], %[temp17] \n\t"
|
||||
"subu %[temp2], %[temp2], %[temp17] \n\t"
|
||||
"mul %[temp17], %[temp15], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp17, temp15)
|
||||
"mul %[temp15], %[temp15], %[kC2] \n\t"
|
||||
"addu %[temp16], %[temp3], %[temp11] \n\t"
|
||||
"subu %[temp3], %[temp3], %[temp11] \n\t"
|
||||
"mul %[temp11], %[temp7], %[kC2] \n\t"
|
||||
"mul %[temp7], %[temp7], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1_IO(temp7, temp19)
|
||||
"addiu %[temp8], %[temp8], 4 \n\t"
|
||||
"addiu %[temp12], %[temp12], 4 \n\t"
|
||||
"addiu %[temp0], %[temp0], 4 \n\t"
|
||||
"addiu %[temp4], %[temp4], 4 \n\t"
|
||||
"sra %[temp17], %[temp17], 16 \n\t"
|
||||
"sra %[temp15], %[temp15], 16 \n\t"
|
||||
"sra %[temp11], %[temp11], 16 \n\t"
|
||||
"sra %[temp7], %[temp7], 16 \n\t"
|
||||
"subu %[temp17], %[temp11], %[temp17] \n\t"
|
||||
"addu %[temp7], %[temp7], %[temp15] \n\t"
|
||||
"addu %[temp15], %[temp3], %[temp17] \n\t"
|
||||
|
@ -313,48 +305,40 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
|||
"addu %[temp16], %[temp8], %[temp10] \n\t"
|
||||
"subu %[temp8], %[temp8], %[temp10] \n\t"
|
||||
"mul %[temp10], %[temp9], %[kC2] \n\t"
|
||||
"mul %[temp17], %[temp11], %[kC1] \n\t"
|
||||
"mul %[temp9], %[temp9], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp17, temp11)
|
||||
MUL_SHIFT_C1_IO(temp9, temp19)
|
||||
"mul %[temp11], %[temp11], %[kC2] \n\t"
|
||||
"sra %[temp10], %[temp10], 16 \n\t"
|
||||
"sra %[temp17], %[temp17], 16 \n\t"
|
||||
"sra %[temp9], %[temp9], 16 \n\t"
|
||||
"sra %[temp11], %[temp11], 16 \n\t"
|
||||
"subu %[temp17], %[temp10], %[temp17] \n\t"
|
||||
"addu %[temp11], %[temp9], %[temp11] \n\t"
|
||||
"addu %[temp10], %[temp12], %[temp14] \n\t"
|
||||
"subu %[temp12], %[temp12], %[temp14] \n\t"
|
||||
"mul %[temp14], %[temp13], %[kC2] \n\t"
|
||||
"mul %[temp9], %[temp15], %[kC1] \n\t"
|
||||
"mul %[temp13], %[temp13], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp9, temp15)
|
||||
MUL_SHIFT_C1_IO(temp13, temp19)
|
||||
"mul %[temp15], %[temp15], %[kC2] \n\t"
|
||||
"sra %[temp14], %[temp14], 16 \n\t"
|
||||
"sra %[temp9], %[temp9], 16 \n\t"
|
||||
"sra %[temp13], %[temp13], 16 \n\t"
|
||||
"sra %[temp15], %[temp15], 16 \n\t"
|
||||
"subu %[temp9], %[temp14], %[temp9] \n\t"
|
||||
"addu %[temp15], %[temp13], %[temp15] \n\t"
|
||||
"addu %[temp14], %[temp0], %[temp2] \n\t"
|
||||
"subu %[temp0], %[temp0], %[temp2] \n\t"
|
||||
"mul %[temp2], %[temp1], %[kC2] \n\t"
|
||||
"mul %[temp13], %[temp3], %[kC1] \n\t"
|
||||
"mul %[temp1], %[temp1], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp13, temp3)
|
||||
MUL_SHIFT_C1_IO(temp1, temp19)
|
||||
"mul %[temp3], %[temp3], %[kC2] \n\t"
|
||||
"sra %[temp2], %[temp2], 16 \n\t"
|
||||
"sra %[temp13], %[temp13], 16 \n\t"
|
||||
"sra %[temp1], %[temp1], 16 \n\t"
|
||||
"sra %[temp3], %[temp3], 16 \n\t"
|
||||
"subu %[temp13], %[temp2], %[temp13] \n\t"
|
||||
"addu %[temp3], %[temp1], %[temp3] \n\t"
|
||||
"addu %[temp2], %[temp4], %[temp6] \n\t"
|
||||
"subu %[temp4], %[temp4], %[temp6] \n\t"
|
||||
"mul %[temp6], %[temp5], %[kC2] \n\t"
|
||||
"mul %[temp1], %[temp7], %[kC1] \n\t"
|
||||
"mul %[temp5], %[temp5], %[kC1] \n\t"
|
||||
MUL_SHIFT_C1(temp1, temp7)
|
||||
MUL_SHIFT_C1_IO(temp5, temp19)
|
||||
"mul %[temp7], %[temp7], %[kC2] \n\t"
|
||||
"sra %[temp6], %[temp6], 16 \n\t"
|
||||
"sra %[temp1], %[temp1], 16 \n\t"
|
||||
"sra %[temp5], %[temp5], 16 \n\t"
|
||||
"sra %[temp7], %[temp7], 16 \n\t"
|
||||
"subu %[temp1], %[temp6], %[temp1] \n\t"
|
||||
"addu %[temp7], %[temp5], %[temp7] \n\t"
|
||||
|
@ -542,7 +526,7 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
|||
[temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11),
|
||||
[temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14),
|
||||
[temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17),
|
||||
[temp18]"=&r"(temp18)
|
||||
[temp18]"=&r"(temp18), [temp19]"=&r"(temp19)
|
||||
: [in]"r"(p_in), [kC1]"r"(kC1), [kC2]"r"(kC2), [dst]"r"(dst)
|
||||
: "memory", "hi", "lo"
|
||||
);
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
|
||||
#include "src/dsp/mips_macro.h"
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
static const int kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int kC2 = WEBP_TRANSFORM_AC3_C2;
|
||||
|
||||
static void TransformDC(const int16_t* in, uint8_t* dst) {
|
||||
int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10;
|
||||
|
@ -49,10 +47,10 @@ static void TransformDC(const int16_t* in, uint8_t* dst) {
|
|||
|
||||
static void TransformAC3(const int16_t* in, uint8_t* dst) {
|
||||
const int a = in[0] + 4;
|
||||
int c4 = MUL(in[4], kC2);
|
||||
const int d4 = MUL(in[4], kC1);
|
||||
const int c1 = MUL(in[1], kC2);
|
||||
const int d1 = MUL(in[1], kC1);
|
||||
int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
|
||||
const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
|
||||
const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
|
||||
const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
|
||||
int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
|
||||
int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18;
|
||||
|
||||
|
@ -479,8 +477,6 @@ static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
|
|||
FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
#undef MUL
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Simple In-loop filtering (Paragraph 15.2)
|
||||
|
||||
|
|
|
@ -37,8 +37,6 @@
|
|||
d1_m = d_tmp1_m + d_tmp2_m; \
|
||||
BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \
|
||||
}
|
||||
#define MULT1(a) ((((a) * 20091) >> 16) + (a))
|
||||
#define MULT2(a) (((a) * 35468) >> 16)
|
||||
|
||||
static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
v8i16 input0, input1;
|
||||
|
@ -124,10 +122,10 @@ static void TransformDC(const int16_t* in, uint8_t* dst) {
|
|||
|
||||
static void TransformAC3(const int16_t* in, uint8_t* dst) {
|
||||
const int a = in[0] + 4;
|
||||
const int c4 = MULT2(in[4]);
|
||||
const int d4 = MULT1(in[4]);
|
||||
const int in2 = MULT2(in[1]);
|
||||
const int in3 = MULT1(in[1]);
|
||||
const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
|
||||
const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
|
||||
const int in2 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
|
||||
const int in3 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
|
||||
v4i32 tmp0 = { 0 };
|
||||
v4i32 out0 = __msa_fill_w(a + d4);
|
||||
v4i32 out1 = __msa_fill_w(a + c4);
|
||||
|
|
|
@ -1000,8 +1000,9 @@ static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride,
|
|||
// libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
|
||||
// same issue with kC1 and vqdmulh that we work around by down shifting kC2
|
||||
|
||||
static const int16_t kC1 = 20091;
|
||||
static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
|
||||
static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int16_t kC2 =
|
||||
WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above.
|
||||
|
||||
#if defined(WEBP_USE_INTRINSICS)
|
||||
static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0,
|
||||
|
@ -1255,15 +1256,12 @@ static void TransformWHT_NEON(const int16_t* in, int16_t* out) {
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) {
|
||||
static const int kC1_full = 20091 + (1 << 16);
|
||||
static const int kC2_full = 35468;
|
||||
const int16x4_t A = vld1_dup_s16(in);
|
||||
const int16x4_t c4 = vdup_n_s16(MUL(in[4], kC2_full));
|
||||
const int16x4_t d4 = vdup_n_s16(MUL(in[4], kC1_full));
|
||||
const int c1 = MUL(in[1], kC2_full);
|
||||
const int d1 = MUL(in[1], kC1_full);
|
||||
const int16x4_t c4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL2(in[4]));
|
||||
const int16x4_t d4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL1(in[4]));
|
||||
const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
|
||||
const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
|
||||
const uint64_t cd = (uint64_t)( d1 & 0xffff) << 0 |
|
||||
(uint64_t)( c1 & 0xffff) << 16 |
|
||||
(uint64_t)(-c1 & 0xffff) << 32 |
|
||||
|
@ -1274,7 +1272,6 @@ static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) {
|
|||
const int16x8_t m2_m3 = vcombine_s16(vqsub_s16(B, c4), vqsub_s16(B, d4));
|
||||
Add4x4_NEON(m0_m1, m2_m3, dst);
|
||||
}
|
||||
#undef MUL
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 4x4
|
||||
|
|
|
@ -196,15 +196,13 @@ static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) {
|
|||
}
|
||||
|
||||
#if (USE_TRANSFORM_AC3 == 1)
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
|
||||
static void TransformAC3(const int16_t* in, uint8_t* dst) {
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
const __m128i A = _mm_set1_epi16(in[0] + 4);
|
||||
const __m128i c4 = _mm_set1_epi16(MUL(in[4], kC2));
|
||||
const __m128i d4 = _mm_set1_epi16(MUL(in[4], kC1));
|
||||
const int c1 = MUL(in[1], kC2);
|
||||
const int d1 = MUL(in[1], kC1);
|
||||
const __m128i c4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL2(in[4]));
|
||||
const __m128i d4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL1(in[4]));
|
||||
const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
|
||||
const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
|
||||
const __m128i CD = _mm_set_epi16(0, 0, 0, 0, -d1, -c1, c1, d1);
|
||||
const __m128i B = _mm_adds_epi16(A, CD);
|
||||
const __m128i m0 = _mm_adds_epi16(B, d4);
|
||||
|
@ -238,7 +236,7 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) {
|
|||
WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
|
||||
WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
|
||||
}
|
||||
#undef MUL
|
||||
|
||||
#endif // USE_TRANSFORM_AC3
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -259,15 +257,15 @@ static WEBP_INLINE void SignedShift8b_SSE2(__m128i* const x) {
|
|||
*x = _mm_packs_epi16(lo_1, hi_1);
|
||||
}
|
||||
|
||||
#define FLIP_SIGN_BIT2(a, b) { \
|
||||
#define FLIP_SIGN_BIT2(a, b) do { \
|
||||
(a) = _mm_xor_si128(a, sign_bit); \
|
||||
(b) = _mm_xor_si128(b, sign_bit); \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#define FLIP_SIGN_BIT4(a, b, c, d) { \
|
||||
#define FLIP_SIGN_BIT4(a, b, c, d) do { \
|
||||
FLIP_SIGN_BIT2(a, b); \
|
||||
FLIP_SIGN_BIT2(c, d); \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
// input/output is uint8_t
|
||||
static WEBP_INLINE void GetNotHEV_SSE2(const __m128i* const p1,
|
||||
|
@ -645,12 +643,12 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
|
|||
(m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \
|
||||
#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) do { \
|
||||
(e1) = _mm_loadu_si128((__m128i*)&(p)[0 * (stride)]); \
|
||||
(e2) = _mm_loadu_si128((__m128i*)&(p)[1 * (stride)]); \
|
||||
(e3) = _mm_loadu_si128((__m128i*)&(p)[2 * (stride)]); \
|
||||
(e4) = _mm_loadu_si128((__m128i*)&(p)[3 * (stride)]); \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#define LOADUV_H_EDGE(p, u, v, stride) do { \
|
||||
const __m128i U = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \
|
||||
|
@ -658,18 +656,18 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
|
|||
(p) = _mm_unpacklo_epi64(U, V); \
|
||||
} while (0)
|
||||
|
||||
#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \
|
||||
#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) do { \
|
||||
LOADUV_H_EDGE(e1, u, v, 0 * (stride)); \
|
||||
LOADUV_H_EDGE(e2, u, v, 1 * (stride)); \
|
||||
LOADUV_H_EDGE(e3, u, v, 2 * (stride)); \
|
||||
LOADUV_H_EDGE(e4, u, v, 3 * (stride)); \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#define STOREUV(p, u, v, stride) { \
|
||||
#define STOREUV(p, u, v, stride) do { \
|
||||
_mm_storel_epi64((__m128i*)&(u)[(stride)], p); \
|
||||
(p) = _mm_srli_si128(p, 8); \
|
||||
_mm_storel_epi64((__m128i*)&(v)[(stride)], p); \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
static WEBP_INLINE void ComplexMask_SSE2(const __m128i* const p1,
|
||||
const __m128i* const p0,
|
||||
|
|
|
@ -203,6 +203,11 @@ extern VP8DecIdct VP8TransformDC;
|
|||
extern VP8DecIdct VP8TransformDCUV;
|
||||
extern VP8WHT VP8TransformWHT;
|
||||
|
||||
#define WEBP_TRANSFORM_AC3_C1 20091
|
||||
#define WEBP_TRANSFORM_AC3_C2 35468
|
||||
#define WEBP_TRANSFORM_AC3_MUL1(a) ((((a) * WEBP_TRANSFORM_AC3_C1) >> 16) + (a))
|
||||
#define WEBP_TRANSFORM_AC3_MUL2(a) (((a) * WEBP_TRANSFORM_AC3_C2) >> 16)
|
||||
|
||||
// *dst is the destination block, with stride BPS. Boundary samples are
|
||||
// assumed accessible when needed.
|
||||
typedef void (*VP8PredFunc)(uint8_t* dst);
|
||||
|
|
|
@ -109,10 +109,6 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) {
|
|||
#define STORE(x, y, v) \
|
||||
dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3))
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
|
||||
static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
|
||||
uint8_t* dst) {
|
||||
int C[4 * 4], *tmp;
|
||||
|
@ -121,8 +117,10 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
|
|||
for (i = 0; i < 4; ++i) { // vertical pass
|
||||
const int a = in[0] + in[8];
|
||||
const int b = in[0] - in[8];
|
||||
const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
|
||||
const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
|
||||
const int c =
|
||||
WEBP_TRANSFORM_AC3_MUL2(in[4]) - WEBP_TRANSFORM_AC3_MUL1(in[12]);
|
||||
const int d =
|
||||
WEBP_TRANSFORM_AC3_MUL1(in[4]) + WEBP_TRANSFORM_AC3_MUL2(in[12]);
|
||||
tmp[0] = a + d;
|
||||
tmp[1] = b + c;
|
||||
tmp[2] = b - c;
|
||||
|
@ -134,10 +132,12 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
|
|||
tmp = C;
|
||||
for (i = 0; i < 4; ++i) { // horizontal pass
|
||||
const int dc = tmp[0] + 4;
|
||||
const int a = dc + tmp[8];
|
||||
const int b = dc - tmp[8];
|
||||
const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
|
||||
const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
|
||||
const int a = dc + tmp[8];
|
||||
const int b = dc - tmp[8];
|
||||
const int c =
|
||||
WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]);
|
||||
const int d =
|
||||
WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]);
|
||||
STORE(0, i, a + d);
|
||||
STORE(1, i, b + c);
|
||||
STORE(2, i, b - c);
|
||||
|
@ -222,7 +222,6 @@ static void FTransformWHT_C(const int16_t* in, int16_t* out) {
|
|||
}
|
||||
#endif // !WEBP_NEON_OMIT_C_CODE
|
||||
|
||||
#undef MUL
|
||||
#undef STORE
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include "src/enc/vp8i_enc.h"
|
||||
#include "src/enc/cost_enc.h"
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
static const int kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int kC2 = WEBP_TRANSFORM_AC3_C2;
|
||||
|
||||
// macro for one vertical pass in ITransformOne
|
||||
// MUL macro inlined
|
||||
|
@ -30,7 +30,7 @@ static const int kC2 = 35468;
|
|||
// A..D - offsets in bytes to load from in buffer
|
||||
// TEMP0..TEMP3 - registers for corresponding tmp elements
|
||||
// TEMP4..TEMP5 - temporary registers
|
||||
#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \
|
||||
#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \
|
||||
"lh %[temp16], " #A "(%[temp20]) \n\t" \
|
||||
"lh %[temp18], " #B "(%[temp20]) \n\t" \
|
||||
"lh %[temp17], " #C "(%[temp20]) \n\t" \
|
||||
|
@ -38,12 +38,10 @@ static const int kC2 = 35468;
|
|||
"addu %[" #TEMP4 "], %[temp16], %[temp18] \n\t" \
|
||||
"subu %[temp16], %[temp16], %[temp18] \n\t" \
|
||||
"mul %[" #TEMP0 "], %[temp17], %[kC2] \n\t" \
|
||||
"mul %[temp18], %[temp19], %[kC1] \n\t" \
|
||||
"mul %[temp17], %[temp17], %[kC1] \n\t" \
|
||||
MUL_SHIFT_C1_IO(temp17, temp18) \
|
||||
MUL_SHIFT_C1(temp18, temp19) \
|
||||
"mul %[temp19], %[temp19], %[kC2] \n\t" \
|
||||
"sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\n" \
|
||||
"sra %[temp18], %[temp18], 16 \n\n" \
|
||||
"sra %[temp17], %[temp17], 16 \n\n" \
|
||||
"sra %[temp19], %[temp19], 16 \n\n" \
|
||||
"subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp18] \n\t" \
|
||||
"addu %[" #TEMP3 "], %[temp17], %[temp19] \n\t" \
|
||||
|
@ -58,17 +56,15 @@ static const int kC2 = 35468;
|
|||
// temp0..temp15 holds tmp[0]..tmp[15]
|
||||
// A - offset in bytes to load from ref and store to dst buffer
|
||||
// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements
|
||||
#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \
|
||||
#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \
|
||||
"addiu %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \
|
||||
"addu %[temp16], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
|
||||
"subu %[temp17], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
|
||||
"mul %[" #TEMP0 "], %[" #TEMP4 "], %[kC2] \n\t" \
|
||||
"mul %[" #TEMP8 "], %[" #TEMP12 "], %[kC1] \n\t" \
|
||||
"mul %[" #TEMP4 "], %[" #TEMP4 "], %[kC1] \n\t" \
|
||||
MUL_SHIFT_C1_IO(TEMP4, TEMP8) \
|
||||
MUL_SHIFT_C1(TEMP8, TEMP12) \
|
||||
"mul %[" #TEMP12 "], %[" #TEMP12 "], %[kC2] \n\t" \
|
||||
"sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \
|
||||
"sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \
|
||||
"sra %[" #TEMP4 "], %[" #TEMP4 "], 16 \n\t" \
|
||||
"sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \
|
||||
"subu %[temp18], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
|
||||
"addu %[temp19], %[" #TEMP4 "], %[" #TEMP12 "] \n\t" \
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
#include "src/enc/cost_enc.h"
|
||||
#include "src/enc/vp8i_enc.h"
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
static const int kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int kC2 = WEBP_TRANSFORM_AC3_C2;
|
||||
|
||||
// O - output
|
||||
// I - input (macro doesn't change it)
|
||||
|
|
|
@ -27,8 +27,9 @@
|
|||
// This code is pretty much the same as TransformOne in the dec_neon.c, except
|
||||
// for subtraction to *ref. See the comments there for algorithmic explanations.
|
||||
|
||||
static const int16_t kC1 = 20091;
|
||||
static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
|
||||
static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1;
|
||||
static const int16_t kC2 =
|
||||
WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above.
|
||||
|
||||
// This code works but is *slower* than the inlined-asm version below
|
||||
// (with gcc-4.6). So we disable it for now. Later, it'll be conditional to
|
||||
|
|
|
@ -19,14 +19,16 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// Helpful macro.
|
||||
|
||||
# define SANITY_CHECK(in, out) \
|
||||
assert((in) != NULL); \
|
||||
assert((out) != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; // Silence unused warning.
|
||||
#define DCHECK(in, out) \
|
||||
do { \
|
||||
assert((in) != NULL); \
|
||||
assert((out) != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; /* Silence unused warning. */ \
|
||||
} while (0)
|
||||
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred,
|
||||
|
@ -49,7 +51,7 @@ static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = inverse ? out : in;
|
||||
|
@ -86,7 +88,7 @@ static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = inverse ? out : in;
|
||||
|
@ -131,7 +133,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = inverse ? out : in;
|
||||
|
@ -165,7 +167,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
|
|||
}
|
||||
#endif // !WEBP_NEON_OMIT_C_CODE
|
||||
|
||||
#undef SANITY_CHECK
|
||||
#undef DCHECK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -189,6 +191,12 @@ static void GradientFilter_C(const uint8_t* data, int width, int height,
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void NoneUnfilter_C(const uint8_t* prev, const uint8_t* in,
|
||||
uint8_t* out, int width) {
|
||||
(void)prev;
|
||||
if (out != in) memcpy(out, in, width * sizeof(*out));
|
||||
}
|
||||
|
||||
static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
|
||||
uint8_t* out, int width) {
|
||||
uint8_t pred = (prev == NULL) ? 0 : prev[0];
|
||||
|
@ -240,7 +248,7 @@ extern void VP8FiltersInitNEON(void);
|
|||
extern void VP8FiltersInitSSE2(void);
|
||||
|
||||
WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
|
||||
WebPUnfilters[WEBP_FILTER_NONE] = NULL;
|
||||
WebPUnfilters[WEBP_FILTER_NONE] = NoneUnfilter_C;
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C;
|
||||
WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C;
|
||||
|
@ -279,6 +287,7 @@ WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
|
|||
}
|
||||
#endif
|
||||
|
||||
assert(WebPUnfilters[WEBP_FILTER_NONE] != NULL);
|
||||
assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL);
|
||||
assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL);
|
||||
assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL);
|
||||
|
|
|
@ -24,14 +24,16 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// Helpful macro.
|
||||
|
||||
# define SANITY_CHECK(in, out) \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; // Silence unused warning.
|
||||
#define DCHECK(in, out) \
|
||||
do { \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; /* Silence unused warning. */ \
|
||||
} while (0)
|
||||
|
||||
#define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \
|
||||
const uint8_t* psrc = (uint8_t*)(SRC); \
|
||||
|
@ -200,7 +202,7 @@ static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = in;
|
||||
|
@ -248,7 +250,7 @@ static WEBP_INLINE void DoVerticalFilter_MIPSdspR2(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = in;
|
||||
|
@ -316,7 +318,7 @@ static void DoGradientFilter_MIPSdspR2(const uint8_t* in,
|
|||
const uint8_t* preds;
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
preds = in;
|
||||
|
@ -378,7 +380,7 @@ static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
|
|||
#undef DO_PREDICT_LINE_VERTICAL
|
||||
#undef PREDICT_LINE_ONE_PASS
|
||||
#undef DO_PREDICT_LINE
|
||||
#undef SANITY_CHECK
|
||||
#undef DCHECK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
|
|
@ -56,12 +56,14 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src,
|
|||
//------------------------------------------------------------------------------
|
||||
// Helpful macro.
|
||||
|
||||
#define SANITY_CHECK(in, out) \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width);
|
||||
#define DCHECK(in, out) \
|
||||
do { \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
} while (0)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Horrizontal filter
|
||||
|
@ -72,7 +74,7 @@ static void HorizontalFilter_MSA(const uint8_t* data, int width, int height,
|
|||
const uint8_t* in = data;
|
||||
uint8_t* out = filtered_data;
|
||||
int row = 1;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
|
||||
// Leftmost pixel is the same as input for topmost scanline.
|
||||
out[0] = in[0];
|
||||
|
@ -135,7 +137,7 @@ static void GradientFilter_MSA(const uint8_t* data, int width, int height,
|
|||
const uint8_t* preds = data;
|
||||
uint8_t* out = filtered_data;
|
||||
int row = 1;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
|
||||
// left prediction for top scan-line
|
||||
out[0] = in[0];
|
||||
|
@ -163,7 +165,7 @@ static void VerticalFilter_MSA(const uint8_t* data, int width, int height,
|
|||
const uint8_t* preds = data;
|
||||
uint8_t* out = filtered_data;
|
||||
int row = 1;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
|
||||
// Very first top-left pixel is copied.
|
||||
out[0] = in[0];
|
||||
|
@ -182,7 +184,7 @@ static void VerticalFilter_MSA(const uint8_t* data, int width, int height,
|
|||
}
|
||||
}
|
||||
|
||||
#undef SANITY_CHECK
|
||||
#undef DCHECK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
|
|
@ -21,14 +21,16 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// Helpful macros.
|
||||
|
||||
# define SANITY_CHECK(in, out) \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; // Silence unused warning.
|
||||
#define DCHECK(in, out) \
|
||||
do { \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; /* Silence unused warning. */ \
|
||||
} while (0)
|
||||
|
||||
// load eight u8 and widen to s16
|
||||
#define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A))
|
||||
|
@ -71,7 +73,7 @@ static WEBP_INLINE void DoHorizontalFilter_NEON(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -110,7 +112,7 @@ static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -172,7 +174,7 @@ static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -201,7 +203,7 @@ static void GradientFilter_NEON(const uint8_t* data, int width, int height,
|
|||
filtered_data);
|
||||
}
|
||||
|
||||
#undef SANITY_CHECK
|
||||
#undef DCHECK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inverse transforms
|
||||
|
|
|
@ -23,14 +23,16 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// Helpful macro.
|
||||
|
||||
# define SANITY_CHECK(in, out) \
|
||||
assert((in) != NULL); \
|
||||
assert((out) != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; // Silence unused warning.
|
||||
#define DCHECK(in, out) \
|
||||
do { \
|
||||
assert((in) != NULL); \
|
||||
assert((out) != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(stride >= width); \
|
||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||
(void)height; /* Silence unused warning. */ \
|
||||
} while (0)
|
||||
|
||||
static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred,
|
||||
uint8_t* dst, int length) {
|
||||
|
@ -78,7 +80,7 @@ static WEBP_INLINE void DoHorizontalFilter_SSE2(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -111,7 +113,7 @@ static WEBP_INLINE void DoVerticalFilter_SSE2(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -174,7 +176,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in,
|
|||
uint8_t* out) {
|
||||
const size_t start_offset = row * stride;
|
||||
const int last_row = row + num_rows;
|
||||
SANITY_CHECK(in, out);
|
||||
DCHECK(in, out);
|
||||
in += start_offset;
|
||||
out += start_offset;
|
||||
|
||||
|
@ -197,7 +199,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in,
|
|||
}
|
||||
}
|
||||
|
||||
#undef SANITY_CHECK
|
||||
#undef DCHECK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -182,9 +182,9 @@ extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16];
|
|||
// -----------------------------------------------------------------------------
|
||||
// Huffman-cost related functions.
|
||||
|
||||
typedef float (*VP8LCostFunc)(const uint32_t* population, int length);
|
||||
typedef float (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
|
||||
int length);
|
||||
typedef uint32_t (*VP8LCostFunc)(const uint32_t* population, int length);
|
||||
typedef uint32_t (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
|
||||
int length);
|
||||
typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256],
|
||||
const int Y[256]);
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#ifndef WEBP_DSP_LOSSLESS_COMMON_H_
|
||||
#define WEBP_DSP_LOSSLESS_COMMON_H_
|
||||
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -166,7 +166,7 @@ uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
|
|||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Transform-related functions use din both encoding and decoding.
|
||||
// Transform-related functions used in both encoding and decoding.
|
||||
|
||||
// Macros used to create a batch predictor that iteratively uses a
|
||||
// one-pixel predictor.
|
||||
|
|
|
@ -636,20 +636,25 @@ void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits,
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static float ExtraCost_C(const uint32_t* population, int length) {
|
||||
static uint32_t ExtraCost_C(const uint32_t* population, int length) {
|
||||
int i;
|
||||
float cost = 0.f;
|
||||
for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
|
||||
uint32_t cost = population[4] + population[5];
|
||||
assert(length % 2 == 0);
|
||||
for (i = 2; i < length / 2 - 1; ++i) {
|
||||
cost += i * (population[2 * i + 2] + population[2 * i + 3]);
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
static float ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
|
||||
int length) {
|
||||
static uint32_t ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
|
||||
int length) {
|
||||
int i;
|
||||
float cost = 0.f;
|
||||
for (i = 2; i < length - 2; ++i) {
|
||||
const int xy = X[i + 2] + Y[i + 2];
|
||||
cost += (i >> 1) * xy;
|
||||
uint32_t cost = X[4] + Y[4] + X[5] + Y[5];
|
||||
assert(length % 2 == 0);
|
||||
for (i = 2; i < length / 2 - 1; ++i) {
|
||||
const int xy0 = X[2 * i + 2] + Y[2 * i + 2];
|
||||
const int xy1 = X[2 * i + 3] + Y[2 * i + 3];
|
||||
cost += i * (xy0 + xy1);
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
|
|
@ -103,8 +103,8 @@ static float FastLog2Slow_MIPS32(uint32_t v) {
|
|||
// cost += i * *(pop + 1);
|
||||
// pop += 2;
|
||||
// }
|
||||
// return (float)cost;
|
||||
static float ExtraCost_MIPS32(const uint32_t* const population, int length) {
|
||||
// return cost;
|
||||
static uint32_t ExtraCost_MIPS32(const uint32_t* const population, int length) {
|
||||
int i, temp0, temp1;
|
||||
const uint32_t* pop = &population[4];
|
||||
const uint32_t* const LoopEnd = &population[length];
|
||||
|
@ -130,7 +130,7 @@ static float ExtraCost_MIPS32(const uint32_t* const population, int length) {
|
|||
: "memory", "hi", "lo"
|
||||
);
|
||||
|
||||
return (float)((int64_t)temp0 << 32 | temp1);
|
||||
return ((int64_t)temp0 << 32 | temp1);
|
||||
}
|
||||
|
||||
// C version of this function:
|
||||
|
@ -148,9 +148,9 @@ static float ExtraCost_MIPS32(const uint32_t* const population, int length) {
|
|||
// pX += 2;
|
||||
// pY += 2;
|
||||
// }
|
||||
// return (float)cost;
|
||||
static float ExtraCostCombined_MIPS32(const uint32_t* const X,
|
||||
const uint32_t* const Y, int length) {
|
||||
// return cost;
|
||||
static uint32_t ExtraCostCombined_MIPS32(const uint32_t* const X,
|
||||
const uint32_t* const Y, int length) {
|
||||
int i, temp0, temp1, temp2, temp3;
|
||||
const uint32_t* pX = &X[4];
|
||||
const uint32_t* pY = &Y[4];
|
||||
|
@ -183,7 +183,7 @@ static float ExtraCostCombined_MIPS32(const uint32_t* const X,
|
|||
: "memory", "hi", "lo"
|
||||
);
|
||||
|
||||
return (float)((int64_t)temp0 << 32 | temp1);
|
||||
return ((int64_t)temp0 << 32 | temp1);
|
||||
}
|
||||
|
||||
#define HUFFMAN_COST_PASS \
|
||||
|
|
|
@ -18,8 +18,53 @@
|
|||
#include <smmintrin.h>
|
||||
#include "src/dsp/lossless.h"
|
||||
|
||||
// For sign-extended multiplying constants, pre-shifted by 5:
|
||||
#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5)
|
||||
//------------------------------------------------------------------------------
|
||||
// Cost operations.
|
||||
|
||||
static WEBP_INLINE uint32_t HorizontalSum_SSE41(__m128i cost) {
|
||||
cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 8));
|
||||
cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 4));
|
||||
return _mm_cvtsi128_si32(cost);
|
||||
}
|
||||
|
||||
static uint32_t ExtraCost_SSE41(const uint32_t* const a, int length) {
|
||||
int i;
|
||||
__m128i cost = _mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[4]);
|
||||
assert(length % 8 == 0);
|
||||
|
||||
for (i = 8; i + 8 <= length; i += 8) {
|
||||
const int j = (i - 2) >> 1;
|
||||
const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]);
|
||||
const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]);
|
||||
const __m128i w = _mm_set_epi32(j + 3, j + 2, j + 1, j);
|
||||
const __m128i a2 = _mm_hadd_epi32(a0, a1);
|
||||
const __m128i mul = _mm_mullo_epi32(a2, w);
|
||||
cost = _mm_add_epi32(mul, cost);
|
||||
}
|
||||
return HorizontalSum_SSE41(cost);
|
||||
}
|
||||
|
||||
static uint32_t ExtraCostCombined_SSE41(const uint32_t* const a,
|
||||
const uint32_t* const b, int length) {
|
||||
int i;
|
||||
__m128i cost = _mm_add_epi32(_mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[4]),
|
||||
_mm_set_epi32(2 * b[7], 2 * b[6], b[5], b[4]));
|
||||
assert(length % 8 == 0);
|
||||
|
||||
for (i = 8; i + 8 <= length; i += 8) {
|
||||
const int j = (i - 2) >> 1;
|
||||
const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]);
|
||||
const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]);
|
||||
const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i]);
|
||||
const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]);
|
||||
const __m128i w = _mm_set_epi32(j + 3, j + 2, j + 1, j);
|
||||
const __m128i a2 = _mm_hadd_epi32(a0, a1);
|
||||
const __m128i b2 = _mm_hadd_epi32(b0, b1);
|
||||
const __m128i mul = _mm_mullo_epi32(_mm_add_epi32(a2, b2), w);
|
||||
cost = _mm_add_epi32(mul, cost);
|
||||
}
|
||||
return HorizontalSum_SSE41(cost);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Subtract-Green Transform
|
||||
|
@ -44,6 +89,9 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data,
|
|||
//------------------------------------------------------------------------------
|
||||
// Color Transform
|
||||
|
||||
// For sign-extended multiplying constants, pre-shifted by 5:
|
||||
#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5)
|
||||
|
||||
#define MK_CST_16(HI, LO) \
|
||||
_mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
|
||||
|
||||
|
@ -143,6 +191,8 @@ static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
|
|||
extern void VP8LEncDspInitSSE41(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) {
|
||||
VP8LExtraCost = ExtraCost_SSE41;
|
||||
VP8LExtraCostCombined = ExtraCostCombined_SSE41;
|
||||
VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41;
|
||||
VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41;
|
||||
VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41;
|
||||
|
|
|
@ -146,9 +146,9 @@ static void ConvertBGRAToRGB_NEON(const uint32_t* src,
|
|||
#define LOAD_U32P_AS_U8(IN) vreinterpret_u8_u32(vld1_u32((IN)))
|
||||
#define LOADQ_U32_AS_U8(IN) vreinterpretq_u8_u32(vdupq_n_u32((IN)))
|
||||
#define LOADQ_U32P_AS_U8(IN) vreinterpretq_u8_u32(vld1q_u32((IN)))
|
||||
#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0);
|
||||
#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0);
|
||||
#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN)));
|
||||
#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0)
|
||||
#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0)
|
||||
#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN)))
|
||||
#define ROTATE32_LEFT(L) vextq_u8((L), (L), 12) // D|C|B|A -> C|B|A|D
|
||||
|
||||
static WEBP_INLINE uint8x8_t Average2_u8_NEON(uint32_t a0, uint32_t a1) {
|
||||
|
|
|
@ -45,28 +45,38 @@
|
|||
"ulw %[" #O2 "], " #I3 "+" XSTR(I9) "*" #I7 "(%[" #I0 "]) \n\t" \
|
||||
"ulw %[" #O3 "], " #I4 "+" XSTR(I9) "*" #I8 "(%[" #I0 "]) \n\t"
|
||||
|
||||
|
||||
// O - output
|
||||
// I - input (macro doesn't change it so it should be different from I)
|
||||
#define MUL_SHIFT_C1(O, I) \
|
||||
"mul %[" #O "], %[" #I "], %[kC1] \n\t" \
|
||||
"sra %[" #O "], %[" #O "], 16 \n\t" \
|
||||
"addu %[" #O "], %[" #O "], %[" #I "] \n\t"
|
||||
#define MUL_SHIFT_C2(O, I) \
|
||||
"mul %[" #O "], %[" #I "], %[kC2] \n\t" \
|
||||
"sra %[" #O "], %[" #O "], 16 \n\t"
|
||||
|
||||
// Same as #define MUL_SHIFT_C1 but I and O are the same. It stores the
|
||||
// intermediary result in TMP.
|
||||
#define MUL_SHIFT_C1_IO(IO, TMP) \
|
||||
"mul %[" #TMP "], %[" #IO "], %[kC1] \n\t" \
|
||||
"sra %[" #TMP "], %[" #TMP "], 16 \n\t" \
|
||||
"addu %[" #IO "], %[" #TMP "], %[" #IO "] \n\t"
|
||||
|
||||
// O - output
|
||||
// IO - input/output
|
||||
// I - input (macro doesn't change it)
|
||||
#define MUL_SHIFT_SUM(O0, O1, O2, O3, O4, O5, O6, O7, \
|
||||
IO0, IO1, IO2, IO3, \
|
||||
I0, I1, I2, I3, I4, I5, I6, I7) \
|
||||
"mul %[" #O0 "], %[" #I0 "], %[kC2] \n\t" \
|
||||
"mul %[" #O1 "], %[" #I0 "], %[kC1] \n\t" \
|
||||
"mul %[" #O2 "], %[" #I1 "], %[kC2] \n\t" \
|
||||
"mul %[" #O3 "], %[" #I1 "], %[kC1] \n\t" \
|
||||
"mul %[" #O4 "], %[" #I2 "], %[kC2] \n\t" \
|
||||
"mul %[" #O5 "], %[" #I2 "], %[kC1] \n\t" \
|
||||
"mul %[" #O6 "], %[" #I3 "], %[kC2] \n\t" \
|
||||
"mul %[" #O7 "], %[" #I3 "], %[kC1] \n\t" \
|
||||
"sra %[" #O0 "], %[" #O0 "], 16 \n\t" \
|
||||
"sra %[" #O1 "], %[" #O1 "], 16 \n\t" \
|
||||
"sra %[" #O2 "], %[" #O2 "], 16 \n\t" \
|
||||
"sra %[" #O3 "], %[" #O3 "], 16 \n\t" \
|
||||
"sra %[" #O4 "], %[" #O4 "], 16 \n\t" \
|
||||
"sra %[" #O5 "], %[" #O5 "], 16 \n\t" \
|
||||
"sra %[" #O6 "], %[" #O6 "], 16 \n\t" \
|
||||
"sra %[" #O7 "], %[" #O7 "], 16 \n\t" \
|
||||
MUL_SHIFT_C2(O0, I0) \
|
||||
MUL_SHIFT_C1(O1, I0) \
|
||||
MUL_SHIFT_C2(O2, I1) \
|
||||
MUL_SHIFT_C1(O3, I1) \
|
||||
MUL_SHIFT_C2(O4, I2) \
|
||||
MUL_SHIFT_C1(O5, I2) \
|
||||
MUL_SHIFT_C2(O6, I3) \
|
||||
MUL_SHIFT_C1(O7, I3) \
|
||||
"addu %[" #IO0 "], %[" #IO0 "], %[" #I4 "] \n\t" \
|
||||
"addu %[" #IO1 "], %[" #IO1 "], %[" #I5 "] \n\t" \
|
||||
"subu %[" #IO2 "], %[" #IO2 "], %[" #I6 "] \n\t" \
|
||||
|
|
|
@ -73,27 +73,25 @@
|
|||
#define ST_UW(...) ST_W(v4u32, __VA_ARGS__)
|
||||
#define ST_SW(...) ST_W(v4i32, __VA_ARGS__)
|
||||
|
||||
#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \
|
||||
static inline TYPE FUNC_NAME(const void* const psrc) { \
|
||||
const uint8_t* const psrc_m = (const uint8_t*)psrc; \
|
||||
TYPE val_m; \
|
||||
asm volatile ( \
|
||||
"" #INSTR " %[val_m], %[psrc_m] \n\t" \
|
||||
: [val_m] "=r" (val_m) \
|
||||
: [psrc_m] "m" (*psrc_m)); \
|
||||
return val_m; \
|
||||
#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \
|
||||
static inline TYPE FUNC_NAME(const void* const psrc) { \
|
||||
const uint8_t* const psrc_m = (const uint8_t*)psrc; \
|
||||
TYPE val_m; \
|
||||
__asm__ volatile("" #INSTR " %[val_m], %[psrc_m] \n\t" \
|
||||
: [val_m] "=r"(val_m) \
|
||||
: [psrc_m] "m"(*psrc_m)); \
|
||||
return val_m; \
|
||||
}
|
||||
|
||||
#define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc)
|
||||
|
||||
#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \
|
||||
static inline void FUNC_NAME(TYPE val, void* const pdst) { \
|
||||
uint8_t* const pdst_m = (uint8_t*)pdst; \
|
||||
TYPE val_m = val; \
|
||||
asm volatile ( \
|
||||
" " #INSTR " %[val_m], %[pdst_m] \n\t" \
|
||||
: [pdst_m] "=m" (*pdst_m) \
|
||||
: [val_m] "r" (val_m)); \
|
||||
#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \
|
||||
static inline void FUNC_NAME(TYPE val, void* const pdst) { \
|
||||
uint8_t* const pdst_m = (uint8_t*)pdst; \
|
||||
TYPE val_m = val; \
|
||||
__asm__ volatile(" " #INSTR " %[val_m], %[pdst_m] \n\t" \
|
||||
: [pdst_m] "=m"(*pdst_m) \
|
||||
: [val_m] "r"(val_m)); \
|
||||
}
|
||||
|
||||
#define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst)
|
||||
|
|
|
@ -36,8 +36,9 @@ static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
|
|||
int thresh) {
|
||||
const int16x8_t tst_ones = vdupq_n_s16(-1);
|
||||
uint32x4_t sum = vdupq_n_u32(0);
|
||||
int i;
|
||||
|
||||
for (int i = 0; i < num_blocks; ++i) {
|
||||
for (i = 0; i < num_blocks; ++i) {
|
||||
// Set DC to zero.
|
||||
const int16x8_t a_0 = vsetq_lane_s16(0, vld1q_s16(levels), 0);
|
||||
const int16x8_t a_1 = vld1q_s16(levels + 8);
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define STORE_32x8(SRC0, SRC1, DST) do { \
|
||||
vst1q_u32((DST) + 0, SRC0); \
|
||||
vst1q_u32((DST) + 4, SRC1); \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
#if (WEBP_RESCALER_RFIX == 32)
|
||||
#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
} while (0)
|
||||
|
||||
// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
|
||||
#define UPSAMPLE_32PIXELS(r1, r2, out) { \
|
||||
#define UPSAMPLE_32PIXELS(r1, r2, out) do { \
|
||||
const __m128i one = _mm_set1_epi8(1); \
|
||||
const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
|
||||
const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
|
||||
|
@ -85,7 +85,7 @@
|
|||
/* pack the alternate pixels */ \
|
||||
PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
|
||||
PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
// Turn the macro into a function for reducing code-size when non-critical
|
||||
static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[],
|
||||
|
@ -229,11 +229,11 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
|||
} \
|
||||
}
|
||||
|
||||
YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4);
|
||||
YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4);
|
||||
YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4)
|
||||
YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4)
|
||||
#if !defined(WEBP_REDUCE_CSP)
|
||||
YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3);
|
||||
YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3);
|
||||
YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3)
|
||||
YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3)
|
||||
YUV444_FUNC(Yuv444ToArgb_SSE2, VP8YuvToArgb32_SSE2, WebPYuv444ToArgb_C, 4)
|
||||
YUV444_FUNC(Yuv444ToRgba4444_SSE2, VP8YuvToRgba444432_SSE2, \
|
||||
WebPYuv444ToRgba4444_C, 2)
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
} while (0)
|
||||
|
||||
// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
|
||||
#define UPSAMPLE_32PIXELS(r1, r2, out) { \
|
||||
#define UPSAMPLE_32PIXELS(r1, r2, out) do { \
|
||||
const __m128i one = _mm_set1_epi8(1); \
|
||||
const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
|
||||
const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
|
||||
|
@ -87,7 +87,7 @@
|
|||
/* pack the alternate pixels */ \
|
||||
PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
|
||||
PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
// Turn the macro into a function for reducing code-size when non-critical
|
||||
static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[],
|
||||
|
@ -217,8 +217,8 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
|||
}
|
||||
|
||||
#if !defined(WEBP_REDUCE_CSP)
|
||||
YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3);
|
||||
YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3);
|
||||
YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3)
|
||||
YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3)
|
||||
#endif // WEBP_REDUCE_CSP
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "src/utils/filters_utils.h"
|
||||
#include "src/utils/quant_levels_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -55,7 +56,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
|
|||
WebPConfig config;
|
||||
WebPPicture picture;
|
||||
|
||||
WebPPictureInit(&picture);
|
||||
if (!WebPPictureInit(&picture)) return 0;
|
||||
picture.width = width;
|
||||
picture.height = height;
|
||||
picture.use_argb = 1;
|
||||
|
@ -66,7 +67,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
|
|||
WebPDispatchAlphaToGreen(data, width, picture.width, picture.height,
|
||||
picture.argb, picture.argb_stride);
|
||||
|
||||
WebPConfigInit(&config);
|
||||
if (!WebPConfigInit(&config)) return 0;
|
||||
config.lossless = 1;
|
||||
// Enable exact, or it would alter RGB values of transparent alpha, which is
|
||||
// normally OK but not here since we are not encoding the input image but an
|
||||
|
@ -83,11 +84,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
|
|||
(use_quality_100 && effort_level == 6) ? 100 : 8.f * effort_level;
|
||||
assert(config.quality >= 0 && config.quality <= 100.f);
|
||||
|
||||
// TODO(urvang): Temporary fix to avoid generating images that trigger
|
||||
// a decoder bug related to alpha with color cache.
|
||||
// See: https://code.google.com/p/webp/issues/detail?id=239
|
||||
// Need to re-enable this later.
|
||||
ok = VP8LEncodeStream(&config, &picture, bw, /*use_cache=*/0);
|
||||
ok = VP8LEncodeStream(&config, &picture, bw);
|
||||
WebPPictureFree(&picture);
|
||||
ok = ok && !bw->error_;
|
||||
if (!ok) {
|
||||
|
|
|
@ -578,7 +578,7 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
|
|||
uint64_t size = 0;
|
||||
uint64_t size_p0 = 0;
|
||||
uint64_t distortion = 0;
|
||||
const uint64_t pixel_count = nb_mbs * 384;
|
||||
const uint64_t pixel_count = (uint64_t)nb_mbs * 384;
|
||||
|
||||
VP8IteratorInit(enc, &it);
|
||||
SetLoopParams(enc, s->q);
|
||||
|
@ -789,7 +789,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
|
|||
VP8EncIterator it;
|
||||
VP8EncProba* const proba = &enc->proba_;
|
||||
const VP8RDLevel rd_opt = enc->rd_opt_level_;
|
||||
const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384;
|
||||
const uint64_t pixel_count = (uint64_t)enc->mb_w_ * enc->mb_h_ * 384;
|
||||
PassStats stats;
|
||||
int ok;
|
||||
|
||||
|
|
|
@ -358,15 +358,17 @@ static WEBP_INLINE float GetCombinedEntropy(const uint32_t* const X,
|
|||
|
||||
// Estimates the Entropy + Huffman + other block overhead size cost.
|
||||
float VP8LHistogramEstimateBits(VP8LHistogram* const p) {
|
||||
return
|
||||
PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_),
|
||||
NULL, &p->is_used_[0])
|
||||
+ PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1])
|
||||
+ PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2])
|
||||
+ PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3])
|
||||
+ PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL, &p->is_used_[4])
|
||||
+ VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES)
|
||||
+ VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
||||
return PopulationCost(p->literal_,
|
||||
VP8LHistogramNumCodes(p->palette_code_bits_), NULL,
|
||||
&p->is_used_[0]) +
|
||||
PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1]) +
|
||||
PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2]) +
|
||||
PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3]) +
|
||||
PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL,
|
||||
&p->is_used_[4]) +
|
||||
(float)VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES,
|
||||
NUM_LENGTH_CODES) +
|
||||
(float)VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -381,9 +383,9 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a,
|
|||
*cost += GetCombinedEntropy(a->literal_, b->literal_,
|
||||
VP8LHistogramNumCodes(palette_code_bits),
|
||||
a->is_used_[0], b->is_used_[0], 0);
|
||||
*cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES,
|
||||
b->literal_ + NUM_LITERAL_CODES,
|
||||
NUM_LENGTH_CODES);
|
||||
*cost += (float)VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES,
|
||||
b->literal_ + NUM_LITERAL_CODES,
|
||||
NUM_LENGTH_CODES);
|
||||
if (*cost > cost_threshold) return 0;
|
||||
|
||||
if (a->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM &&
|
||||
|
@ -417,8 +419,8 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a,
|
|||
*cost +=
|
||||
GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES,
|
||||
a->is_used_[4], b->is_used_[4], 0);
|
||||
*cost +=
|
||||
VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES);
|
||||
*cost += (float)VP8LExtraCostCombined(a->distance_, b->distance_,
|
||||
NUM_DISTANCE_CODES);
|
||||
if (*cost > cost_threshold) return 0;
|
||||
|
||||
return 1;
|
||||
|
@ -506,11 +508,11 @@ static void UpdateHistogramCost(VP8LHistogram* const h) {
|
|||
PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym, &h->is_used_[3]);
|
||||
const float distance_cost =
|
||||
PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) +
|
||||
VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
|
||||
(float)VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
|
||||
const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
|
||||
h->literal_cost_ =
|
||||
PopulationCost(h->literal_, num_codes, NULL, &h->is_used_[0]) +
|
||||
VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES);
|
||||
(float)VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES);
|
||||
h->red_cost_ =
|
||||
PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym, &h->is_used_[1]);
|
||||
h->blue_cost_ =
|
||||
|
@ -1179,7 +1181,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
|||
const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
|
||||
int entropy_combine;
|
||||
uint16_t* const map_tmp =
|
||||
WebPSafeMalloc(2 * image_histo_raw_size, sizeof(map_tmp));
|
||||
WebPSafeMalloc(2 * image_histo_raw_size, sizeof(*map_tmp));
|
||||
uint16_t* const cluster_mappings = map_tmp + image_histo_raw_size;
|
||||
int num_used = image_histo_raw_size;
|
||||
if (orig_histo == NULL || map_tmp == NULL) {
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/enc/vp8i_enc.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -31,8 +31,8 @@ extern "C" {
|
|||
|
||||
// version numbers
|
||||
#define ENC_MAJ_VERSION 1
|
||||
#define ENC_MIN_VERSION 3
|
||||
#define ENC_REV_VERSION 2
|
||||
#define ENC_MIN_VERSION 4
|
||||
#define ENC_REV_VERSION 0
|
||||
|
||||
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "src/enc/vp8li_enc.h"
|
||||
#include "src/utils/bit_writer_utils.h"
|
||||
#include "src/utils/huffman_encode_utils.h"
|
||||
#include "src/utils/palette.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
|
@ -30,298 +31,6 @@
|
|||
// Maximum number of histogram images (sub-blocks).
|
||||
#define MAX_HUFF_IMAGE_SIZE 2600
|
||||
|
||||
// Palette reordering for smaller sum of deltas (and for smaller storage).
|
||||
|
||||
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
|
||||
const uint32_t a = WebPMemToUint32((uint8_t*)p1);
|
||||
const uint32_t b = WebPMemToUint32((uint8_t*)p2);
|
||||
assert(a != b);
|
||||
return (a < b) ? -1 : 1;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
|
||||
return (v <= 128) ? v : (256 - v);
|
||||
}
|
||||
|
||||
// Computes a value that is related to the entropy created by the
|
||||
// palette entry diff.
|
||||
//
|
||||
// Note that the last & 0xff is a no-operation in the next statement, but
|
||||
// removed by most compilers and is here only for regularity of the code.
|
||||
static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
|
||||
const uint32_t diff = VP8LSubPixels(col1, col2);
|
||||
const int kMoreWeightForRGBThanForAlpha = 9;
|
||||
uint32_t score;
|
||||
score = PaletteComponentDistance((diff >> 0) & 0xff);
|
||||
score += PaletteComponentDistance((diff >> 8) & 0xff);
|
||||
score += PaletteComponentDistance((diff >> 16) & 0xff);
|
||||
score *= kMoreWeightForRGBThanForAlpha;
|
||||
score += PaletteComponentDistance((diff >> 24) & 0xff);
|
||||
return score;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
|
||||
const uint32_t tmp = *col1;
|
||||
*col1 = *col2;
|
||||
*col2 = tmp;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The palette has been sorted by alpha. This function checks if the other
|
||||
// components of the palette have a monotonic development with regards to
|
||||
// position in the palette. If all have monotonic development, there is
|
||||
// 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(const uint32_t* const palette,
|
||||
int num_colors) {
|
||||
uint32_t predict = 0x000000;
|
||||
int i;
|
||||
uint8_t sign_found = 0x00;
|
||||
for (i = 0; i < num_colors; ++i) {
|
||||
const uint32_t diff = VP8LSubPixels(palette[i], predict);
|
||||
const uint8_t rd = (diff >> 16) & 0xff;
|
||||
const uint8_t gd = (diff >> 8) & 0xff;
|
||||
const uint8_t bd = (diff >> 0) & 0xff;
|
||||
if (rd != 0x00) {
|
||||
sign_found |= (rd < 0x80) ? 1 : 2;
|
||||
}
|
||||
if (gd != 0x00) {
|
||||
sign_found |= (gd < 0x80) ? 8 : 16;
|
||||
}
|
||||
if (bd != 0x00) {
|
||||
sign_found |= (bd < 0x80) ? 64 : 128;
|
||||
}
|
||||
predict = palette[i];
|
||||
}
|
||||
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 int 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 WebPEncodingSetError(pic, 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 1;
|
||||
}
|
||||
|
||||
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 int 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 1;
|
||||
// Build the co-occurrence matrix.
|
||||
cooccurrence =
|
||||
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
|
||||
if (cooccurrence == NULL) {
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) {
|
||||
WebPSafeFree(cooccurrence);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Palette
|
||||
|
||||
|
@ -336,13 +45,6 @@ typedef enum {
|
|||
kNumEntropyIx = 6
|
||||
} EntropyIx;
|
||||
|
||||
typedef enum {
|
||||
kSortedDefault = 0,
|
||||
kMinimizeDelta = 1,
|
||||
kModifiedZeng = 2,
|
||||
kUnusedPalette = 3,
|
||||
} PaletteSorting;
|
||||
|
||||
typedef enum {
|
||||
kHistoAlpha = 0,
|
||||
kHistoAlphaPred,
|
||||
|
@ -565,7 +267,7 @@ typedef struct {
|
|||
|
||||
// +2 because we add a palette sorting configuration for kPalette and
|
||||
// kPaletteAndSpatial.
|
||||
#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2)
|
||||
#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2 * kPaletteSortingNum)
|
||||
|
||||
static int EncoderAnalyze(VP8LEncoder* const enc,
|
||||
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
|
||||
|
@ -586,13 +288,10 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
|
|||
assert(pic != NULL && pic->argb != NULL);
|
||||
|
||||
// Check whether a palette is possible.
|
||||
enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_);
|
||||
enc->palette_size_ = GetColorPalette(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.
|
||||
|
@ -625,20 +324,29 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
|
|||
// a palette.
|
||||
if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) {
|
||||
assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
|
||||
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.
|
||||
int sorting_method;
|
||||
for (sorting_method = 0; sorting_method < kPaletteSortingNum;
|
||||
++sorting_method) {
|
||||
const PaletteSorting typed_sorting_method =
|
||||
(PaletteSorting)sorting_method;
|
||||
// TODO(vrabaud) kSortedDefault should be tested. It is omitted
|
||||
// for now for backward compatibility.
|
||||
if (typed_sorting_method == kUnusedPalette ||
|
||||
typed_sorting_method == kSortedDefault) {
|
||||
continue;
|
||||
}
|
||||
crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
|
||||
crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
|
||||
typed_sorting_method;
|
||||
++*crunch_configs_size;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
++*crunch_configs_size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1112,10 +820,10 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
|||
static int EncodeImageInternal(
|
||||
VP8LBitWriter* const bw, const uint32_t* const argb,
|
||||
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,
|
||||
const WebPPicture* const pic, int percent_range, int* const percent) {
|
||||
int height, int quality, int low_effort, const CrunchConfig* const config,
|
||||
int* cache_bits, int histogram_bits, size_t init_byte_position,
|
||||
int* const hdr_size, int* const data_size, const WebPPicture* const pic,
|
||||
int percent_range, int* const percent) {
|
||||
const uint32_t histogram_image_xysize =
|
||||
VP8LSubSampleSize(width, histogram_bits) *
|
||||
VP8LSubSampleSize(height, histogram_bits);
|
||||
|
@ -1163,13 +871,9 @@ static int EncodeImageInternal(
|
|||
percent_start += percent_range;
|
||||
remaining_percent -= percent_range;
|
||||
|
||||
if (use_cache) {
|
||||
// If the value is different from zero, it has been set during the
|
||||
// palette analysis.
|
||||
cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
|
||||
} else {
|
||||
cache_bits_init = 0;
|
||||
}
|
||||
// If the value is different from zero, it has been set during the palette
|
||||
// analysis.
|
||||
cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
|
||||
// If several iterations will happen, clone into bw_best.
|
||||
if ((config->sub_configs_size_ > 1 || config->sub_configs_[0].do_no_cache_) &&
|
||||
!VP8LBitWriterClone(bw, &bw_best)) {
|
||||
|
@ -1485,7 +1189,7 @@ static void ClearTransformBuffer(VP8LEncoder* const enc) {
|
|||
// enc->use_predict_, enc->use_cross_color_
|
||||
static int AllocateTransformBuffer(VP8LEncoder* const enc, int width,
|
||||
int height) {
|
||||
const uint64_t image_size = width * height;
|
||||
const uint64_t image_size = (uint64_t)width * height;
|
||||
// VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
|
||||
// pixel in each, plus 2 regular scanlines of bytes.
|
||||
// TODO(skal): Clean up by using arithmetic in bytes instead of words.
|
||||
|
@ -1495,7 +1199,7 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width,
|
|||
: 0;
|
||||
const uint64_t transform_data_size =
|
||||
(enc->use_predict_ || enc->use_cross_color_)
|
||||
? VP8LSubSampleSize(width, enc->transform_bits_) *
|
||||
? (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) *
|
||||
VP8LSubSampleSize(height, enc->transform_bits_)
|
||||
: 0;
|
||||
const uint64_t max_alignment_in_words =
|
||||
|
@ -1758,7 +1462,6 @@ typedef struct {
|
|||
const WebPPicture* picture_;
|
||||
VP8LBitWriter* bw_;
|
||||
VP8LEncoder* enc_;
|
||||
int use_cache_;
|
||||
CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX];
|
||||
int num_crunch_configs_;
|
||||
int red_and_blue_always_zero_;
|
||||
|
@ -1771,7 +1474,6 @@ static int EncodeStreamHook(void* input, void* data2) {
|
|||
const WebPPicture* const picture = params->picture_;
|
||||
VP8LBitWriter* const bw = params->bw_;
|
||||
VP8LEncoder* const enc = params->enc_;
|
||||
const int use_cache = params->use_cache_;
|
||||
const CrunchConfig* const crunch_configs = params->crunch_configs_;
|
||||
const int num_crunch_configs = params->num_crunch_configs_;
|
||||
const int red_and_blue_always_zero = params->red_and_blue_always_zero_;
|
||||
|
@ -1845,19 +1547,11 @@ 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);
|
||||
if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
|
||||
enc->palette_size_, enc->palette_)) {
|
||||
goto Error;
|
||||
}
|
||||
if (!PaletteSort(crunch_configs[idx].palette_sorting_type_, enc->pic_,
|
||||
enc->palette_sorted_, enc->palette_size_,
|
||||
enc->palette_)) {
|
||||
WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
goto Error;
|
||||
}
|
||||
percent_range = remaining_percent / 4;
|
||||
if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) {
|
||||
|
@ -1867,7 +1561,7 @@ static int EncodeStreamHook(void* input, void* data2) {
|
|||
if (!MapImageFromPalette(enc, use_delta_palette)) goto Error;
|
||||
// If using a color cache, do not have it bigger than the number of
|
||||
// colors.
|
||||
if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
|
||||
if (enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
|
||||
enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
|
||||
}
|
||||
}
|
||||
|
@ -1911,7 +1605,7 @@ static int EncodeStreamHook(void* input, void* data2) {
|
|||
// Encode and write the transformed image.
|
||||
if (!EncodeImageInternal(
|
||||
bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_,
|
||||
height, quality, low_effort, use_cache, &crunch_configs[idx],
|
||||
height, quality, low_effort, &crunch_configs[idx],
|
||||
&enc->cache_bits_, enc->histo_bits_, byte_position, &hdr_size,
|
||||
&data_size, picture, remaining_percent, &percent)) {
|
||||
goto Error;
|
||||
|
@ -1953,7 +1647,7 @@ static int EncodeStreamHook(void* input, void* data2) {
|
|||
|
||||
int VP8LEncodeStream(const WebPConfig* const config,
|
||||
const WebPPicture* const picture,
|
||||
VP8LBitWriter* const bw_main, int use_cache) {
|
||||
VP8LBitWriter* const bw_main) {
|
||||
VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture);
|
||||
VP8LEncoder* enc_side = NULL;
|
||||
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
|
||||
|
@ -1975,7 +1669,9 @@ int VP8LEncodeStream(const WebPConfig* const config,
|
|||
}
|
||||
|
||||
// Avoid "garbage value" error from Clang's static analysis tool.
|
||||
WebPPictureInit(&picture_side);
|
||||
if (!WebPPictureInit(&picture_side)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Analyze image (entropy, num_palettes etc)
|
||||
if (!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
|
||||
|
@ -2010,7 +1706,6 @@ int VP8LEncodeStream(const WebPConfig* const config,
|
|||
StreamEncodeContext* const param =
|
||||
(idx == 0) ? ¶ms_main : ¶ms_side;
|
||||
param->config_ = config;
|
||||
param->use_cache_ = use_cache;
|
||||
param->red_and_blue_always_zero_ = red_and_blue_always_zero;
|
||||
if (idx == 0) {
|
||||
param->picture_ = picture;
|
||||
|
@ -2164,7 +1859,7 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||
if (!WebPReportProgress(picture, 2, &percent)) goto UserAbort;
|
||||
|
||||
// Encode main image stream.
|
||||
if (!VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/)) goto Error;
|
||||
if (!VP8LEncodeStream(config, picture, &bw)) goto Error;
|
||||
|
||||
if (!WebPReportProgress(picture, 99, &percent)) goto UserAbort;
|
||||
|
||||
|
|
|
@ -88,11 +88,9 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||
const WebPPicture* const picture);
|
||||
|
||||
// Encodes the main image stream using the supplied bit writer.
|
||||
// If 'use_cache' is false, disables the use of color cache.
|
||||
// Returns false in case of error (stored in picture->error_code).
|
||||
int VP8LEncodeStream(const WebPConfig* const config,
|
||||
const WebPPicture* const picture, VP8LBitWriter* const bw,
|
||||
int use_cache);
|
||||
const WebPPicture* const picture, VP8LBitWriter* const bw);
|
||||
|
||||
#if (WEBP_NEAR_LOSSLESS == 1)
|
||||
// in near_lossless.c
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "src/webp/encode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/mux.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
|
@ -593,16 +594,17 @@ int WebPAnimEncoderRefineRect(
|
|||
int is_lossless, float quality, int* const x_offset, int* const y_offset,
|
||||
int* const width, int* const height) {
|
||||
FrameRectangle rect;
|
||||
const int right = clip(*x_offset + *width, 0, curr_canvas->width);
|
||||
const int left = clip(*x_offset, 0, curr_canvas->width - 1);
|
||||
const int bottom = clip(*y_offset + *height, 0, curr_canvas->height);
|
||||
const int top = clip(*y_offset, 0, curr_canvas->height - 1);
|
||||
int right, left, bottom, top;
|
||||
if (prev_canvas == NULL || curr_canvas == NULL ||
|
||||
prev_canvas->width != curr_canvas->width ||
|
||||
prev_canvas->height != curr_canvas->height ||
|
||||
!prev_canvas->use_argb || !curr_canvas->use_argb) {
|
||||
return 0;
|
||||
}
|
||||
right = clip(*x_offset + *width, 0, curr_canvas->width);
|
||||
left = clip(*x_offset, 0, curr_canvas->width - 1);
|
||||
bottom = clip(*y_offset + *height, 0, curr_canvas->height);
|
||||
top = clip(*y_offset, 0, curr_canvas->height - 1);
|
||||
rect.x_offset_ = left;
|
||||
rect.y_offset_ = top;
|
||||
rect.width_ = clip(right - left, 0, curr_canvas->width - rect.x_offset_);
|
||||
|
@ -1397,7 +1399,10 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
|
|||
}
|
||||
config = *encoder_config;
|
||||
} else {
|
||||
WebPConfigInit(&config);
|
||||
if (!WebPConfigInit(&config)) {
|
||||
MarkError(enc, "Cannot Init config");
|
||||
return 0;
|
||||
}
|
||||
config.lossless = 1;
|
||||
}
|
||||
assert(enc->curr_canvas_ == NULL);
|
||||
|
@ -1418,12 +1423,14 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
|
|||
// -----------------------------------------------------------------------------
|
||||
// Bitstream assembly.
|
||||
|
||||
static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame,
|
||||
WebPPicture* const canvas) {
|
||||
WEBP_NODISCARD static int DecodeFrameOntoCanvas(
|
||||
const WebPMuxFrameInfo* const frame, WebPPicture* const canvas) {
|
||||
const WebPData* const image = &frame->bitstream;
|
||||
WebPPicture sub_image;
|
||||
WebPDecoderConfig config;
|
||||
WebPInitDecoderConfig(&config);
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
return 0;
|
||||
}
|
||||
WebPUtilClearPic(canvas, NULL);
|
||||
if (WebPGetFeatures(image->bytes, image->size, &config.input) !=
|
||||
VP8_STATUS_OK) {
|
||||
|
@ -1582,4 +1589,23 @@ const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc) {
|
|||
return enc->error_str_;
|
||||
}
|
||||
|
||||
WebPMuxError WebPAnimEncoderSetChunk(
|
||||
WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data,
|
||||
int copy_data) {
|
||||
if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return WebPMuxSetChunk(enc->mux_, fourcc, chunk_data, copy_data);
|
||||
}
|
||||
|
||||
WebPMuxError WebPAnimEncoderGetChunk(
|
||||
const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data) {
|
||||
if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return WebPMuxGetChunk(enc->mux_, fourcc, chunk_data);
|
||||
}
|
||||
|
||||
WebPMuxError WebPAnimEncoderDeleteChunk(
|
||||
WebPAnimEncoder* enc, const char fourcc[4]) {
|
||||
if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return WebPMuxDeleteChunk(enc->mux_, fourcc);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -66,14 +66,16 @@ void WebPMuxDelete(WebPMux* mux) {
|
|||
|
||||
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
|
||||
#define SWITCH_ID_LIST(INDEX, LIST) \
|
||||
if (idx == (INDEX)) { \
|
||||
err = ChunkAssignData(&chunk, data, copy_data, tag); \
|
||||
if (err == WEBP_MUX_OK) { \
|
||||
err = ChunkSetHead(&chunk, (LIST)); \
|
||||
if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \
|
||||
do { \
|
||||
if (idx == (INDEX)) { \
|
||||
err = ChunkAssignData(&chunk, data, copy_data, tag); \
|
||||
if (err == WEBP_MUX_OK) { \
|
||||
err = ChunkSetHead(&chunk, (LIST)); \
|
||||
if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \
|
||||
} \
|
||||
return err; \
|
||||
} \
|
||||
return err; \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag,
|
||||
const WebPData* const data, int copy_data) {
|
||||
|
@ -555,7 +557,8 @@ static WebPMuxError MuxCleanup(WebPMux* const mux) {
|
|||
if (num_frames == 1) {
|
||||
WebPMuxImage* frame = NULL;
|
||||
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame);
|
||||
assert(err == WEBP_MUX_OK); // We know that one frame does exist.
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
// We know that one frame does exist.
|
||||
assert(frame != NULL);
|
||||
if (frame->header_ != NULL &&
|
||||
((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) ||
|
||||
|
|
|
@ -28,8 +28,8 @@ extern "C" {
|
|||
// Defines and constants.
|
||||
|
||||
#define MUX_MAJ_VERSION 1
|
||||
#define MUX_MIN_VERSION 3
|
||||
#define MUX_REV_VERSION 2
|
||||
#define MUX_MIN_VERSION 4
|
||||
#define MUX_REV_VERSION 0
|
||||
|
||||
// Chunk object.
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
|
|
|
@ -21,20 +21,23 @@
|
|||
|
||||
// Handy MACRO.
|
||||
#define SWITCH_ID_LIST(INDEX, LIST) \
|
||||
if (idx == (INDEX)) { \
|
||||
const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
|
||||
kChunks[(INDEX)].tag); \
|
||||
if (chunk) { \
|
||||
*data = chunk->data_; \
|
||||
return WEBP_MUX_OK; \
|
||||
} else { \
|
||||
return WEBP_MUX_NOT_FOUND; \
|
||||
do { \
|
||||
if (idx == (INDEX)) { \
|
||||
const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
|
||||
kChunks[(INDEX)].tag); \
|
||||
if (chunk) { \
|
||||
*data = chunk->data_; \
|
||||
return WEBP_MUX_OK; \
|
||||
} else { \
|
||||
return WEBP_MUX_NOT_FOUND; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
|
||||
uint32_t nth, WebPData* const data) {
|
||||
assert(mux != NULL);
|
||||
assert(idx != IDX_LAST_CHUNK);
|
||||
assert(!IsWPI(kChunks[idx].id));
|
||||
WebPDataInit(data);
|
||||
|
||||
|
@ -429,6 +432,7 @@ WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
|
|||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
idx = ChunkGetIndexFromFourCC(fourcc);
|
||||
assert(idx != IDX_LAST_CHUNK);
|
||||
if (IsWPI(kChunks[idx].id)) { // An image chunk.
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
} else if (idx != IDX_UNKNOWN) { // A known chunk type.
|
||||
|
|
|
@ -26,6 +26,8 @@ COMMON_SOURCES += filters_utils.c
|
|||
COMMON_SOURCES += filters_utils.h
|
||||
COMMON_SOURCES += huffman_utils.c
|
||||
COMMON_SOURCES += huffman_utils.h
|
||||
COMMON_SOURCES += palette.c
|
||||
COMMON_SOURCES += palette.h
|
||||
COMMON_SOURCES += quant_levels_dec_utils.c
|
||||
COMMON_SOURCES += quant_levels_dec_utils.h
|
||||
COMMON_SOURCES += rescaler_utils.c
|
||||
|
|
|
@ -122,6 +122,9 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
|||
const int symbol_code_length = code_lengths[symbol];
|
||||
if (code_lengths[symbol] > 0) {
|
||||
if (sorted != NULL) {
|
||||
if(offset[symbol_code_length] >= code_lengths_size) {
|
||||
return 0;
|
||||
}
|
||||
sorted[offset[symbol_code_length]++] = symbol;
|
||||
} else {
|
||||
offset[symbol_code_length]++;
|
||||
|
@ -267,11 +270,11 @@ int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
|
|||
// Have 'segment' point to the first segment for now, 'root'.
|
||||
HuffmanTablesSegment* const root = &huffman_tables->root;
|
||||
huffman_tables->curr_segment = root;
|
||||
root->next = NULL;
|
||||
// Allocate root.
|
||||
root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
|
||||
if (root->start == NULL) return 0;
|
||||
root->curr_table = root->start;
|
||||
root->next = NULL;
|
||||
root->size = size;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,8 @@ typedef struct HuffmanTables {
|
|||
|
||||
// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
|
||||
// memory allocation error, 1 otherwise.
|
||||
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables);
|
||||
WEBP_NODISCARD int VP8LHuffmanTablesAllocate(int size,
|
||||
HuffmanTables* huffman_tables);
|
||||
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
|
||||
|
||||
#define HUFFMAN_PACKED_BITS 6
|
||||
|
@ -91,7 +92,7 @@ struct HTreeGroup {
|
|||
};
|
||||
|
||||
// Creates the instance of HTreeGroup with specified number of tree-groups.
|
||||
HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
|
||||
WEBP_NODISCARD HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
|
||||
|
||||
// Releases the memory allocated for HTreeGroup.
|
||||
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
||||
|
@ -101,8 +102,10 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
|||
// the huffman table.
|
||||
// Returns built table size or 0 in case of error (invalid tree or
|
||||
// memory error).
|
||||
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size);
|
||||
WEBP_NODISCARD int VP8LBuildHuffmanTable(HuffmanTables* const root_table,
|
||||
int root_bits,
|
||||
const int code_lengths[],
|
||||
int code_lengths_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
@ -0,0 +1,402 @@
|
|||
// Copyright 2023 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for palette analysis.
|
||||
//
|
||||
// Author: Vincent Rabaud (vrabaud@google.com)
|
||||
|
||||
#include "src/utils/palette.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dsp/lossless_common.h"
|
||||
#include "src/utils/color_cache_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Palette reordering for smaller sum of deltas (and for smaller storage).
|
||||
|
||||
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
|
||||
const uint32_t a = WebPMemToUint32((uint8_t*)p1);
|
||||
const uint32_t b = WebPMemToUint32((uint8_t*)p2);
|
||||
assert(a != b);
|
||||
return (a < b) ? -1 : 1;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
|
||||
return (v <= 128) ? v : (256 - v);
|
||||
}
|
||||
|
||||
// Computes a value that is related to the entropy created by the
|
||||
// palette entry diff.
|
||||
//
|
||||
// Note that the last & 0xff is a no-operation in the next statement, but
|
||||
// removed by most compilers and is here only for regularity of the code.
|
||||
static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
|
||||
const uint32_t diff = VP8LSubPixels(col1, col2);
|
||||
const int kMoreWeightForRGBThanForAlpha = 9;
|
||||
uint32_t score;
|
||||
score = PaletteComponentDistance((diff >> 0) & 0xff);
|
||||
score += PaletteComponentDistance((diff >> 8) & 0xff);
|
||||
score += PaletteComponentDistance((diff >> 16) & 0xff);
|
||||
score *= kMoreWeightForRGBThanForAlpha;
|
||||
score += PaletteComponentDistance((diff >> 24) & 0xff);
|
||||
return score;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
|
||||
const uint32_t tmp = *col1;
|
||||
*col1 = *col2;
|
||||
*col2 = tmp;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
|
||||
#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
|
||||
|
||||
int GetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
|
||||
int i;
|
||||
int x, y;
|
||||
int num_colors = 0;
|
||||
uint8_t in_use[COLOR_HASH_SIZE] = {0};
|
||||
uint32_t colors[COLOR_HASH_SIZE] = {0};
|
||||
const uint32_t* argb = pic->argb;
|
||||
const int width = pic->width;
|
||||
const int height = pic->height;
|
||||
uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
|
||||
assert(pic != NULL);
|
||||
assert(pic->use_argb);
|
||||
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
int key;
|
||||
if (argb[x] == last_pix) {
|
||||
continue;
|
||||
}
|
||||
last_pix = argb[x];
|
||||
key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
|
||||
while (1) {
|
||||
if (!in_use[key]) {
|
||||
colors[key] = last_pix;
|
||||
in_use[key] = 1;
|
||||
++num_colors;
|
||||
if (num_colors > MAX_PALETTE_SIZE) {
|
||||
return MAX_PALETTE_SIZE + 1; // Exact count not needed.
|
||||
}
|
||||
break;
|
||||
} else if (colors[key] == last_pix) {
|
||||
break; // The color is already there.
|
||||
} else {
|
||||
// Some other color sits here, so do linear conflict resolution.
|
||||
++key;
|
||||
key &= (COLOR_HASH_SIZE - 1); // Key mask.
|
||||
}
|
||||
}
|
||||
}
|
||||
argb += pic->argb_stride;
|
||||
}
|
||||
|
||||
if (palette != NULL) { // Fill the colors into palette.
|
||||
num_colors = 0;
|
||||
for (i = 0; i < COLOR_HASH_SIZE; ++i) {
|
||||
if (in_use[i]) {
|
||||
palette[num_colors] = colors[i];
|
||||
++num_colors;
|
||||
}
|
||||
}
|
||||
qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
|
||||
}
|
||||
return num_colors;
|
||||
}
|
||||
|
||||
#undef COLOR_HASH_SIZE
|
||||
#undef COLOR_HASH_RIGHT_SHIFT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The palette has been sorted by alpha. This function checks if the other
|
||||
// components of the palette have a monotonic development with regards to
|
||||
// position in the palette. If all have monotonic development, there is
|
||||
// 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(const uint32_t* const palette,
|
||||
int num_colors) {
|
||||
uint32_t predict = 0x000000;
|
||||
int i;
|
||||
uint8_t sign_found = 0x00;
|
||||
for (i = 0; i < num_colors; ++i) {
|
||||
const uint32_t diff = VP8LSubPixels(palette[i], predict);
|
||||
const uint8_t rd = (diff >> 16) & 0xff;
|
||||
const uint8_t gd = (diff >> 8) & 0xff;
|
||||
const uint8_t bd = (diff >> 0) & 0xff;
|
||||
if (rd != 0x00) {
|
||||
sign_found |= (rd < 0x80) ? 1 : 2;
|
||||
}
|
||||
if (gd != 0x00) {
|
||||
sign_found |= (gd < 0x80) ? 8 : 16;
|
||||
}
|
||||
if (bd != 0x00) {
|
||||
sign_found |= (bd < 0x80) ? 64 : 128;
|
||||
}
|
||||
predict = palette[i];
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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 int 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 0;
|
||||
}
|
||||
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 1;
|
||||
}
|
||||
|
||||
struct Sum {
|
||||
uint8_t index;
|
||||
uint32_t sum;
|
||||
};
|
||||
|
||||
static int PaletteSortModifiedZeng(const WebPPicture* const pic,
|
||||
const uint32_t* const palette_in,
|
||||
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 1;
|
||||
// Build the co-occurrence matrix.
|
||||
cooccurrence =
|
||||
(uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
|
||||
if (cooccurrence == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (!CoOccurrenceBuild(pic, palette_in, num_colors, cooccurrence)) {
|
||||
WebPSafeFree(cooccurrence);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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_in[remapping[(first + i) % num_colors]];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic,
|
||||
const uint32_t* const palette_sorted, uint32_t num_colors,
|
||||
uint32_t* const palette) {
|
||||
switch (method) {
|
||||
case kSortedDefault:
|
||||
// Nothing to do, we have already sorted the palette.
|
||||
memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
|
||||
return 1;
|
||||
case kMinimizeDelta:
|
||||
PaletteSortMinimizeDeltas(palette_sorted, num_colors, palette);
|
||||
return 1;
|
||||
case kModifiedZeng:
|
||||
return PaletteSortModifiedZeng(pic, palette_sorted, num_colors, palette);
|
||||
case kUnusedPalette:
|
||||
case kPaletteSortingNum:
|
||||
break;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2023 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for palette analysis.
|
||||
//
|
||||
// Author: Vincent Rabaud (vrabaud@google.com)
|
||||
|
||||
#ifndef WEBP_UTILS_PALETTE_H_
|
||||
#define WEBP_UTILS_PALETTE_H_
|
||||
|
||||
#include "src/webp/types.h"
|
||||
|
||||
struct WebPPicture;
|
||||
|
||||
// The different ways a palette can be sorted.
|
||||
typedef enum PaletteSorting {
|
||||
kSortedDefault = 0,
|
||||
// Sorts by minimizing L1 deltas between consecutive colors, giving more
|
||||
// weight to RGB colors.
|
||||
kMinimizeDelta = 1,
|
||||
// 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.
|
||||
kModifiedZeng = 2,
|
||||
kUnusedPalette = 3,
|
||||
kPaletteSortingNum = 4
|
||||
} PaletteSorting;
|
||||
|
||||
// Returns the index of 'color' in the sorted palette 'sorted' of size
|
||||
// 'num_colors'.
|
||||
int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors);
|
||||
|
||||
// Sort palette in increasing order and prepare an inverse mapping array.
|
||||
void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
|
||||
uint32_t sorted[], uint32_t idx_map[]);
|
||||
|
||||
// Returns count of unique colors in 'pic', assuming pic->use_argb is true.
|
||||
// If the unique color count is more than MAX_PALETTE_SIZE, returns
|
||||
// MAX_PALETTE_SIZE+1.
|
||||
// If 'palette' is not NULL and the number of unique colors is less than or
|
||||
// equal to MAX_PALETTE_SIZE, also outputs the actual unique colors into
|
||||
// 'palette' in a sorted order. Note: 'palette' is assumed to be an array
|
||||
// already allocated with at least MAX_PALETTE_SIZE elements.
|
||||
int GetColorPalette(const struct WebPPicture* const pic,
|
||||
uint32_t* const palette);
|
||||
|
||||
// Sorts the palette according to the criterion defined by 'method'.
|
||||
// 'palette_sorted' is the input palette sorted lexicographically, as done in
|
||||
// PrepareMapToPalette. Returns 0 on memory allocation error.
|
||||
int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic,
|
||||
const uint32_t* const palette_sorted, uint32_t num_colors,
|
||||
uint32_t* const palette);
|
||||
|
||||
#endif // WEBP_UTILS_PALETTE_H_
|
|
@ -11,13 +11,13 @@
|
|||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for memcpy()
|
||||
#include "src/webp/decode.h"
|
||||
|
||||
#include "src/utils/palette.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE
|
||||
#include "src/utils/color_cache_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
|
||||
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
|
||||
|
@ -252,66 +252,10 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
|
||||
#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
|
||||
|
||||
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
|
||||
int i;
|
||||
int x, y;
|
||||
int num_colors = 0;
|
||||
uint8_t in_use[COLOR_HASH_SIZE] = { 0 };
|
||||
uint32_t colors[COLOR_HASH_SIZE];
|
||||
const uint32_t* argb = pic->argb;
|
||||
const int width = pic->width;
|
||||
const int height = pic->height;
|
||||
uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
|
||||
assert(pic != NULL);
|
||||
assert(pic->use_argb);
|
||||
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
int key;
|
||||
if (argb[x] == last_pix) {
|
||||
continue;
|
||||
}
|
||||
last_pix = argb[x];
|
||||
key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
|
||||
while (1) {
|
||||
if (!in_use[key]) {
|
||||
colors[key] = last_pix;
|
||||
in_use[key] = 1;
|
||||
++num_colors;
|
||||
if (num_colors > MAX_PALETTE_SIZE) {
|
||||
return MAX_PALETTE_SIZE + 1; // Exact count not needed.
|
||||
}
|
||||
break;
|
||||
} else if (colors[key] == last_pix) {
|
||||
break; // The color is already there.
|
||||
} else {
|
||||
// Some other color sits here, so do linear conflict resolution.
|
||||
++key;
|
||||
key &= (COLOR_HASH_SIZE - 1); // Key mask.
|
||||
}
|
||||
}
|
||||
}
|
||||
argb += pic->argb_stride;
|
||||
}
|
||||
|
||||
if (palette != NULL) { // Fill the colors into palette.
|
||||
num_colors = 0;
|
||||
for (i = 0; i < COLOR_HASH_SIZE; ++i) {
|
||||
if (in_use[i]) {
|
||||
palette[num_colors] = colors[i];
|
||||
++num_colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_colors;
|
||||
return GetColorPalette(pic, palette);
|
||||
}
|
||||
|
||||
#undef COLOR_HASH_SIZE
|
||||
#undef COLOR_HASH_RIGHT_SHIFT
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -198,6 +196,7 @@ WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src,
|
|||
// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'.
|
||||
// Note: 'palette' is assumed to be an array already allocated with at least
|
||||
// MAX_PALETTE_SIZE elements.
|
||||
// TODO(vrabaud) remove whenever we can break the ABI.
|
||||
WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic,
|
||||
uint32_t* const palette);
|
||||
|
||||
|
|
|
@ -48,34 +48,33 @@ WEBP_EXTERN int WebPGetDecoderVersion(void);
|
|||
// RIFF + VP8X + (optional chunks) + VP8(L)
|
||||
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
|
||||
// with the dimensions in *width and *height. The ordering of samples in
|
||||
// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
|
||||
// The returned pointer should be deleted calling WebPFree().
|
||||
// Returns NULL in case of error.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
|
||||
// If the bitstream contains transparency, it is ignored.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height);
|
||||
|
||||
// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
|
||||
// returned is the Y samples buffer. Upon return, *u and *v will point to
|
||||
|
@ -87,10 +86,9 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
|||
// 'width' and 'height' may be NULL, the other pointers must not be.
|
||||
// Returns NULL in case of error.
|
||||
// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr
|
||||
WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height,
|
||||
uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride);
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV(
|
||||
const uint8_t* data, size_t data_size, int* width, int* height,
|
||||
uint8_t** u, uint8_t** v, int* stride, int* uv_stride);
|
||||
|
||||
// These five functions are variants of the above ones, that decode the image
|
||||
// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
|
||||
|
@ -100,22 +98,22 @@ WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
|||
// The parameter 'output_stride' specifies the distance (in bytes)
|
||||
// between scanlines. Hence, output_buffer_size is expected to be at least
|
||||
// output_stride x picture-height.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// RGB and BGR variants. Here too the transparency information, if present,
|
||||
// will be dropped and ignored.
|
||||
WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
|
@ -126,7 +124,7 @@ WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
|
|||
// 'u_size' and 'v_size' respectively.
|
||||
// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
|
||||
// during decoding (or because some buffers were found to be too small).
|
||||
WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
|
@ -217,11 +215,11 @@ struct WebPDecBuffer {
|
|||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
|
||||
|
||||
// Initialize the structure as empty. Must be called before any other use.
|
||||
// Returns false in case of version mismatch
|
||||
static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
|
||||
return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -232,7 +230,7 @@ WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer);
|
|||
//------------------------------------------------------------------------------
|
||||
// Enumeration of the status codes
|
||||
|
||||
typedef enum VP8StatusCode {
|
||||
typedef enum WEBP_NODISCARD VP8StatusCode {
|
||||
VP8_STATUS_OK = 0,
|
||||
VP8_STATUS_OUT_OF_MEMORY,
|
||||
VP8_STATUS_INVALID_PARAM,
|
||||
|
@ -251,23 +249,24 @@ typedef enum VP8StatusCode {
|
|||
// WebPIDecoder object. This object can be left in a SUSPENDED state if the
|
||||
// picture is only partially decoded, pending additional input.
|
||||
// Code example:
|
||||
//
|
||||
// WebPInitDecBuffer(&output_buffer);
|
||||
// output_buffer.colorspace = mode;
|
||||
// ...
|
||||
// WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
|
||||
// while (additional_data_is_available) {
|
||||
// // ... (get additional data in some new_data[] buffer)
|
||||
// status = WebPIAppend(idec, new_data, new_data_size);
|
||||
// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
|
||||
// break; // an error occurred.
|
||||
// }
|
||||
//
|
||||
// // The above call decodes the current available buffer.
|
||||
// // Part of the image can now be refreshed by calling
|
||||
// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||
// }
|
||||
// WebPIDelete(idec);
|
||||
/*
|
||||
WebPInitDecBuffer(&output_buffer);
|
||||
output_buffer.colorspace = mode;
|
||||
...
|
||||
WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
|
||||
while (additional_data_is_available) {
|
||||
// ... (get additional data in some new_data[] buffer)
|
||||
status = WebPIAppend(idec, new_data, new_data_size);
|
||||
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
|
||||
break; // an error occurred.
|
||||
}
|
||||
|
||||
// The above call decodes the current available buffer.
|
||||
// Part of the image can now be refreshed by calling
|
||||
// WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||
}
|
||||
WebPIDelete(idec);
|
||||
*/
|
||||
|
||||
// Creates a new incremental decoder with the supplied buffer parameter.
|
||||
// This output_buffer can be passed NULL, in which case a default output buffer
|
||||
|
@ -281,7 +280,8 @@ typedef enum VP8StatusCode {
|
|||
// within valid bounds.
|
||||
// All other fields of WebPDecBuffer MUST remain constant between calls.
|
||||
// Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewDecoder(
|
||||
WebPDecBuffer* output_buffer);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the RGB/A samples specified by 'csp' into a preallocated
|
||||
|
@ -293,7 +293,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
|
|||
// colorspace 'csp' is taken into account for allocating this buffer. All other
|
||||
// parameters are ignored.
|
||||
// Returns NULL if the allocation failed, or if some parameters are invalid.
|
||||
WEBP_EXTERN WebPIDecoder* WebPINewRGB(
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB(
|
||||
WEBP_CSP_MODE csp,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
|
@ -308,7 +308,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewRGB(
|
|||
// In this case, the output buffer will be automatically allocated (using
|
||||
// MODE_YUVA) when decoding starts. All parameters are then ignored.
|
||||
// Returns NULL if the allocation failed or if a parameter is invalid.
|
||||
WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
|
@ -316,7 +316,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
|
|||
|
||||
// Deprecated version of the above, without the alpha plane.
|
||||
// Kept for backward compatibility.
|
||||
WEBP_EXTERN WebPIDecoder* WebPINewYUV(
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUV(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride);
|
||||
|
@ -346,21 +346,21 @@ WEBP_EXTERN VP8StatusCode WebPIUpdate(
|
|||
// (*last_y, *width etc.) can be NULL if corresponding information is not
|
||||
// needed. The values in these pointers are only valid on successful (non-NULL)
|
||||
// return.
|
||||
WEBP_EXTERN uint8_t* WebPIDecGetRGB(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetRGB(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride);
|
||||
|
||||
// Same as above function to get a YUVA image. Returns pointer to the luma
|
||||
// plane or NULL in case of error. If there is no alpha information
|
||||
// the alpha pointer '*a' will be returned NULL.
|
||||
WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
|
||||
WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height, int* stride, int* uv_stride, int* a_stride);
|
||||
|
||||
// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
|
||||
// alpha information (if present). Kept for backward compatibility.
|
||||
static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
||||
WEBP_NODISCARD static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
||||
const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
|
||||
int* width, int* height, int* stride, int* uv_stride) {
|
||||
return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
|
||||
|
@ -373,7 +373,7 @@ static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
|||
// Returns NULL in case the incremental decoder object is in an invalid state.
|
||||
// Otherwise returns the pointer to the internal representation. This structure
|
||||
// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
|
||||
WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
|
||||
WEBP_NODISCARD WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
|
||||
const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -389,7 +389,7 @@ WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
|
|||
CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
|
||||
|
||||
// C) Adjust 'config', if needed
|
||||
config.no_fancy_upsampling = 1;
|
||||
config.options.no_fancy_upsampling = 1;
|
||||
config.output.colorspace = MODE_BGRA;
|
||||
// etc.
|
||||
|
||||
|
@ -468,12 +468,14 @@ struct WebPDecoderConfig {
|
|||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*,
|
||||
int);
|
||||
|
||||
// Initialize the configuration as empty. This function must always be
|
||||
// called first, unless WebPGetFeatures() is to be called.
|
||||
// Returns false in case of mismatched version.
|
||||
static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPInitDecoderConfig(
|
||||
WebPDecoderConfig* config) {
|
||||
return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -488,8 +490,8 @@ static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
|
|||
// The return WebPIDecoder object must always be deleted calling WebPIDelete().
|
||||
// Returns NULL in case of error (and config->status will then reflect
|
||||
// the error condition, if available).
|
||||
WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode(
|
||||
const uint8_t* data, size_t data_size, WebPDecoderConfig* config);
|
||||
|
||||
// Non-incremental version. This version decodes the full data at once, taking
|
||||
// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
|
||||
#include "./decode.h" // for WEBP_CSP_MODE
|
||||
#include "./mux_types.h"
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -85,13 +86,13 @@ typedef enum WebPDemuxState {
|
|||
} WebPDemuxState;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
|
||||
const WebPData*, int, WebPDemuxState*, int);
|
||||
|
||||
// Parses the full WebP file given by 'data'. For single images the WebP file
|
||||
// header alone or the file header and the chunk header may be absent.
|
||||
// Returns a WebPDemuxer object on successful parse, NULL otherwise.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
|
||||
WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
|
||||
return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -103,7 +104,7 @@ static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
|
|||
// If this data is volatile, the demuxer object should be deleted (by calling
|
||||
// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data.
|
||||
// This is usually an inexpensive operation.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
|
||||
WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
|
||||
const WebPData* data, WebPDemuxState* state) {
|
||||
return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
@ -164,14 +165,14 @@ struct WebPIterator {
|
|||
// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
|
||||
// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of 'iter'.
|
||||
WEBP_EXTERN int WebPDemuxGetFrame(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetFrame(
|
||||
const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
|
||||
|
||||
// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
|
||||
// previous ('iter->frame_num' - 1) frame. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
|
||||
WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
|
||||
|
@ -202,15 +203,16 @@ struct WebPChunkIterator {
|
|||
// payloads are accessed through WebPDemuxGetFrame() and related functions.
|
||||
// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of the iterator.
|
||||
WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
|
||||
const char fourcc[4], int chunk_number,
|
||||
WebPChunkIterator* iter);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
|
||||
const char fourcc[4],
|
||||
int chunk_number,
|
||||
WebPChunkIterator* iter);
|
||||
|
||||
// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
|
||||
// ('iter->chunk_num' - 1) chunk. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
|
||||
WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before destroying the associated WebPDemuxer with
|
||||
|
@ -257,21 +259,21 @@ struct WebPAnimDecoderOptions {
|
|||
};
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
|
||||
WebPAnimDecoderOptions*, int);
|
||||
|
||||
// Should always be called, to initialize a fresh WebPAnimDecoderOptions
|
||||
// structure before modification. Returns false in case of version mismatch.
|
||||
// WebPAnimDecoderOptionsInit() must have succeeded before using the
|
||||
// 'dec_options' object.
|
||||
static WEBP_INLINE int WebPAnimDecoderOptionsInit(
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPAnimDecoderOptionsInit(
|
||||
WebPAnimDecoderOptions* dec_options) {
|
||||
return WebPAnimDecoderOptionsInitInternal(dec_options,
|
||||
WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
||||
const WebPData*, const WebPAnimDecoderOptions*, int);
|
||||
|
||||
// Creates and initializes a WebPAnimDecoder object.
|
||||
|
@ -284,7 +286,7 @@ WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
|||
// Returns:
|
||||
// A pointer to the newly created WebPAnimDecoder object, or NULL in case of
|
||||
// parsing error, invalid option or memory error.
|
||||
static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
|
||||
WEBP_NODISCARD static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
|
||||
const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
|
||||
return WebPAnimDecoderNewInternal(webp_data, dec_options,
|
||||
WEBP_DEMUX_ABI_VERSION);
|
||||
|
@ -306,8 +308,8 @@ struct WebPAnimInfo {
|
|||
// info - (out) global information fetched from the animation.
|
||||
// Returns:
|
||||
// True on success.
|
||||
WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
|
||||
WebPAnimInfo* info);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetInfo(
|
||||
const WebPAnimDecoder* dec, WebPAnimInfo* info);
|
||||
|
||||
// Fetch the next frame from 'dec' based on options supplied to
|
||||
// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
|
||||
|
@ -321,8 +323,9 @@ WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
|
|||
// Returns:
|
||||
// False if any of the arguments are NULL, or if there is a parsing or
|
||||
// decoding error, or if there are no more frames. Otherwise, returns true.
|
||||
WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
uint8_t** buf, int* timestamp);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
uint8_t** buf,
|
||||
int* timestamp);
|
||||
|
||||
// Check if there are more frames left to decode.
|
||||
// Parameters:
|
||||
|
@ -330,7 +333,8 @@ WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
|||
// Returns:
|
||||
// True if 'dec' is not NULL and some frames are yet to be decoded.
|
||||
// Otherwise, returns false.
|
||||
WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(
|
||||
const WebPAnimDecoder* dec);
|
||||
|
||||
// Resets the WebPAnimDecoder object, so that next call to
|
||||
// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be
|
||||
|
@ -348,7 +352,7 @@ WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec);
|
|||
//
|
||||
// Parameters:
|
||||
// dec - (in) decoder instance from which the demuxer object is to be fetched.
|
||||
WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
|
||||
WEBP_NODISCARD WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
|
||||
const WebPAnimDecoder* dec);
|
||||
|
||||
// Deletes the WebPAnimDecoder object.
|
||||
|
|
|
@ -164,13 +164,14 @@ typedef enum WebPPreset {
|
|||
} WebPPreset;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset,
|
||||
float, int);
|
||||
|
||||
// Should always be called, to initialize a fresh WebPConfig structure before
|
||||
// modification. Returns false in case of version mismatch. WebPConfigInit()
|
||||
// must have succeeded before using the 'config' object.
|
||||
// Note that the default values are lossless=0 and quality=75.
|
||||
static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
|
||||
return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
|
||||
WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
@ -179,8 +180,9 @@ static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
|
|||
// set of parameters (referred to by 'preset') and a given quality factor.
|
||||
// This function can be called as a replacement to WebPConfigInit(). Will
|
||||
// return false in case of error.
|
||||
static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
|
||||
WebPPreset preset, float quality) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
|
||||
WebPPreset preset,
|
||||
float quality) {
|
||||
return WebPConfigInitInternal(config, preset, quality,
|
||||
WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
@ -191,11 +193,12 @@ static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
|
|||
// speed and final compressed size.
|
||||
// This function will overwrite several fields from config: 'method', 'quality'
|
||||
// and 'lossless'. Returns false in case of parameter error.
|
||||
WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config,
|
||||
int level);
|
||||
|
||||
// Returns true if 'config' is non-NULL and all configuration parameters are
|
||||
// within their valid ranges.
|
||||
WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Input / Output
|
||||
|
@ -255,8 +258,8 @@ WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer);
|
|||
// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
|
||||
// completion, writer.mem and writer.size will hold the coded data.
|
||||
// writer.mem must be freed by calling WebPMemoryWriterClear.
|
||||
WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
||||
const WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPMemoryWrite(
|
||||
const uint8_t* data, size_t data_size, const WebPPicture* picture);
|
||||
|
||||
// Progress hook, called from time to time to report progress. It can return
|
||||
// false to request an abort of the encoding process, or true otherwise if
|
||||
|
@ -364,13 +367,13 @@ struct WebPPicture {
|
|||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
|
||||
|
||||
// Should always be called, to initialize the structure. Returns false in case
|
||||
// of version mismatch. WebPPictureInit() must have succeeded before using the
|
||||
// 'picture' object.
|
||||
// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
|
||||
static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
|
||||
return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -381,7 +384,7 @@ static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
|
|||
// Allocate y/u/v buffers as per colorspace/width/height specification.
|
||||
// Note! This function will free the previous buffer if needed.
|
||||
// Returns false in case of memory error.
|
||||
WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
|
||||
|
||||
// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
|
||||
// Note that this function does _not_ free the memory used by the 'picture'
|
||||
|
@ -394,7 +397,8 @@ WEBP_EXTERN void WebPPictureFree(WebPPicture* picture);
|
|||
// will fully own the copied pixels (this is not a view). The 'dst' picture need
|
||||
// not be initialized as its content is overwritten.
|
||||
// Returns false in case of memory allocation error.
|
||||
WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src,
|
||||
WebPPicture* dst);
|
||||
|
||||
// Compute the single distortion for packed planes of samples.
|
||||
// 'src' will be compared to 'ref', and the raw distortion stored into
|
||||
|
@ -403,19 +407,18 @@ WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
|
|||
// 'x_step' is the horizontal stride (in bytes) between samples.
|
||||
// 'src/ref_stride' is the byte distance between rows.
|
||||
// Returns false in case of error (bad parameter, memory allocation error, ...).
|
||||
WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
|
||||
const uint8_t* ref, size_t ref_stride,
|
||||
int width, int height,
|
||||
size_t x_step,
|
||||
int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
|
||||
float* distortion, float* result);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPlaneDistortion(
|
||||
const uint8_t* src, size_t src_stride,
|
||||
const uint8_t* ref, size_t ref_stride, int width, int height, size_t x_step,
|
||||
int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
|
||||
float* distortion, float* result);
|
||||
|
||||
// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
|
||||
// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is
|
||||
// always performed using ARGB samples. Hence if the input is YUV(A), the
|
||||
// picture will be internally converted to ARGB (just for the measurement).
|
||||
// Warning: this function is rather CPU-intensive.
|
||||
WEBP_EXTERN int WebPPictureDistortion(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureDistortion(
|
||||
const WebPPicture* src, const WebPPicture* ref,
|
||||
int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
|
||||
float result[5]);
|
||||
|
@ -428,8 +431,8 @@ WEBP_EXTERN int WebPPictureDistortion(
|
|||
// must be fully be comprised inside the 'src' source picture. If the source
|
||||
// picture uses the YUV420 colorspace, the top and left coordinates will be
|
||||
// snapped to even values.
|
||||
WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
|
||||
int left, int top, int width, int height);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureCrop(
|
||||
WebPPicture* picture, int left, int top, int width, int height);
|
||||
|
||||
// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
|
||||
// is defined by the top-left corner pixel coordinates (left, top) as well
|
||||
|
@ -442,9 +445,9 @@ WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
|
|||
// with WebPPictureInit() if it is different from 'src', since its content will
|
||||
// be overwritten.
|
||||
// Returns false in case of invalid parameters.
|
||||
WEBP_EXTERN int WebPPictureView(const WebPPicture* src,
|
||||
int left, int top, int width, int height,
|
||||
WebPPicture* dst);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureView(
|
||||
const WebPPicture* src, int left, int top, int width, int height,
|
||||
WebPPicture* dst);
|
||||
|
||||
// Returns true if the 'picture' is actually a view and therefore does
|
||||
// not own the memory for pixels.
|
||||
|
@ -455,29 +458,30 @@ WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture);
|
|||
// dimension will be calculated preserving the aspect ratio.
|
||||
// No gamma correction is applied.
|
||||
// Returns false in case of error (invalid parameter or insufficient memory).
|
||||
WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture, int width, int height);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture,
|
||||
int width, int height);
|
||||
|
||||
// Colorspace conversion function to import RGB samples.
|
||||
// Previous buffer will be free'd, if any.
|
||||
// *rgb buffer should have a size of at least height * rgb_stride.
|
||||
// Returns false in case of memory error.
|
||||
WEBP_EXTERN int WebPPictureImportRGB(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGB(
|
||||
WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
|
||||
// Same, but for RGBA buffer.
|
||||
WEBP_EXTERN int WebPPictureImportRGBA(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBA(
|
||||
WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
|
||||
// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
|
||||
// input buffer ignoring the alpha channel. Avoids needing to copy the data
|
||||
// to a temporary 24-bit RGB buffer to import the RGB only.
|
||||
WEBP_EXTERN int WebPPictureImportRGBX(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBX(
|
||||
WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
|
||||
|
||||
// Variants of the above, but taking BGR(A|X) input.
|
||||
WEBP_EXTERN int WebPPictureImportBGR(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGR(
|
||||
WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
|
||||
WEBP_EXTERN int WebPPictureImportBGRA(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRA(
|
||||
WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
|
||||
WEBP_EXTERN int WebPPictureImportBGRX(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRX(
|
||||
WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
|
||||
|
||||
// Converts picture->argb data to the YUV420A format. The 'colorspace'
|
||||
|
@ -486,24 +490,24 @@ WEBP_EXTERN int WebPPictureImportBGRX(
|
|||
// non-opaque transparent values is detected, and 'colorspace' will be
|
||||
// adjusted accordingly. Note that this method is lossy.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture,
|
||||
WebPEncCSP /*colorspace = WEBP_YUV420*/);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVA(
|
||||
WebPPicture* picture, WebPEncCSP /*colorspace = WEBP_YUV420*/);
|
||||
|
||||
// Same as WebPPictureARGBToYUVA(), but the conversion is done using
|
||||
// pseudo-random dithering with a strength 'dithering' between
|
||||
// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
|
||||
// for photographic picture.
|
||||
WEBP_EXTERN int WebPPictureARGBToYUVADithered(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVADithered(
|
||||
WebPPicture* picture, WebPEncCSP colorspace, float dithering);
|
||||
|
||||
// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion.
|
||||
// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion
|
||||
// Downsampling is handled with extra care in case of color clipping. This
|
||||
// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
|
||||
// and sharper YUV representation.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
|
||||
// kept for backward compatibility:
|
||||
WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
||||
|
||||
// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
|
||||
// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to
|
||||
|
@ -511,7 +515,7 @@ WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
|||
// Note that the use of this colorspace is discouraged if one has access to the
|
||||
// raw ARGB samples, since using YUV420 is comparatively lossy.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
|
||||
|
||||
// Helper function: given a width x height plane of RGBA or YUV(A) samples
|
||||
// clean-up or smoothen the YUV or RGB samples under fully transparent area,
|
||||
|
@ -541,7 +545,8 @@ WEBP_EXTERN void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb);
|
|||
// the former for lossy encoding, and the latter for lossless encoding
|
||||
// (when config.lossless is true). Automatic conversion from one format to
|
||||
// another is provided but they both incur some loss.
|
||||
WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPEncode(const WebPConfig* config,
|
||||
WebPPicture* picture);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
#define WEBP_WEBP_MUX_H_
|
||||
|
||||
#include "./mux_types.h"
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_MUX_ABI_VERSION 0x0109 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Mux API
|
||||
|
@ -70,7 +71,7 @@ typedef struct WebPMuxAnimParams WebPMuxAnimParams;
|
|||
typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
|
||||
|
||||
// Error codes
|
||||
typedef enum WebPMuxError {
|
||||
typedef enum WEBP_NODISCARD WebPMuxError {
|
||||
WEBP_MUX_OK = 1,
|
||||
WEBP_MUX_NOT_FOUND = 0,
|
||||
WEBP_MUX_INVALID_ARGUMENT = -1,
|
||||
|
@ -104,13 +105,13 @@ WEBP_EXTERN int WebPGetMuxVersion(void);
|
|||
// Life of a Mux object
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN WebPMux* WebPNewInternal(int);
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPNewInternal(int);
|
||||
|
||||
// Creates an empty mux object.
|
||||
// Returns:
|
||||
// A pointer to the newly created empty mux object.
|
||||
// Or NULL in case of memory error.
|
||||
static WEBP_INLINE WebPMux* WebPMuxNew(void) {
|
||||
WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxNew(void) {
|
||||
return WebPNewInternal(WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -123,18 +124,21 @@ WEBP_EXTERN void WebPMuxDelete(WebPMux* mux);
|
|||
// Mux creation.
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int);
|
||||
WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int,
|
||||
int);
|
||||
|
||||
// Creates a mux object from raw data given in WebP RIFF format.
|
||||
// Parameters:
|
||||
// bitstream - (in) the bitstream data in WebP RIFF format
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// object and value 0 indicates data will NOT be copied. If the
|
||||
// data is not copied, it must exist for the lifetime of the
|
||||
// mux object.
|
||||
// Returns:
|
||||
// A pointer to the mux object created from given data - on success.
|
||||
// NULL - In case of invalid data or memory error.
|
||||
static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
|
||||
int copy_data) {
|
||||
WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxCreate(
|
||||
const WebPData* bitstream, int copy_data) {
|
||||
return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
|
@ -154,7 +158,9 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
|
|||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// chunk_data - (in) the chunk data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// object and value 0 indicates data will NOT be copied. If the
|
||||
// data is not copied, it must exist until a call to
|
||||
// WebPMuxAssemble() is made.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
|
@ -217,7 +223,9 @@ struct WebPMuxFrameInfo {
|
|||
// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
|
||||
// WebP file (non-animated)
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// object and value 0 indicates data will NOT be copied. If the
|
||||
// data is not copied, it must exist until a call to
|
||||
// WebPMuxAssemble() is made.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
|
@ -235,7 +243,9 @@ WEBP_EXTERN WebPMuxError WebPMuxSetImage(
|
|||
// mux - (in/out) object to which the frame is to be added
|
||||
// frame - (in) frame data.
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// object and value 0 indicates data will NOT be copied. If the
|
||||
// data is not copied, it must exist until a call to
|
||||
// WebPMuxAssemble() is made.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
|
||||
// or if content of 'frame' is invalid.
|
||||
|
@ -449,7 +459,7 @@ WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal(
|
|||
// structure before modification. Returns false in case of version mismatch.
|
||||
// WebPAnimEncoderOptionsInit() must have succeeded before using the
|
||||
// 'enc_options' object.
|
||||
static WEBP_INLINE int WebPAnimEncoderOptionsInit(
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPAnimEncoderOptionsInit(
|
||||
WebPAnimEncoderOptions* enc_options) {
|
||||
return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
@ -490,7 +500,7 @@ static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
|
|||
// Returns:
|
||||
// On error, returns false and frame->error_code is set appropriately.
|
||||
// Otherwise, returns true.
|
||||
WEBP_EXTERN int WebPAnimEncoderAdd(
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAdd(
|
||||
WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
|
||||
const struct WebPConfig* config);
|
||||
|
||||
|
@ -503,8 +513,8 @@ WEBP_EXTERN int WebPAnimEncoderAdd(
|
|||
// webp_data - (out) generated WebP bitstream.
|
||||
// Returns:
|
||||
// True on success.
|
||||
WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
|
||||
WebPData* webp_data);
|
||||
WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
|
||||
WebPData* webp_data);
|
||||
|
||||
// Get error string corresponding to the most recent call using 'enc'. The
|
||||
// returned string is owned by 'enc' and is valid only until the next call to
|
||||
|
@ -521,6 +531,57 @@ WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc);
|
|||
// enc - (in/out) object to be deleted
|
||||
WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Non-image chunks.
|
||||
|
||||
// Note: Only non-image related chunks should be managed through chunk APIs.
|
||||
// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH").
|
||||
|
||||
// Adds a chunk with id 'fourcc' and data 'chunk_data' in the enc object.
|
||||
// Any existing chunk(s) with the same id will be removed.
|
||||
// Parameters:
|
||||
// enc - (in/out) object to which the chunk is to be added
|
||||
// fourcc - (in) a character array containing the fourcc of the given chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF", etc.
|
||||
// chunk_data - (in) the chunk data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the enc
|
||||
// object and value 0 indicates data will NOT be copied. If the
|
||||
// data is not copied, it must exist until a call to
|
||||
// WebPAnimEncoderAssemble() is made.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN WebPMuxError WebPAnimEncoderSetChunk(
|
||||
WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data,
|
||||
int copy_data);
|
||||
|
||||
// Gets a reference to the data of the chunk with id 'fourcc' in the enc object.
|
||||
// The caller should NOT free the returned data.
|
||||
// Parameters:
|
||||
// enc - (in) object from which the chunk data is to be fetched
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF", etc.
|
||||
// chunk_data - (out) returned chunk data
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given id.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN WebPMuxError WebPAnimEncoderGetChunk(
|
||||
const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data);
|
||||
|
||||
// Deletes the chunk with the given 'fourcc' from the enc object.
|
||||
// Parameters:
|
||||
// enc - (in/out) object from which the chunk is to be deleted
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF", etc.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if enc or fourcc is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given fourcc.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN WebPMuxError WebPAnimEncoderDeleteChunk(
|
||||
WebPAnimEncoder* enc, const char fourcc[4]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -79,7 +79,8 @@ static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
|
|||
|
||||
// Allocates necessary storage for 'dst' and copies the contents of 'src'.
|
||||
// Returns true on success.
|
||||
static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
|
||||
WEBP_NODISCARD static WEBP_INLINE int WebPDataCopy(const WebPData* src,
|
||||
WebPData* dst) {
|
||||
if (src == NULL || dst == NULL) return 0;
|
||||
WebPDataInit(dst);
|
||||
if (src->bytes != NULL && src->size != 0) {
|
||||
|
|
|
@ -36,18 +36,39 @@ typedef long long int int64_t;
|
|||
#define WEBP_INLINE __forceinline
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifndef WEBP_NODISCARD
|
||||
#if defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD
|
||||
#if (defined(__cplusplus) && __cplusplus >= 201700L) || \
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
|
||||
#define WEBP_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
// gcc's __has_attribute does not work for enums.
|
||||
#if defined(__clang__) && defined(__has_attribute)
|
||||
#if __has_attribute(warn_unused_result)
|
||||
#define WEBP_NODISCARD __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define WEBP_NODISCARD
|
||||
#endif /* __has_attribute(warn_unused_result) */
|
||||
#else
|
||||
#define WEBP_NODISCARD
|
||||
#endif /* defined(__clang__) && defined(__has_attribute) */
|
||||
#endif /* (defined(__cplusplus) && __cplusplus >= 201700L) ||
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) */
|
||||
#else
|
||||
#define WEBP_NODISCARD
|
||||
#endif /* defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD */
|
||||
#endif /* WEBP_NODISCARD */
|
||||
|
||||
#ifndef WEBP_EXTERN
|
||||
// This explicitly marks library functions and allows for changing the
|
||||
// signature for e.g., Windows DLL builds.
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# if defined(_WIN32) && defined(WEBP_DLL)
|
||||
# define WEBP_EXTERN __declspec(dllexport)
|
||||
# elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define WEBP_EXTERN extern __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# if defined(_MSC_VER) && defined(WEBP_DLL)
|
||||
# define WEBP_EXTERN __declspec(dllexport)
|
||||
# else
|
||||
# define WEBP_EXTERN extern
|
||||
# endif
|
||||
# endif /* __GNUC__ >= 4 */
|
||||
# define WEBP_EXTERN extern
|
||||
# endif /* defined(_WIN32) && defined(WEBP_DLL) */
|
||||
#endif /* WEBP_EXTERN */
|
||||
|
||||
// Macro to check ABI compatibility (same major revision number)
|
||||
|
@ -60,7 +81,7 @@ extern "C" {
|
|||
// Allocates 'size' bytes of memory. Returns NULL upon error. Memory
|
||||
// must be deallocated by calling WebPFree(). This function is made available
|
||||
// by the core 'libwebp' library.
|
||||
WEBP_EXTERN void* WebPMalloc(size_t size);
|
||||
WEBP_NODISCARD WEBP_EXTERN void* WebPMalloc(size_t size);
|
||||
|
||||
// Releases memory returned by the WebPDecode*() functions (from decode.h).
|
||||
WEBP_EXTERN void WebPFree(void* ptr);
|
||||
|
|
|
@ -104,6 +104,8 @@
|
|||
- glad: 2.0.5 ==> 2.0.6
|
||||
- yasio: 4.2.1 ==> 4.2.2
|
||||
- llhttp: 9.2.0 ==> 9.2.1
|
||||
- libwebp: 1.3.2 ==> 1.4.0
|
||||
- astcenc: 4.7.0 ==> 4.8.0
|
||||
- stb_image: 2.28 ==> 2.29
|
||||
- luajit: 2.1-9cc2e42 ==> 2.1-d06beb0
|
||||
- c-ares: 1.25.0 ==> 1.28.1
|
||||
|
|
Loading…
Reference in New Issue