NEON: implement several alpha-processing functions

- ApplyAlphaMultiply
 - DispatchAlpha
 - DispatchAlphaToGreen
 - ExtractAlpha

Decoding to Argb / rgbA / ... is 10-15% faster (measured on N4)

new file: alpha_processing_neon.c

Change-Id: I40f1a809e9885d1031ff0bc886d8d001efa66bca
This commit is contained in:
Pascal Massimino 2017-01-11 17:39:29 +01:00
parent 48b1e85fbe
commit 49d0280df1
7 changed files with 190 additions and 0 deletions

View File

@ -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 \

View File

@ -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 \

View File

@ -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"

View File

@ -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 \

View File

@ -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

View File

@ -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();

View File

@ -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