diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | Makefile.vc | 1 | ||||
-rw-r--r-- | build.gradle | 1 | ||||
-rw-r--r-- | makefile.unix | 1 | ||||
-rw-r--r-- | src/dsp/Makefile.am | 1 | ||||
-rw-r--r-- | src/dsp/alpha_processing.c | 6 | ||||
-rw-r--r-- | src/dsp/alpha_processing_neon.c | 179 | ||||
-rw-r--r-- | src/dsp/alpha_processing_sse2.c | 74 | ||||
-rw-r--r-- | src/dsp/dec_clip_tables.c | 8 | ||||
-rw-r--r-- | src/dsp/lossless_enc_sse2.c | 6 | ||||
-rw-r--r-- | src/enc/backward_references.c | 2 |
11 files changed, 235 insertions, 45 deletions
@@ -40,6 +40,7 @@ demux_srcs := \ dsp_dec_srcs := \ src/dsp/alpha_processing.c \ src/dsp/alpha_processing_mips_dsp_r2.c \ + src/dsp/alpha_processing_neon.$(NEON) \ src/dsp/alpha_processing_sse2.c \ src/dsp/alpha_processing_sse41.c \ src/dsp/argb.c \ diff --git a/Makefile.vc b/Makefile.vc index 70d5f28a..15703cd0 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -188,6 +188,7 @@ DEMUX_OBJS = \ DSP_DEC_OBJS = \ $(DIROBJ)\dsp\alpha_processing.obj \ $(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\alpha_processing_neon.obj \ $(DIROBJ)\dsp\alpha_processing_sse2.obj \ $(DIROBJ)\dsp\alpha_processing_sse41.obj \ $(DIROBJ)\dsp\cpu.obj \ diff --git a/build.gradle b/build.gradle index 23421112..d281a229 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,7 @@ model { srcDir "src/dsp" include "alpha_processing.c" include "alpha_processing_mips_dsp_r2.c" + include "alpha_processing_neon.$NEON" include "alpha_processing_sse2.c" include "alpha_processing_sse41.c" include "argb.c" diff --git a/makefile.unix b/makefile.unix index 0c99505b..f0931a69 100644 --- a/makefile.unix +++ b/makefile.unix @@ -131,6 +131,7 @@ DEMUX_OBJS = \ DSP_DEC_OBJS = \ src/dsp/alpha_processing.o \ src/dsp/alpha_processing_mips_dsp_r2.o \ + src/dsp/alpha_processing_neon.o \ src/dsp/alpha_processing_sse2.o \ src/dsp/alpha_processing_sse41.o \ src/dsp/cpu.o \ diff --git a/src/dsp/Makefile.am b/src/dsp/Makefile.am index 7f013e86..120c2883 100644 --- a/src/dsp/Makefile.am +++ b/src/dsp/Makefile.am @@ -74,6 +74,7 @@ libwebpdspdecode_sse2_la_CPPFLAGS = $(libwebpdsp_sse2_la_CPPFLAGS) libwebpdspdecode_sse2_la_CFLAGS = $(libwebpdsp_sse2_la_CFLAGS) libwebpdspdecode_neon_la_SOURCES = +libwebpdspdecode_neon_la_SOURCES += alpha_processing_neon.c libwebpdspdecode_neon_la_SOURCES += dec_neon.c libwebpdspdecode_neon_la_SOURCES += lossless_neon.c libwebpdspdecode_neon_la_SOURCES += neon.h diff --git a/src/dsp/alpha_processing.c b/src/dsp/alpha_processing.c index 1716cace..fd564d5d 100644 --- a/src/dsp/alpha_processing.c +++ b/src/dsp/alpha_processing.c @@ -346,6 +346,7 @@ int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int); extern void WebPInitAlphaProcessingMIPSdspR2(void); extern void WebPInitAlphaProcessingSSE2(void); extern void WebPInitAlphaProcessingSSE41(void); +extern void WebPInitAlphaProcessingNEON(void); static volatile VP8CPUInfo alpha_processing_last_cpuinfo_used = (VP8CPUInfo)&alpha_processing_last_cpuinfo_used; @@ -373,6 +374,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) { #endif } #endif +#if defined(WEBP_USE_NEON) + if (VP8GetCPUInfo(kNEON)) { + WebPInitAlphaProcessingNEON(); + } +#endif #if defined(WEBP_USE_MIPS_DSP_R2) if (VP8GetCPUInfo(kMIPSdspR2)) { WebPInitAlphaProcessingMIPSdspR2(); diff --git a/src/dsp/alpha_processing_neon.c b/src/dsp/alpha_processing_neon.c new file mode 100644 index 00000000..ed845729 --- /dev/null +++ b/src/dsp/alpha_processing_neon.c @@ -0,0 +1,179 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel, NEON version. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "./dsp.h" + +#if defined(WEBP_USE_NEON) + +#include "./neon.h" + +//------------------------------------------------------------------------------ + +#define MULTIPLIER(a) ((a) * 0x8081) +#define PREMULTIPLY(x, m) (((x) * (m)) >> 23) + +#define MULTIPLY_BY_ALPHA(V, ALPHA, OTHER) do { \ + const uint8x8_t alpha = (V).val[(ALPHA)]; \ + const uint16x8_t r1 = vmull_u8((V).val[1], alpha); \ + const uint16x8_t g1 = vmull_u8((V).val[2], alpha); \ + const uint16x8_t b1 = vmull_u8((V).val[(OTHER)], alpha); \ + /* we use: v / 255 = (v + 1 + (v >> 8)) >> 8 */ \ + const uint16x8_t r2 = vsraq_n_u16(r1, r1, 8); \ + const uint16x8_t g2 = vsraq_n_u16(g1, g1, 8); \ + const uint16x8_t b2 = vsraq_n_u16(b1, b1, 8); \ + const uint16x8_t r3 = vaddq_u16(r2, kOne); \ + const uint16x8_t g3 = vaddq_u16(g2, kOne); \ + const uint16x8_t b3 = vaddq_u16(b2, kOne); \ + (V).val[1] = vshrn_n_u16(r3, 8); \ + (V).val[2] = vshrn_n_u16(g3, 8); \ + (V).val[(OTHER)] = vshrn_n_u16(b3, 8); \ +} while (0) + +static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first, + int w, int h, int stride) { + const uint16x8_t kOne = vdupq_n_u16(1u); + while (h-- > 0) { + uint32_t* const rgbx = (uint32_t*)rgba; + int i = 0; + if (alpha_first) { + for (; i + 8 <= w; i += 8) { + // load aaaa...|rrrr...|gggg...|bbbb... + uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i)); + MULTIPLY_BY_ALPHA(RGBX, 0, 3); + vst4_u8((uint8_t*)(rgbx + i), RGBX); + } + } else { + for (; i + 8 <= w; i += 8) { + uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i)); + MULTIPLY_BY_ALPHA(RGBX, 3, 0); + vst4_u8((uint8_t*)(rgbx + i), RGBX); + } + } + // Finish with left-overs. + for (; i < w; ++i) { + uint8_t* const rgb = rgba + (alpha_first ? 1 : 0); + const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3); + const uint32_t a = alpha[4 * i]; + if (a != 0xff) { + const uint32_t mult = MULTIPLIER(a); + rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult); + rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult); + rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult); + } + } + rgba += stride; + } +} +#undef MULTIPLY_BY_ALPHA +#undef MULTIPLIER +#undef PREMULTIPLY + +//------------------------------------------------------------------------------ + +static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint8_t* dst, int dst_stride) { + uint32_t alpha_mask = 0xffffffffu; + uint8x8_t mask8 = vdup_n_u8(0xff); + uint32_t tmp[2]; + int i, j; + for (j = 0; j < height; ++j) { + // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb + // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store. + // Hence the test with 'width - 1' instead of just 'width'. + for (i = 0; i + 8 <= width - 1; i += 8) { + uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(dst + 4 * i)); + const uint8x8_t alphas = vld1_u8(alpha + i); + rgbX.val[0] = alphas; + vst4_u8((uint8_t*)(dst + 4 * i), rgbX); + mask8 = vand_u8(mask8, alphas); + } + for (; i < width; ++i) { + const uint32_t alpha_value = alpha[i]; + dst[4 * i] = alpha_value; + alpha_mask &= alpha_value; + } + alpha += alpha_stride; + dst += dst_stride; + } + vst1_u8((uint8_t*)tmp, mask8); + alpha_mask &= tmp[0]; + alpha_mask &= tmp[1]; + return (alpha_mask != 0xffffffffu); +} + +static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint32_t* dst, int dst_stride) { + int i, j; + uint8x8x4_t greens; // leave A/R/B channels zero'd. + greens.val[0] = vdup_n_u8(0); + greens.val[2] = vdup_n_u8(0); + greens.val[3] = vdup_n_u8(0); + for (j = 0; j < height; ++j) { + for (i = 0; i + 8 <= width; i += 8) { + greens.val[1] = vld1_u8(alpha + i); + vst4_u8((uint8_t*)(dst + i), greens); + } + for (; i < width; ++i) dst[i] = alpha[i] << 8; + alpha += alpha_stride; + dst += dst_stride; + } +} + +static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride, + int width, int height, + uint8_t* alpha, int alpha_stride) { + uint32_t alpha_mask = 0xffffffffu; + uint8x8_t mask8 = vdup_n_u8(0xff); + uint32_t tmp[2]; + int i, j; + for (j = 0; j < height; ++j) { + // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb + // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store. + // Hence the test with 'width - 1' instead of just 'width'. + for (i = 0; i + 8 <= width - 1; i += 8) { + const uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(argb + 4 * i)); + const uint8x8_t alphas = rgbX.val[0]; + vst1_u8((uint8_t*)(alpha + i), alphas); + mask8 = vand_u8(mask8, alphas); + } + for (; i < width; ++i) { + alpha[i] = argb[4 * i]; + alpha_mask &= alpha[i]; + } + argb += argb_stride; + alpha += alpha_stride; + } + vst1_u8((uint8_t*)tmp, mask8); + alpha_mask &= tmp[0]; + alpha_mask &= tmp[1]; + return (alpha_mask == 0xffffffffu); +} + +//------------------------------------------------------------------------------ + +extern void WebPInitAlphaProcessingNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingNEON(void) { + WebPApplyAlphaMultiply = ApplyAlphaMultiply_NEON; + WebPDispatchAlpha = DispatchAlpha_NEON; + WebPDispatchAlphaToGreen = DispatchAlphaToGreen_NEON; + WebPExtractAlpha = ExtractAlpha_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingNEON) + +#endif // WEBP_USE_NEON diff --git a/src/dsp/alpha_processing_sse2.c b/src/dsp/alpha_processing_sse2.c index bdf141e5..83dc559f 100644 --- a/src/dsp/alpha_processing_sse2.c +++ b/src/dsp/alpha_processing_sse2.c @@ -151,30 +151,35 @@ static int ExtractAlpha(const uint8_t* argb, int argb_stride, // We can't use a 'const int' for the SHUFFLE value, because it has to be an // immediate in the _mm_shufflexx_epi16() instruction. We really need a macro. -// We use: v / 255 = (v + 1 + (v >> 8)) >> 8, where v = alpha * {r,g,b} is -// a 16bit value. -#define APPLY_ALPHA(RGBX, SHUFFLE) do { \ - const __m128i argb0 = _mm_loadl_epi64((const __m128i*)&(RGBX)); \ - const __m128i argb1 = _mm_unpacklo_epi8(argb0, zero); \ - const __m128i alpha0 = _mm_or_si128(argb1, kMask); \ - const __m128i alpha1 = _mm_shufflelo_epi16(alpha0, SHUFFLE); \ - const __m128i alpha2 = _mm_shufflehi_epi16(alpha1, SHUFFLE); \ - /* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \ - const __m128i A0 = _mm_mullo_epi16(alpha2, argb1); \ - const __m128i A1 = _mm_srli_epi16(A0, 8); \ - const __m128i A2 = _mm_add_epi16(A1, A0); \ - const __m128i A3 = _mm_add_epi16(A2, one); \ - const __m128i A4 = _mm_srli_epi16(A3, 8); \ - const __m128i A5 = _mm_packus_epi16(A4, zero); \ - _mm_storel_epi64((__m128i*)&(RGBX), A5); \ +// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit +// value. +#define APPLY_ALPHA(RGBX, SHUFFLE) do { \ + const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \ + const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \ + const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \ + const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \ + const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \ + const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \ + const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \ + const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \ + const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \ + /* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \ + const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \ + const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \ + const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \ + const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \ + const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \ + const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \ + const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \ + _mm_storeu_si128((__m128i*)&(RGBX), A3); \ } while (0) static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first, int w, int h, int stride) { const __m128i zero = _mm_setzero_si128(); - const __m128i one = _mm_set1_epi16(1); + const __m128i kMult = _mm_set1_epi16(0x8081u); const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0); - const int kSpan = 2; + const int kSpan = 4; while (h-- > 0) { uint32_t* const rgbx = (uint32_t*)rgba; int i; @@ -214,10 +219,11 @@ static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) { const int kSpan = 2; const __m128i zero = _mm_setzero_si128(); const __m128i k128 = _mm_set1_epi16(128); + const __m128i kMult = _mm_set1_epi16(0x0101); const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0); for (x = 0; x + kSpan <= width; x += kSpan) { // To compute 'result = (int)(a * x / 255. + .5)', we use: - // tmp = a * v + 128, result = (tmp + (tmp >> 8)) >> 8 + // tmp = a * v + 128, result = (tmp * 0x0101u) >> 16 const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]); const __m128i A1 = _mm_unpacklo_epi8(A0, zero); const __m128i A2 = _mm_or_si128(A1, kMask); @@ -226,10 +232,8 @@ static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) { // here, A4 = [ff a0 a0 a0][ff a1 a1 a1] const __m128i A5 = _mm_mullo_epi16(A4, A1); const __m128i A6 = _mm_add_epi16(A5, k128); - const __m128i A7 = _mm_srli_epi16(A6, 8); - const __m128i A8 = _mm_add_epi16(A7, A6); - const __m128i A9 = _mm_srli_epi16(A8, 8); - const __m128i A10 = _mm_packus_epi16(A9, zero); + const __m128i A7 = _mm_mulhi_epu16(A6, kMult); + const __m128i A10 = _mm_packus_epi16(A7, zero); _mm_storel_epi64((__m128i*)&ptr[x], A10); } } @@ -241,23 +245,19 @@ static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha, int width, int inverse) { int x = 0; if (!inverse) { - const int kSpan = 8; const __m128i zero = _mm_setzero_si128(); - const __m128i kRound = _mm_set1_epi16(1 << 7); - const int w2 = width & ~(kSpan - 1); - for (x = 0; x < w2; x += kSpan) { + const __m128i k128 = _mm_set1_epi16(128); + const __m128i kMult = _mm_set1_epi16(0x0101); + for (x = 0; x + 8 <= width; x += 8) { const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]); + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]); const __m128i v1 = _mm_unpacklo_epi8(v0, zero); - const __m128i alpha0 = _mm_loadl_epi64((const __m128i*)&alpha[x]); - const __m128i alpha1 = _mm_unpacklo_epi8(alpha0, zero); - const __m128i alpha2 = _mm_unpacklo_epi8(alpha0, alpha0); - const __m128i v2 = _mm_mulhi_epu16(v1, alpha2); - const __m128i v3 = _mm_mullo_epi16(v1, alpha1); - const __m128i v4 = _mm_adds_epu16(v2, v3); - const __m128i v5 = _mm_adds_epu16(v4, kRound); - const __m128i v6 = _mm_srli_epi16(v5, 8); - const __m128i v7 = _mm_packus_epi16(v6, zero); - _mm_storel_epi64((__m128i*)&ptr[x], v7); + const __m128i a1 = _mm_unpacklo_epi8(a0, zero); + const __m128i v2 = _mm_mullo_epi16(v1, a1); + const __m128i v3 = _mm_add_epi16(v2, k128); + const __m128i v4 = _mm_mulhi_epu16(v3, kMult); + const __m128i v5 = _mm_packus_epi16(v4, zero); + _mm_storel_epi64((__m128i*)&ptr[x], v5); } } width -= x; diff --git a/src/dsp/dec_clip_tables.c b/src/dsp/dec_clip_tables.c index 3b6dde86..74ba34c0 100644 --- a/src/dsp/dec_clip_tables.c +++ b/src/dsp/dec_clip_tables.c @@ -63,7 +63,7 @@ static const uint8_t abs0[255 + 255 + 1] = { 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; -static const int8_t sclip1[1020 + 1020 + 1] = { +static const uint8_t sclip1[1020 + 1020 + 1] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, @@ -236,7 +236,7 @@ static const int8_t sclip1[1020 + 1020 + 1] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }; -static const int8_t sclip2[112 + 112 + 1] = { +static const uint8_t sclip2[112 + 112 + 1] = { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, @@ -339,8 +339,8 @@ static volatile int tables_ok = 0; #endif -const int8_t* const VP8ksclip1 = &sclip1[1020]; -const int8_t* const VP8ksclip2 = &sclip2[112]; +const int8_t* const VP8ksclip1 = (const int8_t*)&sclip1[1020]; +const int8_t* const VP8ksclip2 = (const int8_t*)&sclip2[112]; const uint8_t* const VP8kclip1 = &clip1[255]; const uint8_t* const VP8kabs0 = &abs0[255]; diff --git a/src/dsp/lossless_enc_sse2.c b/src/dsp/lossless_enc_sse2.c index a7c67f45..8ad85d94 100644 --- a/src/dsp/lossless_enc_sse2.c +++ b/src/dsp/lossless_enc_sse2.c @@ -431,9 +431,9 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits, // 000a000b000c000d | (where a/b/c/d are 2 bits). const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]); const __m128i mul = _mm_mullo_epi16(in, mul_cst); // 00ab00b000cd00d0 - const __m128i and = _mm_and_si128(mul, mask_mul); // 00ab000000cd0000 - const __m128i shift = _mm_srli_epi32(and, 12); // 00000000ab000000 - const __m128i pack = _mm_or_si128(shift, and); // 00000000abcd0000 + const __m128i tmp = _mm_and_si128(mul, mask_mul); // 00ab000000cd0000 + const __m128i shift = _mm_srli_epi32(tmp, 12); // 00000000ab000000 + const __m128i pack = _mm_or_si128(shift, tmp); // 00000000abcd0000 // Convert to 0xff00**00. const __m128i res = _mm_or_si128(pack, mask_or); _mm_storeu_si128((__m128i*)dst, res); diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 6eb7d30c..4c480f12 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -1576,7 +1576,7 @@ static int ComputeCacheEntropies(const uint32_t* argb, // Update the histograms for distance/length. int len = PixOrCopyLength(v); int code_dist, code_len, extra_bits; - uint32_t argb_prev = *argb - 1; + uint32_t argb_prev = *argb ^ 0xffffffffu; VP8LPrefixEncodeBits(len, &code_len, &extra_bits); VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code_dist, &extra_bits); for (i = 1; i <= cache_bits_max; ++i) { |