mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-15 13:29:54 +02:00
Compare commits
19 Commits
306335198d
...
1.0.0
Author | SHA1 | Date | |
---|---|---|---|
127a4d7c99 | |||
b2c2d237a4 | |||
384e0c32e7 | |||
a19a6bd30b | |||
ec495eb030 | |||
7c2c00851a | |||
0b271f10c4 | |||
916aac82c5 | |||
99d0790233 | |||
c0226fd91c | |||
a14e0f6465 | |||
667d17a8a4 | |||
f4cf238a41 | |||
61ff26aeeb | |||
6f643f2417 | |||
706ff9c325 | |||
a0b85e4a36 | |||
dad31750e3 | |||
4b282e13ad |
@ -143,8 +143,18 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
|||||||
if (!ok) return 0; // These are fatal failures, can't proceed.
|
if (!ok) return 0; // These are fatal failures, can't proceed.
|
||||||
|
|
||||||
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||||||
ok = CompareValues(img1->loop_count, img2->loop_count,
|
int max_loop_count_workaround = 0;
|
||||||
"Loop count mismatch") && ok;
|
// Transcodes to webp increase the gif loop count by 1 for compatibility.
|
||||||
|
// When the gif has the maximum value the webp value will be off by one.
|
||||||
|
if ((img1->format == ANIM_GIF && img1->loop_count == 65536 &&
|
||||||
|
img2->format == ANIM_WEBP && img2->loop_count == 65535) ||
|
||||||
|
(img1->format == ANIM_WEBP && img1->loop_count == 65535 &&
|
||||||
|
img2->format == ANIM_GIF && img2->loop_count == 65536)) {
|
||||||
|
max_loop_count_workaround = 1;
|
||||||
|
}
|
||||||
|
ok = (max_loop_count_workaround ||
|
||||||
|
CompareValues(img1->loop_count, img2->loop_count,
|
||||||
|
"Loop count mismatch")) && ok;
|
||||||
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
||||||
premultiply) && ok;
|
premultiply) && ok;
|
||||||
}
|
}
|
||||||
|
@ -275,6 +275,7 @@ static int ReadAnimatedWebP(const char filename[],
|
|||||||
prev_frame_timestamp = timestamp;
|
prev_frame_timestamp = timestamp;
|
||||||
}
|
}
|
||||||
ok = dump_ok;
|
ok = dump_ok;
|
||||||
|
if (ok) image->format = ANIM_WEBP;
|
||||||
|
|
||||||
End:
|
End:
|
||||||
WebPAnimDecoderDelete(dec);
|
WebPAnimDecoderDelete(dec);
|
||||||
@ -687,6 +688,7 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
image->format = ANIM_GIF;
|
||||||
DGifCloseFile(gif, NULL);
|
DGifCloseFile(gif, NULL);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ANIM_GIF,
|
||||||
|
ANIM_WEBP
|
||||||
|
} AnimatedFileFormat;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t* rgba; // Decoded and reconstructed full frame.
|
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||||
int duration; // Frame duration in milliseconds.
|
int duration; // Frame duration in milliseconds.
|
||||||
@ -29,6 +34,7 @@ typedef struct {
|
|||||||
} DecodedFrame;
|
} DecodedFrame;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
AnimatedFileFormat format;
|
||||||
uint32_t canvas_width;
|
uint32_t canvas_width;
|
||||||
uint32_t canvas_height;
|
uint32_t canvas_height;
|
||||||
uint32_t bgcolor;
|
uint32_t bgcolor;
|
||||||
|
@ -478,7 +478,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
stored_loop_count = 1;
|
stored_loop_count = 1;
|
||||||
loop_count = 1;
|
loop_count = 1;
|
||||||
}
|
}
|
||||||
} else if (loop_count > 0) {
|
} else if (loop_count > 0 && loop_count < 65535) {
|
||||||
// adapt GIF's semantic to WebP's (except in the infinite-loop case)
|
// adapt GIF's semantic to WebP's (except in the infinite-loop case)
|
||||||
loop_count += 1;
|
loop_count += 1;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,8 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
|||||||
} else { // RGB checks
|
} else { // RGB checks
|
||||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||||
const int stride = abs(buf->stride);
|
const int stride = abs(buf->stride);
|
||||||
const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
|
const uint64_t size =
|
||||||
|
MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride);
|
||||||
ok &= (size <= buf->size);
|
ok &= (size <= buf->size);
|
||||||
ok &= (stride >= width * kModeBpp[mode]);
|
ok &= (stride >= width * kModeBpp[mode]);
|
||||||
ok &= (buf->rgba != NULL);
|
ok &= (buf->rgba != NULL);
|
||||||
|
@ -140,10 +140,9 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
|||||||
if (NeedCompressedAlpha(idec)) {
|
if (NeedCompressedAlpha(idec)) {
|
||||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||||
dec->alpha_data_ += offset;
|
dec->alpha_data_ += offset;
|
||||||
if (alph_dec != NULL) {
|
if (alph_dec != NULL && alph_dec->vp8l_dec_ != NULL) {
|
||||||
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
|
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
|
||||||
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
|
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
|
||||||
assert(alph_vp8l_dec != NULL);
|
|
||||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
|
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
|
||||||
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
|
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
|
||||||
dec->alpha_data_ + ALPHA_HEADER_LEN,
|
dec->alpha_data_ + ALPHA_HEADER_LEN,
|
||||||
@ -283,10 +282,8 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
|||||||
|
|
||||||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||||
if (idec->state_ == STATE_VP8_DATA) {
|
if (idec->state_ == STATE_VP8_DATA) {
|
||||||
VP8Io* const io = &idec->io_;
|
// Synchronize the thread, clean-up and check for errors.
|
||||||
if (io->teardown != NULL) {
|
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||||
io->teardown(io);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
idec->state_ = STATE_ERROR;
|
idec->state_ = STATE_ERROR;
|
||||||
return error;
|
return error;
|
||||||
@ -451,7 +448,10 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
|||||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||||
VP8Io* const io = &idec->io_;
|
VP8Io* const io = &idec->io_;
|
||||||
|
|
||||||
assert(dec->ready_);
|
// Make sure partition #0 has been read before, to set dec to ready_.
|
||||||
|
if (!dec->ready_) {
|
||||||
|
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||||
|
}
|
||||||
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
||||||
if (idec->last_mb_y_ != dec->mb_y_) {
|
if (idec->last_mb_y_ != dec->mb_y_) {
|
||||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||||
@ -491,6 +491,7 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
|||||||
}
|
}
|
||||||
// Synchronize the thread and check for errors.
|
// Synchronize the thread and check for errors.
|
||||||
if (!VP8ExitCritical(dec, io)) {
|
if (!VP8ExitCritical(dec, io)) {
|
||||||
|
idec->state_ = STATE_ERROR; // prevent re-entry in IDecError
|
||||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||||
}
|
}
|
||||||
dec->ready_ = 0;
|
dec->ready_ = 0;
|
||||||
@ -571,6 +572,10 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
|||||||
status = DecodePartition0(idec);
|
status = DecodePartition0(idec);
|
||||||
}
|
}
|
||||||
if (idec->state_ == STATE_VP8_DATA) {
|
if (idec->state_ == STATE_VP8_DATA) {
|
||||||
|
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||||
|
if (dec == NULL) {
|
||||||
|
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||||
|
}
|
||||||
status = DecodeRemaining(idec);
|
status = DecodeRemaining(idec);
|
||||||
}
|
}
|
||||||
if (idec->state_ == STATE_VP8L_HEADER) {
|
if (idec->state_ == STATE_VP8L_HEADER) {
|
||||||
|
@ -884,7 +884,11 @@ static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) {
|
|||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
#if !defined(WORDS_BIGENDIAN)
|
||||||
memcpy(&pattern, src, sizeof(uint16_t));
|
memcpy(&pattern, src, sizeof(uint16_t));
|
||||||
|
#else
|
||||||
|
pattern = ((uint32_t)src[0] << 8) | src[1];
|
||||||
|
#endif
|
||||||
#if defined(__arm__) || defined(_M_ARM)
|
#if defined(__arm__) || defined(_M_ARM)
|
||||||
pattern |= pattern << 16;
|
pattern |= pattern << 16;
|
||||||
#elif defined(WEBP_USE_MIPS_DSP_R2)
|
#elif defined(WEBP_USE_MIPS_DSP_R2)
|
||||||
@ -1523,7 +1527,6 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec,
|
|||||||
if (dec == NULL) return 0;
|
if (dec == NULL) return 0;
|
||||||
|
|
||||||
assert(alph_dec != NULL);
|
assert(alph_dec != NULL);
|
||||||
alph_dec->vp8l_dec_ = dec;
|
|
||||||
|
|
||||||
dec->width_ = alph_dec->width_;
|
dec->width_ = alph_dec->width_;
|
||||||
dec->height_ = alph_dec->height_;
|
dec->height_ = alph_dec->height_;
|
||||||
@ -1555,11 +1558,12 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec,
|
|||||||
|
|
||||||
if (!ok) goto Err;
|
if (!ok) goto Err;
|
||||||
|
|
||||||
|
// Only set here, once we are sure it is valid (to avoid thread races).
|
||||||
|
alph_dec->vp8l_dec_ = dec;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
Err:
|
Err:
|
||||||
VP8LDelete(alph_dec->vp8l_dec_);
|
VP8LDelete(dec);
|
||||||
alph_dec->vp8l_dec_ = NULL;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first,
|
|||||||
static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
|
static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
|
||||||
int width, int height,
|
int width, int height,
|
||||||
uint8_t* dst, int dst_stride) {
|
uint8_t* dst, int dst_stride) {
|
||||||
uint32_t alpha_mask = 0xffffffffu;
|
uint32_t alpha_mask = 0xffu;
|
||||||
uint8x8_t mask8 = vdup_n_u8(0xff);
|
uint8x8_t mask8 = vdup_n_u8(0xff);
|
||||||
uint32_t tmp[2];
|
uint32_t tmp[2];
|
||||||
int i, j;
|
int i, j;
|
||||||
@ -107,6 +107,7 @@ static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
|
|||||||
dst += dst_stride;
|
dst += dst_stride;
|
||||||
}
|
}
|
||||||
vst1_u8((uint8_t*)tmp, mask8);
|
vst1_u8((uint8_t*)tmp, mask8);
|
||||||
|
alpha_mask *= 0x01010101;
|
||||||
alpha_mask &= tmp[0];
|
alpha_mask &= tmp[0];
|
||||||
alpha_mask &= tmp[1];
|
alpha_mask &= tmp[1];
|
||||||
return (alpha_mask != 0xffffffffu);
|
return (alpha_mask != 0xffffffffu);
|
||||||
@ -134,7 +135,7 @@ static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride,
|
|||||||
static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
|
static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
|
||||||
int width, int height,
|
int width, int height,
|
||||||
uint8_t* alpha, int alpha_stride) {
|
uint8_t* alpha, int alpha_stride) {
|
||||||
uint32_t alpha_mask = 0xffffffffu;
|
uint32_t alpha_mask = 0xffu;
|
||||||
uint8x8_t mask8 = vdup_n_u8(0xff);
|
uint8x8_t mask8 = vdup_n_u8(0xff);
|
||||||
uint32_t tmp[2];
|
uint32_t tmp[2];
|
||||||
int i, j;
|
int i, j;
|
||||||
@ -156,6 +157,7 @@ static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
|
|||||||
alpha += alpha_stride;
|
alpha += alpha_stride;
|
||||||
}
|
}
|
||||||
vst1_u8((uint8_t*)tmp, mask8);
|
vst1_u8((uint8_t*)tmp, mask8);
|
||||||
|
alpha_mask *= 0x01010101;
|
||||||
alpha_mask &= tmp[0];
|
alpha_mask &= tmp[0];
|
||||||
alpha_mask &= tmp[1];
|
alpha_mask &= tmp[1];
|
||||||
return (alpha_mask == 0xffffffffu);
|
return (alpha_mask == 0xffffffffu);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Row import
|
// Row import
|
||||||
@ -108,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);
|
||||||
@ -119,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,22 +136,21 @@ 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(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef MULT_FIX_FLOOR
|
||||||
#undef MULT_FIX
|
#undef MULT_FIX
|
||||||
#undef ROUNDER
|
#undef ROUNDER
|
||||||
|
|
||||||
|
@ -209,6 +209,7 @@ static void ExportRowExpand_MIPS32(WebPRescaler* const wrk) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 // disabled for now. TODO(skal): make match the C-code
|
||||||
static void ExportRowShrink_MIPS32(WebPRescaler* const wrk) {
|
static void ExportRowShrink_MIPS32(WebPRescaler* const wrk) {
|
||||||
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||||
uint8_t* dst = wrk->dst;
|
uint8_t* dst = wrk->dst;
|
||||||
@ -273,6 +274,7 @@ static void ExportRowShrink_MIPS32(WebPRescaler* const wrk) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Entry point
|
// Entry point
|
||||||
@ -283,7 +285,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPS32(void) {
|
|||||||
WebPRescalerImportRowExpand = ImportRowExpand_MIPS32;
|
WebPRescalerImportRowExpand = ImportRowExpand_MIPS32;
|
||||||
WebPRescalerImportRowShrink = ImportRowShrink_MIPS32;
|
WebPRescalerImportRowShrink = ImportRowShrink_MIPS32;
|
||||||
WebPRescalerExportRowExpand = ExportRowExpand_MIPS32;
|
WebPRescalerExportRowExpand = ExportRowExpand_MIPS32;
|
||||||
WebPRescalerExportRowShrink = ExportRowShrink_MIPS32;
|
// WebPRescalerExportRowShrink = ExportRowShrink_MIPS32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !WEBP_USE_MIPS32
|
#else // !WEBP_USE_MIPS32
|
||||||
|
@ -20,10 +20,12 @@
|
|||||||
|
|
||||||
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Row export
|
// Row export
|
||||||
|
|
||||||
|
#if 0 // disabled for now. TODO(skal): make match the C-code
|
||||||
static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
|
static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
|
||||||
int i;
|
int i;
|
||||||
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||||
@ -105,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(*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 {
|
||||||
@ -154,13 +155,13 @@ 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(*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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
|
static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
|
||||||
int i;
|
int i;
|
||||||
@ -216,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);
|
||||||
@ -288,12 +288,12 @@ 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef MULT_FIX_FLOOR
|
||||||
#undef MULT_FIX
|
#undef MULT_FIX
|
||||||
#undef ROUNDER
|
#undef ROUNDER
|
||||||
|
|
||||||
@ -304,7 +304,7 @@ extern void WebPRescalerDspInitMIPSdspR2(void);
|
|||||||
|
|
||||||
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPSdspR2(void) {
|
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPSdspR2(void) {
|
||||||
WebPRescalerExportRowExpand = ExportRowExpand_MIPSdspR2;
|
WebPRescalerExportRowExpand = ExportRowExpand_MIPSdspR2;
|
||||||
WebPRescalerExportRowShrink = ExportRowShrink_MIPSdspR2;
|
// WebPRescalerExportRowShrink = ExportRowShrink_MIPSdspR2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !WEBP_USE_MIPS_DSP_R2
|
#else // !WEBP_USE_MIPS_DSP_R2
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
#define CALC_MULT_FIX_16(in0, in1, in2, in3, scale, shift, dst) do { \
|
#define CALC_MULT_FIX_16(in0, in1, in2, in3, scale, shift, dst) do { \
|
||||||
v4u32 tmp0, tmp1, tmp2, tmp3; \
|
v4u32 tmp0, tmp1, tmp2, tmp3; \
|
||||||
@ -165,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,6 +261,7 @@ static void RescalerExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 // disabled for now. TODO(skal): make match the C-code
|
||||||
static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow,
|
static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow,
|
||||||
uint8_t* dst, int length,
|
uint8_t* dst, int length,
|
||||||
const uint32_t yscale,
|
const uint32_t yscale,
|
||||||
@ -340,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(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,6 +424,7 @@ static void RescalerExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
|
|||||||
ExportRowShrink_1(irow, dst, x_out_max, wrk);
|
ExportRowShrink_1(irow, dst, x_out_max, wrk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Entry point
|
// Entry point
|
||||||
@ -434,7 +433,7 @@ extern void WebPRescalerDspInitMSA(void);
|
|||||||
|
|
||||||
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMSA(void) {
|
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMSA(void) {
|
||||||
WebPRescalerExportRowExpand = RescalerExportRowExpand_MIPSdspR2;
|
WebPRescalerExportRowExpand = RescalerExportRowExpand_MIPSdspR2;
|
||||||
WebPRescalerExportRowShrink = RescalerExportRowShrink_MIPSdspR2;
|
// WebPRescalerExportRowShrink = RescalerExportRowShrink_MIPSdspR2;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !WEBP_USE_MSA
|
#else // !WEBP_USE_MSA
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
#define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
#define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
#define MULT_FIX_FLOOR_C(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
#define LOAD_32x4(SRC, DST) const uint32x4_t DST = vld1q_u32((SRC))
|
#define LOAD_32x4(SRC, DST) const uint32x4_t DST = vld1q_u32((SRC))
|
||||||
#define LOAD_32x8(SRC, DST0, DST1) \
|
#define LOAD_32x8(SRC, DST0, DST1) \
|
||||||
@ -35,8 +36,11 @@
|
|||||||
|
|
||||||
#if (WEBP_RESCALER_RFIX == 32)
|
#if (WEBP_RESCALER_RFIX == 32)
|
||||||
#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
|
#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
|
||||||
#define MULT_FIX(A, B) /* note: B is actualy scale>>1. See MAKE_HALF_CST */ \
|
// note: B is actualy scale>>1. See MAKE_HALF_CST
|
||||||
|
#define MULT_FIX(A, B) \
|
||||||
vreinterpretq_u32_s32(vqrdmulhq_s32(vreinterpretq_s32_u32((A)), (B)))
|
vreinterpretq_u32_s32(vqrdmulhq_s32(vreinterpretq_s32_u32((A)), (B)))
|
||||||
|
#define MULT_FIX_FLOOR(A, B) \
|
||||||
|
vreinterpretq_u32_s32(vqdmulhq_s32(vreinterpretq_s32_u32((A)), (B)))
|
||||||
#else
|
#else
|
||||||
#error "MULT_FIX/WEBP_RESCALER_RFIX need some more work"
|
#error "MULT_FIX/WEBP_RESCALER_RFIX need some more work"
|
||||||
#endif
|
#endif
|
||||||
@ -77,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);
|
||||||
@ -98,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) {
|
||||||
@ -106,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,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(B0, fxy_scale_half);
|
const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half);
|
||||||
const uint32x4_t C1 = MULT_FIX(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_C(irow[x_out] - frac, wrk->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 {
|
||||||
@ -157,19 +158,24 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef MULT_FIX_FLOOR_C
|
||||||
|
#undef MULT_FIX_C
|
||||||
|
#undef MULT_FIX_FLOOR
|
||||||
|
#undef MULT_FIX
|
||||||
|
#undef ROUNDER
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
extern void WebPRescalerDspInitNEON(void);
|
extern void WebPRescalerDspInitNEON(void);
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
// input: 8 bytes ABCDEFGH -> output: A0E0B0F0C0G0D0H0
|
// input: 8 bytes ABCDEFGH -> output: A0E0B0F0C0G0D0H0
|
||||||
static void LoadTwoPixels_SSE2(const uint8_t* const src, __m128i* out) {
|
static void LoadTwoPixels_SSE2(const uint8_t* const src, __m128i* out) {
|
||||||
@ -244,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);
|
||||||
@ -278,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,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);
|
||||||
@ -326,10 +320,9 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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(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 {
|
||||||
@ -345,13 +338,13 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef MULT_FIX_FLOOR
|
||||||
#undef MULT_FIX
|
#undef MULT_FIX
|
||||||
#undef ROUNDER
|
#undef ROUNDER
|
||||||
|
|
||||||
|
@ -577,7 +577,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
(CostModel*)WebPSafeCalloc(1ULL, cost_model_size);
|
(CostModel*)WebPSafeCalloc(1ULL, cost_model_size);
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
CostManager* cost_manager =
|
CostManager* cost_manager =
|
||||||
(CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager));
|
(CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));
|
||||||
int offset_prev = -1, len_prev = -1;
|
int offset_prev = -1, len_prev = -1;
|
||||||
double offset_cost = -1;
|
double offset_cost = -1;
|
||||||
int first_offset_is_constant = -1; // initialized with 'impossible' value
|
int first_offset_is_constant = -1; // initialized with 'impossible' value
|
||||||
|
@ -910,13 +910,14 @@ static VP8LBackwardRefs* GetBackwardReferences(
|
|||||||
quality >= 25) {
|
quality >= 25) {
|
||||||
const VP8LHashChain* const hash_chain_tmp =
|
const VP8LHashChain* const hash_chain_tmp =
|
||||||
(lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
|
(lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
|
||||||
if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
|
double bit_cost_trace;
|
||||||
hash_chain_tmp, best, worst)) {
|
if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
|
||||||
double bit_cost_trace;
|
hash_chain_tmp, best, worst)) {
|
||||||
VP8LHistogramCreate(histo, worst, *cache_bits);
|
goto Error;
|
||||||
bit_cost_trace = VP8LHistogramEstimateBits(histo);
|
|
||||||
if (bit_cost_trace < bit_cost_best) best = worst;
|
|
||||||
}
|
}
|
||||||
|
VP8LHistogramCreate(histo, worst, *cache_bits);
|
||||||
|
bit_cost_trace = VP8LHistogramEstimateBits(histo);
|
||||||
|
if (bit_cost_trace < bit_cost_best) best = worst;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackwardReferences2DLocality(width, best);
|
BackwardReferences2DLocality(width, best);
|
||||||
|
@ -1685,11 +1685,16 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
|
const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
|
||||||
int ok_main;
|
int ok_main;
|
||||||
|
|
||||||
|
if (enc_main == NULL || !VP8LBitWriterInit(&bw_side, 0)) {
|
||||||
|
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||||
|
VP8LEncoderDelete(enc_main);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Analyze image (entropy, num_palettes etc)
|
// Analyze image (entropy, num_palettes etc)
|
||||||
if (enc_main == NULL ||
|
if (!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
|
||||||
!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
|
|
||||||
&red_and_blue_always_zero) ||
|
&red_and_blue_always_zero) ||
|
||||||
!EncoderInit(enc_main) || !VP8LBitWriterInit(&bw_side, 0)) {
|
!EncoderInit(enc_main)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#ifndef WEBP_MUX_MUXI_H_
|
#ifndef WEBP_MUX_MUXI_H_
|
||||||
#define WEBP_MUX_MUXI_H_
|
#define WEBP_MUX_MUXI_H_
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "src/dec/vp8i_dec.h"
|
#include "src/dec/vp8i_dec.h"
|
||||||
#include "src/dec/vp8li_dec.h"
|
#include "src/dec/vp8li_dec.h"
|
||||||
@ -143,13 +144,13 @@ void ChunkListDelete(WebPChunk** const chunk_list);
|
|||||||
|
|
||||||
// Returns size of the chunk including chunk header and padding byte (if any).
|
// Returns size of the chunk including chunk header and padding byte (if any).
|
||||||
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
||||||
|
assert(chunk_size <= MAX_CHUNK_PAYLOAD);
|
||||||
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size of a chunk including header and padding.
|
// Size of a chunk including header and padding.
|
||||||
static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
|
static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
|
||||||
const size_t data_size = chunk->data_.size;
|
const size_t data_size = chunk->data_.size;
|
||||||
assert(data_size < MAX_CHUNK_PAYLOAD);
|
|
||||||
return SizeWithPadding(data_size);
|
return SizeWithPadding(data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
|
|||||||
// Sanity checks.
|
// Sanity checks.
|
||||||
if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
|
if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
|
||||||
chunk_size = GetLE32(data + TAG_SIZE);
|
chunk_size = GetLE32(data + TAG_SIZE);
|
||||||
|
if (chunk_size > MAX_CHUNK_PAYLOAD) return WEBP_MUX_BAD_DATA;
|
||||||
|
|
||||||
{
|
{
|
||||||
const size_t chunk_disk_size = SizeWithPadding(chunk_size);
|
const size_t chunk_disk_size = SizeWithPadding(chunk_size);
|
||||||
@ -137,6 +138,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
|||||||
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_IMAGE:
|
case WEBP_CHUNK_IMAGE:
|
||||||
|
if (wpi->img_ != NULL) goto Fail; // Only 1 image chunk allowed.
|
||||||
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
||||||
if (!MuxImageFinalize(wpi)) goto Fail;
|
if (!MuxImageFinalize(wpi)) goto Fail;
|
||||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||||
@ -187,7 +189,7 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
size = bitstream->size;
|
size = bitstream->size;
|
||||||
|
|
||||||
if (data == NULL) return NULL;
|
if (data == NULL) return NULL;
|
||||||
if (size < RIFF_HEADER_SIZE) return NULL;
|
if (size < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) return NULL;
|
||||||
if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
|
if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
|
||||||
GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
|
GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -196,8 +198,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
mux = WebPMuxNew();
|
mux = WebPMuxNew();
|
||||||
if (mux == NULL) return NULL;
|
if (mux == NULL) return NULL;
|
||||||
|
|
||||||
if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err;
|
|
||||||
|
|
||||||
tag = GetLE32(data + RIFF_HEADER_SIZE);
|
tag = GetLE32(data + RIFF_HEADER_SIZE);
|
||||||
if (tag != kChunks[IDX_VP8].tag &&
|
if (tag != kChunks[IDX_VP8].tag &&
|
||||||
tag != kChunks[IDX_VP8L].tag &&
|
tag != kChunks[IDX_VP8L].tag &&
|
||||||
@ -205,13 +205,17 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
goto Err; // First chunk should be VP8, VP8L or VP8X.
|
goto Err; // First chunk should be VP8, VP8L or VP8X.
|
||||||
}
|
}
|
||||||
|
|
||||||
riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE));
|
riff_size = GetLE32(data + TAG_SIZE);
|
||||||
if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) {
|
if (riff_size > MAX_CHUNK_PAYLOAD) goto Err;
|
||||||
goto Err;
|
|
||||||
} else {
|
// Note this padding is historical and differs from demux.c which does not
|
||||||
if (riff_size < size) { // Redundant data after last chunk.
|
// pad the file size.
|
||||||
size = riff_size; // To make sure we don't read any data beyond mux_size.
|
riff_size = SizeWithPadding(riff_size);
|
||||||
}
|
if (riff_size < CHUNK_HEADER_SIZE) goto Err;
|
||||||
|
if (riff_size > size) goto Err;
|
||||||
|
// There's no point in reading past the end of the RIFF chunk.
|
||||||
|
if (size > riff_size + CHUNK_HEADER_SIZE) {
|
||||||
|
size = riff_size + CHUNK_HEADER_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = data + size;
|
end = data + size;
|
||||||
@ -260,6 +264,7 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
|
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
|
||||||
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
|
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
|
||||||
if (id == WEBP_CHUNK_VP8X) { // grab global specs
|
if (id == WEBP_CHUNK_VP8X) { // grab global specs
|
||||||
|
if (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err;
|
||||||
mux->canvas_width_ = GetLE24(data + 12) + 1;
|
mux->canvas_width_ = GetLE24(data + 12) + 1;
|
||||||
mux->canvas_height_ = GetLE24(data + 15) + 1;
|
mux->canvas_height_ = GetLE24(data + 15) + 1;
|
||||||
}
|
}
|
||||||
|
@ -261,9 +261,15 @@ static void CleanupParams(SmoothParams* const p) {
|
|||||||
|
|
||||||
int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride,
|
int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride,
|
||||||
int strength) {
|
int strength) {
|
||||||
const int radius = 4 * strength / 100;
|
int radius = 4 * strength / 100;
|
||||||
|
|
||||||
if (strength < 0 || strength > 100) return 0;
|
if (strength < 0 || strength > 100) return 0;
|
||||||
if (data == NULL || width <= 0 || height <= 0) return 0; // bad params
|
if (data == NULL || width <= 0 || height <= 0) return 0; // bad params
|
||||||
|
|
||||||
|
// limit the filter size to not exceed the image dimensions
|
||||||
|
if (2 * radius + 1 > width) radius = (width - 1) >> 1;
|
||||||
|
if (2 * radius + 1 > height) radius = (height - 1) >> 1;
|
||||||
|
|
||||||
if (radius > 0) {
|
if (radius > 0) {
|
||||||
SmoothParams p;
|
SmoothParams p;
|
||||||
memset(&p, 0, sizeof(p));
|
memset(&p, 0, sizeof(p));
|
||||||
|
Reference in New Issue
Block a user