Update libwebp to 1.0.3

This commit is contained in:
volzhs 2019-10-27 21:29:43 +09:00
parent 9e572b5bac
commit 074d421dca
40 changed files with 454 additions and 332 deletions

View file

@ -250,7 +250,7 @@ from the Android NDK r18.
## libwebp ## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/ - Upstream: https://chromium.googlesource.com/webm/libwebp/
- Version: 1.0.2 - Version: 1.0.3
- License: BSD-3-Clause - License: BSD-3-Clause
Files extracted from upstream source: Files extracted from upstream source:

View file

@ -61,12 +61,17 @@ static const uint16_t kAcTable[128] = {
void VP8ParseQuant(VP8Decoder* const dec) { void VP8ParseQuant(VP8Decoder* const dec) {
VP8BitReader* const br = &dec->br_; VP8BitReader* const br = &dec->br_;
const int base_q0 = VP8GetValue(br, 7); const int base_q0 = VP8GetValue(br, 7, "global-header");
const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dqy1_dc = VP8Get(br, "global-header") ?
const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; VP8GetSignedValue(br, 4, "global-header") : 0;
const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dqy2_dc = VP8Get(br, "global-header") ?
const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; VP8GetSignedValue(br, 4, "global-header") : 0;
const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0; const int dqy2_ac = VP8Get(br, "global-header") ?
VP8GetSignedValue(br, 4, "global-header") : 0;
const int dquv_dc = VP8Get(br, "global-header") ?
VP8GetSignedValue(br, 4, "global-header") : 0;
const int dquv_ac = VP8Get(br, "global-header") ?
VP8GetSignedValue(br, 4, "global-header") : 0;
const VP8SegmentHeader* const hdr = &dec->segment_hdr_; const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
int i; int i;

View file

@ -296,20 +296,21 @@ static void ParseIntraMode(VP8BitReader* const br,
// to decode more than 1 keyframe. // to decode more than 1 keyframe.
if (dec->segment_hdr_.update_map_) { if (dec->segment_hdr_.update_map_) {
// Hardcoded tree parsing // Hardcoded tree parsing
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0], "segments")
? VP8GetBit(br, dec->proba_.segments_[1]) ? VP8GetBit(br, dec->proba_.segments_[1], "segments")
: 2 + VP8GetBit(br, dec->proba_.segments_[2]); : VP8GetBit(br, dec->proba_.segments_[2], "segments") + 2;
} else { } else {
block->segment_ = 0; // default for intra block->segment_ = 0; // default for intra
} }
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_); if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip");
block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first block->is_i4x4_ = !VP8GetBit(br, 145, "block-size");
if (!block->is_i4x4_) { if (!block->is_i4x4_) {
// Hardcoded 16x16 intra-mode decision tree. // Hardcoded 16x16 intra-mode decision tree.
const int ymode = const int ymode =
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED) VP8GetBit(br, 156, "pred-modes") ?
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED); (VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED) :
(VP8GetBit(br, 163, "pred-modes") ? V_PRED : DC_PRED);
block->imodes_[0] = ymode; block->imodes_[0] = ymode;
memset(top, ymode, 4 * sizeof(*top)); memset(top, ymode, 4 * sizeof(*top));
memset(left, ymode, 4 * sizeof(*left)); memset(left, ymode, 4 * sizeof(*left));
@ -323,22 +324,25 @@ static void ParseIntraMode(VP8BitReader* const br,
const uint8_t* const prob = kBModesProba[top[x]][ymode]; const uint8_t* const prob = kBModesProba[top[x]][ymode];
#if (USE_GENERIC_TREE == 1) #if (USE_GENERIC_TREE == 1)
// Generic tree-parsing // Generic tree-parsing
int i = kYModesIntra4[VP8GetBit(br, prob[0])]; int i = kYModesIntra4[VP8GetBit(br, prob[0], "pred-modes")];
while (i > 0) { while (i > 0) {
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])]; i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i], "pred-modes")];
} }
ymode = -i; ymode = -i;
#else #else
// Hardcoded tree parsing // Hardcoded tree parsing
ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED : ymode = !VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED :
!VP8GetBit(br, prob[1]) ? B_TM_PRED : !VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED :
!VP8GetBit(br, prob[2]) ? B_VE_PRED : !VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED :
!VP8GetBit(br, prob[3]) ? !VP8GetBit(br, prob[3], "pred-modes") ?
(!VP8GetBit(br, prob[4]) ? B_HE_PRED : (!VP8GetBit(br, prob[4], "pred-modes") ? B_HE_PRED :
(!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) : (!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED
(!VP8GetBit(br, prob[6]) ? B_LD_PRED : : B_VR_PRED)) :
(!VP8GetBit(br, prob[7]) ? B_VL_PRED : (!VP8GetBit(br, prob[6], "pred-modes") ? B_LD_PRED :
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED))); (!VP8GetBit(br, prob[7], "pred-modes") ? B_VL_PRED :
(!VP8GetBit(br, prob[8], "pred-modes") ? B_HD_PRED
: B_HU_PRED))
);
#endif // USE_GENERIC_TREE #endif // USE_GENERIC_TREE
top[x] = ymode; top[x] = ymode;
} }
@ -348,9 +352,9 @@ static void ParseIntraMode(VP8BitReader* const br,
} }
} }
// Hardcoded UVMode decision tree // Hardcoded UVMode decision tree
block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED block->uvmode_ = !VP8GetBit(br, 142, "pred-modes-uv") ? DC_PRED
: !VP8GetBit(br, 114) ? V_PRED : !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED
: VP8GetBit(br, 183) ? TM_PRED : H_PRED; : VP8GetBit(br, 183, "pred-modes-uv") ? TM_PRED : H_PRED;
} }
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) { int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
@ -514,8 +518,10 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
for (b = 0; b < NUM_BANDS; ++b) { for (b = 0; b < NUM_BANDS; ++b) {
for (c = 0; c < NUM_CTX; ++c) { for (c = 0; c < NUM_CTX; ++c) {
for (p = 0; p < NUM_PROBAS; ++p) { for (p = 0; p < NUM_PROBAS; ++p) {
const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ? const int v =
VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p]; VP8GetBit(br, CoeffsUpdateProba[t][b][c][p], "global-header") ?
VP8GetValue(br, 8, "global-header") :
CoeffsProba0[t][b][c][p];
proba->bands_[t][b].probas_[c][p] = v; proba->bands_[t][b].probas_[c][p] = v;
} }
} }
@ -524,9 +530,8 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]]; proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
} }
} }
dec->use_skip_proba_ = VP8Get(br); dec->use_skip_proba_ = VP8Get(br, "global-header");
if (dec->use_skip_proba_) { if (dec->use_skip_proba_) {
dec->skip_p_ = VP8GetValue(br, 8); dec->skip_p_ = VP8GetValue(br, 8, "global-header");
} }
} }

View file

@ -161,23 +161,26 @@ static int ParseSegmentHeader(VP8BitReader* br,
VP8SegmentHeader* hdr, VP8Proba* proba) { VP8SegmentHeader* hdr, VP8Proba* proba) {
assert(br != NULL); assert(br != NULL);
assert(hdr != NULL); assert(hdr != NULL);
hdr->use_segment_ = VP8Get(br); hdr->use_segment_ = VP8Get(br, "global-header");
if (hdr->use_segment_) { if (hdr->use_segment_) {
hdr->update_map_ = VP8Get(br); hdr->update_map_ = VP8Get(br, "global-header");
if (VP8Get(br)) { // update data if (VP8Get(br, "global-header")) { // update data
int s; int s;
hdr->absolute_delta_ = VP8Get(br); hdr->absolute_delta_ = VP8Get(br, "global-header");
for (s = 0; s < NUM_MB_SEGMENTS; ++s) { for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0; hdr->quantizer_[s] = VP8Get(br, "global-header") ?
VP8GetSignedValue(br, 7, "global-header") : 0;
} }
for (s = 0; s < NUM_MB_SEGMENTS; ++s) { for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0; hdr->filter_strength_[s] = VP8Get(br, "global-header") ?
VP8GetSignedValue(br, 6, "global-header") : 0;
} }
} }
if (hdr->update_map_) { if (hdr->update_map_) {
int s; int s;
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u; proba->segments_[s] = VP8Get(br, "global-header") ?
VP8GetValue(br, 8, "global-header") : 255u;
} }
} }
} else { } else {
@ -205,7 +208,7 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
size_t last_part; size_t last_part;
size_t p; size_t p;
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1; dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2, "global-header")) - 1;
last_part = dec->num_parts_minus_one_; last_part = dec->num_parts_minus_one_;
if (size < 3 * last_part) { if (size < 3 * last_part) {
// we can't even read the sizes with sz[]! That's a failure. // we can't even read the sizes with sz[]! That's a failure.
@ -229,21 +232,21 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
// Paragraph 9.4 // Paragraph 9.4
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
VP8FilterHeader* const hdr = &dec->filter_hdr_; VP8FilterHeader* const hdr = &dec->filter_hdr_;
hdr->simple_ = VP8Get(br); hdr->simple_ = VP8Get(br, "global-header");
hdr->level_ = VP8GetValue(br, 6); hdr->level_ = VP8GetValue(br, 6, "global-header");
hdr->sharpness_ = VP8GetValue(br, 3); hdr->sharpness_ = VP8GetValue(br, 3, "global-header");
hdr->use_lf_delta_ = VP8Get(br); hdr->use_lf_delta_ = VP8Get(br, "global-header");
if (hdr->use_lf_delta_) { if (hdr->use_lf_delta_) {
if (VP8Get(br)) { // update lf-delta? if (VP8Get(br, "global-header")) { // update lf-delta?
int i; int i;
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
if (VP8Get(br)) { if (VP8Get(br, "global-header")) {
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6); hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
} }
} }
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
if (VP8Get(br)) { if (VP8Get(br, "global-header")) {
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6); hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
} }
} }
} }
@ -352,8 +355,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
buf_size -= frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_;
if (frm_hdr->key_frame_) { if (frm_hdr->key_frame_) {
pic_hdr->colorspace_ = VP8Get(br); pic_hdr->colorspace_ = VP8Get(br, "global-header");
pic_hdr->clamp_type_ = VP8Get(br); pic_hdr->clamp_type_ = VP8Get(br, "global-header");
} }
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) { if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
@ -378,7 +381,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
"Not a key frame."); "Not a key frame.");
} }
VP8Get(br); // ignore the value of update_proba_ VP8Get(br, "global-header"); // ignore the value of update_proba_
VP8ParseProba(br, dec); VP8ParseProba(br, dec);
@ -403,28 +406,28 @@ static const uint8_t kZigzag[16] = {
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2 // See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
int v; int v;
if (!VP8GetBit(br, p[3])) { if (!VP8GetBit(br, p[3], "coeffs")) {
if (!VP8GetBit(br, p[4])) { if (!VP8GetBit(br, p[4], "coeffs")) {
v = 2; v = 2;
} else { } else {
v = 3 + VP8GetBit(br, p[5]); v = 3 + VP8GetBit(br, p[5], "coeffs");
} }
} else { } else {
if (!VP8GetBit(br, p[6])) { if (!VP8GetBit(br, p[6], "coeffs")) {
if (!VP8GetBit(br, p[7])) { if (!VP8GetBit(br, p[7], "coeffs")) {
v = 5 + VP8GetBit(br, 159); v = 5 + VP8GetBit(br, 159, "coeffs");
} else { } else {
v = 7 + 2 * VP8GetBit(br, 165); v = 7 + 2 * VP8GetBit(br, 165, "coeffs");
v += VP8GetBit(br, 145); v += VP8GetBit(br, 145, "coeffs");
} }
} else { } else {
const uint8_t* tab; const uint8_t* tab;
const int bit1 = VP8GetBit(br, p[8]); const int bit1 = VP8GetBit(br, p[8], "coeffs");
const int bit0 = VP8GetBit(br, p[9 + bit1]); const int bit0 = VP8GetBit(br, p[9 + bit1], "coeffs");
const int cat = 2 * bit1 + bit0; const int cat = 2 * bit1 + bit0;
v = 0; v = 0;
for (tab = kCat3456[cat]; *tab; ++tab) { for (tab = kCat3456[cat]; *tab; ++tab) {
v += v + VP8GetBit(br, *tab); v += v + VP8GetBit(br, *tab, "coeffs");
} }
v += 3 + (8 << cat); v += 3 + (8 << cat);
} }
@ -438,24 +441,24 @@ static int GetCoeffsFast(VP8BitReader* const br,
int ctx, const quant_t dq, int n, int16_t* out) { int ctx, const quant_t dq, int n, int16_t* out) {
const uint8_t* p = prob[n]->probas_[ctx]; const uint8_t* p = prob[n]->probas_[ctx];
for (; n < 16; ++n) { for (; n < 16; ++n) {
if (!VP8GetBit(br, p[0])) { if (!VP8GetBit(br, p[0], "coeffs")) {
return n; // previous coeff was last non-zero coeff return n; // previous coeff was last non-zero coeff
} }
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs while (!VP8GetBit(br, p[1], "coeffs")) { // sequence of zero coeffs
p = prob[++n]->probas_[0]; p = prob[++n]->probas_[0];
if (n == 16) return 16; if (n == 16) return 16;
} }
{ // non zero coeff { // non zero coeff
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
int v; int v;
if (!VP8GetBit(br, p[2])) { if (!VP8GetBit(br, p[2], "coeffs")) {
v = 1; v = 1;
p = p_ctx[1]; p = p_ctx[1];
} else { } else {
v = GetLargeValue(br, p); v = GetLargeValue(br, p);
p = p_ctx[2]; p = p_ctx[2];
} }
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
} }
} }
return 16; return 16;
@ -468,24 +471,24 @@ static int GetCoeffsAlt(VP8BitReader* const br,
int ctx, const quant_t dq, int n, int16_t* out) { int ctx, const quant_t dq, int n, int16_t* out) {
const uint8_t* p = prob[n]->probas_[ctx]; const uint8_t* p = prob[n]->probas_[ctx];
for (; n < 16; ++n) { for (; n < 16; ++n) {
if (!VP8GetBitAlt(br, p[0])) { if (!VP8GetBitAlt(br, p[0], "coeffs")) {
return n; // previous coeff was last non-zero coeff return n; // previous coeff was last non-zero coeff
} }
while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs while (!VP8GetBitAlt(br, p[1], "coeffs")) { // sequence of zero coeffs
p = prob[++n]->probas_[0]; p = prob[++n]->probas_[0];
if (n == 16) return 16; if (n == 16) return 16;
} }
{ // non zero coeff { // non zero coeff
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
int v; int v;
if (!VP8GetBitAlt(br, p[2])) { if (!VP8GetBitAlt(br, p[2], "coeffs")) {
v = 1; v = 1;
p = p_ctx[1]; p = p_ctx[1];
} else { } else {
v = GetLargeValue(br, p); v = GetLargeValue(br, p);
p = p_ctx[2]; p = p_ctx[2];
} }
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
} }
} }
return 16; return 16;

View file

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

View file

@ -362,12 +362,8 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
VP8LMetadata* const hdr = &dec->hdr_; VP8LMetadata* const hdr = &dec->hdr_;
uint32_t* huffman_image = NULL; uint32_t* huffman_image = NULL;
HTreeGroup* htree_groups = NULL; HTreeGroup* htree_groups = NULL;
// When reading htrees, some might be unused, as the format allows it.
// We will still read them but put them in this htree_group_bogus.
HTreeGroup htree_group_bogus;
HuffmanCode* huffman_tables = NULL; HuffmanCode* huffman_tables = NULL;
HuffmanCode* huffman_tables_bogus = NULL; HuffmanCode* huffman_table = NULL;
HuffmanCode* next = NULL;
int num_htree_groups = 1; int num_htree_groups = 1;
int num_htree_groups_max = 1; int num_htree_groups_max = 1;
int max_alphabet_size = 0; int max_alphabet_size = 0;
@ -418,12 +414,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
if (*mapped_group == -1) *mapped_group = num_htree_groups++; if (*mapped_group == -1) *mapped_group = num_htree_groups++;
huffman_image[i] = *mapped_group; huffman_image[i] = *mapped_group;
} }
huffman_tables_bogus = (HuffmanCode*)WebPSafeMalloc(
table_size, sizeof(*huffman_tables_bogus));
if (huffman_tables_bogus == NULL) {
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
goto Error;
}
} else { } else {
num_htree_groups = num_htree_groups_max; num_htree_groups = num_htree_groups_max;
} }
@ -453,63 +443,71 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
goto Error; goto Error;
} }
next = huffman_tables; huffman_table = huffman_tables;
for (i = 0; i < num_htree_groups_max; ++i) { for (i = 0; i < num_htree_groups_max; ++i) {
// If the index "i" is unused in the Huffman image, read the coefficients // If the index "i" is unused in the Huffman image, just make sure the
// but store them to a bogus htree_group. // coefficients are valid but do not store them.
const int is_bogus = (mapping != NULL && mapping[i] == -1); if (mapping != NULL && mapping[i] == -1) {
HTreeGroup* const htree_group = for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
is_bogus ? &htree_group_bogus : int alphabet_size = kAlphabetSize[j];
&htree_groups[(mapping == NULL) ? i : mapping[i]]; if (j == 0 && color_cache_bits > 0) {
HuffmanCode** const htrees = htree_group->htrees; alphabet_size += (1 << color_cache_bits);
HuffmanCode* huffman_tables_i = is_bogus ? huffman_tables_bogus : next; }
int size; // Passing in NULL so that nothing gets filled.
int total_size = 0; if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, NULL)) {
int is_trivial_literal = 1; goto Error;
int max_bits = 0;
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
int alphabet_size = kAlphabetSize[j];
htrees[j] = huffman_tables_i;
if (j == 0 && color_cache_bits > 0) {
alphabet_size += 1 << color_cache_bits;
}
size =
ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables_i);
if (size == 0) {
goto Error;
}
if (is_trivial_literal && kLiteralMap[j] == 1) {
is_trivial_literal = (huffman_tables_i->bits == 0);
}
total_size += huffman_tables_i->bits;
huffman_tables_i += size;
if (j <= ALPHA) {
int local_max_bits = code_lengths[0];
int k;
for (k = 1; k < alphabet_size; ++k) {
if (code_lengths[k] > local_max_bits) {
local_max_bits = code_lengths[k];
}
} }
max_bits += local_max_bits;
} }
} } else {
if (!is_bogus) next = huffman_tables_i; HTreeGroup* const htree_group =
htree_group->is_trivial_literal = is_trivial_literal; &htree_groups[(mapping == NULL) ? i : mapping[i]];
htree_group->is_trivial_code = 0; HuffmanCode** const htrees = htree_group->htrees;
if (is_trivial_literal) { int size;
const int red = htrees[RED][0].value; int total_size = 0;
const int blue = htrees[BLUE][0].value; int is_trivial_literal = 1;
const int alpha = htrees[ALPHA][0].value; int max_bits = 0;
htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue; for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) { int alphabet_size = kAlphabetSize[j];
htree_group->is_trivial_code = 1; htrees[j] = huffman_table;
htree_group->literal_arb |= htrees[GREEN][0].value << 8; if (j == 0 && color_cache_bits > 0) {
alphabet_size += (1 << color_cache_bits);
}
size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
if (size == 0) {
goto Error;
}
if (is_trivial_literal && kLiteralMap[j] == 1) {
is_trivial_literal = (huffman_table->bits == 0);
}
total_size += huffman_table->bits;
huffman_table += size;
if (j <= ALPHA) {
int local_max_bits = code_lengths[0];
int k;
for (k = 1; k < alphabet_size; ++k) {
if (code_lengths[k] > local_max_bits) {
local_max_bits = code_lengths[k];
}
}
max_bits += local_max_bits;
}
} }
htree_group->is_trivial_literal = is_trivial_literal;
htree_group->is_trivial_code = 0;
if (is_trivial_literal) {
const int red = htrees[RED][0].value;
const int blue = htrees[BLUE][0].value;
const int alpha = htrees[ALPHA][0].value;
htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue;
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
htree_group->is_trivial_code = 1;
htree_group->literal_arb |= htrees[GREEN][0].value << 8;
}
}
htree_group->use_packed_table =
!htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
} }
htree_group->use_packed_table =
!htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
} }
ok = 1; ok = 1;
@ -521,7 +519,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
Error: Error:
WebPSafeFree(code_lengths); WebPSafeFree(code_lengths);
WebPSafeFree(huffman_tables_bogus);
WebPSafeFree(mapping); WebPSafeFree(mapping);
if (!ok) { if (!ok) {
WebPSafeFree(huffman_image); WebPSafeFree(huffman_image);

View file

@ -25,7 +25,7 @@
#define DMUX_MAJ_VERSION 1 #define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 0 #define DMUX_MIN_VERSION 0
#define DMUX_REV_VERSION 2 #define DMUX_REV_VERSION 3
typedef struct { typedef struct {
size_t start_; // start location of the data size_t start_; // start location of the data

View file

@ -214,7 +214,7 @@ static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
// Alpha detection // Alpha detection
static int HasAlpha8b_SSE2(const uint8_t* src, int length) { static int HasAlpha8b_SSE2(const uint8_t* src, int length) {
const __m128i all_0xff = _mm_set1_epi8(0xff); const __m128i all_0xff = _mm_set1_epi8((char)0xff);
int i = 0; int i = 0;
for (; i + 16 <= length; i += 16) { for (; i + 16 <= length; i += 16) {
const __m128i v = _mm_loadu_si128((const __m128i*)(src + i)); const __m128i v = _mm_loadu_si128((const __m128i*)(src + i));
@ -228,7 +228,7 @@ static int HasAlpha8b_SSE2(const uint8_t* src, int length) {
static int HasAlpha32b_SSE2(const uint8_t* src, int length) { static int HasAlpha32b_SSE2(const uint8_t* src, int length) {
const __m128i alpha_mask = _mm_set1_epi32(0xff); const __m128i alpha_mask = _mm_set1_epi32(0xff);
const __m128i all_0xff = _mm_set1_epi8(0xff); const __m128i all_0xff = _mm_set1_epi8((char)0xff);
int i = 0; int i = 0;
// We don't know if we can access the last 3 bytes after the last alpha // We don't know if we can access the last 3 bytes after the last alpha
// value 'src[4 * length - 4]' (because we don't know if alpha is the first // value 'src[4 * length - 4]' (because we don't know if alpha is the first

View file

@ -173,8 +173,8 @@ static int AndroidCPUInfo(CPUFeature feature) {
const AndroidCpuFamily cpu_family = android_getCpuFamily(); const AndroidCpuFamily cpu_family = android_getCpuFamily();
const uint64_t cpu_features = android_getCpuFeatures(); const uint64_t cpu_features = android_getCpuFeatures();
if (feature == kNEON) { if (feature == kNEON) {
return (cpu_family == ANDROID_CPU_FAMILY_ARM && return cpu_family == ANDROID_CPU_FAMILY_ARM &&
0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON)); (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
} }
return 0; return 0;
} }

View file

@ -326,7 +326,7 @@ static WEBP_INLINE void Update2Pixels_SSE2(__m128i* const pi, __m128i* const qi,
const __m128i a1_lo = _mm_srai_epi16(*a0_lo, 7); const __m128i a1_lo = _mm_srai_epi16(*a0_lo, 7);
const __m128i a1_hi = _mm_srai_epi16(*a0_hi, 7); const __m128i a1_hi = _mm_srai_epi16(*a0_hi, 7);
const __m128i delta = _mm_packs_epi16(a1_lo, a1_hi); const __m128i delta = _mm_packs_epi16(a1_lo, a1_hi);
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8((char)0x80);
*pi = _mm_adds_epi8(*pi, delta); *pi = _mm_adds_epi8(*pi, delta);
*qi = _mm_subs_epi8(*qi, delta); *qi = _mm_subs_epi8(*qi, delta);
FLIP_SIGN_BIT2(*pi, *qi); FLIP_SIGN_BIT2(*pi, *qi);
@ -338,9 +338,9 @@ static WEBP_INLINE void NeedsFilter_SSE2(const __m128i* const p1,
const __m128i* const q0, const __m128i* const q0,
const __m128i* const q1, const __m128i* const q1,
int thresh, __m128i* const mask) { int thresh, __m128i* const mask) {
const __m128i m_thresh = _mm_set1_epi8(thresh); const __m128i m_thresh = _mm_set1_epi8((char)thresh);
const __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1) const __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1)
const __m128i kFE = _mm_set1_epi8(0xFE); const __m128i kFE = _mm_set1_epi8((char)0xFE);
const __m128i t2 = _mm_and_si128(t1, kFE); // set lsb of each byte to zero const __m128i t2 = _mm_and_si128(t1, kFE); // set lsb of each byte to zero
const __m128i t3 = _mm_srli_epi16(t2, 1); // abs(p1 - q1) / 2 const __m128i t3 = _mm_srli_epi16(t2, 1); // abs(p1 - q1) / 2
@ -360,7 +360,7 @@ static WEBP_INLINE void DoFilter2_SSE2(__m128i* const p1, __m128i* const p0,
__m128i* const q0, __m128i* const q1, __m128i* const q0, __m128i* const q1,
int thresh) { int thresh) {
__m128i a, mask; __m128i a, mask;
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8((char)0x80);
// convert p1/q1 to int8_t (for GetBaseDelta_SSE2) // convert p1/q1 to int8_t (for GetBaseDelta_SSE2)
const __m128i p1s = _mm_xor_si128(*p1, sign_bit); const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
const __m128i q1s = _mm_xor_si128(*q1, sign_bit); const __m128i q1s = _mm_xor_si128(*q1, sign_bit);
@ -380,7 +380,7 @@ static WEBP_INLINE void DoFilter4_SSE2(__m128i* const p1, __m128i* const p0,
const __m128i* const mask, const __m128i* const mask,
int hev_thresh) { int hev_thresh) {
const __m128i zero = _mm_setzero_si128(); const __m128i zero = _mm_setzero_si128();
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8((char)0x80);
const __m128i k64 = _mm_set1_epi8(64); const __m128i k64 = _mm_set1_epi8(64);
const __m128i k3 = _mm_set1_epi8(3); const __m128i k3 = _mm_set1_epi8(3);
const __m128i k4 = _mm_set1_epi8(4); const __m128i k4 = _mm_set1_epi8(4);
@ -427,7 +427,7 @@ static WEBP_INLINE void DoFilter6_SSE2(__m128i* const p2, __m128i* const p1,
const __m128i* const mask, const __m128i* const mask,
int hev_thresh) { int hev_thresh) {
const __m128i zero = _mm_setzero_si128(); const __m128i zero = _mm_setzero_si128();
const __m128i sign_bit = _mm_set1_epi8(0x80); const __m128i sign_bit = _mm_set1_epi8((char)0x80);
__m128i a, not_hev; __m128i a, not_hev;
// compute hev mask // compute hev mask
@ -941,7 +941,7 @@ static void VR4_SSE2(uint8_t* dst) { // Vertical-Right
const __m128i ABCD0 = _mm_srli_si128(XABCD, 1); const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0); const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
const __m128i _XABCD = _mm_slli_si128(XABCD, 1); const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0); const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0);
const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0); const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i avg2 = _mm_subs_epu8(avg1, lsb);

View file

@ -777,7 +777,7 @@ static WEBP_INLINE void VR4_SSE2(uint8_t* dst,
const __m128i ABCD0 = _mm_srli_si128(XABCD, 1); const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0); const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
const __m128i _XABCD = _mm_slli_si128(XABCD, 1); const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0); const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0);
const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0); const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i avg2 = _mm_subs_epu8(avg1, lsb);

View file

@ -33,9 +33,9 @@ static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred,
uint8_t* dst, int length, int inverse) { uint8_t* dst, int length, int inverse) {
int i; int i;
if (inverse) { if (inverse) {
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i]; for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] + pred[i]);
} else { } else {
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i]; for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] - pred[i]);
} }
} }
@ -155,7 +155,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
const int pred = GradientPredictor_C(preds[w - 1], const int pred = GradientPredictor_C(preds[w - 1],
preds[w - stride], preds[w - stride],
preds[w - stride - 1]); preds[w - stride - 1]);
out[w] = in[w] + (inverse ? pred : -pred); out[w] = (uint8_t)(in[w] + (inverse ? pred : -pred));
} }
++row; ++row;
preds += stride; preds += stride;
@ -194,7 +194,7 @@ static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
uint8_t pred = (prev == NULL) ? 0 : prev[0]; uint8_t pred = (prev == NULL) ? 0 : prev[0];
int i; int i;
for (i = 0; i < width; ++i) { for (i = 0; i < width; ++i) {
out[i] = pred + in[i]; out[i] = (uint8_t)(pred + in[i]);
pred = out[i]; pred = out[i];
} }
} }
@ -206,7 +206,7 @@ static void VerticalUnfilter_C(const uint8_t* prev, const uint8_t* in,
HorizontalUnfilter_C(NULL, in, out, width); HorizontalUnfilter_C(NULL, in, out, width);
} else { } else {
int i; int i;
for (i = 0; i < width; ++i) out[i] = prev[i] + in[i]; for (i = 0; i < width; ++i) out[i] = (uint8_t)(prev[i] + in[i]);
} }
} }
#endif // !WEBP_NEON_OMIT_C_CODE #endif // !WEBP_NEON_OMIT_C_CODE
@ -220,7 +220,7 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
int i; int i;
for (i = 0; i < width; ++i) { for (i = 0; i < width; ++i) {
top = prev[i]; // need to read this first, in case prev==out top = prev[i]; // need to read this first, in case prev==out
left = in[i] + GradientPredictor_C(left, top, top_left); left = (uint8_t)(in[i] + GradientPredictor_C(left, top, top_left));
top_left = top; top_left = top;
out[i] = left; out[i] = left;
} }

View file

@ -163,7 +163,8 @@ static void GradientPredictDirect_SSE2(const uint8_t* const row,
_mm_storel_epi64((__m128i*)(out + i), H); _mm_storel_epi64((__m128i*)(out + i), H);
} }
for (; i < length; ++i) { for (; i < length; ++i) {
out[i] = row[i] - GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]); const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
out[i] = (uint8_t)(row[i] - delta);
} }
} }
@ -188,7 +189,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in,
// Filter line-by-line. // Filter line-by-line.
while (row < last_row) { while (row < last_row) {
out[0] = in[0] - in[-stride]; out[0] = (uint8_t)(in[0] - in[-stride]);
GradientPredictDirect_SSE2(in + 1, in + 1 - stride, out + 1, width - 1); GradientPredictDirect_SSE2(in + 1, in + 1 - stride, out + 1, width - 1);
++row; ++row;
in += stride; in += stride;
@ -223,7 +224,7 @@ static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
uint8_t* out, int width) { uint8_t* out, int width) {
int i; int i;
__m128i last; __m128i last;
out[0] = in[0] + (prev == NULL ? 0 : prev[0]); out[0] = (uint8_t)(in[0] + (prev == NULL ? 0 : prev[0]));
if (width <= 1) return; if (width <= 1) return;
last = _mm_set_epi32(0, 0, 0, out[0]); last = _mm_set_epi32(0, 0, 0, out[0]);
for (i = 1; i + 8 <= width; i += 8) { for (i = 1; i + 8 <= width; i += 8) {
@ -238,7 +239,7 @@ static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
_mm_storel_epi64((__m128i*)(out + i), A7); _mm_storel_epi64((__m128i*)(out + i), A7);
last = _mm_srli_epi64(A7, 56); last = _mm_srli_epi64(A7, 56);
} }
for (; i < width; ++i) out[i] = in[i] + out[i - 1]; for (; i < width; ++i) out[i] = (uint8_t)(in[i] + out[i - 1]);
} }
static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in, static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
@ -259,7 +260,7 @@ static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
_mm_storeu_si128((__m128i*)&out[i + 0], C0); _mm_storeu_si128((__m128i*)&out[i + 0], C0);
_mm_storeu_si128((__m128i*)&out[i + 16], C1); _mm_storeu_si128((__m128i*)&out[i + 16], C1);
} }
for (; i < width; ++i) out[i] = in[i] + prev[i]; for (; i < width; ++i) out[i] = (uint8_t)(in[i] + prev[i]);
} }
} }
@ -296,7 +297,8 @@ static void GradientPredictInverse_SSE2(const uint8_t* const in,
_mm_storel_epi64((__m128i*)&row[i], out); _mm_storel_epi64((__m128i*)&row[i], out);
} }
for (; i < length; ++i) { for (; i < length; ++i) {
row[i] = in[i] + GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]); const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
row[i] = (uint8_t)(in[i] + delta);
} }
} }
} }
@ -306,7 +308,7 @@ static void GradientUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
if (prev == NULL) { if (prev == NULL) {
HorizontalUnfilter_SSE2(NULL, in, out, width); HorizontalUnfilter_SSE2(NULL, in, out, width);
} else { } else {
out[0] = in[0] + prev[0]; // predict from above out[0] = (uint8_t)(in[0] + prev[0]); // predict from above
GradientPredictInverse_SSE2(in + 1, prev + 1, out + 1, width - 1); GradientPredictInverse_SSE2(in + 1, prev + 1, out + 1, width - 1);
} }
} }

View file

@ -270,14 +270,14 @@ void VP8LTransformColorInverse_C(const VP8LMultipliers* const m,
int i; int i;
for (i = 0; i < num_pixels; ++i) { for (i = 0; i < num_pixels; ++i) {
const uint32_t argb = src[i]; const uint32_t argb = src[i];
const uint32_t green = argb >> 8; const int8_t green = (int8_t)(argb >> 8);
const uint32_t red = argb >> 16; const uint32_t red = argb >> 16;
int new_red = red & 0xff; int new_red = red & 0xff;
int new_blue = argb & 0xff; int new_blue = argb & 0xff;
new_red += ColorTransformDelta(m->green_to_red_, green); new_red += ColorTransformDelta(m->green_to_red_, green);
new_red &= 0xff; new_red &= 0xff;
new_blue += ColorTransformDelta(m->green_to_blue_, green); new_blue += ColorTransformDelta(m->green_to_blue_, green);
new_blue += ColorTransformDelta(m->red_to_blue_, new_red); new_blue += ColorTransformDelta(m->red_to_blue_, (int8_t)new_red);
new_blue &= 0xff; new_blue &= 0xff;
dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
} }

View file

@ -515,13 +515,17 @@ static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, int8_t color) {
return ((int)color_pred * color) >> 5; return ((int)color_pred * color) >> 5;
} }
static WEBP_INLINE int8_t U32ToS8(uint32_t v) {
return (int8_t)(v & 0xff);
}
void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data,
int num_pixels) { int num_pixels) {
int i; int i;
for (i = 0; i < num_pixels; ++i) { for (i = 0; i < num_pixels; ++i) {
const uint32_t argb = data[i]; const uint32_t argb = data[i];
const uint32_t green = argb >> 8; const int8_t green = U32ToS8(argb >> 8);
const uint32_t red = argb >> 16; const int8_t red = U32ToS8(argb >> 16);
int new_red = red & 0xff; int new_red = red & 0xff;
int new_blue = argb & 0xff; int new_blue = argb & 0xff;
new_red -= ColorTransformDelta(m->green_to_red_, green); new_red -= ColorTransformDelta(m->green_to_red_, green);
@ -535,7 +539,7 @@ void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data,
static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red, static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
uint32_t argb) { uint32_t argb) {
const uint32_t green = argb >> 8; const int8_t green = U32ToS8(argb >> 8);
int new_red = argb >> 16; int new_red = argb >> 16;
new_red -= ColorTransformDelta(green_to_red, green); new_red -= ColorTransformDelta(green_to_red, green);
return (new_red & 0xff); return (new_red & 0xff);
@ -544,9 +548,9 @@ static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
uint8_t red_to_blue, uint8_t red_to_blue,
uint32_t argb) { uint32_t argb) {
const uint32_t green = argb >> 8; const int8_t green = U32ToS8(argb >> 8);
const uint32_t red = argb >> 16; const int8_t red = U32ToS8(argb >> 16);
uint8_t new_blue = argb; uint8_t new_blue = argb & 0xff;
new_blue -= ColorTransformDelta(green_to_blue, green); new_blue -= ColorTransformDelta(green_to_blue, green);
new_blue -= ColorTransformDelta(red_to_blue, red); new_blue -= ColorTransformDelta(red_to_blue, red);
return (new_blue & 0xff); return (new_blue & 0xff);
@ -558,7 +562,7 @@ void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride,
while (tile_height-- > 0) { while (tile_height-- > 0) {
int x; int x;
for (x = 0; x < tile_width; ++x) { for (x = 0; x < tile_width; ++x) {
++histo[TransformColorRed(green_to_red, argb[x])]; ++histo[TransformColorRed((uint8_t)green_to_red, argb[x])];
} }
argb += stride; argb += stride;
} }
@ -571,7 +575,8 @@ void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride,
while (tile_height-- > 0) { while (tile_height-- > 0) {
int x; int x;
for (x = 0; x < tile_width; ++x) { for (x = 0; x < tile_width; ++x) {
++histo[TransformColorBlue(green_to_blue, red_to_blue, argb[x])]; ++histo[TransformColorBlue((uint8_t)green_to_blue, (uint8_t)red_to_blue,
argb[x])];
} }
argb += stride; argb += stride;
} }

View file

@ -363,7 +363,7 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
assert(xbits <= 3); assert(xbits <= 3);
switch (xbits) { switch (xbits) {
case 0: { case 0: {
const __m128i ff = _mm_set1_epi16(0xff00); const __m128i ff = _mm_set1_epi16((short)0xff00);
const __m128i zero = _mm_setzero_si128(); const __m128i zero = _mm_setzero_si128();
// Store 0xff000000 | (row[x] << 8). // Store 0xff000000 | (row[x] << 8).
for (x = 0; x + 16 <= width; x += 16, dst += 16) { for (x = 0; x + 16 <= width; x += 16, dst += 16) {
@ -382,7 +382,7 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
break; break;
} }
case 1: { case 1: {
const __m128i ff = _mm_set1_epi16(0xff00); const __m128i ff = _mm_set1_epi16((short)0xff00);
const __m128i mul = _mm_set1_epi16(0x110); const __m128i mul = _mm_set1_epi16(0x110);
for (x = 0; x + 16 <= width; x += 16, dst += 8) { for (x = 0; x + 16 <= width; x += 16, dst += 8) {
// 0a0b | (where a/b are 4 bits). // 0a0b | (where a/b are 4 bits).

View file

@ -51,9 +51,9 @@ static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
int histo[]) { int histo[]) {
const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue)); const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue));
const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue)); const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue));
const __m128i mask_g = _mm_set1_epi16(0xff00); // green mask const __m128i mask_g = _mm_set1_epi16((short)0xff00); // green mask
const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1, const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1,
-1, -1, -1, -1, -1, -1, -1); -1, -1, -1, -1, -1, -1, -1);
const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1,

View file

@ -10,6 +10,8 @@
#ifndef WEBP_DSP_QUANT_H_ #ifndef WEBP_DSP_QUANT_H_
#define WEBP_DSP_QUANT_H_ #define WEBP_DSP_QUANT_H_
#include <string.h>
#include "src/dsp/dsp.h" #include "src/dsp/dsp.h"
#include "src/webp/types.h" #include "src/webp/types.h"
@ -67,4 +69,17 @@ static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
#endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) && #endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) &&
// !defined(WEBP_HAVE_NEON_RTCD) // !defined(WEBP_HAVE_NEON_RTCD)
static WEBP_INLINE int IsFlatSource16(const uint8_t* src) {
const uint32_t v = src[0] * 0x01010101u;
int i;
for (i = 0; i < 16; ++i) {
if (memcmp(src + 0, &v, 4) || memcmp(src + 4, &v, 4) ||
memcmp(src + 8, &v, 4) || memcmp(src + 12, &v, 4)) {
return 0;
}
src += BPS;
}
return 1;
}
#endif // WEBP_DSP_QUANT_H_ #endif // WEBP_DSP_QUANT_H_

View file

@ -109,8 +109,7 @@ void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
for (x_out = 0; x_out < x_out_max; ++x_out) { for (x_out = 0; x_out < x_out_max; ++x_out) {
const uint32_t J = frow[x_out]; const uint32_t J = frow[x_out];
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} else { } else {
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
@ -120,8 +119,7 @@ void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
+ (uint64_t)B * irow[x_out]; + (uint64_t)B * irow[x_out];
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} }
} }
@ -138,17 +136,15 @@ void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
assert(!wrk->y_expand); assert(!wrk->y_expand);
if (yscale) { if (yscale) {
for (x_out = 0; x_out < x_out_max; ++x_out) { for (x_out = 0; x_out < x_out_max; ++x_out) {
const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale); const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale); const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = frac; // new fractional start irow[x_out] = frac; // new fractional start
} }
} else { } else {
for (x_out = 0; x_out < x_out_max; ++x_out) { for (x_out = 0; x_out < x_out_max; ++x_out) {
const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale); const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = 0; irow[x_out] = 0;
} }
} }

View file

@ -107,10 +107,9 @@ static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
); );
} }
for (i = 0; i < (x_out_max & 0x3); ++i) { for (i = 0; i < (x_out_max & 0x3); ++i) {
const uint32_t frac = (uint32_t)MULT_FIX(*frow++, yscale); const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(*frow++, yscale);
const int v = (int)MULT_FIX_FLOOR(*irow - frac, wrk->fxy_scale); const int v = (int)MULT_FIX(*irow - frac, wrk->fxy_scale);
assert(v >= 0 && v <= 255); *dst++ = (v > 255) ? 255u : (uint8_t)v;
*dst++ = v;
*irow++ = frac; // new fractional start *irow++ = frac; // new fractional start
} }
} else { } else {
@ -157,8 +156,7 @@ static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
} }
for (i = 0; i < (x_out_max & 0x3); ++i) { for (i = 0; i < (x_out_max & 0x3); ++i) {
const int v = (int)MULT_FIX_FLOOR(*irow, wrk->fxy_scale); const int v = (int)MULT_FIX_FLOOR(*irow, wrk->fxy_scale);
assert(v >= 0 && v <= 255); *dst++ = (v > 255) ? 255u : (uint8_t)v;
*dst++ = v;
*irow++ = 0; *irow++ = 0;
} }
} }
@ -219,8 +217,7 @@ static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
for (i = 0; i < (x_out_max & 0x3); ++i) { for (i = 0; i < (x_out_max & 0x3); ++i) {
const uint32_t J = *frow++; const uint32_t J = *frow++;
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); *dst++ = (v > 255) ? 255u : (uint8_t)v;
*dst++ = v;
} }
} else { } else {
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
@ -291,8 +288,7 @@ static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
+ (uint64_t)B * *irow++; + (uint64_t)B * *irow++;
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); *dst++ = (v > 255) ? 255u : (uint8_t)v;
*dst++ = v;
} }
} }
} }

View file

@ -166,8 +166,7 @@ static WEBP_INLINE void ExportRowExpand_0(const uint32_t* frow, uint8_t* dst,
for (x_out = 0; x_out < length; ++x_out) { for (x_out = 0; x_out < length; ++x_out) {
const uint32_t J = frow[x_out]; const uint32_t J = frow[x_out];
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} }
} }
@ -241,8 +240,7 @@ static WEBP_INLINE void ExportRowExpand_1(const uint32_t* frow, uint32_t* irow,
+ (uint64_t)B * irow[x_out]; + (uint64_t)B * irow[x_out];
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} }
} }
@ -342,10 +340,9 @@ static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow,
length -= 4; length -= 4;
} }
for (x_out = 0; x_out < length; ++x_out) { for (x_out = 0; x_out < length; ++x_out) {
const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale); const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale); const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = frac; irow[x_out] = frac;
} }
} }
@ -406,8 +403,7 @@ static WEBP_INLINE void ExportRowShrink_1(uint32_t* irow, uint8_t* dst,
} }
for (x_out = 0; x_out < length; ++x_out) { for (x_out = 0; x_out < length; ++x_out) {
const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale); const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = 0; irow[x_out] = 0;
} }
} }

View file

@ -81,14 +81,13 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half); const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half);
const uint16x4_t C0 = vmovn_u32(B0); const uint16x4_t C0 = vmovn_u32(B0);
const uint16x4_t C1 = vmovn_u32(B1); const uint16x4_t C1 = vmovn_u32(B1);
const uint8x8_t D = vmovn_u16(vcombine_u16(C0, C1)); const uint8x8_t D = vqmovn_u16(vcombine_u16(C0, C1));
vst1_u8(dst + x_out, D); vst1_u8(dst + x_out, D);
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const uint32_t J = frow[x_out]; const uint32_t J = frow[x_out];
const int v = (int)MULT_FIX_C(J, fy_scale); const int v = (int)MULT_FIX_C(J, fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} else { } else {
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
@ -102,7 +101,7 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half); const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half);
const uint16x4_t E0 = vmovn_u32(D0); const uint16x4_t E0 = vmovn_u32(D0);
const uint16x4_t E1 = vmovn_u32(D1); const uint16x4_t E1 = vmovn_u32(D1);
const uint8x8_t F = vmovn_u16(vcombine_u16(E0, E1)); const uint8x8_t F = vqmovn_u16(vcombine_u16(E0, E1));
vst1_u8(dst + x_out, F); vst1_u8(dst + x_out, F);
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
@ -110,8 +109,7 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
+ (uint64_t)B * irow[x_out]; + (uint64_t)B * irow[x_out];
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
const int v = (int)MULT_FIX_C(J, fy_scale); const int v = (int)MULT_FIX_C(J, fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} }
} }
@ -135,23 +133,22 @@ static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) {
for (x_out = 0; x_out < max_span; x_out += 8) { for (x_out = 0; x_out < max_span; x_out += 8) {
LOAD_32x8(frow + x_out, in0, in1); LOAD_32x8(frow + x_out, in0, in1);
LOAD_32x8(irow + x_out, in2, in3); LOAD_32x8(irow + x_out, in2, in3);
const uint32x4_t A0 = MULT_FIX(in0, yscale_half); const uint32x4_t A0 = MULT_FIX_FLOOR(in0, yscale_half);
const uint32x4_t A1 = MULT_FIX(in1, yscale_half); const uint32x4_t A1 = MULT_FIX_FLOOR(in1, yscale_half);
const uint32x4_t B0 = vqsubq_u32(in2, A0); const uint32x4_t B0 = vqsubq_u32(in2, A0);
const uint32x4_t B1 = vqsubq_u32(in3, A1); const uint32x4_t B1 = vqsubq_u32(in3, A1);
const uint32x4_t C0 = MULT_FIX_FLOOR(B0, fxy_scale_half); const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half);
const uint32x4_t C1 = MULT_FIX_FLOOR(B1, fxy_scale_half); const uint32x4_t C1 = MULT_FIX(B1, fxy_scale_half);
const uint16x4_t D0 = vmovn_u32(C0); const uint16x4_t D0 = vmovn_u32(C0);
const uint16x4_t D1 = vmovn_u32(C1); const uint16x4_t D1 = vmovn_u32(C1);
const uint8x8_t E = vmovn_u16(vcombine_u16(D0, D1)); const uint8x8_t E = vqmovn_u16(vcombine_u16(D0, D1));
vst1_u8(dst + x_out, E); vst1_u8(dst + x_out, E);
STORE_32x8(A0, A1, irow + x_out); STORE_32x8(A0, A1, irow + x_out);
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const uint32_t frac = (uint32_t)MULT_FIX_C(frow[x_out], yscale); const uint32_t frac = (uint32_t)MULT_FIX_FLOOR_C(frow[x_out], yscale);
const int v = (int)MULT_FIX_FLOOR_C(irow[x_out] - frac, fxy_scale); const int v = (int)MULT_FIX_C(irow[x_out] - frac, fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = frac; // new fractional start irow[x_out] = frac; // new fractional start
} }
} else { } else {
@ -161,14 +158,13 @@ static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) {
const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half); const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half);
const uint16x4_t B0 = vmovn_u32(A0); const uint16x4_t B0 = vmovn_u32(A0);
const uint16x4_t B1 = vmovn_u32(A1); const uint16x4_t B1 = vmovn_u32(A1);
const uint8x8_t C = vmovn_u16(vcombine_u16(B0, B1)); const uint8x8_t C = vqmovn_u16(vcombine_u16(B0, B1));
vst1_u8(dst + x_out, C); vst1_u8(dst + x_out, C);
STORE_32x8(zero, zero, irow + x_out); STORE_32x8(zero, zero, irow + x_out);
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale); const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = 0; irow[x_out] = 0;
} }
} }

View file

@ -225,35 +225,6 @@ static WEBP_INLINE void ProcessRow_SSE2(const __m128i* const A0,
_mm_storel_epi64((__m128i*)dst, G); _mm_storel_epi64((__m128i*)dst, G);
} }
static WEBP_INLINE void ProcessRow_Floor_SSE2(const __m128i* const A0,
const __m128i* const A1,
const __m128i* const A2,
const __m128i* const A3,
const __m128i* const mult,
uint8_t* const dst) {
const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0);
const __m128i B0 = _mm_mul_epu32(*A0, *mult);
const __m128i B1 = _mm_mul_epu32(*A1, *mult);
const __m128i B2 = _mm_mul_epu32(*A2, *mult);
const __m128i B3 = _mm_mul_epu32(*A3, *mult);
const __m128i D0 = _mm_srli_epi64(B0, WEBP_RESCALER_RFIX);
const __m128i D1 = _mm_srli_epi64(B1, WEBP_RESCALER_RFIX);
#if (WEBP_RESCALER_RFIX < 32)
const __m128i D2 =
_mm_and_si128(_mm_slli_epi64(B2, 32 - WEBP_RESCALER_RFIX), mask);
const __m128i D3 =
_mm_and_si128(_mm_slli_epi64(B3, 32 - WEBP_RESCALER_RFIX), mask);
#else
const __m128i D2 = _mm_and_si128(B2, mask);
const __m128i D3 = _mm_and_si128(B3, mask);
#endif
const __m128i E0 = _mm_or_si128(D0, D2);
const __m128i E1 = _mm_or_si128(D1, D3);
const __m128i F = _mm_packs_epi32(E0, E1);
const __m128i G = _mm_packus_epi16(F, F);
_mm_storel_epi64((__m128i*)dst, G);
}
static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) { static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
int x_out; int x_out;
uint8_t* const dst = wrk->dst; uint8_t* const dst = wrk->dst;
@ -274,8 +245,7 @@ static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const uint32_t J = frow[x_out]; const uint32_t J = frow[x_out];
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} else { } else {
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
@ -308,8 +278,7 @@ static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
+ (uint64_t)B * irow[x_out]; + (uint64_t)B * irow[x_out];
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
const int v = (int)MULT_FIX(J, wrk->fy_scale); const int v = (int)MULT_FIX(J, wrk->fy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
} }
} }
} }
@ -328,20 +297,15 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
const int scale_xy = wrk->fxy_scale; const int scale_xy = wrk->fxy_scale;
const __m128i mult_xy = _mm_set_epi32(0, scale_xy, 0, scale_xy); const __m128i mult_xy = _mm_set_epi32(0, scale_xy, 0, scale_xy);
const __m128i mult_y = _mm_set_epi32(0, yscale, 0, yscale); const __m128i mult_y = _mm_set_epi32(0, yscale, 0, yscale);
const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) { for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
__m128i A0, A1, A2, A3, B0, B1, B2, B3; __m128i A0, A1, A2, A3, B0, B1, B2, B3;
LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3); LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3);
LoadDispatchAndMult_SSE2(frow + x_out, &mult_y, &B0, &B1, &B2, &B3); LoadDispatchAndMult_SSE2(frow + x_out, &mult_y, &B0, &B1, &B2, &B3);
{ {
const __m128i C0 = _mm_add_epi64(B0, rounder); const __m128i D0 = _mm_srli_epi64(B0, WEBP_RESCALER_RFIX); // = frac
const __m128i C1 = _mm_add_epi64(B1, rounder); const __m128i D1 = _mm_srli_epi64(B1, WEBP_RESCALER_RFIX);
const __m128i C2 = _mm_add_epi64(B2, rounder); const __m128i D2 = _mm_srli_epi64(B2, WEBP_RESCALER_RFIX);
const __m128i C3 = _mm_add_epi64(B3, rounder); const __m128i D3 = _mm_srli_epi64(B3, WEBP_RESCALER_RFIX);
const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX); // = frac
const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
const __m128i D2 = _mm_srli_epi64(C2, WEBP_RESCALER_RFIX);
const __m128i D3 = _mm_srli_epi64(C3, WEBP_RESCALER_RFIX);
const __m128i E0 = _mm_sub_epi64(A0, D0); // irow[x] - frac const __m128i E0 = _mm_sub_epi64(A0, D0); // irow[x] - frac
const __m128i E1 = _mm_sub_epi64(A1, D1); const __m128i E1 = _mm_sub_epi64(A1, D1);
const __m128i E2 = _mm_sub_epi64(A2, D2); const __m128i E2 = _mm_sub_epi64(A2, D2);
@ -352,14 +316,13 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
const __m128i G1 = _mm_or_si128(D1, F3); const __m128i G1 = _mm_or_si128(D1, F3);
_mm_storeu_si128((__m128i*)(irow + x_out + 0), G0); _mm_storeu_si128((__m128i*)(irow + x_out + 0), G0);
_mm_storeu_si128((__m128i*)(irow + x_out + 4), G1); _mm_storeu_si128((__m128i*)(irow + x_out + 4), G1);
ProcessRow_Floor_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out); ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out);
} }
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const uint32_t frac = (int)MULT_FIX(frow[x_out], yscale); const uint32_t frac = (int)MULT_FIX_FLOOR(frow[x_out], yscale);
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale); const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = frac; // new fractional start irow[x_out] = frac; // new fractional start
} }
} else { } else {
@ -375,8 +338,7 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
} }
for (; x_out < x_out_max; ++x_out) { for (; x_out < x_out_max; ++x_out) {
const int v = (int)MULT_FIX(irow[x_out], scale); const int v = (int)MULT_FIX(irow[x_out], scale);
assert(v >= 0 && v <= 255); dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
dst[x_out] = v;
irow[x_out] = 0; irow[x_out] = 0;
} }
} }

View file

@ -191,13 +191,14 @@ void VP8LHashChainClear(VP8LHashChain* const p) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define HASH_MULTIPLIER_HI (0xc6a4a793ULL) static const uint32_t kHashMultiplierHi = 0xc6a4a793u;
#define HASH_MULTIPLIER_LO (0x5bd1e996ULL) static const uint32_t kHashMultiplierLo = 0x5bd1e996u;
static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) { static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
uint32_t GetPixPairHash64(const uint32_t* const argb) {
uint32_t key; uint32_t key;
key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu; key = argb[1] * kHashMultiplierHi;
key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu; key += argb[0] * kHashMultiplierLo;
key = key >> (32 - HASH_BITS); key = key >> (32 - HASH_BITS);
return key; return key;
} }

View file

@ -929,9 +929,8 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
} }
mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings)); mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings));
if (mappings == NULL || !HistoQueueInit(&histo_queue, kHistoQueueSize)) { if (mappings == NULL) return 0;
goto End; if (!HistoQueueInit(&histo_queue, kHistoQueueSize)) goto End;
}
// Fill the initial mapping. // Fill the initial mapping.
for (j = 0, iter = 0; iter < image_histo->size; ++iter) { for (j = 0, iter = 0; iter < image_histo->size; ++iter) {
if (histograms[iter] == NULL) continue; if (histograms[iter] == NULL) continue;

View file

@ -202,7 +202,7 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
} }
if ((value >> 24) == 0 || (value >> 24) == 0xff) { if ((value >> 24) == 0 || (value >> 24) == 0xff) {
// Preserve transparency of fully transparent or fully opaque pixels. // Preserve transparency of fully transparent or fully opaque pixels.
a = NearLosslessDiff(value >> 24, predict >> 24); a = NearLosslessDiff((value >> 24) & 0xff, (predict >> 24) & 0xff);
} else { } else {
a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization); a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization);
} }
@ -215,12 +215,12 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
// The amount by which green has been adjusted during quantization. It is // The amount by which green has been adjusted during quantization. It is
// subtracted from red and blue for compensation, to avoid accumulating two // subtracted from red and blue for compensation, to avoid accumulating two
// quantization errors in them. // quantization errors in them.
green_diff = NearLosslessDiff(new_green, value >> 8); green_diff = NearLosslessDiff(new_green, (value >> 8) & 0xff);
} }
r = NearLosslessComponent(NearLosslessDiff(value >> 16, green_diff), r = NearLosslessComponent(NearLosslessDiff((value >> 16) & 0xff, green_diff),
(predict >> 16) & 0xff, 0xff - new_green, (predict >> 16) & 0xff, 0xff - new_green,
quantization); quantization);
b = NearLosslessComponent(NearLosslessDiff(value, green_diff), b = NearLosslessComponent(NearLosslessDiff(value & 0xff, green_diff),
predict & 0xff, 0xff - new_green, quantization); predict & 0xff, 0xff - new_green, quantization);
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
} }
@ -587,7 +587,7 @@ static void GetBestGreenToRed(
} }
} }
} }
best_tx->green_to_red_ = green_to_red_best; best_tx->green_to_red_ = (green_to_red_best & 0xff);
} }
static float GetPredictionCostCrossColorBlue( static float GetPredictionCostCrossColorBlue(
@ -666,8 +666,8 @@ static void GetBestGreenRedToBlue(
break; // out of iter-loop. break; // out of iter-loop.
} }
} }
best_tx->green_to_blue_ = green_to_blue_best; best_tx->green_to_blue_ = green_to_blue_best & 0xff;
best_tx->red_to_blue_ = red_to_blue_best; best_tx->red_to_blue_ = red_to_blue_best & 0xff;
} }
#undef kGreenRedToBlueMaxIters #undef kGreenRedToBlueMaxIters
#undef kGreenRedToBlueNumAxis #undef kGreenRedToBlueNumAxis

View file

@ -33,7 +33,7 @@
// number of non-zero coeffs below which we consider the block very flat // number of non-zero coeffs below which we consider the block very flat
// (and apply a penalty to complex predictions) // (and apply a penalty to complex predictions)
#define FLATNESS_LIMIT_I16 10 // I16 mode #define FLATNESS_LIMIT_I16 0 // I16 mode (special case)
#define FLATNESS_LIMIT_I4 3 // I4 mode #define FLATNESS_LIMIT_I4 3 // I4 mode
#define FLATNESS_LIMIT_UV 2 // UV mode #define FLATNESS_LIMIT_UV 2 // UV mode
#define FLATNESS_PENALTY 140 // roughly ~1bit per block #define FLATNESS_PENALTY 140 // roughly ~1bit per block
@ -988,6 +988,7 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
VP8ModeScore* rd_cur = &rd_tmp; VP8ModeScore* rd_cur = &rd_tmp;
VP8ModeScore* rd_best = rd; VP8ModeScore* rd_best = rd;
int mode; int mode;
int is_flat = IsFlatSource16(it->yuv_in_ + Y_OFF_ENC);
rd->mode_i16 = -1; rd->mode_i16 = -1;
for (mode = 0; mode < NUM_PRED_MODES; ++mode) { for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
@ -1003,10 +1004,14 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0; tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0;
rd_cur->H = VP8FixedCostsI16[mode]; rd_cur->H = VP8FixedCostsI16[mode];
rd_cur->R = VP8GetCostLuma16(it, rd_cur); rd_cur->R = VP8GetCostLuma16(it, rd_cur);
if (mode > 0 && if (is_flat) {
IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) { // refine the first impression (which was in pixel space)
// penalty to avoid flat area to be mispredicted by complex mode is_flat = IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16);
rd_cur->R += FLATNESS_PENALTY * kNumBlocks; if (is_flat) {
// Block is very flat. We put emphasis on the distortion being very low!
rd_cur->D *= 2;
rd_cur->SD *= 2;
}
} }
// Since we always examine Intra16 first, we can overwrite *rd directly. // Since we always examine Intra16 first, we can overwrite *rd directly.
@ -1087,7 +1092,8 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
: 0; : 0;
rd_tmp.H = mode_costs[mode]; rd_tmp.H = mode_costs[mode];
// Add flatness penalty // Add flatness penalty, to avoid flat area to be mispredicted
// by a complex mode.
if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) { if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) {
rd_tmp.R = FLATNESS_PENALTY * kNumBlocks; rd_tmp.R = FLATNESS_PENALTY * kNumBlocks;
} else { } else {
@ -1242,11 +1248,19 @@ static void RefineUsingDistortion(VP8EncIterator* const it,
if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) { if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) {
continue; continue;
} }
if (score < best_score) { if (score < best_score) {
best_mode = mode; best_mode = mode;
best_score = score; best_score = score;
} }
} }
if (it->x_ == 0 || it->y_ == 0) {
// avoid starting a checkerboard resonance from the border. See bug #432.
if (IsFlatSource16(src)) {
best_mode = (it->x_ == 0) ? 0 : 2;
try_both_modes = 0; // stick to i16
}
}
VP8SetIntra16Mode(it, best_mode); VP8SetIntra16Mode(it, best_mode);
// we'll reconstruct later, if i16 mode actually gets selected // we'll reconstruct later, if i16 mode actually gets selected
} }

View file

@ -32,7 +32,7 @@ extern "C" {
// version numbers // version numbers
#define ENC_MAJ_VERSION 1 #define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 0 #define ENC_MIN_VERSION 0
#define ENC_REV_VERSION 2 #define ENC_REV_VERSION 3
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost

View file

@ -29,7 +29,7 @@ extern "C" {
#define MUX_MAJ_VERSION 1 #define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 0 #define MUX_MIN_VERSION 0
#define MUX_REV_VERSION 2 #define MUX_REV_VERSION 3
// Chunk object. // Chunk object.
typedef struct WebPChunk WebPChunk; typedef struct WebPChunk WebPChunk;

View file

@ -104,7 +104,8 @@ void VP8LoadNewBytes(VP8BitReader* const br) {
} }
// Read a bit with proba 'prob'. Speed-critical function! // Read a bit with proba 'prob'. Speed-critical function!
static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) { static WEBP_INLINE int VP8GetBit(VP8BitReader* const br,
int prob, const char label[]) {
// Don't move this declaration! It makes a big speed difference to store // Don't move this declaration! It makes a big speed difference to store
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
// alter br->range_ value. // alter br->range_ value.
@ -129,13 +130,14 @@ static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
br->bits_ -= shift; br->bits_ -= shift;
} }
br->range_ = range - 1; br->range_ = range - 1;
BT_TRACK(br);
return bit; return bit;
} }
} }
// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here) // simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here)
static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
int VP8GetSigned(VP8BitReader* const br, int v) { int VP8GetSigned(VP8BitReader* const br, int v, const char label[]) {
if (br->bits_ < 0) { if (br->bits_ < 0) {
VP8LoadNewBytes(br); VP8LoadNewBytes(br);
} }
@ -148,11 +150,13 @@ int VP8GetSigned(VP8BitReader* const br, int v) {
br->range_ += mask; br->range_ += mask;
br->range_ |= 1; br->range_ |= 1;
br->value_ -= (bit_t)((split + 1) & mask) << pos; br->value_ -= (bit_t)((split + 1) & mask) << pos;
BT_TRACK(br);
return (v ^ mask) - mask; return (v ^ mask) - mask;
} }
} }
static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) { static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br,
int prob, const char label[]) {
// Don't move this declaration! It makes a big speed difference to store // Don't move this declaration! It makes a big speed difference to store
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
// alter br->range_ value. // alter br->range_ value.
@ -179,6 +183,7 @@ static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) {
br->bits_ -= shift; br->bits_ -= shift;
} }
br->range_ = range; br->range_ = range;
BT_TRACK(br);
return bit; return bit;
} }
} }

View file

@ -109,17 +109,18 @@ void VP8LoadFinalBytes(VP8BitReader* const br) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Higher-level calls // Higher-level calls
uint32_t VP8GetValue(VP8BitReader* const br, int bits) { uint32_t VP8GetValue(VP8BitReader* const br, int bits, const char label[]) {
uint32_t v = 0; uint32_t v = 0;
while (bits-- > 0) { while (bits-- > 0) {
v |= VP8GetBit(br, 0x80) << bits; v |= VP8GetBit(br, 0x80, label) << bits;
} }
return v; return v;
} }
int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { int32_t VP8GetSignedValue(VP8BitReader* const br, int bits,
const int value = VP8GetValue(br, bits); const char label[]) {
return VP8Get(br) ? -value : value; const int value = VP8GetValue(br, bits, label);
return VP8Get(br, label) ? -value : value;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -227,3 +228,78 @@ uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Bit-tracing tool
#if (BITTRACE > 0)
#include <stdlib.h> // for atexit()
#include <stdio.h>
#include <string.h>
#define MAX_NUM_LABELS 32
static struct {
const char* label;
int size;
int count;
} kLabels[MAX_NUM_LABELS];
static int last_label = 0;
static int last_pos = 0;
static const uint8_t* buf_start = NULL;
static int init_done = 0;
static void PrintBitTraces(void) {
int i;
int scale = 1;
int total = 0;
const char* units = "bits";
#if (BITTRACE == 2)
scale = 8;
units = "bytes";
#endif
for (i = 0; i < last_label; ++i) total += kLabels[i].size;
if (total < 1) total = 1; // avoid rounding errors
printf("=== Bit traces ===\n");
for (i = 0; i < last_label; ++i) {
const int skip = 16 - (int)strlen(kLabels[i].label);
const int value = (kLabels[i].size + scale - 1) / scale;
assert(skip > 0);
printf("%s \%*s: %6d %s \t[%5.2f%%] [count: %7d]\n",
kLabels[i].label, skip, "", value, units,
100.f * kLabels[i].size / total,
kLabels[i].count);
}
total = (total + scale - 1) / scale;
printf("Total: %d %s\n", total, units);
}
void BitTrace(const struct VP8BitReader* const br, const char label[]) {
int i, pos;
if (!init_done) {
memset(kLabels, 0, sizeof(kLabels));
atexit(PrintBitTraces);
buf_start = br->buf_;
init_done = 1;
}
pos = (int)(br->buf_ - buf_start) * 8 - br->bits_;
// if there's a too large jump, we've changed partition -> reset counter
if (abs(pos - last_pos) > 32) {
buf_start = br->buf_;
pos = 0;
last_pos = 0;
}
if (br->range_ >= 0x7f) pos += kVP8Log2Range[br->range_ - 0x7f];
for (i = 0; i < last_label; ++i) {
if (!strcmp(label, kLabels[i].label)) break;
}
if (i == MAX_NUM_LABELS) abort(); // overflow!
kLabels[i].label = label;
kLabels[i].size += pos - last_pos;
kLabels[i].count += 1;
if (i == last_label) ++last_label;
last_pos = pos;
}
#endif // BITTRACE > 0
//------------------------------------------------------------------------------

View file

@ -21,6 +21,27 @@
#endif #endif
#include "src/webp/types.h" #include "src/webp/types.h"
// Warning! This macro triggers quite some MACRO wizardry around func signature!
#if !defined(BITTRACE)
#define BITTRACE 0 // 0 = off, 1 = print bits, 2 = print bytes
#endif
#if (BITTRACE > 0)
struct VP8BitReader;
extern void BitTrace(const struct VP8BitReader* const br, const char label[]);
#define BT_TRACK(br) BitTrace(br, label)
#define VP8Get(BR, L) VP8GetValue(BR, 1, L)
#else
#define BT_TRACK(br)
// We'll REMOVE the 'const char label[]' from all signatures and calls (!!):
#define VP8GetValue(BR, N, L) VP8GetValue(BR, N)
#define VP8Get(BR, L) VP8GetValue(BR, 1, L)
#define VP8GetSignedValue(BR, N, L) VP8GetSignedValue(BR, N)
#define VP8GetBit(BR, P, L) VP8GetBit(BR, P)
#define VP8GetBitAlt(BR, P, L) VP8GetBitAlt(BR, P)
#define VP8GetSigned(BR, V, L) VP8GetSigned(BR, V)
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -102,17 +123,15 @@ void VP8BitReaderSetBuffer(VP8BitReader* const br,
void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset); void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset);
// return the next value made of 'num_bits' bits // return the next value made of 'num_bits' bits
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); uint32_t VP8GetValue(VP8BitReader* const br, int num_bits, const char label[]);
static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
return VP8GetValue(br, 1);
}
// return the next value with sign-extension. // return the next value with sign-extension.
int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits); int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits,
const char label[]);
// bit_reader_inl.h will implement the following methods: // bit_reader_inl.h will implement the following methods:
// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) // static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob, ...)
// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) // static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v, ...)
// and should be included by the .c files that actually need them. // and should be included by the .c files that actually need them.
// This is to avoid recompiling the whole library whenever this file is touched, // This is to avoid recompiling the whole library whenever this file is touched,
// and also allowing platform-specific ad-hoc hacks. // and also allowing platform-specific ad-hoc hacks.

View file

@ -70,7 +70,7 @@ static void Flush(VP8BitWriter* const bw) {
const int value = (bits & 0x100) ? 0x00 : 0xff; const int value = (bits & 0x100) ? 0x00 : 0xff;
for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value; for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value;
} }
bw->buf_[pos++] = bits; bw->buf_[pos++] = bits & 0xff;
bw->pos_ = pos; bw->pos_ = pos;
} else { } else {
bw->run_++; // delay writing of bytes 0xff, pending eventual carry. bw->run_++; // delay writing of bytes 0xff, pending eventual carry.

View file

@ -17,6 +17,7 @@
#include <assert.h> #include <assert.h>
#include "src/dsp/dsp.h"
#include "src/webp/types.h" #include "src/webp/types.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -30,10 +31,11 @@ typedef struct {
int hash_bits_; int hash_bits_;
} VP8LColorCache; } VP8LColorCache;
static const uint64_t kHashMul = 0x1e35a7bdull; static const uint32_t kHashMul = 0x1e35a7bdu;
static WEBP_INLINE int VP8LHashPix(uint32_t argb, int shift) { static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
return (int)(((argb * kHashMul) & 0xffffffffu) >> shift); int VP8LHashPix(uint32_t argb, int shift) {
return (int)((argb * kHashMul) >> shift);
} }
static WEBP_INLINE uint32_t VP8LColorCacheLookup( static WEBP_INLINE uint32_t VP8LColorCacheLookup(

View file

@ -91,7 +91,8 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
assert(code_lengths_size != 0); assert(code_lengths_size != 0);
assert(code_lengths != NULL); assert(code_lengths != NULL);
assert(root_table != NULL); assert((root_table != NULL && sorted != NULL) ||
(root_table == NULL && sorted == NULL));
assert(root_bits > 0); assert(root_bits > 0);
// Build histogram of code lengths. // Build histogram of code lengths.
@ -120,16 +121,22 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
for (symbol = 0; symbol < code_lengths_size; ++symbol) { for (symbol = 0; symbol < code_lengths_size; ++symbol) {
const int symbol_code_length = code_lengths[symbol]; const int symbol_code_length = code_lengths[symbol];
if (code_lengths[symbol] > 0) { if (code_lengths[symbol] > 0) {
sorted[offset[symbol_code_length]++] = symbol; if (sorted != NULL) {
sorted[offset[symbol_code_length]++] = symbol;
} else {
offset[symbol_code_length]++;
}
} }
} }
// Special case code with only one value. // Special case code with only one value.
if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) {
HuffmanCode code; if (sorted != NULL) {
code.bits = 0; HuffmanCode code;
code.value = (uint16_t)sorted[0]; code.bits = 0;
ReplicateValue(table, 1, total_size, code); code.value = (uint16_t)sorted[0];
ReplicateValue(table, 1, total_size, code);
}
return total_size; return total_size;
} }
@ -151,6 +158,7 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
if (num_open < 0) { if (num_open < 0) {
return 0; return 0;
} }
if (root_table == NULL) continue;
for (; count[len] > 0; --count[len]) { for (; count[len] > 0; --count[len]) {
HuffmanCode code; HuffmanCode code;
code.bits = (uint8_t)len; code.bits = (uint8_t)len;
@ -169,6 +177,7 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
if (num_open < 0) { if (num_open < 0) {
return 0; return 0;
} }
if (root_table == NULL) continue;
for (; count[len] > 0; --count[len]) { for (; count[len] > 0; --count[len]) {
HuffmanCode code; HuffmanCode code;
if ((key & mask) != low) { if ((key & mask) != low) {
@ -206,7 +215,10 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
const int code_lengths[], int code_lengths_size) { const int code_lengths[], int code_lengths_size) {
int total_size; int total_size;
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE); assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
if (code_lengths_size <= SORTED_SIZE_CUTOFF) { if (root_table == NULL) {
total_size = BuildHuffmanTable(NULL, root_bits,
code_lengths, code_lengths_size, NULL);
} else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
// use local stack-allocated array. // use local stack-allocated array.
uint16_t sorted[SORTED_SIZE_CUTOFF]; uint16_t sorted[SORTED_SIZE_CUTOFF];
total_size = BuildHuffmanTable(root_table, root_bits, total_size = BuildHuffmanTable(root_table, root_bits,

View file

@ -78,6 +78,8 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
// the huffman table. // the huffman table.
// Returns built table size or 0 in case of error (invalid tree or // Returns built table size or 0 in case of error (invalid tree or
// memory error). // memory error).
// If root_table is NULL, it returns 0 if a lookup cannot be built, something
// > 0 otherwise (but not the table size).
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
const int code_lengths[], int code_lengths_size); const int code_lengths[], int code_lengths_size);

View file

@ -84,14 +84,14 @@ int WebPRescalerGetScaledDimensions(int src_width, int src_height,
int height = *scaled_height; int height = *scaled_height;
// if width is unspecified, scale original proportionally to height ratio. // if width is unspecified, scale original proportionally to height ratio.
if (width == 0) { if (width == 0 && src_height > 0) {
width = width =
(int)(((uint64_t)src_width * height + src_height / 2) / src_height); (int)(((uint64_t)src_width * height + src_height - 1) / src_height);
} }
// if height is unspecified, scale original proportionally to width ratio. // if height is unspecified, scale original proportionally to width ratio.
if (height == 0) { if (height == 0 && src_width > 0) {
height = height =
(int)(((uint64_t)src_height * width + src_width / 2) / src_width); (int)(((uint64_t)src_height * width + src_width - 1) / src_width);
} }
// Check if the overall dimensions still make sense. // Check if the overall dimensions still make sense.
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {

View file

@ -217,8 +217,12 @@ static THREADFN ThreadLoop(void* ptr) {
done = 1; done = 1;
} }
// signal to the main thread that we're done (for Sync()) // signal to the main thread that we're done (for Sync())
pthread_cond_signal(&impl->condition_); // Note the associated mutex does not need to be held when signaling the
// condition. Unlocking the mutex first may improve performance in some
// implementations, avoiding the case where the waiting thread can't
// reacquire the mutex when woken.
pthread_mutex_unlock(&impl->mutex_); pthread_mutex_unlock(&impl->mutex_);
pthread_cond_signal(&impl->condition_);
} }
return THREAD_RETURN(NULL); // Thread is finished return THREAD_RETURN(NULL); // Thread is finished
} }
@ -240,7 +244,13 @@ static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) {
// assign new status and release the working thread if needed // assign new status and release the working thread if needed
if (new_status != OK) { if (new_status != OK) {
worker->status_ = new_status; worker->status_ = new_status;
// Note the associated mutex does not need to be held when signaling the
// condition. Unlocking the mutex first may improve performance in some
// implementations, avoiding the case where the waiting thread can't
// reacquire the mutex when woken.
pthread_mutex_unlock(&impl->mutex_);
pthread_cond_signal(&impl->condition_); pthread_cond_signal(&impl->condition_);
return;
} }
} }
pthread_mutex_unlock(&impl->mutex_); pthread_mutex_unlock(&impl->mutex_);

View file

@ -92,14 +92,14 @@ static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
// Store 16, 24 or 32 bits in little-endian order. // Store 16, 24 or 32 bits in little-endian order.
static WEBP_INLINE void PutLE16(uint8_t* const data, int val) { static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
assert(val < (1 << 16)); assert(val < (1 << 16));
data[0] = (val >> 0); data[0] = (val >> 0) & 0xff;
data[1] = (val >> 8); data[1] = (val >> 8) & 0xff;
} }
static WEBP_INLINE void PutLE24(uint8_t* const data, int val) { static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
assert(val < (1 << 24)); assert(val < (1 << 24));
PutLE16(data, val & 0xffff); PutLE16(data, val & 0xffff);
data[2] = (val >> 16); data[2] = (val >> 16) & 0xff;
} }
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {

View file

@ -62,6 +62,10 @@ WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra,
// These functions are the equivalent of the above, but compressing in a // These functions are the equivalent of the above, but compressing in a
// lossless manner. Files are usually larger than lossy format, but will // lossless manner. Files are usually larger than lossy format, but will
// not suffer any compression loss. // not suffer any compression loss.
// Note these functions, like the lossy versions, use the library's default
// settings. For lossless this means 'exact' is disabled. RGB values in
// transparent areas will be modified to improve compression. To avoid this,
// use WebPEncode() and set WebPConfig::exact to 1.
WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb, WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb,
int width, int height, int stride, int width, int height, int stride,
uint8_t** output); uint8_t** output);