/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <arm_neon.h>
#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale)
#define SCALE_FILTER_NAME MAKENAME(_filter_scale)
#define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine)
#define AFFINE_FILTER_NAME MAKENAME(_filter_affine)
#define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp)
#define PERSP_FILTER_NAME MAKENAME(_filter_persp)
#define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x)
#define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y)
#define PACK_FILTER_X4_NAME MAKENAME(_pack_filter_x4)
#define PACK_FILTER_Y4_NAME MAKENAME(_pack_filter_y4)
#ifndef PREAMBLE
#define PREAMBLE(state)
#define PREAMBLE_PARAM_X
#define PREAMBLE_PARAM_Y
#define PREAMBLE_ARG_X
#define PREAMBLE_ARG_Y
#endif
static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
uint32_t xy[], int count, int x, int y) {
SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
SkMatrix::kScale_Mask)) == 0);
PREAMBLE(s);
// we store y, x, x, x, x, x
const unsigned maxX = s.fPixmap.width() - 1;
SkFractionalInt fx;
{
const SkBitmapProcStateAutoMapper mapper(s, x, y);
const unsigned maxY = s.fPixmap.height() - 1;
*xy++ = TILEY_PROCF(mapper.fixedY(), maxY);
fx = mapper.fractionalIntX();
}
if (0 == maxX) {
// all of the following X values must be 0
memset(xy, 0, count * sizeof(uint16_t));
return;
}
const SkFractionalInt dx = s.fInvSxFractionalInt;
#ifdef CHECK_FOR_DECAL
// test if we don't need to apply the tile proc
const SkFixed fixedFx = SkFractionalIntToFixed(fx);
const SkFixed fixedDx = SkFractionalIntToFixed(dx);
if (can_truncate_to_fixed_for_decal(fixedFx, fixedDx, count, maxX)) {
decal_nofilter_scale_neon(xy, fixedFx, fixedDx, count);
return;
}
#endif
if (count >= 8) {
SkFractionalInt dx2 = dx+dx;
SkFractionalInt dx4 = dx2+dx2;
SkFractionalInt dx8 = dx4+dx4;
// now build fx/fx+dx/fx+2dx/fx+3dx
SkFractionalInt fx1, fx2, fx3;
int32x4_t lbase, hbase;
int16_t *dst16 = (int16_t *)xy;
fx1 = fx+dx;
fx2 = fx1+dx;
fx3 = fx2+dx;
lbase = vdupq_n_s32(SkFractionalIntToFixed(fx));
lbase = vsetq_lane_s32(SkFractionalIntToFixed(fx1), lbase, 1);
lbase = vsetq_lane_s32(SkFractionalIntToFixed(fx2), lbase, 2);
lbase = vsetq_lane_s32(SkFractionalIntToFixed(fx3), lbase, 3);
hbase = vaddq_s32(lbase, vdupq_n_s32(SkFractionalIntToFixed(dx4)));
// store & bump
while (count >= 8) {
int16x8_t fx8;
fx8 = TILEX_PROCF_NEON8(lbase, hbase, maxX);
vst1q_s16(dst16, fx8);
// but preserving base & on to the next
lbase = vaddq_s32 (lbase, vdupq_n_s32(SkFractionalIntToFixed(dx8)));
hbase = vaddq_s32 (hbase, vdupq_n_s32(SkFractionalIntToFixed(dx8)));
dst16 += 8;
count -= 8;
fx += dx8;
};
xy = (uint32_t *) dst16;
}
uint16_t* xx = (uint16_t*)xy;
for (int i = count; i > 0; --i) {
*xx++ = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX);
fx += dx;
}
}
static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
uint32_t xy[], int count, int x, int y) {
SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
SkMatrix::kScale_Mask |
SkMatrix::kAffine_Mask)) == 0);
PREAMBLE(s);
const SkBitmapProcStateAutoMapper mapper(s, x, y);
SkFractionalInt fx = mapper.fractionalIntX();
SkFractionalInt fy = mapper.fractionalIntY();
SkFractionalInt dx = s.fInvSxFractionalInt;
SkFractionalInt dy = s.fInvKyFractionalInt;
int maxX = s.fPixmap.width() - 1;
int maxY = s.fPixmap.height() - 1;
if (count >= 8) {
SkFractionalInt dx4 = dx * 4;
SkFractionalInt dy4 = dy * 4;
SkFractionalInt dx8 = dx * 8;
SkFractionalInt dy8 = dy * 8;
int32x4_t xbase, ybase;
int32x4_t x2base, y2base;
int16_t *dst16 = (int16_t *) xy;
// now build fx, fx+dx, fx+2dx, fx+3dx
xbase = vdupq_n_s32(SkFractionalIntToFixed(fx));
xbase = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx), xbase, 1);
xbase = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx+dx), xbase, 2);
xbase = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx+dx+dx), xbase, 3);
// same for fy
ybase = vdupq_n_s32(SkFractionalIntToFixed(fy));
ybase = vsetq_lane_s32(SkFractionalIntToFixed(fy+dy), ybase, 1);
ybase = vsetq_lane_s32(SkFractionalIntToFixed(fy+dy+dy), ybase, 2);
ybase = vsetq_lane_s32(SkFractionalIntToFixed(fy+dy+dy+dy), ybase, 3);
x2base = vaddq_s32(xbase, vdupq_n_s32(SkFractionalIntToFixed(dx4)));
y2base = vaddq_s32(ybase, vdupq_n_s32(SkFractionalIntToFixed(dy4)));
// store & bump
do {
int16x8x2_t hi16;
hi16.val[0] = TILEX_PROCF_NEON8(xbase, x2base, maxX);
hi16.val[1] = TILEY_PROCF_NEON8(ybase, y2base, maxY);
vst2q_s16(dst16, hi16);
// moving base and on to the next
xbase = vaddq_s32(xbase, vdupq_n_s32(SkFractionalIntToFixed(dx8)));
ybase = vaddq_s32(ybase, vdupq_n_s32(SkFractionalIntToFixed(dy8)));
x2base = vaddq_s32(x2base, vdupq_n_s32(SkFractionalIntToFixed(dx8)));
y2base = vaddq_s32(y2base, vdupq_n_s32(SkFractionalIntToFixed(dy8)));
dst16 += 16; // 8x32 aka 16x16
count -= 8;
fx += dx8;
fy += dy8;
} while (count >= 8);
xy = (uint32_t *) dst16;
}
for (int i = count; i > 0; --i) {
*xy++ = (TILEY_PROCF(SkFractionalIntToFixed(fy), maxY) << 16) |
TILEX_PROCF(SkFractionalIntToFixed(fx), maxX);
fx += dx; fy += dy;
}
}
static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
uint32_t* SK_RESTRICT xy,
int count, int x, int y) {
SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
PREAMBLE(s);
// max{X,Y} are int here, but later shown/assumed to fit in 16 bits
int maxX = s.fPixmap.width() - 1;
int maxY = s.fPixmap.height() - 1;
SkPerspIter iter(s.fInvMatrix,
SkIntToScalar(x) + SK_ScalarHalf,
SkIntToScalar(y) + SK_ScalarHalf, count);
while ((count = iter.next()) != 0) {
const SkFixed* SK_RESTRICT srcXY = iter.getXY();
if (count >= 8) {
int32_t *mysrc = (int32_t *) srcXY;
int16_t *mydst = (int16_t *) xy;
do {
int16x8x2_t hi16;
int32x4x2_t xy1, xy2;
xy1 = vld2q_s32(mysrc);
xy2 = vld2q_s32(mysrc+8);
hi16.val[0] = TILEX_PROCF_NEON8(xy1.val[0], xy2.val[0], maxX);
hi16.val[1] = TILEY_PROCF_NEON8(xy1.val[1], xy2.val[1], maxY);
vst2q_s16(mydst, hi16);
count -= 8; // 8 iterations
mysrc += 16; // 16 longs
mydst += 16; // 16 shorts, aka 8 longs
} while (count >= 8);
// get xy and srcXY fixed up
srcXY = (const SkFixed *) mysrc;
xy = (uint32_t *) mydst;
}
while (--count >= 0) {
*xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
TILEX_PROCF(srcXY[0], maxX);
srcXY += 2;
}
}
}
static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
SkFixed one PREAMBLE_PARAM_Y) {
unsigned i = TILEY_PROCF(f, max);
i = (i << 4) | EXTRACT_LOW_BITS(f, max);
return (i << 14) | (TILEY_PROCF((f + one), max));
}
static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
SkFixed one PREAMBLE_PARAM_X) {
unsigned i = TILEX_PROCF(f, max);
i = (i << 4) | EXTRACT_LOW_BITS(f, max);
return (i << 14) | (TILEX_PROCF((f + one), max));
}
static inline int32x4_t PACK_FILTER_X4_NAME(int32x4_t f, unsigned max,
SkFixed one PREAMBLE_PARAM_X) {
int32x4_t ret, res, wide_one;
// Prepare constants
wide_one = vdupq_n_s32(one);
// Step 1
res = TILEX_PROCF_NEON4(f, max);
// Step 2
ret = EXTRACT_LOW_BITS_NEON4(f, max);
ret = vsliq_n_s32(ret, res, 4);
// Step 3
res = TILEX_PROCF_NEON4(f + wide_one, max);
ret = vorrq_s32(vshlq_n_s32(ret, 14), res);
return ret;
}
static inline int32x4_t PACK_FILTER_Y4_NAME(int32x4_t f, unsigned max,
SkFixed one PREAMBLE_PARAM_X) {
int32x4_t ret, res, wide_one;
// Prepare constants
wide_one = vdupq_n_s32(one);
// Step 1
res = TILEY_PROCF_NEON4(f, max);
// Step 2
ret = EXTRACT_LOW_BITS_NEON4(f, max);
ret = vsliq_n_s32(ret, res, 4);
// Step 3
res = TILEY_PROCF_NEON4(f + wide_one, max);
ret = vorrq_s32(vshlq_n_s32(ret, 14), res);
return ret;
}
static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
uint32_t xy[], int count, int x, int y) {
SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
SkMatrix::kScale_Mask)) == 0);
SkASSERT(s.fInvKy == 0);
PREAMBLE(s);
const unsigned maxX = s.fPixmap.width() - 1;
const SkFixed one = s.fFilterOneX;
const SkFractionalInt dx = s.fInvSxFractionalInt;
SkFractionalInt fx;
{
const SkBitmapProcStateAutoMapper mapper(s, x, y);
const SkFixed fy = mapper.fixedY();
const unsigned maxY = s.fPixmap.height() - 1;
// compute our two Y values up front
*xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
// now initialize fx
fx = mapper.fractionalIntX();
}
#ifdef CHECK_FOR_DECAL
// test if we don't need to apply the tile proc
const SkFixed fixedFx = SkFractionalIntToFixed(fx);
const SkFixed fixedDx = SkFractionalIntToFixed(dx);
if (can_truncate_to_fixed_for_decal(fixedFx, fixedDx, count, maxX)) {
decal_filter_scale_neon(xy, fixedFx, fixedDx, count);
return;
}
#endif
{
if (count >= 4) {
int32x4_t wide_fx;
wide_fx = vdupq_n_s32(SkFractionalIntToFixed(fx));
wide_fx = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx), wide_fx, 1);
wide_fx = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx+dx), wide_fx, 2);
wide_fx = vsetq_lane_s32(SkFractionalIntToFixed(fx+dx+dx+dx), wide_fx, 3);
while (count >= 4) {
int32x4_t res;
res = PACK_FILTER_X4_NAME(wide_fx, maxX, one PREAMBLE_ARG_X);
vst1q_u32(xy, vreinterpretq_u32_s32(res));
wide_fx += vdupq_n_s32(SkFractionalIntToFixed(dx+dx+dx+dx));
fx += dx+dx+dx+dx;
xy += 4;
count -= 4;
}
}
while (--count >= 0) {
*xy++ = PACK_FILTER_X_NAME(SkFractionalIntToFixed(fx), maxX, one PREAMBLE_ARG_X);
fx += dx;
}
}
}
static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
uint32_t xy[], int count, int x, int y) {
SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
SkMatrix::kScale_Mask |
SkMatrix::kAffine_Mask)) == 0);
PREAMBLE(s);
const SkBitmapProcStateAutoMapper mapper(s, x, y);
SkFixed oneX = s.fFilterOneX;
SkFixed oneY = s.fFilterOneY;
SkFixed fx = mapper.fixedX();
SkFixed fy = mapper.fixedY();
SkFixed dx = s.fInvSx;
SkFixed dy = s.fInvKy;
unsigned maxX = s.fPixmap.width() - 1;
unsigned maxY = s.fPixmap.height() - 1;
if (count >= 4) {
int32x4_t wide_fy, wide_fx;
wide_fx = vdupq_n_s32(fx);
wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
wide_fy = vdupq_n_s32(fy);
wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1);
wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2);
wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3);
while (count >= 4) {
int32x4x2_t vxy;
// do the X side, then the Y side, then interleave them
vxy.val[0] = PACK_FILTER_Y4_NAME(wide_fy, maxY, oneY PREAMBLE_ARG_Y);
vxy.val[1] = PACK_FILTER_X4_NAME(wide_fx, maxX, oneX PREAMBLE_ARG_X);
// interleave as YXYXYXYX as part of the storing
vst2q_s32((int32_t*)xy, vxy);
// prepare next iteration
wide_fx += vdupq_n_s32(dx+dx+dx+dx);
fx += dx + dx + dx + dx;
wide_fy += vdupq_n_s32(dy+dy+dy+dy);
fy += dy+dy+dy+dy;
xy += 8; // 4 x's, 4 y's
count -= 4;
}
}
while (--count >= 0) {
// NB: writing Y/X
*xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
fy += dy;
*xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
fx += dx;
}
}
static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
uint32_t* SK_RESTRICT xy, int count,
int x, int y) {
SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
PREAMBLE(s);
unsigned maxX = s.fPixmap.width() - 1;
unsigned maxY = s.fPixmap.height() - 1;
SkFixed oneX = s.fFilterOneX;
SkFixed oneY = s.fFilterOneY;
SkPerspIter iter(s.fInvMatrix,
SkIntToScalar(x) + SK_ScalarHalf,
SkIntToScalar(y) + SK_ScalarHalf, count);
while ((count = iter.next()) != 0) {
const SkFixed* SK_RESTRICT srcXY = iter.getXY();
while (count >= 4) {
int32x4_t wide_x, wide_y;
int32x4x2_t vxy, vresyx;
// load src: x-y-x-y-x-y-x-y
vxy = vld2q_s32(srcXY);
// do the X side, then the Y side, then interleave them
wide_x = vsubq_s32(vxy.val[0], vdupq_n_s32(oneX>>1));
wide_y = vsubq_s32(vxy.val[1], vdupq_n_s32(oneY>>1));
vresyx.val[0] = PACK_FILTER_Y4_NAME(wide_y, maxY, oneY PREAMBLE_ARG_Y);
vresyx.val[1] = PACK_FILTER_X4_NAME(wide_x, maxX, oneX PREAMBLE_ARG_X);
// store interleaved as y-x-y-x-y-x-y-x (NB != read order)
vst2q_s32((int32_t*)xy, vresyx);
// on to the next iteration
srcXY += 2*4;
count -= 4;
xy += 2*4;
}
while (--count >= 0) {
// NB: we read x/y, we write y/x
*xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
oneY PREAMBLE_ARG_Y);
*xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
oneX PREAMBLE_ARG_X);
srcXY += 2;
}
}
}
const SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
SCALE_NOFILTER_NAME,
SCALE_FILTER_NAME,
AFFINE_NOFILTER_NAME,
AFFINE_FILTER_NAME,
PERSP_NOFILTER_NAME,
PERSP_FILTER_NAME
};
#undef TILEX_PROCF_NEON8
#undef TILEY_PROCF_NEON8
#undef TILEX_PROCF_NEON4
#undef TILEY_PROCF_NEON4
#undef EXTRACT_LOW_BITS_NEON4
#undef MAKENAME
#undef TILEX_PROCF
#undef TILEY_PROCF
#ifdef CHECK_FOR_DECAL
#undef CHECK_FOR_DECAL
#endif
#undef SCALE_NOFILTER_NAME
#undef SCALE_FILTER_NAME
#undef AFFINE_NOFILTER_NAME
#undef AFFINE_FILTER_NAME
#undef PERSP_NOFILTER_NAME
#undef PERSP_FILTER_NAME
#undef PREAMBLE
#undef PREAMBLE_PARAM_X
#undef PREAMBLE_PARAM_Y
#undef PREAMBLE_ARG_X
#undef PREAMBLE_ARG_Y
#undef EXTRACT_LOW_BITS