pikapython/package/Arm2D/arm_2d_helium.c
2021-11-09 22:19:51 +08:00

6549 lines
277 KiB
C

/*
* Copyright (C) 2010-2021 Arm Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* ----------------------------------------------------------------------
* Project: Arm-2D Library
* Title: arm-2d_helium.c
* Description: Acceleration extensions using Helium.
*
* $Date: 22. Sep 2021
* $Revision: V.0.12.0
*
* Target Processor: Cortex-M cores with Helium
*
* -------------------------------------------------------------------- */
#define __ARM_2D_IMPL__
#include "arm_2d.h"
#include "__arm_2d_impl.h"
#if defined(__ARM_2D_HAS_HELIUM__) && __ARM_2D_HAS_HELIUM__
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunknown-warning-option"
# pragma clang diagnostic ignored "-Wreserved-identifier"
# pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
# pragma clang diagnostic ignored "-Wcast-qual"
# pragma clang diagnostic ignored "-Wcast-align"
# pragma clang diagnostic ignored "-Wextra-semi-stmt"
# pragma clang diagnostic ignored "-Wsign-conversion"
# pragma clang diagnostic ignored "-Wunused-function"
# pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
# pragma clang diagnostic ignored "-Wdouble-promotion"
# pragma clang diagnostic ignored "-Wunused-parameter"
# pragma clang diagnostic ignored "-Wimplicit-float-conversion"
# pragma clang diagnostic ignored "-Wimplicit-int-conversion"
# pragma clang diagnostic ignored "-Wtautological-pointer-compare"
# pragma clang diagnostic ignored "-Wmissing-prototypes"
# pragma clang diagnostic ignored "-Wsign-compare"
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
# pragma clang diagnostic ignored "-Wpadded"
# pragma clang diagnostic ignored "-Wvector-conversion"
# pragma clang diagnostic ignored "-Wundef"
#endif
#include "__arm_2d_paving_helium.h"
#include "__arm_2d_math_helium.h"
#include "__arm_2d_utils_helium.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief initialise the helium service service
*! \param none
*! \return none
*/
void __arm_2d_helium_init(void)
{
/* even if this is empty, do not remove it */
}
/*----------------------------------------------------------------------------*
* Code Template for tile operations *
*----------------------------------------------------------------------------*/
#define __API_COLOUR c8bit
#define __API_INT_TYPE uint8_t
#define __API_INT_TYPE_BIT_NUM 8
#include "__arm_2d_copy_helium.inc"
#define __API_COLOUR rgb16
#define __API_INT_TYPE uint16_t
#define __API_INT_TYPE_BIT_NUM 16
#include "__arm_2d_copy_helium.inc"
#define __API_COLOUR rgb32
#define __API_INT_TYPE uint32_t
#define __API_INT_TYPE_BIT_NUM 32
#include "__arm_2d_copy_helium.inc"
/*----------------------------------------------------------------------------*
* Specialized Copy Routines *
*----------------------------------------------------------------------------*/
static
void __arm_copy_16_mve_narrow( uint16_t *phwSource,
int16_t iSourceStride,
uint16_t *phwTarget,
int16_t iTargetStride,
arm_2d_size_t *ptCopySize)
{
#ifdef USE_MVE_INTRINSICS
for (int32_t x = 0; x < ptCopySize->iWidth; x++) {
uint16x8_t srcStr = vidupq_u16((uint32_t) 0, 1);
uint16x8_t dstStr = vidupq_u16((uint32_t) 0, 1);
srcStr = srcStr * iSourceStride;
dstStr = dstStr * iTargetStride;
for (int32_t y = 0; y < ptCopySize->iHeight / 8; y++) {
uint16x8_t in = vldrhq_gather_shifted_offset_u16(phwSource, srcStr);
srcStr = vaddq_n_u16(srcStr, (8 * iSourceStride));
vstrhq_scatter_shifted_offset_u16(phwTarget, dstStr, in);
dstStr = vaddq_n_u16(dstStr, (8 * iTargetStride));
}
phwSource++;
phwTarget++;
}
#else
__asm volatile(
" clrm {r2, r4} \n"
" vidup.u16 q0, r2, #1 \n"
" vmul.i16 q2, q0, %[iSourceStride] \n"
" vidup.u16 q1, r4, #1 \n"
" vmul.i16 q3, q1, %[iTargetStride] \n"
"3: \n"
/* outer loop, iterates over columns */
/* size = ptCopySize->iWidth */
" vmov q0, q2 \n"
" vmov q1, q3 \n"
/* inner loop, iterates over rows (size = ptCopySize->iHeight) */
" wlstp.16 lr, %[iHeight], 1f \n"
".p2align 2 \n"
"2: \n"
" vldrh.u16 q4, [%[phwSource], q0, uxtw #1] \n"
" vadd.i16 q0, q0, %[iSourceStridex8] \n"
" vstrh.16 q4, [%[phwTarget], q1, uxtw #1] \n"
" vadd.i16 q1, q1, %[iTargetStridex8] \n"
" letp lr, 2b \n"
"1: \n"
" add.n %[phwSource], #2 \n"
" add.n %[phwTarget], #2 \n"
" subs %[iWidth], #1 \n"
" bne 3b \n"
: [phwTarget] "+r"(phwTarget), [phwSource] "+r"(phwSource)
: [iHeight] "r"(ptCopySize->iHeight), [iWidth] "r" (ptCopySize->iWidth),
[iSourceStride] "r" (iSourceStride),[iSourceStridex8] "r" (iSourceStride*8),
[iTargetStride] "r" (iTargetStride),[iTargetStridex8] "r" (iTargetStride*8)
: "r2", "r4", "q0", "q1", "q2", "q3", "q4", "memory", "r14", "cc");
#endif
}
static
void __arm_copy_32_mve_narrow( uint32_t *pwSource,
int32_t iSourceStride,
uint32_t *pwTarget,
int32_t iTargetStride,
arm_2d_size_t *ptCopySize)
{
#ifdef USE_MVE_INTRINSICS
for (int_fast32_t x = 0; x < ptCopySize->iWidth; x++) {
uint32x4_t srcStr = vidupq_u32((uint32_t) 0, 1);
uint32x4_t dstStr = vidupq_u32((uint32_t) 0, 1);
srcStr = srcStr * iSourceStride;
dstStr = dstStr * iTargetStride;
for (int_fast32_t y = 0; y < ptCopySize->iHeight / 4; y++) {
uint32x4_t in = vldrwq_gather_shifted_offset_u32(pwSource, srcStr);
srcStr = vaddq_n_u32(srcStr, (4 * iSourceStride));
vstrwq_scatter_shifted_offset_u32(pwTarget, dstStr, in);
dstStr = vaddq_n_u32(dstStr, (4 * iTargetStride));
}
pwSource++;
pwTarget++;
}
#else
__asm volatile(
" clrm {r2, r4} \n"
" vidup.u32 q0, r2, #1 \n"
" vmul.i32 q2, q0, %[iSourceStride] \n"
" vidup.u32 q1, r4, #1 \n"
" vmul.i32 q3, q1, %[iTargetStride] \n"
"3: \n"
/* outer loop, iterates over columns */
/* size = ptCopySize->iWidth */
" vmov q0, q2 \n"
" vmov q1, q3 \n"
/* inner loop, iterates over rows (size = ptCopySize->iHeight) */
" wlstp.32 lr, %[iHeight], 1f \n"
".p2align 2 \n"
"2: \n"
" vldrw.u32 q4, [%[pwSource], q0, uxtw #2] \n"
" vadd.i32 q0, q0, %[iSourceStridex4] \n"
" vstrw.32 q4, [%[pwTarget], q1, uxtw #2] \n"
" vadd.i32 q1, q1, %[iTargetStridex4] \n"
" letp lr, 2b \n"
"1: \n"
" add.n %[pwSource], #4 \n"
" add.n %[pwTarget], #4 \n"
" subs %[iWidth], #1 \n"
" bne 3b \n"
: [pwTarget] "+r"(pwTarget), [pwSource] "+r"(pwSource)
: [iHeight] "r"(ptCopySize->iHeight), [iWidth] "r" (ptCopySize->iWidth),
[iSourceStride] "r" (iSourceStride),[iSourceStridex4] "r" (iSourceStride*4),
[iTargetStride] "r" (iTargetStride),[iTargetStridex4] "r" (iTargetStride*4)
: "r2", "r4", "q0", "q1", "q2", "q3", "q4", "memory", "r14", "cc");
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb16_copy( uint16_t *phwSource,
int16_t iSourceStride,
uint16_t *phwTarget,
int16_t iTargetStride,
arm_2d_size_t *ptCopySize)
{
/*
* 16-bit Narrow copy case:
* use column copy with scatter / gather
*/
if(ptCopySize->iWidth <= 4) {
__arm_copy_16_mve_narrow(phwSource,
iSourceStride,
phwTarget,
iTargetStride,
ptCopySize);
} else if((((uint32_t)phwSource & 3) == 0) && (((uint32_t)phwTarget & 3) == 0)
&& ((iSourceStride & 3) == 0) && ((iTargetStride & 3) ==0)) {
/*
* source / dst & strides are 64-bit aligned
* use scalar LDRD/STRD, faster than back to back vector VLDR/VSTR on M55
*/
__asm volatile(
"3: \n"
" mov r0, %[phwSource] \n"
" mov r1, %[phwTarget] \n"
/* scalar version faster (no DTCM bank conflict)*/
" wls lr, %[iWidth], 1f \n"
".p2align 2 \n"
"2: \n"
" ldrd r2, r3, [r0], #8 \n"
" strd r2, r3, [r1], #8 \n"
" le lr, 2b \n"
"1: \n"
// tail
" wls lr, %[iWidthTail], 1f \n"
".p2align 2 \n"
"2: \n"
" ldrh r2, [r0], #2 \n"
" strh r2, [r1], #2 \n"
" le lr, 2b \n"
"1: \n"
" add %[phwSource], %[iSourceStride] \n"
" add %[phwTarget], %[iTargetStride] \n"
" subs %[iHeight], #1 \n"
" bne 3b \n"
: [phwTarget] "+r"(phwTarget), [phwSource] "+r"(phwSource)
: [iHeight] "r"(ptCopySize->iHeight), [iWidth] "r" (ptCopySize->iWidth/4),
[iWidthTail] "r" (ptCopySize->iWidth & 3),
[iSourceStride] "r" (iSourceStride*sizeof(uint16_t)),
[iTargetStride] "r" (iTargetStride*sizeof(uint16_t))
: "r0", "r1", "r2", "r3", "q0", "memory", "r14", "cc"
);
}
else
{
/*
* generic column major 16-bit 2D copy
*/
int32_t iWidth = ptCopySize->iWidth;
int32_t iHeight = ptCopySize->iHeight;
__asm volatile(
" mov r2, %[iHeight] \n"
"3: \n"
" mov r0, %[phwSource] \n"
" mov r1, %[phwTarget] \n"
" wlstp.16 lr, %[iWidth], 1f \n"
".p2align 2 \n"
"2: \n"
" vldrh.u16 q0, [r0], #16 \n"
" vstrh.16 q0, [r1], #16 \n"
" letp lr, 2b \n"
"1: \n"
" add %[phwSource], %[iSourceStride] \n"
" add %[phwTarget], %[iTargetStride] \n"
" subs r2, #1 \n"
" bne 3b \n"
: [phwTarget] "+r"(phwTarget), [phwSource] "+r"(phwSource)
: [iHeight] "r"(iHeight), [iWidth] "r" (iWidth),
[iSourceStride] "r" (iSourceStride*sizeof(uint16_t)),
[iTargetStride] "r" (iTargetStride*sizeof(uint16_t))
: "r0", "r1", "r2", "q0", "memory", "r14", "cc");
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb32_copy( uint32_t *pwSource,
int16_t iSourceStride,
uint32_t *pwTarget,
int16_t iTargetStride,
arm_2d_size_t *ptCopySize)
{
if(ptCopySize->iWidth <= 2) {
/*
* 32-bit Narrow copy case:
* use column copy with scatter / gather
*/
__arm_copy_32_mve_narrow(pwSource,
iSourceStride,
pwTarget,
iTargetStride,
ptCopySize);
} else if((((uint32_t)pwSource & 3) == 0) && (((uint32_t)pwTarget & 3) == 0)
&& ((iSourceStride & 3) == 0) && ((iTargetStride & 3) ==0)) {
/*
* source / dst & strides are 64-bit aligned
* use scalar LDRD/STRD, faster than back to back vector VLDR/VSTR on M55
*/
__asm volatile(
"3: \n"
" mov r0, %[pwSource] \n"
" mov r1, %[pwTarget] \n"
/* scalar version faster (no DTCM bank conflict)*/
" wls lr, %[iWidth], 1f \n"
".p2align 2 \n"
"2: \n"
" ldrd r2, r3, [r0], #8 \n"
" strd r2, r3, [r1], #8 \n"
" le lr, 2b \n"
"1: \n"
// tail
" wls lr, %[iWidthTail], 1f \n"
".p2align 2 \n"
"2: \n"
" ldr r2, [r0], #4 \n"
" str r2, [r1], #4 \n"
" le lr, 2b \n"
"1: \n"
" add %[pwSource], %[iSourceStride] \n"
" add %[pwTarget], %[iTargetStride] \n"
" subs %[iHeight], #1 \n"
" bne 3b \n"
: [pwTarget] "+r"(pwTarget), [pwSource] "+r"(pwSource)
: [iHeight] "r"(ptCopySize->iHeight), [iWidth] "r" (ptCopySize->iWidth/2),
[iWidthTail] "r" (ptCopySize->iWidth & 1),
[iSourceStride] "r" (iSourceStride*sizeof(uint32_t)),
[iTargetStride] "r" (iTargetStride*sizeof(uint32_t))
: "r0", "r1", "r2", "r3", "q0", "memory", "r14", "cc"
);
}
else
{
/*
* generic column major 32-bit 2D copy
*/
__asm volatile(
"3: \n"
" mov r0, %[pwSource] \n"
" mov r1, %[pwTarget] \n"
" wlstp.32 lr, %[iWidth], 1f \n"
".p2align 2 \n"
"2: \n"
" vldrw.32 q0, [r0], #16 \n"
" vstrw.32 q0, [r1], #16 \n"
" letp lr, 2b \n"
"1: \n"
" add %[pwSource], %[iSourceStride] \n"
" add %[pwTarget], %[iTargetStride] \n"
" subs %[iHeight], #1 \n"
" bne 3b \n"
: [pwTarget] "+r"(pwTarget), [pwSource] "+r"(pwSource)
: [iHeight] "r"(ptCopySize->iHeight), [iWidth] "r" (ptCopySize->iWidth),
[iSourceStride] "r" (iSourceStride*sizeof(uint32_t)),
[iTargetStride] "r" (iTargetStride*sizeof(uint32_t))
: "r0", "r1", "q0", "memory", "r14", "cc");
}
}
/*----------------------------------------------------------------------------*
* alpha blending *
*----------------------------------------------------------------------------*/
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_alpha_blending(uint8_t * __RESTRICT pSourceBase,
int16_t iSourceStride,
uint8_t * __RESTRICT pTargetBase,
int16_t iTargetStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint_fast8_t chRatio)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16_t hwRatioCompl = 256 - chRatio;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pSource = pSourceBase;
uint8_t *pTarget = pTargetBase;
int32_t blkCnt = iWidth;
#ifdef USE_MVE_INTRINSICS
do {
mve_pred16_t tailPred = vctp16q(blkCnt);
uint16x8_t vecTgt = vldrbq_z_u16(pTarget, tailPred);
uint16x8_t vecSrc = vldrbq_z_u16(pSource, tailPred);
vecTgt = vmulq_x(vecTgt, hwRatioCompl, tailPred);
vecTgt = vmlaq_m(vecTgt, vecSrc, chRatio, tailPred);
vecTgt = vecTgt >> 8;
vstrbq_p_u16(pTarget , vecTgt , tailPred);
pSource += 8;
pTarget += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else
__asm volatile(
" vldrb.u16 q0, [%[pTarget]] \n"
".p2align 2 \n"
" wls lr, %[loopCnt], 1f \n"
"2: \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q0, q1, %[chRatio] \n"
" vldrb.u16 q2, [%[pTarget], #8] \n"
" vshr.u16 q0, q0, #8 \n"
" vstrb.u16 q0, [%[pTarget]], #8 \n"
" vmul.u16 q2, q2, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q2, q1, %[chRatio] \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vshr.u16 q2, q2, #8 \n"
" vstrb.u16 q2, [%[pTarget]], #8 \n"
" le lr, 2b \n"
"1: \n"
/* tail */
" wlstp.16 lr, %[tail], 1f \n"
"2: \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q0, q1, %[chRatio] \n"
" vshr.u16 q1, q0, #8 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vstrb.u16 q1, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget), [pSource] "+r" (pSource)
: [chRatio] "r" (chRatio), [hwRatioCompl] "r" (hwRatioCompl),
[loopCnt] "r"(blkCnt/16), [tail] "r"(blkCnt & 0xf)
:"q0", "q1", "q2", "memory", "r14");
#endif
pSourceBase += (iSourceStride);
pTargetBase += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_alpha_blending_colour_keying(uint8_t * __RESTRICT pSourceBase,
int16_t iSourceStride,
uint8_t * __RESTRICT pTargetBase,
int16_t iTargetStride,
arm_2d_size_t *
__RESTRICT ptCopySize,
uint_fast8_t chRatio,
uint8_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16_t hwRatioCompl = 256 - chRatio;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pSource = pSourceBase;
uint8_t *pTarget = pTargetBase;
int32_t blkCnt = iWidth;
#ifdef USE_MVE_INTRINSICS
do {
mve_pred16_t tailPred = vctp16q(blkCnt);
uint16x8_t vecTgt = vldrbq_z_u16(pTarget, tailPred);
uint16x8_t vecSrc = vldrbq_z_u16(pSource, tailPred);
vecTgt = vmulq_x(vecTgt, hwRatioCompl, tailPred);
vecTgt = vmlaq_m(vecTgt, vecSrc, chRatio, tailPred);
vecTgt = vecTgt >> 8;
vstrbq_p_u16(pTarget , vecTgt ,
vcmpneq_m_n_u16(vecSrc, (uint16_t)Colour, tailPred));
pSource += 8;
pTarget += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else
__asm volatile(
" vldrb.u16 q0, [%[pTarget]] \n"
".p2align 2 \n"
" wls lr, %[loopCnt], 1f \n"
"2: \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q0, q1, %[chRatio] \n"
" vldrb.u16 q2, [%[pTarget], #8] \n"
" vshr.u16 q0, q0, #8 \n"
" vpt.u16 ne, q1, %[Colour] \n"
" vstrbt.u16 q0, [%[pTarget]], #8 \n"
" vmul.u16 q2, q2, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q2, q1, %[chRatio] \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vshr.u16 q2, q2, #8 \n"
" vpt.u16 ne, q1, %[Colour] \n"
" vstrbt.u16 q2, [%[pTarget]], #8 \n"
" le lr, 2b \n"
"1: \n"
/* tail */
" wlstp.16 lr, %[tail], 1f \n"
"2: \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vldrb.u16 q1, [%[pSource]], #8 \n"
" vmla.u16 q0, q1, %[chRatio] \n"
" vshr.u16 q2, q0, #8 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vpt.u16 ne, q1, %[Colour] \n"
" vstrbt.u16 q2, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget), [pSource] "+r" (pSource)
: [chRatio] "r" (chRatio), [hwRatioCompl] "r" (hwRatioCompl),
[loopCnt] "r"(blkCnt/16), [tail] "r"(blkCnt & 0xf),
[Colour] "r" (Colour)
:"q0", "q1", "q2", "memory", "r14");
#endif
pSourceBase += (iSourceStride);
pTargetBase += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_colour_filling_with_opacity(uint8_t * __restrict pTargetBase,
int16_t iTargetStride,
arm_2d_size_t *
__restrict ptCopySize,
uint8_t Colour, uint_fast8_t chRatio)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16_t hwRatioCompl = 256 - chRatio;
uint16x8_t vecSrc = vdupq_n_u16(Colour);
for (int_fast16_t y = 0; y < iHeight; y++) {
uint8_t *pTarget = pTargetBase;
int32_t blkCnt = iWidth;
#ifdef USE_MVE_INTRINSICS
do {
mve_pred16_t tailPred = vctp16q(blkCnt);
uint16x8_t vecTgt = vldrbq_z_u16(pTarget, tailPred);
vecTgt = vmulq_x(vecTgt, hwRatioCompl, tailPred);
vecTgt = vmlaq_m(vecTgt, vecSrc, chRatio, tailPred);
vecTgt = vecTgt >> 8;
vstrbq_p_u16(pTarget , vecTgt , tailPred);
pTarget += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else
__asm volatile(
" vldrb.u16 q0, [%[pTarget]] \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
".p2align 2 \n"
" wls lr, %[loopCnt], 1f \n"
"2: \n"
" vmla.u16 q0, %[vecSrc], %[chRatio] \n"
" vldrb.u16 q2, [%[pTarget], #8] \n"
" vshr.u16 q0, q0, #8 \n"
" vmul.u16 q2, q2, %[hwRatioCompl] \n"
" vstrb.u16 q0, [%[pTarget]], #8 \n"
" vmla.u16 q2, %[vecSrc], %[chRatio] \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vshr.u16 q2, q2, #8 \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vstrb.u16 q2, [%[pTarget]], #8 \n"
" le lr, 2b \n"
"1: \n"
/* tail */
" wlstp.16 lr, %[tail], 1f \n"
"2: \n"
" vmla.u16 q0, %[vecSrc], %[chRatio] \n"
" vshr.u16 q2, q0, #8 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vmul.u16 q0, q0, %[hwRatioCompl] \n"
" vstrb.u16 q2, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget)
: [chRatio] "r" (chRatio), [hwRatioCompl] "r" (hwRatioCompl),
[loopCnt] "r"(blkCnt/16), [tail] "r"(blkCnt & 0xf),
[vecSrc] "t" (vecSrc)
:"q0", "q2", "memory", "r14");
#endif
pTargetBase += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_alpha_blending( uint16_t *phwSourceBase,
int16_t iSourceStride,
uint16_t *phwTargetBase,
int16_t iTargetStride,
arm_2d_size_t *ptCopySize,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt;
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t vecMaskGpck = vdupq_n_u16(0x00fc);
for (int32_t y = 0; y < ptCopySize->iHeight; y++) {
const uint16_t *phwSource = phwSourceBase;
uint16_t *phwTarget = phwTargetBase;
blkCnt = ptCopySize->iWidth;
do {
uint16x8_t vecIn;
uint16x8_t vecR0, vecB0, vecG0;
uint16x8_t vecR1, vecB1, vecG1;
/* unpack 1st stream */
vecIn = vld1q(phwSource);
vecR0 = vecIn & vecMaskR;
vecB0 = vecIn >> 11;
vecG0 = vecIn >> 5;
vecG0 = vecG0 & vecMaskG;
/* unpack 2nd stream */
vecIn = vld1q(phwTarget);
vecR1 = vecIn & vecMaskR;
vecB1 = vecIn >> 11;
vecG1 = vecIn >> 5;
vecG1 = vecG1 & vecMaskG;
/* merge */
vecR0 = vecR0 * ratio1x8 + vecR1 * ratio2x8;
vecR0 = vecR0 >> 8;
vecG0 = vecG0 * ratio1x4 + vecG1 * ratio2x4;
vecG0 = vecG0 >> 8;
vecB0 = vecB0 * ratio1x8 + vecB1 * ratio2x8;
vecB0 = vecB0 >> 8;
/* pack */
uint16x8_t vOut = vecR0 >> 3 | vmulq((vecG0 & vecMaskGpck), 8)
| vmulq((vecB0 & vecMaskBpck), 256);
vst1q(phwTarget, vOut);
phwSource += 8;
phwTarget += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
phwSourceBase += iSourceStride;
phwTargetBase += iTargetStride;
}
#else /* USE_MVE_INTRINSICS */
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint32_t iWidth = ptCopySize->iWidth;
int32_t row = ptCopySize->iHeight;
uint16x8_t scratch[1];
vst1q((uint16_t *)scratch, vdupq_n_u16(0x00fc));
do {
const uint16_t *pSource = phwSourceBase;
uint16_t *pTarget = phwTargetBase;
register unsigned loopCnt __asm("lr");
loopCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrh.u16 q4, [%[pTarget]] \n"
" vldrh.u16 q5, [%[pSource]], #16 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
// B target extraction
// right shift by 5 (x 1/32) for M55 friendly
// IV / Mul pipe interleaving
" vqdmulh.s16 q2, q4, %[rshft5] \n"
" vand q7, q4, %[vecMaskR] \n"
" vmul.i16 q6, q7, %[ratio2x8] \n"
// B source extraction
" vand q7, q5, %[vecMaskR] \n"
// B mix
" vmla.u16 q6, q7, %[ratio1x8] \n"
// G extraction
" vand q2, q2, %[vecMaskG] \n"
" vshr.u16 q7, q5, #5 \n"
" vmul.i16 q2, q2, %[ratio2x4] \n"
// G extraction
" vand q7, q7, %[vecMaskG] \n"
// G mix
" vmla.u16 q2, q7, %[ratio1x4] \n"
// R extraction
" vshr.u16 q4, q4, #11 \n"
" vmul.i16 q7, q4, %[ratio2x8] \n"
// R extraction
" vshr.u16 q5, q5, #11 \n"
// R mix
" vmla.u16 q7, q5, %[ratio1x8] \n"
" vshr.u16 q2, q2, #8 \n"
" vldrh.16 q5, [%[scratch]] \n"
" vand q2, q2, q5 \n"
// vmulq((vecG0 & 0x00fc), 8)
" vmul.i16 q2, q2, %[eight] \n"
" vshr.u16 q4, q7, #8 \n"
// schedule next source load
" vldrh.u16 q5, [%[pSource]], #16 \n"
" vand q7, q4, %[vecMaskBpck] \n"
// pack R & G
// vmulq((vecG0 & vecMaskGpck), 8) + vmulq((vecR0 & vecMaskRpck), 256)
" vmla.u16 q2, q7, %[twofiftysix] \n"
// downshift B ((vecB0 >> 8) >> 3)
" vshr.u16 q7, q6, #11 \n"
// schedule next target load (pre offset as target not imcrementred so far)
" vldrh.u16 q4, [%[pTarget], #16] \n"
// pack blue with R&G
" vorr q2, q2, q7 \n"
" vstrh.16 q2, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
: [pSource] "+r"(pSource), [pTarget] "+r" (pTarget), [loopCnt] "+r"(loopCnt)
: [vecMaskR] "t" (vecMaskR), [vecMaskG] "t" (vecMaskG),
[vecMaskBpck] "t" (vecMaskBpck),
[ratio1x8] "r" (ratio1x8), [ratio2x8] "r" (ratio2x8),
[ratio1x4] "r" (ratio1x4), [ratio2x4] "r" (ratio2x4),
[eight] "r" (8), [twofiftysix] "r" (256), [rshft5] "r" (1024), [scratch] "r" (scratch)
: "q2", "q4", "q5", "q6", "q7", "memory" );
phwSourceBase += iSourceStride;
phwTargetBase += iTargetStride;
} while (--row);
#endif /* USE_MVE_INTRINSICS */
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_colour_filling_with_opacity(
uint16_t *__RESTRICT pTargetBase,
int16_t iTargetStride,
arm_2d_size_t *__RESTRICT ptCopySize,
uint16_t Colour,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt;
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t vecMaskGpck = vdupq_n_u16(0x00fc);
uint16x8_t vecIn;
uint16x8_t vecColorR, vecColorB, vecColorG;
/* unpack color & scale */
vecIn = vdupq_n_u16(Colour);
vecColorR = (vecIn & vecMaskR) * ratio1x8;
vecColorB = (vecIn >> 11) * ratio1x8;
vecColorG = vecIn >> 5;
vecColorG = (vecColorG & vecMaskG) * ratio1x4;
for (int32_t y = 0; y < ptCopySize->iHeight; y++) {
uint16_t *phwTarget = pTargetBase;
blkCnt = ptCopySize->iWidth;
do {
uint16x8_t vecR0, vecB0, vecG0;
uint16x8_t vecR1, vecB1, vecG1;
/* unpack stream */
vecIn = vld1q(phwTarget);
vecR1 = vecIn & vecMaskR;
vecB1 = vecIn >> 11;
vecG1 = vecIn >> 5;
vecG1 = vecG1 & vecMaskG;
/* merge */
vecR0 = vecColorR + vecR1 * ratio2x8;
vecR0 = vecR0 >> 8;
vecG0 = vecColorG + vecG1 * ratio2x4;
vecG0 = vecG0 >> 8;
vecB0 = vecColorB + vecB1 * ratio2x8;
vecB0 = vecB0 >> 8;
/* pack */
uint16x8_t vOut = vecR0 >> 3 | vmulq((vecG0 & vecMaskGpck), 8)
| vmulq((vecB0 & vecMaskBpck), 256);
vst1q(phwTarget, vOut);
phwTarget += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
pTargetBase += iTargetStride;
}
#else /* USE_MVE_INTRINSICS */
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t vecColorR, vecColorB, vecColorG;
uint16x8_t scratch[4];
/* unpack color */
uint16x8_t vecIn = vdupq_n_u16(Colour);
vecColorR = vecIn & vecMaskR;
vecColorB = vecIn >> 11;
vecColorG = vecIn >> 5;
vecColorG = vecColorG & vecMaskG;
vst1q((uint16_t*)scratch, vecColorR * ratio1x8);
vst1q((uint16_t*)&scratch[1], vecColorB * ratio1x8);
vst1q((uint16_t*)&scratch[2], vecColorG * ratio1x4);
vst1q((uint16_t*)&scratch[3], vdupq_n_u16(0x00fc));
int32_t row = ptCopySize->iHeight;
do {
uint16_t *phwTarget = pTargetBase;
register unsigned loopCnt __asm("lr");
loopCnt = ptCopySize->iWidth;
__asm volatile(
" vldrh.u16 q4, [%[phwTarget]] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
".p2align 2 \n"
"2: \n"
// B target extraction
" vand q7, q4, %[vecMaskR] \n"
" vldrh.u16 q6, [%[scratch]] \n"
" vshr.u16 q2, q4, #5 \n"
// B mix
" vmla.u16 q6, q7, %[ratio2x8] \n"
// G extraction
" vand q7, q2, %[vecMaskG] \n"
// G extraction
" vldrh.u16 q2, [%[scratch], #32] \n"
// G mix
" vmla.u16 q2, q7, %[ratio2x4] \n"
" vshr.u16 q4, q4, #11 \n"
// R extraction
" vldrh.u16 q7, [%[scratch], #16] \n"
" vshr.u16 q2, q2, #8 \n"
// R mix
" vmla.u16 q7, q4, %[ratio2x8] \n"
" vshr.u16 q4, q7, #8 \n"
// load duplicated 0xfc mask
" vldrh.u16 q7, [%[scratch], #48] \n"
" vand q2, q2, q7 \n"
" vmul.i16 q2, q2, %[eight] \n"
" vand q7, q4, %[vecMaskBpck] \n"
// pack R & G
" vmla.u16 q2, q7, %[twofiftysix] \n"
// downshift B ((vecB0 >> 8) >> 3)
" vshr.u16 q7, q6, #11 \n"
// schedule next target load
" vldrh.u16 q4, [%[phwTarget], #16] \n"
// pack blue with R&G
" vorr q2, q2, q7 \n"
" vstrh.16 q2, [%[phwTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
: [phwTarget] "+r" (phwTarget), [loopCnt] "+r"(loopCnt)
: [vecMaskR] "t" (vecMaskR), [vecMaskG] "t" (vecMaskG),
[vecMaskBpck] "t" (vecMaskBpck),
[ratio2x8] "r" (ratio2x8), [ratio2x4] "r" (ratio2x4),
[eight] "r" (8), [twofiftysix] "r" (256), [scratch] "r" (scratch)
: "q2", "q4", "q5", "q6", "q7", "memory" );
pTargetBase += iTargetStride;
} while (--row);
#endif /* USE_MVE_INTRINSICS */
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_alpha_blending_colour_keying(
uint16_t * __RESTRICT phwSource,
int16_t iSourceStride,
uint16_t * __RESTRICT phwTarget,
int16_t iTargetStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint_fast8_t chRatio,
uint32_t hwColour)
{
#ifdef USE_MVE_INTRINSICS
uint32_t iHeight = ptCopySize->iHeight;
uint32_t iWidth = ptCopySize->iWidth;
int32_t blkCnt;
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t vecMaskGpck = vdupq_n_u16(0x00fc);
for (uint32_t y = 0; y < iHeight; y++) {
// - inconditional blending + predicated dst update
const uint16_t *pSource = phwSource;
uint16_t *pTarget = phwTarget;
blkCnt = iWidth >> 3;
while (blkCnt > 0) {
uint16x8_t vecInSrc, vecInDst;
uint16x8_t vecR0, vecB0, vecG0;
uint16x8_t vecR1, vecB1, vecG1;
/* unpack 1st stream */
vecInSrc = vld1q(pSource);
vecR0 = vandq(vecInSrc, vecMaskR);
vecB0 = vshrq(vecInSrc, 11);
vecG0 = vshrq(vecInSrc, 5);
vecG0 = vandq(vecG0, vecMaskG);
/* unpack 2nd stream */
vecInDst = vld1q(pTarget);
vecR1 = vandq(vecInDst, vecMaskR);
vecB1 = vshrq(vecInDst, 11);
vecG1 = vshrq(vecInDst, 5);
vecG1 = vandq(vecG1, vecMaskG);
/* merge */
vecR0 = vmlaq(vmulq(vecR0, ratio1x8), vecR1, ratio2x8);
vecR0 = vshrq(vecR0, 8);
vecG0 = vmlaq(vmulq(vecG0, ratio1x4), vecG1, ratio2x4);
vecG0 = vshrq(vecG0, 8);
vecB0 = vmlaq(vmulq(vecB0, ratio1x8), vecB1, ratio2x8);
vecB0 = vshrq(vecB0, 8);
/* pack */
uint16x8_t vOut = vorrq(vshrq(vecR0, 3),
vmulq(vandq(vecG0, vecMaskGpck), 8));
vOut = vorrq(vOut, vmulq(vandq(vecB0, vecMaskBpck), 256));
vst1q_p(pTarget, vOut, vcmpneq_n_s16(vecInSrc, hwColour));
pSource += 8;
pTarget += 8;
blkCnt--;
}
blkCnt = iWidth & 7;
if (blkCnt > 0U) {
uint16x8_t vecInSrc, vecInDst;
uint16x8_t vecR0, vecB0, vecG0;
uint16x8_t vecR1, vecB1, vecG1;
/* unpack 1st stream */
vecInSrc = vld1q(pSource);
vecR0 = vandq(vecInSrc, vecMaskR);
vecB0 = vshrq(vecInSrc, 11);
vecG0 = vshrq(vecInSrc, 5);
vecG0 = vandq(vecG0, vecMaskG);
/* unpack 2nd stream */
vecInDst = vld1q(pTarget);
vecR1 = vandq(vecInDst, vecMaskR);
vecB1 = vshrq(vecInDst, 11);
vecG1 = vshrq(vecInDst, 5);
vecG1 = vandq(vecG1, vecMaskG);
/* merge */
vecR0 = vmlaq(vmulq(vecR0, ratio1x8), vecR1, ratio2x8);
vecR0 = vshrq(vecR0, 8);
vecG0 = vmlaq(vmulq(vecG0, ratio1x4), vecG1, ratio2x4);
vecG0 = vshrq(vecG0, 8);
vecB0 = vmlaq(vmulq(vecB0, ratio1x8), vecB1, ratio2x8);
vecB0 = vshrq(vecB0, 8);
/* pack */
uint16x8_t vOut = vorrq(vshrq(vecR0, 3),
vmulq(vandq(vecG0, vecMaskGpck), 8));
vOut = vorrq(vOut,
vmulq(vandq(vecB0, vecMaskBpck), 256));
vst1q_p(pTarget, vOut,
vcmpneq_m_n_s16(vecInSrc, hwColour, vctp16q(blkCnt)));
}
phwSource += iSourceStride;
phwTarget += iTargetStride;
}
#else
uint32_t iHeight = ptCopySize->iHeight;
uint32_t iWidth = ptCopySize->iWidth;
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t scratch[1];
vst1q((uint16_t *)scratch, vdupq_n_u16(0x00fc));
for (uint32_t y = 0; y < iHeight; y++) {
const uint16_t *pSource = phwSource;
uint16_t *pTarget = phwTarget;
register unsigned loopCnt __asm("lr");
loopCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrh.u16 q4, [%[pTarget]] \n"
" vldrh.u16 q5, [%[pSource]], #16 \n"
" vand q7, q4, %[vecMaskR] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
// B target extraction
" vshr.u16 q2, q4, #5 \n"
" vmul.i16 q6, q7, %[ratio2x8] \n"
// B source extraction
" vand q7, q5, %[vecMaskR] \n"
// B mix
" vmla.u16 q6, q7, %[ratio1x8] \n"
// G extraction
" vand q2, q2, %[vecMaskG] \n"
" vshr.u16 q7, q5, #5 \n"
" vmul.i16 q2, q2, %[ratio2x4] \n"
// G extraction
" vand q7, q7, %[vecMaskG] \n"
// G mix
" vmla.u16 q2, q7, %[ratio1x4] \n"
// R extraction
" vshr.u16 q4, q4, #11 \n"
" vmul.i16 q7, q4, %[ratio2x8] \n"
// R extraction
" vshr.u16 q5, q5, #11 \n"
// R mix
" vmla.u16 q7, q5, %[ratio1x8] \n"
" vshr.u16 q2, q2, #8 \n"
" vldrh.16 q5, [%[scratch]] \n"
" vand q2, q2, q5 \n"
// vmulq((vecG0 & 0x00fc), 8)
" vmul.i16 q2, q2, %[eight] \n"
" vshr.u16 q4, q7, #8 \n"
// schedule next source load
" vldrh.u16 q5, [%[pSource]], #16 \n"
" vand q7, q4, %[vecMaskBpck] \n"
// pack R & G
// vmulq((vecG0 & vecMaskGpck), 8) + vmulq((vecR0 & vecMaskRpck), 256)
" vmla.u16 q2, q7, %[twofiftysix] \n"
// downshift B ((vecB0 >> 8) >> 3)
" vshr.u16 q7, q6, #11 \n"
// schedule next target load (pre offset as target not imcrementred so far)
" vldrh.u16 q4, [%[pTarget], #16] \n"
// pack blue with R&G
" vorr q2, q2, q7 \n"
" vldrh.u16 q6, [%[pSource], #-32] \n"
" vand q7, q4, %[vecMaskR] \n"
" vpt.u16 ne, q6, %[hwColour] \n"
" vstrht.16 q2, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
: [pSource] "+r"(pSource), [pTarget] "+r" (pTarget), [loopCnt] "+r"(loopCnt)
: [vecMaskR] "t" (vecMaskR), [vecMaskG] "t" (vecMaskG),
[vecMaskBpck] "t" (vecMaskBpck),
[ratio1x8] "r" (ratio1x8), [ratio2x8] "r" (ratio2x8),
[ratio1x4] "r" (ratio1x4), [ratio2x4] "r" (ratio2x4),
[eight] "r" (8), [twofiftysix] "r" (256), [hwColour] "r" (hwColour), [scratch] "r" (scratch)
: "q2", "q4", "q5", "q6", "q7", "memory" );
phwSource += (iSourceStride);
phwTarget += (iTargetStride);
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_alpha_blending( uint32_t *pwSourceBase,
int16_t iSourceStride,
uint32_t *pwTargetBase,
int16_t iTargetStride,
arm_2d_size_t *ptCopySize,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
int32_t blkCnt;
int32_t row = ptCopySize->iHeight;
while (row > 0) {
const uint32_t *pwSource = pwSourceBase;
uint32_t *pwTarget = pwTargetBase;
/* byte extraction into 16-bit vector */
uint16x8_t vecSrc = vldrbq_u16((const uint8_t *)pwSource);
uint16x8_t vecTrg = vldrbq_u16((const uint8_t *)pwTarget);
pwSource += 2;
blkCnt = ptCopySize->iWidth;
while (blkCnt > 0) {
vstrbq_u16((const uint8_t *)pwTarget,
vmlaq(vmulq(vecSrc, chRatio), vecTrg, chRatioCompl) >> 8);
pwTarget += 2;
vecSrc = vldrbq_u16((const uint8_t *)pwSource);
vecTrg = vldrbq_u16((const uint8_t *)pwTarget);
pwSource += 2;
blkCnt -= 2;
}
pwSourceBase += iSourceStride;
pwTargetBase += iTargetStride;
row--;
}
#else
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
register unsigned blkCnt __asm("lr");
int32_t row = ptCopySize->iHeight;
while(row > 0)
{
blkCnt = ptCopySize->iWidth*4;
const uint32_t *pwSource = pwSourceBase;
uint32_t *pwTarget = pwTargetBase;
__asm volatile(
" vldrb.u16 q0, [%[pwSource]], #8 \n"
" vldrb.u16 q1, [%[pwTarget]] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vmul.u16 q2, q0, %[chRatio] \n"
" vldrb.u16 q0, [%[pwSource]], #8 \n"
" vmla.u16 q2, q1, %[chRatioCompl] \n"
" vldrb.u16 q1, [%[pwTarget], #8] \n"
" vshr.u16 q2, q2, #8 \n"
" vstrb.16 q2, [%[pwTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pwSource] "+l"(pwSource), [pwTarget] "+l"(pwTarget),
[loopCnt] "+r"(blkCnt)
: [chRatio] "r" (chRatio), [chRatioCompl] "r" (chRatioCompl)
: "q0", "q1", "q2", "memory" );
pwSourceBase += iSourceStride;
pwTargetBase += iTargetStride;
row--;
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_colour_filling_with_opacity(
uint32_t *__RESTRICT pTargetBase,
int16_t iTargetStride,
arm_2d_size_t *__RESTRICT ptCopySize,
uint32_t Colour,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
int32_t blkCnt;
int32_t row = ptCopySize->iHeight;
uint32_t scratch[2];
uint16x8_t vColor;
scratch[0] = scratch[1] = Colour;
vColor = vldrbq_u16((uint8_t *) scratch);
vColor = vColor * (uint16_t)chRatio;
while (row > 0) {
uint32_t *pTarget = pTargetBase;
blkCnt = ptCopySize->iWidth;
while (blkCnt > 0) {
/* byte extraction into 16-bit vector */
uint16x8_t vecTrg = vldrbq_u16((uint8_t *)pTarget);
vstrbq_u16((uint8_t *)pTarget, vmlaq(vColor, vecTrg, chRatioCompl) >> 8);
pTarget += 2;
blkCnt -= 2;
}
pTargetBase += iTargetStride;
row--;
}
#else /* USE_MVE_INTRINSICS */
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
int32_t blkCnt;
int32_t row = ptCopySize->iHeight;
uint32_t scratch[2];
uint16x8_t vColor;
scratch[0] = scratch[1] = Colour;
vColor = vldrbq_u16((uint8_t *) scratch);
vColor = vColor * (uint16_t)chRatio;
while (row > 0) {
uint32_t *pTarget = pTargetBase;
blkCnt = ptCopySize->iWidth*4;
__asm volatile(
/* preload */
" vldrb.u16 q1, [%[pTarget]] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
".p2align 2 \n"
"2: \n"
" vmov q2, %[vColor] \n"
" vmla.u16 q2, q1, %[chRatioCompl] \n"
" vldrb.u16 q1, [%[pTarget], #8] \n"
" vshr.u16 q2, q2, #8 \n"
" vstrb.16 q2, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+l"(pTarget)
: [loopCnt] "r"(blkCnt), [chRatioCompl] "r" (chRatioCompl), [vColor] "t" (vColor)
: "q0", "q1", "q2", "memory" );
pTargetBase += iTargetStride;
row--;
}
#endif /* USE_MVE_INTRINSICS */
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_alpha_blending_colour_keying(uint32_t * __RESTRICT pSourceBase,
int16_t iSourceStride,
uint32_t * __RESTRICT pTargetBase,
int16_t iTargetStride,
arm_2d_size_t *
__RESTRICT ptCopySize,
uint_fast8_t chRatio,
uint32_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16_t chRatioCompl = 256 - chRatio;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint32_t *pSource = pSourceBase;
uint32_t *pTarget = pTargetBase;
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt = iWidth;
do {
mve_pred16_t p = vctp32q(blkCnt);
uint8x16_t vSrc8 = vld1q_z(pSource, p);
uint8x16_t vTrg8 = vld1q_z(pTarget, p);
/* 16-bit expansion A/G src pixels */
uint16x8_t vSrc16b = vmovlbq_x(vSrc8, p);
/* 16-bit expansion R/B src pixels */
uint16x8_t vSrc16t = vmovltq_x(vSrc8, p);
/* 16-bit expansion A/G target pixels */
uint16x8_t vTrg16b = vmovlbq_x(vTrg8, p);
/* 16-bit expansion R/B target pixels */
uint16x8_t vTrg16t = vmovltq_x(vTrg8, p);
/* A/G blending */
int16x8_t vecOutb = vmlaq_m(vmulq_x(vSrc16b, chRatio, p), vTrg16b, chRatioCompl, p);
/* R/B blending */
int16x8_t vecOutt = vmlaq_m(vmulq_x(vSrc16t, chRatio, p), vTrg16t, chRatioCompl, p);
/* merge into 8-bit vector */
int8x16_t vecOut8 = vuninitializedq_s8();
vecOut8 = vqshrnbq_m_n_s16(vecOut8, vecOutb, 8, p);
vecOut8 = vqshrntq_m_n_s16(vecOut8, vecOutt, 8, p);
// update if (*pSourceBase != Colour)
vst1q_p_u32(pTarget, (uint32x4_t) vecOut8,
vcmpneq_m_n_u32((uint32x4_t) vSrc8, Colour, p));
pSource += 4;
pTarget += 4;
blkCnt -= 4;
}
while (blkCnt > 0);
#else // USE_MVE_INTRINSICS
__asm volatile (
".p2align 2 \n"
/* preload uint32x4_t target vector */
" vldrw.u32 q2, [%[targ]] \n"
" wlstp.32 lr, %[loopCnt], 1f \n"
"2: \n"
/* 16-bit expansion A/G target pixels */
" vmovlb.u8 q3, q2 \n"
" vldrw.u32 q0, [%[src]], #16 \n"
/* 16-bit expansion A/G source pixels */
" vmovlb.u8 q1, q0 \n"
" vmul.i16 q1, q1, %[ratio] \n"
/* 16-bit expansion R/B target pixels */
" vmovlt.u8 q2, q2 \n"
/* A/G blending */
" vmla.u16 q1, q3, %[ratioCmp] \n"
/* 16-bit expansion R/B source pixels */
" vmovlt.u8 q3, q0 \n"
" vmul.i16 q3, q3, %[ratio] \n"
/* merge A/G into 8-bit vector */
" vqshrnb.s16 q1, q1, #8 \n"
/* R/B blending */
" vmla.u16 q3, q2, %[ratioCmp] \n"
/* preload next target */
" vldrw.u32 q2, [%[targ], #16] \n"
/* merge R/B into 8-bit vector */
" vqshrnt.s16 q1, q3, #8 \n"
/* update if (*pSourceBase != Colour) */
" vpt.i32 ne, q0, %[color] \n"
" vstrwt.32 q1, [%[targ]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[targ] "+r" (pTarget), [src] "+r" (pSource)
:[loopCnt] "r" (iWidth), [ratio] "r" (chRatio),
[ratioCmp] "r" (chRatioCompl), [color] "r" (Colour)
:"r14", "q0", "q1", "q2", "q3", "memory");
#endif
pSourceBase += (iSourceStride);
pTargetBase += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_alpha_blending_direct(const uint16_t *phwSource,
const uint16_t *phwBackground,
uint16_t *phwDestination,
uint32_t wPixelCount,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt;
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
uint16x8_t vecMaskGpck = vdupq_n_u16(0x00fc);
blkCnt = wPixelCount;
do {
uint16x8_t vecIn;
uint16x8_t vecR0, vecB0, vecG0;
uint16x8_t vecR1, vecB1, vecG1;
/* unpack 1st stream */
vecIn = vld1q(phwSource);
phwSource += 8;
vecR0 = vecIn & vecMaskR;
vecB0 = vecIn >> 11;
vecG0 = vecIn >> 5;
vecG0 = vecG0 & vecMaskG;
/* unpack 2nd stream */
vecIn = vld1q(phwBackground);
phwBackground += 8;
vecR1 = vecIn & vecMaskR;
vecB1 = vecIn >> 11;
vecG1 = vecIn >> 5;
vecG1 = vecG1 & vecMaskG;
/* merge */
vecR0 = vecR0 * ratio1x8 + vecR1 * ratio2x8;
vecR0 = vecR0 >> 8;
vecG0 = vecG0 * ratio1x4 + vecG1 * ratio2x4;
vecG0 = vecG0 >> 8;
vecB0 = vecB0 * ratio1x8 + vecB1 * ratio2x8;
vecB0 = vecB0 >> 8;
/* pack */
uint16x8_t vOut =
vecR0 >> 3 | vmulq((vecG0 & vecMaskGpck), 8)
| vmulq((vecB0 & vecMaskBpck), 256);
vst1q(phwDestination, vOut);
phwDestination += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else /* USE_MVE_INTRINSICS */
uint16_t ratio1x8 = chRatio * 8;
uint16_t ratio1x4 = chRatio * 4;
uint16_t ratio2x8 = (256 - chRatio) * 8;
uint16_t ratio2x4 = (256 - chRatio) * 4;
uint16x8_t vecMaskR = vdupq_n_u16(0x001f);
uint16x8_t vecMaskG = vdupq_n_u16(0x003f);
uint16x8_t vecMaskBpck = vdupq_n_u16(0x00f8);
register unsigned loopCnt __asm("lr") = (wPixelCount);
__asm volatile(
" vldrh.u16 q4, [%[in2]], #16 \n"
" vmov.i16 q6, #0x00fc \n"
" vstrw.32 q6, [sp] \n"
" vldrh.u16 q5, [%[in1]], #16 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vand q6, q4, %[vecMaskR] \n"
" vmul.i16 q6, q6, %[ratio2x8] \n"
" vshr.u16 q2, q4, #5 \n"
" vand q7, q5, %[vecMaskR] \n"
" vmla.u16 q6, q7, %[ratio1x8] \n"
" vand q2, q2, %[vecMaskG] \n"
" vshr.u16 q7, q5, #5 \n"
" vmul.i16 q2, q2, %[ratio2x4] \n"
" vand q7, q7, %[vecMaskG] \n"
" vmla.u16 q2, q7, %[ratio1x4] \n"
" vshr.u16 q4, q4, #11 \n"
" vmul.i16 q7, q4, %[ratio2x8] \n"
" vshr.u16 q5, q5, #11 \n"
" vshr.u16 q2, q2, #8 \n"
" vmla.u16 q7, q5, %[ratio1x8] \n"
// " vmov.i16 q6, #0x00fc \n"
" vshr.u16 q7, q7, #8 \n"
// " vmov.i16 q6, #0x00fc \n"
/* load 0x00fc instead of mov for better overlap opportunity */
" vldrw.u32 q4, [sp] \n"
" vand q2, q2, q4 \n"
" vmul.i16 q2, q2, %[eight] \n"
" vand q4, q7, %[vecMaskBpck] \n" // Q7 = vecB0
" vldrh.u16 q5, [%[in1]], #16 \n"
" vmla.u16 q2, q4, %[twofiftysix] \n"
// (vecR0 >> 3) >> 8
" vshr.u16 q6, q6, #11 \n"
" vldrh.u16 q4, [%[in2]], #16 \n"
" vorr q2, q2, q6 \n"
" vstrh.16 q2, [%[out]], #16 \n"
" letp lr, 2b \n"
"1: \n"
: [in1] "+r"(phwSource), [in2] "+r"(phwBackground),
[out] "+r" (phwDestination), [loopCnt] "+r"(loopCnt)
: [vecMaskR] "t" (vecMaskR), [vecMaskG] "t" (vecMaskG),
[vecMaskBpck] "t" (vecMaskBpck),
[ratio1x8] "r" (ratio1x8), [ratio2x8] "r" (ratio2x8),
[ratio1x4] "r" (ratio1x4), [ratio2x4] "r" (ratio2x4),
[eight] "r" (8), [twofiftysix] "r" (256)
: "q2", "q4", "q5", "q6", "q7", "memory" );
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_alpha_blending_direct(const uint32_t *pwSource,
const uint32_t *pwBackground,
uint32_t *pwDestination,
uint32_t wPixelCount,
uint_fast8_t chRatio)
{
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt;
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
uint16x8_t vecSrc, vecBckg;
vecSrc = vldrbq_u16((uint8_t const *) pwSource);
pwSource += 2;
vecBckg = vldrbq_u16((uint8_t const *) pwBackground);
pwBackground += 2;
blkCnt = wPixelCount;
do {
uint16x8_t vecOut;
vecOut = vmulq_n_u16(vecSrc, (uint16_t) chRatio);
vecSrc = vldrbq_u16((uint8_t const *) pwSource);
pwSource += 2;
vecOut = vmlaq_n_u16(vecOut, vecBckg, chRatioCompl);
vecBckg = vldrbq_u16((uint8_t const *) pwBackground);
pwBackground += 2;
vecOut = vecOut >> 8;
vstrbq_u16((uint8_t *) pwDestination, vecOut);
pwDestination += 2;
blkCnt -= 2;
}
while (blkCnt > 0);
#else /* USE_MVE_INTRINSICS */
uint16_t chRatioCompl = 256 - (uint16_t) chRatio;
register unsigned blkCnt __asm("lr") = (wPixelCount * 4);
__asm volatile(
" vldrb.u16 q0, [%[pwSource]], #8 \n"
" vldrb.u16 q1, [%[pwBackg]], #8 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vmul.u16 q2, q0, %[chRatio] \n"
" vldrb.u16 q0, [%[pwSource]], #8 \n"
" vmla.u16 q2, q1, %[chRatioCompl] \n"
" vldrb.u16 q1, [%[pwBackg]], #8 \n"
" vshr.u16 q2, q2, #8 \n"
" vstrb.16 q2, [%[pwDest]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pwSource] "+l"(pwSource), [pwBackg] "+l"(pwBackground),
[pwDest] "+l" (pwDestination), [loopCnt] "+r"(blkCnt)
: [chRatio] "r" (chRatio), [chRatioCompl] "r" (chRatioCompl)
: "q0", "q1", "q2", "memory" );
#endif
}
static
mve_pred16_t arm_2d_is_point_vec_inside_region_s16(const arm_2d_region_t * ptRegion,
const arm_2d_point_s16x8_t * ptPoint)
{
mve_pred16_t p0 = vcmpgeq(ptPoint->X, ptRegion->tLocation.iX);
p0 = vcmpgeq_m(ptPoint->Y, ptRegion->tLocation.iY, p0);
p0 = vcmpltq_m(ptPoint->X, ptRegion->tLocation.iX + ptRegion->tSize.iWidth, p0);
p0 = vcmpltq_m(ptPoint->Y, ptRegion->tLocation.iY + ptRegion->tSize.iHeight, p0);
return p0;
}
static
mve_pred16_t arm_2d_is_point_vec_inside_region_s32(const arm_2d_region_t * ptRegion,
const arm_2d_point_s32x4_t * ptPoint)
{
mve_pred16_t p0 = vcmpgeq_n_s32(ptPoint->X, ptRegion->tLocation.iX);
p0 = vcmpgeq_m_n_s32(ptPoint->Y, ptRegion->tLocation.iY, p0);
p0 = vcmpltq_m_n_s32(ptPoint->X, ptRegion->tLocation.iX + ptRegion->tSize.iWidth, p0);
p0 = vcmpltq_m_n_s32(ptPoint->Y, ptRegion->tLocation.iY + ptRegion->tSize.iHeight, p0);
return p0;
}
/**
@brief return 3 vector of 16-bit channels (8-bit widened) taken from a memory reference
@param[in] pMem pointer to packed 8-bit channel
@param[out] R vector of 16-bit widened R channel
@param[out] G vector of 16-bit widened G channel
@param[out] B vector of 16-bit widened B channel
*/
void __arm_2d_unpack_rgb888_from_mem(const uint8_t * pMem, uint16x8_t * R, uint16x8_t * G,
uint16x8_t * B)
{
uint16x8_t sg = vidupq_n_u16(0, 4);
*R = vldrbq_gather_offset_u16(pMem, sg);
*G = vldrbq_gather_offset_u16(pMem + 1, sg);
*B = vldrbq_gather_offset_u16(pMem + 2, sg);
}
/**
@brief interleave 3 x 16-bit widened vectors into 8-bit memory reference
(4th channel untouched)
@param[in] pMem pointer to packed 8-bit channel
@param[in] R vector of 16-bit widened R channel
@param[in] G vector of 16-bit widened G channel
@param[in] B vector of 16-bit widened B channel
*/
void __arm_2d_pack_rgb888_to_mem(uint8_t * pMem, uint16x8_t R, uint16x8_t G, uint16x8_t B)
{
uint16x8_t sg = vidupq_n_u16(0, 4);
vstrbq_scatter_offset_u16(pMem, sg, R);
vstrbq_scatter_offset_u16(pMem + 1, sg, G);
vstrbq_scatter_offset_u16(pMem + 2, sg, B);
//vstrbq_scatter_offset_u16(pMem + 3, sg, vdupq_n_u16(0));
}
/**
unpack vectors of 8-bit widened pixels read from a input 2D coordinates if fits inside the region of
interest or alternative target pixel if content matches color mask
(vector of packed pixels & region of interest name implicit and fixed to respectively
vTarget and ptOrigValidRegion)
Update global predictor tracking region fit & color mask comparison.
*/
#define __ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vecX, vecY, ptVal8, MaskColour, pGlb) \
arm_2d_point_s16x8_t vPoint = {.X = vecX,.Y = vecY }; \
/* set vector predicate if point is inside the region */ \
mve_pred16_t p = \
arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint); \
pGlb |= p; \
/* prepare vector of point offsets */ \
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1; \
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride; \
\
/* base pointer update to compensate offset */ \
uint8_t *pOriginCorrected = pOrigin + (correctionOffset * iOrigStride); \
/* retrieve all point values */ \
ptVal8 = \
vldrbq_gather_offset_z_u16(pOriginCorrected, ptOffs, predTail); \
\
/* combine 2 predicates set to true if point is in the region & values */ \
/* different from color mask */ \
p = vcmpneq_m_n_u16(ptVal8, MaskColour, p); \
pGlb |= p; \
ptVal8 = vpselq_u16(ptVal8, vTarget, p);
/**
unpack vectors of pixels read from a input 2D coordinates if fits inside the region of
interest or alternative target pixel if content matches color mask
(vector of packed pixels & region of interest name implicit and fixed to respectively
vTarget and ptOrigValidRegion)
Update global predictor tracking region fit & color mask comparison.
*/
#define __ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vecX, vecY, R, G, B, MaskColour, pGlb) \
arm_2d_point_s16x8_t vPoint = {.X = vecX,.Y = vecY }; \
/* set vector predicate if point is inside the region */ \
mve_pred16_t p = \
arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint); \
pGlb |= p; \
\
/* prepare vector of point offsets */ \
uint16x8_t ptOffs = vPoint.X + vPoint.Y * iOrigStride; \
/* retrieve all point values */ \
uint16x8_t ptVal = \
vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail); \
\
/* combine 2 predicates set to true if point is in the region & values different from color mask */\
p = vcmpneq_m_n_u16(ptVal, MaskColour, p); \
pGlb |= p; \
ptVal = vpselq_u16(ptVal, vTarget, p); \
\
/* expand channels */ \
__arm_2d_rgb565_unpack_single_vec(ptVal, &R, &G, &B);
/**
Same as above but use offset compensation during gather load.
unpack vectors of pixels read from a input 2D coordinates if fits inside the region of
interest or alternative target pixel if content matches color mask
(vector of packed pixels & region of interest name implicit and fixed to respectively
vTarget and ptOrigValidRegion)
Update global predictor tracking region fit & color mask comparison.
*/
#define __ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vecX, vecY, R, G, B, MaskColour, pGlb) \
arm_2d_point_s16x8_t vPoint = {.X = vecX,.Y = vecY }; \
/* set vector predicate if point is inside the region */ \
mve_pred16_t p = \
arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint); \
pGlb |= p; \
/* prepare vector of point offsets */ \
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1; \
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride; \
\
/* base pointer update to compensate offset */ \
uint16_t *pOriginCorrected = pOrigin + (correctionOffset * iOrigStride); \
/* retrieve all point values */ \
uint16x8_t ptVal = \
vldrhq_gather_shifted_offset_z_u16(pOriginCorrected, ptOffs, predTail); \
\
/* combine 2 predicates set to true if point is in the region & values different from color mask */\
p = vcmpneq_m_n_u16(ptVal, MaskColour, p); \
pGlb |= p; \
ptVal = vpselq_u16(ptVal, vTarget, p); \
\
/* expand channels */ \
__arm_2d_rgb565_unpack_single_vec(ptVal, &R, &G, &B);
/**
unpack vectors of 32-bit pixels read from a input 2D coordinates if fits inside the region of
interest or alternative target pixel if content matches color mask
16-bit vector processed in 2 parts because of 32-bit requirements, so handles 8 x 32-bit vectors
(vectors of packed pixels & region of interest name implicit and fixed to respectively
vTargetLo, vectorHi and ptOrigValidRegion)
Update 2 global predictors tracking region fit & color mask comparison for 1st and 2nd half.
*/
#define __ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vecX, vecY, R, G, B, MaskColour, pGlbLo, pGlbHi) \
arm_2d_point_s16x8_t vPoint = {.X = vecX,.Y = vecY }; \
arm_2d_point_s32x4_t tPointLo, tPointHi; \
\
/* split 16-bit point vector into 2 x 32-bit vectors */ \
vst1q(pscratch16, vPoint.X); \
tPointLo.X = vldrhq_s32(pscratch16); \
tPointHi.X = vldrhq_s32(pscratch16 + 4); \
\
vst1q(pscratch16, vPoint.Y); \
tPointLo.Y = vldrhq_s32(pscratch16); \
tPointHi.Y = vldrhq_s32(pscratch16 + 4); \
\
/* 1st half */ \
\
/* set vector predicate if point is inside the region */ \
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointLo); \
/* prepare vector of point offsets */ \
uint32x4_t ptOffs = tPointLo.X + tPointLo.Y * iOrigStride; \
\
/* retrieve all point values */ \
uint32x4_t ptVal = vldrwq_gather_shifted_offset_z_u32(pOrigin, ptOffs, predTailLow); \
\
/* combine 2 predicates set to true if point is in the region & values different from color mask */\
p = vcmpneq_m_n_u32(ptVal, MaskColour, p); \
pGlbLo |= p; \
ptVal = vpselq_u32(ptVal, vTargetLo, p); \
\
vst1q(scratch32, ptVal); \
\
/* 2nd half */ \
\
/* set vector predicate if point is inside the region */ \
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointHi); \
/* prepare vector of point offsets */ \
ptOffs = tPointHi.X + tPointHi.Y * iOrigStride; \
\
/* retrieve all point values */ \
ptVal = vldrwq_gather_shifted_offset_z_u32(pOrigin, ptOffs, predTailHigh); \
\
/* combine 2 predicates set to true if point is in the region & values different from color mask */\
p = vcmpneq_m_n_u32(ptVal, MaskColour, p); \
pGlbHi |= p; \
ptVal = vpselq_u32(ptVal, vTargetHi, p); \
\
vst1q(scratch32 + 4, ptVal); \
\
/* expand channels */ \
__arm_2d_unpack_rgb888_from_mem((uint8_t *) scratch32, &R, &G, &B);
/**
Alpha blending of a packed vector with 3 vectors of single R, G & B channels
*/
#define __ARM_2D_BLEND_RGB565_TARGET_RGBVEC(chOpacity, vPackedTarget, vAvgR, vAvgG, vAvgB, vBlended) \
uint16x8_t vTargetR, vTargetG, vTargetB; \
\
__arm_2d_rgb565_unpack_single_vec(vTarget, &vTargetR, &vTargetG, &vTargetB); \
\
uint16_t chOpacityCompl = (256 - chOpacity); \
\
/* merge */ \
vAvgR = vAvgR * chOpacityCompl + vTargetR * chOpacity; \
vAvgR = vAvgR >> 8; \
\
vAvgG = vAvgG * chOpacityCompl + vTargetG * chOpacity; \
vAvgG = vAvgG >> 8; \
\
vAvgB = vAvgB * chOpacityCompl + vTargetB * chOpacity; \
vAvgB = vAvgB >> 8; \
\
vBlended = __arm_2d_rgb565_pack_single_vec(vAvgR, vAvgG, vAvgB);
#if __ARM_2D_HAS_HELIUM_FLOAT__ \
&& !__ARM_2D_CFG_FORCED_FIXED_POINT_ROTATION__
#define __CALIB 0.009f16
/**
Scale Gray8 channel
*/
#define __ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vScal) \
vAvgPixel = vScal * vcvtq_f16_s16(ptVal8);
/**
Scale Gray8 channel with accumulation
*/
#define __ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vScal) \
vAvgPixel += vScal * vcvtq_f16_s16(ptVal8);
/**
Scale R, G & B channels
*/
#define __ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vScal) \
vAvgPixelR = vScal * vcvtq_f16_s16(R); \
vAvgPixelG = vScal * vcvtq_f16_s16(G); \
vAvgPixelB = vScal * vcvtq_f16_s16(B);
/**
Scale R, G & B channels with accumulation
*/
#define __ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vScal) \
vAvgPixelR += vScal * vcvtq_f16_s16(R); \
vAvgPixelG += vScal * vcvtq_f16_s16(G); \
vAvgPixelB += vScal * vcvtq_f16_s16(B);
static
bool __arm_2d_rotate_regression(arm_2d_size_t * __RESTRICT ptCopySize,
arm_2d_location_t * pSrcPoint,
float fAngle,
arm_2d_location_t * tOffset,
arm_2d_location_t * center,
int32_t iOrigStride,
arm_2d_rot_linear_regr_t regrCoefs[]
)
{
int32_t iHeight = ptCopySize->iHeight;
int32_t iWidth = ptCopySize->iWidth;
float invHeight = iHeight > 1 ? 1.0f / (float) (iHeight - 1) : __LARGEINVF32;
arm_2d_point_s32x4_t vPointCornerI;
int32x4_t vCornerX = { 0, 1, 0, 1 };
int32x4_t vCornerY = { 0, 0, 1, 1 };
float cosAngle = arm_cos_f32(fAngle);
float sinAngle = arm_sin_f32(fAngle);
arm_2d_point_float_t centerf;
float slopeX, slopeY;
bool gatherLoadIdxOverflow = 0;
//printf("invHeight %f\n",invHeight);
centerf.fX = (float) center->iX;
centerf.fY = (float) center->iY;
vPointCornerI.X = vdupq_n_s32(pSrcPoint->iX + tOffset->iX);
vPointCornerI.X = vPointCornerI.X + vmulq_n_s32(vCornerX, (iWidth - 1));
vPointCornerI.Y = vdupq_n_s32(pSrcPoint->iY + tOffset->iY);
vPointCornerI.Y = vPointCornerI.Y + vmulq_n_s32(vCornerY, (iHeight - 1));
/*
Vector version of:
int16_t iX = ptLocation->iX - ptCenter->iX;
int16_t iY = ptLocation->iY - ptCenter->iY;
float cosAngle = arm_cos_f32(fAngle);
float sinAngle = arm_sin_f32(fAngle);
ptOutBuffer->fY = (iY * cosAngle + iX * sinAngle + ptCenter->iY);
ptOutBuffer->fX = (-iY * sinAngle + iX * cosAngle + ptCenter->iX);
*/
arm_2d_point_f32x4_t vTmp, vPointCornerF;
vTmp.X = vsubq_n_f32(vcvtq_f32_s32(vPointCornerI.X), centerf.fX);
vTmp.Y = vsubq_n_f32(vcvtq_f32_s32(vPointCornerI.Y), centerf.fY);
vPointCornerF.X = vmulq_n_f32(vTmp.X, cosAngle) - vmulq_n_f32(vTmp.Y, sinAngle);
vPointCornerF.X = vaddq_n_f32(vPointCornerF.X, centerf.fX);
vPointCornerF.Y = vmulq_n_f32(vTmp.X, sinAngle) + vmulq_n_f32(vTmp.Y, cosAngle);
vPointCornerF.Y = vaddq_n_f32(vPointCornerF.Y, centerf.fY);
/*
Check whether rotated index offsets could exceed 16-bit limits
used in subsequent gather loads
This will occur for parts of large images (e.g. 320*200)
To avoid unconditional penalties for small/medium images,
returns a speculative overflow allowing to handle large offsets.
*/
float32_t maxY = vmaxnmvq(0.0f, vPointCornerF.Y);
if((iOrigStride * maxY) > (float)(UINT16_MAX))
gatherLoadIdxOverflow = true;
/* interpolation in Y direction for 1st elements column */
slopeX = (vPointCornerF.X[2] - vPointCornerF.X[0]) * invHeight;
slopeY = (vPointCornerF.Y[2] - vPointCornerF.Y[0]) * invHeight;
regrCoefs[0].slopeY = slopeY;
regrCoefs[0].slopeX = slopeX;
regrCoefs[0].interceptY = vPointCornerF.Y[0];
regrCoefs[0].interceptX = vPointCornerF.X[0];
/* interpolation in Y direction for the last elements column */
slopeX = (vPointCornerF.X[3] - vPointCornerF.X[1]) * invHeight;
slopeY = (vPointCornerF.Y[3] - vPointCornerF.Y[1]) * invHeight;
regrCoefs[1].slopeY = slopeY;
regrCoefs[1].slopeX = slopeX;
regrCoefs[1].interceptY = vPointCornerF.Y[1];
regrCoefs[1].interceptX = vPointCornerF.X[1];
return gatherLoadIdxOverflow;
}
static
void __arm_2d_impl_gray8_get_pixel_colour(arm_2d_point_f16x8_t
* ptPoint,
arm_2d_region_t *
ptOrigValidRegion,
uint8_t * pOrigin,
int16_t iOrigStride,
uint8_t * pTarget,
uint8_t MaskColour, uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vldrbq_u16(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixel;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t ptVal8;
/* Bottom Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTR);
}
/* pack */
uint16x8_t TempPixel = vcvtq_s16_f16(vAvgPixel);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(TempPixel, vTarget, predGlb);
#else
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y)
};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
/* retrieve all point values */
uint16x8_t ptVal = vldrbq_gather_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vstrbq_p_u16(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_gray8_get_pixel_colour_with_alpha(arm_2d_point_f16x8_t * ptPoint,
arm_2d_region_t * ptOrigValidRegion,
uint8_t * pOrigin,
int16_t iOrigStride,
uint8_t * pTarget,
uint8_t MaskColour,
uint8_t chOpacity, uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vldrbq_u16(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixel;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t ptVal8;
/* Bottom Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi,
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTR);
}
/* blending */
uint16x8_t vAvg = vcvtq_s16_f16(vAvgPixel);
uint16_t chTransparency = 256 - (chOpacity);
uint16x8_t vBlended = (vAvg * chTransparency + vTarget * chOpacity) >> 8;
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* set vector predicate if point is inside the region */
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y)
};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* retrieve all point values */
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
uint16x8_t ptVal = vldrbq_gather_offset_z_u16(pOrigin, ptOffs, predTail);
/* alpha blending */
uint16_t chTransparency = 256 - (chOpacity);
uint16x8_t vBlended = (ptVal * chTransparency + vTarget * chOpacity) >> 8;
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vstrbq_p_u16(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour(arm_2d_point_f16x8_t * ptPoint,
arm_2d_region_t * ptOrigValidRegion,
uint16_t * pOrigin,
int16_t iOrigStride,
uint16_t * pTarget,
uint16_t MaskColour, uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
uint16x8_t TempPixel = __arm_2d_rgb565_pack_single_vec(vcvtq_s16_f16(vAvgPixelR),
vcvtq_s16_f16(vAvgPixelG),
vcvtq_s16_f16(vAvgPixelB));
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(TempPixel, vTarget, predGlb);
#else
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y) };
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
uint16x8_t ptOffs = vPoint.X + vPoint.Y * iOrigStride;
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
/* update target pixels */
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_offs_compensated(arm_2d_point_f16x8_t *
ptPoint,
arm_2d_region_t *
ptOrigValidRegion,
uint16_t * pOrigin,
int16_t iOrigStride,
uint16_t * pTarget,
uint16_t MaskColour,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi,
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
uint16x8_t TempPixel = __arm_2d_rgb565_pack_single_vec(vcvtq_s16_f16(vAvgPixelR),
vcvtq_s16_f16(vAvgPixelG),
vcvtq_s16_f16(vAvgPixelB));
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(TempPixel, vTarget, predGlb);
#else
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y) };
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_with_alpha(
arm_2d_point_f16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint8_t chOpacity,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* blending */
uint16x8_t vAvgR, vAvgG, vAvgB;
vAvgR = vcvtq_s16_f16(vAvgPixelR);
vAvgG = vcvtq_s16_f16(vAvgPixelG);
vAvgB = vcvtq_s16_f16(vAvgPixelB);
uint16x8_t vBlended;
__ARM_2D_BLEND_RGB565_TARGET_RGBVEC(chOpacity, vTarget, vAvgR, vAvgG, vAvgB, vBlended);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* set vector predicate if point is inside the region */
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y) };
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
uint16x8_t ptOffs = vPoint.X + vPoint.Y * iOrigStride;
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* alpha blending */
uint16x8_t vBlended =
__arm_2d_rgb565_alpha_blending_single_vec(ptVal, vTarget, chOpacity);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_with_alpha_offs_compensated(
arm_2d_point_f16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint8_t chOpacity,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* blending */
uint16x8_t vAvgR, vAvgG, vAvgB;
vAvgR = vcvtq_s16_f16(vAvgPixelR);
vAvgG = vcvtq_s16_f16(vAvgPixelG);
vAvgB = vcvtq_s16_f16(vAvgPixelB);
uint16x8_t vBlended;
__ARM_2D_BLEND_RGB565_TARGET_RGBVEC(chOpacity, vTarget, vAvgR, vAvgG, vAvgB, vBlended);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* set vector predicate if point is inside the region */
arm_2d_point_s16x8_t vPoint = {
.X = vcvtq_s16_f16(ptPoint->X),
.Y = vcvtq_s16_f16(ptPoint->Y) };
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs =
vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* retrieve all point values */
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* alpha blending */
uint16x8_t vBlended =
__arm_2d_rgb565_alpha_blending_single_vec(ptVal, vTarget, chOpacity);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_cccn888_get_pixel_colour( arm_2d_point_f16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint32_t *pOrigin,
int16_t iOrigStride,
uint32_t *pTarget,
uint32_t MaskColour,
int16_t elts)
{
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
ARM_ALIGN(8) uint32_t scratch32[32];
int16_t *pscratch16 = (int16_t *) scratch32;
uint32x4_t vTargetLo = vld1q(pTarget);
uint32x4_t vTargetHi = vld1q(pTarget + 4);
mve_pred16_t predTailLow = vctp32q(elts);
mve_pred16_t predTailHigh = elts - 4 > 0 ? vctp32q(elts - 4) : 0;
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulators */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlbLo = 0, predGlbHi = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi,
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
__arm_2d_pack_rgb888_to_mem((uint8_t *) scratch32, vcvtq_s16_f16(vAvgPixelR),
vcvtq_s16_f16(vAvgPixelG), vcvtq_s16_f16(vAvgPixelB));
uint32x4_t TempPixel = vld1q(scratch32);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetLo, predGlbLo);
vst1q_p(pTarget, TempPixel, predTailLow);
TempPixel = vld1q(scratch32 + 4);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetHi, predGlbHi);
vst1q_p(pTarget + 4, TempPixel, predTailHigh);
#else
arm_2d_point_s32x4_t tPointLo, tPointHi;
ARM_ALIGN(8) int16_t scratch[8];
mve_pred16_t p;
/* split 16-bit point vector into 2 x 32-bit vectors */
vst1q(scratch, vcvtq_s16_f16(ptPoint->X));
tPointLo.X = vldrhq_s32(scratch);
tPointHi.X = vldrhq_s32(scratch + 4);
vst1q(scratch, vcvtq_s16_f16(ptPoint->Y));
tPointLo.Y = vldrhq_s32(scratch);
tPointHi.Y = vldrhq_s32(scratch + 4);
/* 1st half */
/* set vector predicate if point is inside the region */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointLo);
/* prepare vector of point offsets */
uint32x4_t ptOffs = tPointLo.X + tPointLo.Y * iOrigStride;
uint32x4_t vPixel = vld1q(pTarget);
/* retrieve all point values */
uint32x4_t ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vPixel = vpselq_u32(ptVal, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget, vPixel, vctp32q(elts));
elts -= 4;
if (elts > 0) {
/* second half */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointHi);
ptOffs = tPointHi.X + tPointHi.Y * iOrigStride;
vPixel = vld1q(pTarget + 4);
ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vPixel = vpselq_u32(ptVal, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget + 4, vPixel, vctp32q(elts));
}
#endif
}
static
void __arm_2d_impl_cccn888_get_pixel_colour_with_alpha(
arm_2d_point_f16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint32_t *pOrigin,
int16_t iOrigStride,
uint32_t *pTarget,
uint32_t MaskColour,
uint8_t chOpacity,
int16_t elts)
{
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
ARM_ALIGN(8) uint32_t scratch32[32];
int16_t *pscratch16 = (int16_t *) scratch32;
uint32x4_t vTargetLo = vld1q(pTarget);
uint32x4_t vTargetHi = vld1q(pTarget + 4);
mve_pred16_t predTailLow = vctp32q(elts);
mve_pred16_t predTailHigh = elts - 4 > 0 ? vctp32q(elts - 4) : 0;
float16x8_t vOne = vdupq_n_f16(1.0f);
int16x8_t vXi = vcvtq_s16_f16(ptPoint->X);
int16x8_t vYi = vcvtq_s16_f16(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_f16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_f16(ptPoint->Y, 0));
float16x8_t vWX = ptPoint->X - vcvtq_f16_s16(vXi);
float16x8_t vWY = ptPoint->Y - vcvtq_f16_s16(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
float16x8_t vAreaTR = vWX * vWY;
float16x8_t vAreaTL = (vOne - vWX) * vWY;
float16x8_t vAreaBR = vWX * (vOne - vWY);
float16x8_t vAreaBL = (vOne - vWX) * (vOne - vWY);
/* accumulated pixel vectors */
float16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulators */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlbLo = 0, predGlbHi = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi,
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* alpha blending */
uint16x8_t vTargetR, vTargetG, vTargetB;
__arm_2d_unpack_rgb888_from_mem((const uint8_t *) pTarget, &vTargetR, &vTargetG, &vTargetB);
uint16_t chOpacityCompl = (256 - chOpacity);
uint16x8_t vAvgR, vAvgG, vAvgB;
vAvgR = vcvtq_s16_f16(vAvgPixelR);
vAvgG = vcvtq_s16_f16(vAvgPixelG);
vAvgB = vcvtq_s16_f16(vAvgPixelB);
/* merge */
vAvgR = vAvgR * chOpacityCompl + vTargetR * chOpacity;
vAvgR = vAvgR >> 8;
vAvgG = vAvgG * chOpacityCompl + vTargetG * chOpacity;
vAvgG = vAvgG >> 8;
vAvgB = vAvgB * chOpacityCompl + vTargetB * chOpacity;
vAvgB = vAvgB >> 8;
/* pack */
__arm_2d_pack_rgb888_to_mem((uint8_t *) scratch32, vAvgR, vAvgG, vAvgB);
uint32x4_t TempPixel = vld1q(scratch32);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetLo, predGlbLo);
vst1q_p(pTarget, TempPixel, predTailLow);
TempPixel = vld1q(scratch32 + 4);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetHi, predGlbHi);
vst1q_p(pTarget + 4, TempPixel, predTailHigh);
#else
arm_2d_point_s32x4_t tPointLo, tPointHi;
ARM_ALIGN(8) int16_t scratch[8];
ARM_ALIGN(8) uint32_t blendled[4];
mve_pred16_t p;
/* split 16-bit point vector into 2 x 32-bit vectors */
vst1q(scratch, vcvtq_s16_f16(ptPoint->X));
tPointLo.X = vldrhq_s32(scratch);
tPointHi.X = vldrhq_s32(scratch + 4);
vst1q(scratch, vcvtq_s16_f16(ptPoint->Y));
tPointLo.Y = vldrhq_s32(scratch);
tPointHi.Y = vldrhq_s32(scratch + 4);
/* 1st half */
/* set vector predicate if point is inside the region */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointLo);
/* prepare vector of point offsets */
uint32x4_t ptOffs = tPointLo.X + tPointLo.Y * iOrigStride;
uint32x4_t vPixel = vld1q(pTarget);
/* retrieve all point values */
uint32x4_t ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vstrwq_u32((uint32_t *) scratch, ptVal);
/* alpha-blending (requires widened inputs) */
vstrbq_u16((uint8_t *) blendled,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *) scratch),
vldrbq_u16((uint8_t const *) pTarget), chOpacity));
vstrbq_u16((uint8_t *) blendled + 2,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *)scratch + 4),
vldrbq_u16((uint8_t const *)pTarget + 2), chOpacity));
uint32x4_t vBlended = vld1q(blendled);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vPixel = vpselq_u32(vBlended, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget, vPixel, vctp32q(elts));
elts -= 4;
if(elts > 0) {
/* second half */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointHi);
ptOffs = tPointHi.X + tPointHi.Y * iOrigStride;
vPixel = vld1q(pTarget);
ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vstrwq_u32((uint32_t *) scratch, ptVal);
/* alpha-blending (requires widened inputs) */
vstrbq_u16((uint8_t *) blendled,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *) scratch),
vldrbq_u16((uint8_t const *) pTarget), chOpacity));
vstrbq_u16((uint8_t *) blendled + 2,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *)scratch + 4),
vldrbq_u16((uint8_t const *)pTarget + 2), chOpacity));
vBlended = vld1q(blendled);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vPixel = vpselq_u32(vBlended, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget + 4, vPixel, vctp32q(elts));
}
#endif
}
#else /* __ARM_2D_HAS_HELIUM_FLOAT__ && ! __ARM_2D_CFG_FORCED_FIXED_POINT_ROTATION__ */
/* extra calibration removed in fixed-point code since offset is lower than Q9.6 representation */
#define ONE_BY_2PI_Q31 341782637.0f
#define ARSHIFT(x, shift) (shift > 0 ? x >> shift : x << (-shift))
#define TO_Q16(x) ((x) << 16)
#define GET_Q6INT(x) ((x) >> 6)
#define SET_Q6INT(x) ((x) << 6)
/**
Scale Gray8 channel
*/
#define __ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vScal) \
vAvgPixel = vmulhq_u16(vScal, ptVal8);
/**
Scale Gray8 channel with accumulation
*/
#define __ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vScal) \
vAvgPixel += vmulhq_u16(vScal, ptVal8);
/**
Scale R, G & B channels
*/
#define __ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vScal) \
vAvgPixelR = vmulhq_u16(vScal, R); \
vAvgPixelG = vmulhq_u16(vScal, G); \
vAvgPixelB = vmulhq_u16(vScal, B);
/**
Scale R, G & B channels with accumulation
*/
#define __ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vScal) \
vAvgPixelR += vmulhq_u16(vScal, R); \
vAvgPixelG += vmulhq_u16(vScal, G); \
vAvgPixelB += vmulhq_u16(vScal, B);
#ifdef VECTORIZED_ROTATION_REGR
/* disabled as slower than scalar */
static
bool __arm_2d_rotate_regression(arm_2d_size_t * __RESTRICT ptCopySize,
arm_2d_location_t * pSrcPoint,
float fAngle,
arm_2d_location_t * tOffset,
arm_2d_location_t * center,
int32_t iOrigStride,
arm_2d_rot_linear_regr_t regrCoefs[]
)
{
int32_t iHeight = ptCopySize->iHeight;
int32_t iWidth = ptCopySize->iWidth;
q31_t invHeightFx = 0x7fffffff / (iHeight - 1);
arm_2d_point_s32x4_t vPointCornerI;
int32_t AngleFx = (int32_t) roundf(fAngle * ONE_BY_2PI_Q31);
q31_t cosAngleFx = arm_cos_q31(AngleFx);
q31_t sinAngleFx = arm_sin_q31(AngleFx);
int32x4_t vCornerX = { 0, 1, 0, 1 };
int32x4_t vCornerY = { 0, 0, 1, 1 };
bool gatherLoadIdxOverflow = 0;
vPointCornerI.X = vdupq_n_s32(pSrcPoint->iX + tOffset->iX);
vPointCornerI.X = vPointCornerI.X + vmulq_n_s32(vCornerX, (iWidth - 1));
vPointCornerI.Y = vdupq_n_s32(pSrcPoint->iY + tOffset->iY);
vPointCornerI.Y = vPointCornerI.Y + vmulq_n_s32(vCornerY, (iHeight - 1));
/*
Vector version of:
int16_t iX = ptLocation->iX - ptCenter->iX;
int16_t iY = ptLocation->iY - ptCenter->iY;
q31_t cosAngleFx = arm_cos_q31(fAngle);
q31_t sinAngleFx = arm_sin_q31(fAngle);
tPointCornerFx[0][0].Y =
__QDADD(__QDADD(centerQ16.Y, MULTFX(iYQ16, cosAngleFx)), MULTFX(iXQ16, sinAngleFx));
tPointCornerFx[0][0].X =
__QDSUB(__QDADD(centerQ16.X, MULTFX(iXQ16, cosAngleFx)), MULTFX(iYQ16, sinAngleFx));
*/
arm_2d_point_s32x4_t vTmp1;
vTmp1.X = vsubq_n_s16(vPointCornerI.X, center->iX);
vTmp1.Y = vsubq_n_s16(vPointCornerI.Y, center->iY);
vTmp1.X <<= 16;
vTmp1.Y <<= 16;
vPointCornerI.X =
vqsubq(vqdmulhq_n_s32(vTmp1.X, cosAngleFx), vqdmulhq_n_s32(vTmp1.Y, sinAngleFx));
vPointCornerI.X = vqaddq_n_s32(vPointCornerI.X, (center->iX << 16));
vPointCornerI.Y = vqdmlahq(vqdmulhq_n_s32(vTmp1.X, sinAngleFx), vTmp1.Y, cosAngleFx);
vPointCornerI.Y = vqaddq_n_s32(vPointCornerI.Y, (center->iY << 16));
/*
Check whether rotated index offsets could exceed 16-bit limits
used in subsequent gather loads
This will occur for parts of large images (e.g. 320*200)
To avoid unconditional penalties for small/medium images,
returns a speculative overflow allowing to handle large offsets.
*/
int32_t maxY = vmaxvq(0.0f, vPointCornerI.Y);
if(MULTFX(TO_Q16(iOrigStride), maxY) > UINT16_MAX)
gatherLoadIdxOverflow = true;
/* regression parameters */
vTmp1.X[0] = vPointCornerI.X[0];
vTmp1.X[1] = vPointCornerI.X[1];
vTmp1.X[2] = vPointCornerI.Y[0];
vTmp1.X[3] = vPointCornerI.Y[1];
vTmp1.Y[0] = vPointCornerI.X[2];
vTmp1.Y[1] = vPointCornerI.X[3];
vTmp1.Y[2] = vPointCornerI.Y[2];
vTmp1.Y[3] = vPointCornerI.Y[3];
/* slopes */
vTmp1.X = vqdmulhq_n_s32(vTmp1.Y - vTmp1.X, invHeightFx);
regrCoefs[0].slopeY = vTmp1.X[2];
regrCoefs[0].slopeX = vTmp1.X[0];
regrCoefs[0].interceptY = vPointCornerI.Y[0];
regrCoefs[0].interceptX = vPointCornerI.X[0];
regrCoefs[1].slopeY = vTmp1.X[3];
regrCoefs[1].slopeX = vTmp1.X[1];
regrCoefs[1].interceptY = vPointCornerI.Y[1];
regrCoefs[1].interceptX = vPointCornerI.X[1];
return gatherLoadIdxOverflow;
}
#else
static
bool __arm_2d_rotate_regression(arm_2d_size_t * __RESTRICT ptCopySize,
arm_2d_location_t * pSrcPoint,
float fAngle,
arm_2d_location_t * tOffset,
arm_2d_location_t * center,
int32_t iOrigStride,
arm_2d_rot_linear_regr_t regrCoefs[]
)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
q31_t invHeightFx = 0x7fffffff / (iHeight - 1);
int32_t AngleFx = lroundf(fAngle * ONE_BY_2PI_Q31);
q31_t cosAngleFx = arm_cos_q31(AngleFx);
q31_t sinAngleFx = arm_sin_q31(AngleFx);
arm_2d_point_fx_t tPointCornerFx[2][2];
arm_2d_point_fx_t centerQ16;
arm_2d_point_fx_t srcPointQ16;
arm_2d_point_fx_t tOffsetQ16;
arm_2d_point_fx_t tmp;
int32_t iXQ16, iYQ16;
bool gatherLoadIdxOverflow = 0;
/* Q16 conversion */
centerQ16.X = TO_Q16(center->iX);
centerQ16.Y = TO_Q16(center->iY);
srcPointQ16.X = TO_Q16(pSrcPoint->iX);
srcPointQ16.Y = TO_Q16(pSrcPoint->iY);
tOffsetQ16.X = TO_Q16(tOffset->iX);
tOffsetQ16.Y = TO_Q16(tOffset->iY);
/* (0,0) corner */
tmp.X = srcPointQ16.X + 0 + tOffsetQ16.X;
tmp.Y = srcPointQ16.Y + 0 + tOffsetQ16.Y;
iXQ16 = tmp.X - centerQ16.X;
iYQ16 = tmp.Y - centerQ16.Y;
tPointCornerFx[0][0].Y =
__QDADD(__QDADD(centerQ16.Y, MULTFX(iYQ16, cosAngleFx)), MULTFX(iXQ16, sinAngleFx));
tPointCornerFx[0][0].X =
__QDSUB(__QDADD(centerQ16.X, MULTFX(iXQ16, cosAngleFx)), MULTFX(iYQ16, sinAngleFx));
/* ((iWidth - 1),0) corner */
tmp.X = srcPointQ16.X + 0 + tOffsetQ16.X + TO_Q16(iWidth - 1);
iXQ16 = tmp.X - centerQ16.X;
tPointCornerFx[1][0].Y =
__QDADD(__QDADD(centerQ16.Y, MULTFX(iYQ16, cosAngleFx)), MULTFX(iXQ16, sinAngleFx));
tPointCornerFx[1][0].X =
__QDSUB(__QDADD(centerQ16.X, MULTFX(iXQ16, cosAngleFx)), MULTFX(iYQ16, sinAngleFx));
/* ((iWidth - 1),(iHeight - 1)) corner */
tmp.Y = srcPointQ16.Y + tOffsetQ16.Y + TO_Q16(iHeight - 1);
iYQ16 = tmp.Y - centerQ16.Y;
tPointCornerFx[1][1].Y =
__QDADD(__QDADD(centerQ16.Y, MULTFX(iYQ16, cosAngleFx)), MULTFX(iXQ16, sinAngleFx));
tPointCornerFx[1][1].X =
__QDSUB(__QDADD(centerQ16.X, MULTFX(iXQ16, cosAngleFx)), MULTFX(iYQ16, sinAngleFx));
/* (0,(iHeight - 1)) corner */
tmp.X = srcPointQ16.X + 0 + tOffsetQ16.X;
iXQ16 = tmp.X - centerQ16.X;
tPointCornerFx[0][1].Y =
__QDADD(__QDADD(centerQ16.Y, MULTFX(iYQ16, cosAngleFx)), MULTFX(iXQ16, sinAngleFx));
tPointCornerFx[0][1].X =
__QDSUB(__QDADD(centerQ16.X, MULTFX(iXQ16, cosAngleFx)), MULTFX(iYQ16, sinAngleFx));
/*
Check whether rotated index offsets could exceed 16-bit limits
used in subsequent gather loads
This will occur for parts of large images (e.g. 320*200)
To avoid unconditional penalties for small/medium images,
returns a speculative overflow allowing to handle large offsets.
*/
int32_t maxY = MAX(MAX
(MAX(tPointCornerFx[0][0].Y, tPointCornerFx[0][1].Y),
tPointCornerFx[1][0].Y),
tPointCornerFx[1][1].Y);
if(MULTFX(TO_Q16(iOrigStride), maxY) > UINT16_MAX)
gatherLoadIdxOverflow = true;
/* regression */
int32_t slopeXFx, slopeYFx;
/* interpolation in Y direction for 1st elements column */
slopeXFx = MULTFX((tPointCornerFx[0][1].X - tPointCornerFx[0][0].X), invHeightFx);
slopeYFx = MULTFX((tPointCornerFx[0][1].Y - tPointCornerFx[0][0].Y), invHeightFx);
regrCoefs[0].slopeY = slopeYFx * 2;
regrCoefs[0].slopeX = slopeXFx * 2;
regrCoefs[0].interceptY = tPointCornerFx[0][0].Y;
regrCoefs[0].interceptX = tPointCornerFx[0][0].X;
/* interpolation in Y direction for the last elements column */
slopeXFx = MULTFX((tPointCornerFx[1][1].X - tPointCornerFx[1][0].X), invHeightFx);
slopeYFx = MULTFX((tPointCornerFx[1][1].Y - tPointCornerFx[1][0].Y), invHeightFx);
regrCoefs[1].slopeY = slopeYFx* 2;
regrCoefs[1].slopeX = slopeXFx* 2;
regrCoefs[1].interceptY = tPointCornerFx[1][0].Y;
regrCoefs[1].interceptX = tPointCornerFx[1][0].X;
return gatherLoadIdxOverflow;
}
#endif
static
void __arm_2d_impl_gray8_get_pixel_colour(arm_2d_point_s16x8_t * ptPoint,
arm_2d_region_t * ptOrigValidRegion,
uint8_t * pOrigin,
int16_t iOrigStride,
uint8_t * pTarget,
uint8_t MaskColour, uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vldrbq_u16(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixel;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t ptVal8;
/* Bottom Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, ptVal8, MaskColour,
predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1), ptVal8, MaskColour,
predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTR);
}
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vAvgPixel, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)
};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
/* retrieve all point values */
uint16x8_t ptVal = vldrbq_gather_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vstrbq_p_u16(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_gray8_get_pixel_colour_with_alpha(arm_2d_point_s16x8_t * ptPoint,
arm_2d_region_t *
ptOrigValidRegion,
uint8_t * pOrigin,
int16_t iOrigStride,
uint8_t * pTarget,
uint8_t MaskColour,
uint8_t chOpacity, uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vldrbq_u16(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixel;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t ptVal8;
/* Bottom Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC(vAvgPixel, ptVal8, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, ptVal8, MaskColour,
predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1), ptVal8, MaskColour,
predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_GRAY8_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
ptVal8, MaskColour, predGlb);
__ARM_2D_SCALE_GRAY8VEC_ACC(vAvgPixel, ptVal8, vAreaTR);
}
/* alpha blending */
uint16_t chTransparency = 256 - (chOpacity);
uint16x8_t vBlended = (vAvgPixel * chTransparency + vTarget * chOpacity) >> 8;
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)
};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs = vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* retrieve all point values */
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
uint16x8_t ptVal = vldrbq_gather_offset_z_u16(pOrigin, ptOffs, predTail);
/* alpha blending */
uint16_t chTransparency = 256 - (chOpacity);
uint16x8_t vBlended = (ptVal * chTransparency + vTarget * chOpacity) >> 8;
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vstrbq_p_u16(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour( arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
uint16x8_t TempPixel = __arm_2d_rgb565_pack_single_vec(vAvgPixelR,
vAvgPixelG,
vAvgPixelB);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(TempPixel, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
uint16x8_t ptOffs = vPoint.X + vPoint.Y * iOrigStride;
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_offs_compensated( arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
uint16x8_t TempPixel = __arm_2d_rgb565_pack_single_vec(vAvgPixelR,
vAvgPixelG,
vAvgPixelB);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(TempPixel, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs =
vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vTarget = vpselq_u16(ptVal, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_with_alpha(
arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint8_t chOpacity,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* alpha blending */
uint16x8_t vBlended;
__ARM_2D_BLEND_RGB565_TARGET_RGBVEC(chOpacity, vTarget, vAvgPixelR, vAvgPixelG, vAvgPixelB, vBlended);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
uint16x8_t ptOffs = vPoint.X + vPoint.Y * iOrigStride;
/* retrieve all point values */
uint16x8_t ptVal = vldrhq_gather_shifted_offset_u16(pOrigin, ptOffs);
/* alpha blending */
uint16x8_t vBlended =
__arm_2d_rgb565_alpha_blending_single_vec(ptVal, vTarget, chOpacity);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_rgb565_get_pixel_colour_with_alpha_offs_compensated(
arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint16_t *pOrigin,
int16_t iOrigStride,
uint16_t *pTarget,
uint16_t MaskColour,
uint8_t chOpacity,
uint32_t elts)
{
mve_pred16_t predTail = vctp16q(elts);
uint16x8_t vTarget = vld1q(pTarget);
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - SET_Q6INT(vXi);
int16x8_t vWY = ptPoint->Y - SET_Q6INT(vYi);
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulator */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlb = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vYi, R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vXi, vaddq_n_s16(vYi, 1), R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB565_GET_RGBVEC_FROM_POINT_FAR(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlb);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* alpha blending */
uint16x8_t vBlended;
__ARM_2D_BLEND_RGB565_TARGET_RGBVEC(chOpacity, vTarget, vAvgPixelR, vAvgPixelG, vAvgPixelB, vBlended);
/* select between target pixel, averaged pixed */
vTarget = vpselq_u16(vBlended, vTarget, predGlb);
#else
/* extract integer part */
arm_2d_point_s16x8_t vPoint = {
.X = GET_Q6INT(ptPoint->X),
.Y = GET_Q6INT(ptPoint->Y)};
/* set vector predicate if point is inside the region */
mve_pred16_t p = arm_2d_is_point_vec_inside_region_s16(ptOrigValidRegion, &vPoint);
/* prepare vector of point offsets */
/* correctionOffset avoid 16-bit overflow */
int16_t correctionOffset = vminvq_s16(0x7fff, vPoint.Y) - 1;
uint16x8_t ptOffs =
vPoint.X + (vPoint.Y - correctionOffset) * iOrigStride;
/* retrieve all point values */
/* base pointer update to compensate offset */
pOrigin += (correctionOffset * iOrigStride);
uint16x8_t ptVal = vldrhq_gather_shifted_offset_z_u16(pOrigin, ptOffs, predTail);
/* alpha blending */
uint16x8_t vBlended =
__arm_2d_rgb565_alpha_blending_single_vec(ptVal, vTarget, chOpacity);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vTarget = vpselq_u16(vBlended, vTarget, vcmpneq_m_n_u16(ptVal, MaskColour, p));
#endif
vst1q_p(pTarget, vTarget, predTail);
}
static
void __arm_2d_impl_cccn888_get_pixel_colour( arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint32_t *pOrigin,
int16_t iOrigStride,
uint32_t *pTarget,
uint32_t MaskColour,
int16_t elts)
{
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
ARM_ALIGN(8) uint32_t scratch32[32];
int16_t *pscratch16 = (int16_t *) scratch32;
uint32x4_t vTargetLo = vld1q(pTarget);
uint32x4_t vTargetHi = vld1q(pTarget + 4);
mve_pred16_t predTailLow = vctp32q(elts);
mve_pred16_t predTailHigh = elts - 4 > 0 ? vctp32q(elts - 4) : 0;
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - (SET_Q6INT(vXi));
int16x8_t vWY = ptPoint->Y - (SET_Q6INT(vYi));
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulators */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlbLo = 0, predGlbHi = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi,
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* pack */
__arm_2d_pack_rgb888_to_mem((uint8_t *) scratch32, vAvgPixelR, vAvgPixelG, vAvgPixelB);
uint32x4_t TempPixel = vld1q(scratch32);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetLo, predGlbLo);
vst1q_p(pTarget, TempPixel, predTailLow);
TempPixel = vld1q(scratch32 + 4);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetHi, predGlbHi);
vst1q_p(pTarget + 4, TempPixel, predTailHigh);
#else
arm_2d_point_s32x4_t tPointLo, tPointHi;
ARM_ALIGN(8) int16_t scratch[8];
mve_pred16_t p;
/* split 16-bit point vector into 2 x 32-bit vectors */
vst1q(scratch, GET_Q6INT(ptPoint->X));
tPointLo.X = vldrhq_s32(scratch);
tPointHi.X = vldrhq_s32(scratch + 4);
vst1q(scratch, GET_Q6INT(ptPoint->Y));
tPointLo.Y = vldrhq_s32(scratch);
tPointHi.Y = vldrhq_s32(scratch + 4);
/* 1st half */
/* set vector predicate if point is inside the region */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointLo);
/* prepare vector of point offsets */
uint32x4_t ptOffs = tPointLo.X + tPointLo.Y * iOrigStride;
uint32x4_t vPixel = vld1q(pTarget);
/* retrieve all point values */
uint32x4_t ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
/* combine 2 predicates set to true if point is in the region & values different from color mask */
vPixel = vpselq_u32(ptVal, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget, vPixel, vctp32q(elts));
elts -= 4;
if (elts > 0) {
/* second half */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointHi);
ptOffs = tPointHi.X + tPointHi.Y * iOrigStride;
vPixel = vld1q(pTarget + 4);
ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vPixel = vpselq_u32(ptVal, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget + 4, vPixel, vctp32q(elts));
}
#endif
}
static
void __arm_2d_impl_cccn888_get_pixel_colour_with_alpha(
arm_2d_point_s16x8_t *ptPoint,
arm_2d_region_t *ptOrigValidRegion,
uint32_t *pOrigin,
int16_t iOrigStride,
uint32_t *pTarget,
uint32_t MaskColour,
uint8_t chOpacity,
int16_t elts)
{
#if defined(__ARM_2D_HAS_INTERPOLATION_ROTATION__) && __ARM_2D_HAS_INTERPOLATION_ROTATION__
ARM_ALIGN(8) uint32_t scratch32[32];
int16_t *pscratch16 = (int16_t *) scratch32;
uint32x4_t vTargetLo = vld1q(pTarget);
uint32x4_t vTargetHi = vld1q(pTarget + 4);
mve_pred16_t predTailLow = vctp32q(elts);
mve_pred16_t predTailHigh = elts - 4 > 0 ? vctp32q(elts - 4) : 0;
int16x8_t vOne = vdupq_n_s16(SET_Q6INT(1));
int16x8_t vXi = GET_Q6INT(ptPoint->X);
int16x8_t vYi = GET_Q6INT(ptPoint->Y);
vXi = vsubq_m_n_s16(vXi, vXi, 1, vcmpltq_n_s16(ptPoint->X, 0));
vYi = vsubq_m_n_s16(vYi, vYi, 1, vcmpltq_n_s16(ptPoint->Y, 0));
int16x8_t vWX = ptPoint->X - (SET_Q6INT(vXi));
int16x8_t vWY = ptPoint->Y - (SET_Q6INT(vYi));
/* combination of Bottom / Top & Left / Right areas contributions */
uint16x8_t vAreaTR = vmulq_u16(vWX, vWY);
uint16x8_t vAreaTL = vmulq_u16((vOne - vWX), vWY);
uint16x8_t vAreaBR = vmulq_u16(vWX, (vOne - vWY));
uint16x8_t vAreaBL = vmulq_u16((vOne - vWX), (vOne - vWY));
/* Q16 conversion */
vAreaTR = vqshlq_n_u16(vAreaTR, 4);
vAreaTL = vqshlq_n_u16(vAreaTL, 4);
vAreaBR = vqshlq_n_u16(vAreaBR, 4);
vAreaBL = vqshlq_n_u16(vAreaBL, 4);
/* accumulated pixel vectors */
uint16x8_t vAvgPixelR, vAvgPixelG, vAvgPixelB;
/* predicate accumulators */
/* tracks all predications conditions for selecting final */
/* averaged pixed / target pixel */
/* can probably optimized away since averaged target pixel is */
/* equivalent to original pixel, but precision limitations would introduce small errors */
mve_pred16_t predGlbLo = 0, predGlbHi = 0;
/*
* accumulate / average over the 4 neigbouring pixels
*/
uint16x8_t R, G, B;
/* Bottom Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vYi, R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBL);
}
/* Bottom Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vYi,
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaBR);
}
/* Top Left averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vXi, vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTL);
}
/* Top Right averaging */
{
__ARM_2D_RGB888_GET_RGBVEC_FROM_POINT(vaddq_n_s16(vXi, 1), vaddq_n_s16(vYi, 1),
R, G, B, MaskColour, predGlbLo, predGlbHi);
__ARM_2D_SCALE_RGBVEC_ACC(vAvgPixelR, vAvgPixelG, vAvgPixelB, R, G, B, vAreaTR);
}
/* alpha blending */
uint16x8_t vTargetR, vTargetG, vTargetB;
__arm_2d_unpack_rgb888_from_mem((const uint8_t *) pTarget, &vTargetR, &vTargetG, &vTargetB);
uint16_t chOpacityCompl = (256 - chOpacity);
/* merge */
vAvgPixelR = vmlaq_n_u16(vmulq_n_u16(vAvgPixelR, chOpacityCompl), vTargetR, chOpacity);
vAvgPixelR = vAvgPixelR >> 8;
vAvgPixelG = vmlaq_n_u16(vmulq_n_u16(vAvgPixelG, chOpacityCompl), vTargetG, chOpacity);
vAvgPixelG = vAvgPixelG >> 8;
vAvgPixelB = vmlaq_n_u16(vmulq_n_u16(vAvgPixelB, chOpacityCompl), vTargetB, chOpacity);
vAvgPixelB = vAvgPixelB >> 8;
/* pack */
__arm_2d_pack_rgb888_to_mem((uint8_t *) scratch32, vAvgPixelR, vAvgPixelG, vAvgPixelB);
uint32x4_t TempPixel = vld1q(scratch32);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetLo, predGlbLo);
vst1q_p(pTarget, TempPixel, predTailLow);
TempPixel = vld1q(scratch32 + 4);
/* select between target pixel, averaged pixed */
TempPixel = vpselq_u32(TempPixel, vTargetHi, predGlbHi);
vst1q_p(pTarget + 4, TempPixel, predTailHigh);
#else
arm_2d_point_s32x4_t tPointLo, tPointHi;
ARM_ALIGN(8) int16_t scratch[8];
ARM_ALIGN(8) uint32_t blendled[4];
mve_pred16_t p;
/* split 16-bit point vector into 2 x 32-bit vectors */
vst1q(scratch, GET_Q6INT(ptPoint->X));
tPointLo.X = vldrhq_s32(scratch);
tPointHi.X = vldrhq_s32(scratch + 4);
vst1q(scratch, GET_Q6INT(ptPoint->Y));
tPointLo.Y = vldrhq_s32(scratch);
tPointHi.Y = vldrhq_s32(scratch + 4);
/* 1st half */
/* set vector predicate if point is inside the region */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointLo);
/* prepare vector of point offsets */
uint32x4_t ptOffs = tPointLo.X + tPointLo.Y * iOrigStride;
uint32x4_t vPixel = vld1q(pTarget);
/* retrieve all point values */
uint32x4_t ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vstrwq_u32((uint32_t *) scratch, ptVal);
/* alpha-blending (requires widened inputs) */
vstrbq_u16((uint8_t *) blendled,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *) scratch),
vldrbq_u16((uint8_t const *) pTarget), chOpacity));
vstrbq_u16((uint8_t *) blendled + 2,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *)scratch + 4),
vldrbq_u16((uint8_t const *)pTarget + 2), chOpacity));
uint32x4_t vBlended = vld1q(blendled);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vPixel = vpselq_u32(vBlended, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget, vPixel, vctp32q(elts));
elts -= 4;
if(elts > 0) {
/* second half */
p = arm_2d_is_point_vec_inside_region_s32(ptOrigValidRegion, &tPointHi);
ptOffs = tPointHi.X + tPointHi.Y * iOrigStride;
vPixel = vld1q(pTarget);
ptVal = vldrwq_gather_shifted_offset_u32(pOrigin, ptOffs);
vstrwq_u32((uint32_t *) scratch, ptVal);
/* alpha-blending (requires widened inputs) */
vstrbq_u16((uint8_t *) blendled,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *) scratch),
vldrbq_u16((uint8_t const *) pTarget), chOpacity));
vstrbq_u16((uint8_t *) blendled + 2,
__rgb888_alpha_blending_direct_single_vec(vldrbq_u16((uint8_t const *)scratch + 4),
vldrbq_u16((uint8_t const *)pTarget + 2), chOpacity));
vBlended = vld1q(blendled);
/* combine 2 predicates, set to true, if point is in the region & values different from color mask */
vPixel = vpselq_u32(vBlended, vPixel, vcmpneq_m_n_u32(ptVal, MaskColour, p));
vst1q_p(pTarget + 4, vPixel, vctp32q(elts));
}
#endif
}
#endif
#define __API_INT_TYPE_BIT_NUM 8
#define __API_COLOUR gray8
#include "__arm_2d_rotate_helium.inc"
#define __API_INT_TYPE_BIT_NUM 16
#define __API_COLOUR rgb565
#include "__arm_2d_rotate_helium.inc"
#define __API_INT_TYPE_BIT_NUM 32
#define __API_COLOUR cccn888
#include "__arm_2d_rotate_helium.inc"
/* rgb8_draw_pattern helpers */
/*
* enable to pick gather load offset based on initial offset
* e.g. if iOffset = 3
* will get {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2}
*/
static uint8_t __draw_pattern_src_incr_c8bit[32] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3
};
/*
* enable to pick vector bitmask based on initial offset
* e.g. if iOffset = 3
* will get {8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4}
*/
static uint8_t __draw_pattern_src_bitmask_c8bit[32] = {
1, 2, 4, 8, 16, 32, 64, 128,
1, 2, 4, 8, 16, 32, 64, 128,
1, 2, 4, 8, 16, 32, 64, 128,
1, 2, 4, 8, 16, 32, 64, 128,
};
/* rgb16_draw_pattern helpers */
/*
* enable to pick gather load offset based on initial offset
* e.g. if iOffset = 3
* will get {0, 0, 0, 0, 0, 1, 1, 1}
*/
static uint16_t __draw_pattern_src_incr_rgb16[16] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1
};
/*
* enable to pick vector bitmask based on initial offset
* e.g. if iOffset = 3
* will get {8, 16, 32, 64, 128, 1, 2, 4}
*/
static uint16_t __draw_pattern_src_bitmask_rgb16[16] = {
1, 2, 4, 8, 16, 32, 64, 128,
1, 2, 4, 8, 16, 32, 64, 128,
};
/* rgb32_draw_pattern helpers */
static uint32_t __draw_pattern_src_incr_rgb32[16] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1
};
static uint32_t __draw_pattern_src_bitmask_rgb32[16] = {
1, 2, 4, 8, 16, 32, 64, 128,
1, 2, 4, 8, 16, 32, 64, 128,
};
/*! adding support with c code template */
#define __API_COLOUR c8bit
#define __API_ELT_SZ 8
#include "__arm_2d_draw_pattern_helium.inc"
#define __API_COLOUR rgb16
#define __API_ELT_SZ 16
#include "__arm_2d_draw_pattern_helium.inc"
#define __API_COLOUR rgb32
#define __API_ELT_SZ 32
#include "__arm_2d_draw_pattern_helium.inc"
#define __API_COLOUR c8bit
#define __API_ELT_SZ 8
#include "__arm_2d_fill_colour_helium.inc"
#define __API_COLOUR rgb16
#define __API_ELT_SZ 16
#include "__arm_2d_fill_colour_helium.inc"
#define __API_COLOUR rgb32
#define __API_ELT_SZ 32
#include "__arm_2d_fill_colour_helium.inc"
/**
8-bit pixel color fill alpha/channel mask with/without opacity MVE intrinsic generator
- TRGT_LOAD is a contigous / strided target load function
C8BIT_TRGT_LOAD / C8BIT_TRGT_LOAD_STRIDE
- STRIDE is an optional vector of offset for gather load
- SCAL_OPACITY is extra alpha scaling function
C8BIT_SCAL_OPACITY_NONE / C8BIT_SCAL_OPACITY
- OPACITY is an optinal 8-bit vector with duplicated opacity values
(need vector format to be used with VMULH.U8)
- ALPHA_SZ, alpha chan width (1 or 4 for resp. 8 or 32-bit type)
Macro assumes pTarget8/ pAlpha are already setup
*/
#define C8BIT_COLOUR_FILLING_MASK_INNER_MVE(TRGT_LOAD, STRIDE, SCAL_OPACITY, \
OPACITY, ALPHA_SZ) \
int32_t blkCnt = iWidth; \
do { \
mve_pred16_t tailPred = vctp16q(blkCnt); \
\
uint16x8_t vecTarget = vldrbq_z_u16(pTarget8, tailPred); \
uint16x8_t vecTransp = TRGT_LOAD(pAlpha, STRIDE, tailPred); \
\
vecTransp = SCAL_OPACITY(vecTransp, OPACITY, tailPred); \
uint16x8_t vecAlpha = vsubq_x_u16(v256, vecTransp, tailPred); \
\
vecTarget = vmulq_x(vecTarget, vecAlpha, tailPred); \
vecTarget = vmlaq_m(vecTarget, vecTransp, (uint16_t) Colour, tailPred); \
vecTarget = vecTarget >> 8; \
\
vstrbq_p_u16(pTarget8, vecTarget, tailPred); \
\
pAlpha += (8 * ALPHA_SZ); \
pTarget8 += 8; \
blkCnt -= 8; \
} \
while (blkCnt > 0);
/**
RGB565 pixel color fill alpha/channel mask with/without opacity MVE intrinsic generator
- TRGT_LOAD is a contigous / strided target load function
RGB565_TRGT_LOAD / RGB565_TRGT_LOAD_STRIDE
- STRIDE is an optional vector of offset for gather load
- SCAL_OPACITY is extra alpha scaling function
RGB565_SCAL_OPACITY_NONE / RGB565_SCAL_OPACITY
- OPACITY is an optinal 8-bit vector with duplicated opacity values
(need vector format to be used with VMULH.U8)
- P_ALPHA, 8-bit or 32-bit alpha chan. pointer
- ALPHA_SZ, alpha chan width (1 or 4 for resp. 8 or 32-bit type)
Does not generate a tail-predicated loop as relying on pack/unpack functions.
Predication is only applied in the final stage during pixel store.
*/
#define RGB565_COLOUR_FILLING_MASK_MVE(TRGT_LOAD, STRIDE, SCAL_OPACITY, OPACITY, \
P_ALPHA, ALPHA_SZ) \
uint16x8_t v256 = vdupq_n_u16(256); \
\
for (int_fast16_t y = 0; y < iHeight; y++) { \
const uint8_t *pAlpha = (const uint8_t *)P_ALPHA; \
uint16_t *pCurTarget = pTarget; \
int32_t blkCnt = iWidth; \
\
do { \
uint16x8_t vecTarget = vld1q(pCurTarget); \
uint16x8_t vecTransp = TRGT_LOAD(pAlpha, STRIDE); \
vecTransp = SCAL_OPACITY(vecTransp, OPACITY); \
uint16x8_t vecAlpha = vsubq_u16(v256, vecTransp); \
uint16x8_t vecR, vecG, vecB; \
\
__arm_2d_rgb565_unpack_single_vec(vecTarget, &vecR, &vecG, &vecB); \
\
/* blending using alpha vector weights */ \
vecR = vmulq(vecR, vecAlpha); \
vecR = vmlaq(vecR, vecTransp, (uint16_t) tSrcPix.R); \
vecR = vecR >> 8; \
\
vecG = vmulq(vecG, vecAlpha); \
vecG = vmlaq(vecG, vecTransp, (uint16_t) tSrcPix.G); \
vecG = vecG >> 8; \
\
vecB = vmulq(vecB, vecAlpha); \
vecB = vmlaq(vecB, vecTransp, (uint16_t) tSrcPix.B); \
vecB = vecB >> 8; \
\
vecTarget = __arm_2d_rgb565_pack_single_vec(vecR, vecG, vecB); \
\
/* tail predication */ \
vst1q_p_u16(pCurTarget, vecTarget, vctp16q(blkCnt)); \
\
pAlpha += (8 * ALPHA_SZ); \
pCurTarget += 8; \
blkCnt -= 8; \
} \
while (blkCnt > 0); \
\
P_ALPHA += (iAlphaStride); \
pTarget += (iTargetStride); \
}
/**
CCCN888 pixel color fill alpha/channel mask with/without opacity MVE intrinsic generator
- TRGT_LOAD is a contigous / strided target load function
CCCN888_TRGT_LOAD / CCCN888_TRGT_LOAD_STRIDE
- STRIDE is an optional vector of offset for gather load
- SCAL_OPACITY is extra alpha scaling function
CCCN888_SCAL_OPACITY_NONE / CCCN888_SCAL_OPACITY
- OPACITY is an optinal 8-bit vector with duplicated opacity values
(need vector format to be used with VMULH.U8)
- ALPHA_SZ, alpha chan width (1 or 4 for resp. 8 or 32-bit type)
Macro assumes pTargetCh0/1/2 & pAlpha are already setup
*/
#define CCCN888_COLOUR_FILLING_MASK_INNER_MVE(TRGT_LOAD, STRIDE, SCAL_OPACITY, \
OPACITY, ALPHA_SZ) \
int32_t blkCnt = iWidth; \
\
do { \
mve_pred16_t tailPred = vctp16q(blkCnt); \
\
/* expand chan0, chan1, chan2 */ \
uint16x8_t vecTargetC0 = vldrbq_gather_offset_z_u16(pTargetCh0, vStride4Offs, \
tailPred); \
uint16x8_t vecTargetC1 = vldrbq_gather_offset_z_u16(pTargetCh1, vStride4Offs, \
tailPred); \
uint16x8_t vecTargetC2 = vldrbq_gather_offset_z_u16(pTargetCh2, vStride4Offs, \
tailPred); \
uint16x8_t vecTransp = TRGT_LOAD(pAlpha, STRIDE, tailPred); \
\
vecTransp = SCAL_OPACITY(vecTransp, OPACITY, tailPred); \
\
uint16x8_t vecAlpha = vsubq_x_u16(v256, vecTransp, tailPred); \
\
/* scale ch0 vector with alpha vector */ \
vecTargetC0 = vmulq_x(vecTargetC0, vecAlpha, tailPred); \
/* blend ch0 vector with input ch0 color*/ \
vecTargetC0 = vmlaq_m(vecTargetC0, vecTransp, (uint16_t) c0, tailPred); \
vecTargetC0 = vecTargetC0 >> 8; \
\
/* repeat for ch1 and ch2 */ \
vecTargetC1 = vmulq_x(vecTargetC1, vecAlpha, tailPred); \
vecTargetC1 = vmlaq_m(vecTargetC1, vecTransp, (uint16_t) c1, tailPred); \
vecTargetC1 = vecTargetC1 >> 8; \
\
vecTargetC2 = vmulq_x(vecTargetC2, vecAlpha, tailPred); \
vecTargetC2 = vmlaq_m(vecTargetC2, vecTransp, (uint16_t) c2, tailPred); \
vecTargetC2 = vecTargetC2 >> 8; \
\
/* store and merge chan0, chan1, chan2 */ \
vstrbq_scatter_offset_p_u16(pTargetCh0, vStride4Offs, vecTargetC0, tailPred); \
vstrbq_scatter_offset_p_u16(pTargetCh1, vStride4Offs, vecTargetC1, tailPred); \
vstrbq_scatter_offset_p_u16(pTargetCh2, vStride4Offs, vecTargetC2, tailPred); \
\
pAlpha += 8 * ALPHA_SZ; \
pTargetCh0 += 8*4; \
pTargetCh1 += 8*4; \
pTargetCh2 += 8*4; \
blkCnt -= 8; \
} \
while (blkCnt > 0);
#define C8BIT_TRGT_LOAD(base, stride, pred) vldrbq_z_u16(base, pred)
#define C8BIT_TRGT_LOAD_STRIDE(base, stride, pred) vldrbq_gather_offset_z_u16(base, stride, pred);
#define C8BIT_SCAL_OPACITY_NONE(transp, opac, pred) transp
#define C8BIT_SCAL_OPACITY(transp, opac, pred) (uint16x8_t) vmulhq_x((uint8x16_t) transp, opac, pred)
#define RGB565_TRGT_LOAD(base, stride) vldrbq_u16(base)
#define RGB565_TRGT_LOAD_STRIDE(base, stride) vldrbq_gather_offset_u16(base, stride);
#define RGB565_SCAL_OPACITY_NONE(transp, opac) transp
#define RGB565_SCAL_OPACITY(transp, opac) (uint16x8_t) vmulhq((uint8x16_t) transp, opac)
#define CCCN888_TRGT_LOAD(base, stride, pred) vldrbq_z_u16(base, pred)
#define CCCN888_TRGT_LOAD_STRIDE(base, stride, pred) vldrbq_gather_offset_z_u16(base, stride, pred);
#define CCCN888_SCAL_OPACITY_NONE(transp, opac, pred) transp
#define CCCN888_SCAL_OPACITY(transp, opac, pred) (uint16x8_t) vmulhq_x((uint8x16_t) transp, opac, pred)
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_colour_filling_mask(uint8_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint8_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t * pAlpha = pchAlpha;
uint8_t * pTarget8 = pTarget;
#ifdef USE_MVE_INTRINSICS
C8BIT_COLOUR_FILLING_MASK_INNER_MVE(C8BIT_TRGT_LOAD, _,
C8BIT_SCAL_OPACITY_NONE, _, 1);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrb.u16 q0, [%[pTarget]] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vmla.u16 q3, q1, %[Colour] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget8), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[Colour] "r"(Colour)
:"q0", "q1", "q2", "q3", "memory");
#endif
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_colour_filling_mask_opacity(uint8_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t *
__RESTRICT ptCopySize,
uint8_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
uint16x8_t v256 = vdupq_n_u16(256);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t * pAlpha = pchAlpha;
uint8_t * pTarget8 = pTarget;
#ifdef USE_MVE_INTRINSICS
C8BIT_COLOUR_FILLING_MASK_INNER_MVE(C8BIT_TRGT_LOAD, _,
C8BIT_SCAL_OPACITY, vOpacity, 1);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrb.u16 q0, [%[pTarget]] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vmla.u16 q3, q1, %[Colour] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget8), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[Colour] "r"(Colour),[vOpacity] "t"(vOpacity)
:"q0", "q1", "q2", "q3", "memory");
#endif
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_colour_filling_channel_mask(uint8_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint8_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = (const uint8_t *)pwAlpha;
uint8_t * pTarget8 = pTarget;
#ifdef USE_MVE_INTRINSICS
C8BIT_COLOUR_FILLING_MASK_INNER_MVE(C8BIT_TRGT_LOAD_STRIDE, vStride4Offs,
C8BIT_SCAL_OPACITY_NONE, _, 4);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrb.u16 q0, [%[pTarget]] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
" vmul.u16 q3, q0, q2 \n"
" add %[pAlpha], %[pAlpha], #(8*4) \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" vmla.u16 q3, q1, %[Colour] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget8), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[Colour] "r"(Colour),[str4Offs] "t"(vStride4Offs)
:"q0", "q1", "q2", "q3", "memory");
#endif
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_gray8_colour_filling_channel_mask_opacity(uint8_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t *
__RESTRICT ptCopySize,
uint8_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = (const uint8_t *)pwAlpha;
uint8_t *pTarget8 = pTarget;
#ifdef USE_MVE_INTRINSICS
C8BIT_COLOUR_FILLING_MASK_INNER_MVE(C8BIT_TRGT_LOAD_STRIDE, vStride4Offs,
C8BIT_SCAL_OPACITY, vOpacity, 4);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
" vldrb.u16 q0, [%[pTarget]] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTarget], #8] \n"
" add %[pAlpha], %[pAlpha], #(8*4) \n"
" vmla.u16 q3, q1, %[Colour] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pTarget] "+r"(pTarget8), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[Colour] "r"(Colour),[vOpacity] "t"(vOpacity),
[str4Offs] "t"(vStride4Offs)
:"q0", "q1", "q2", "q3", "memory");
#endif
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_colour_filling_mask(uint16_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint16_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
__arm_2d_color_fast_rgb_t tSrcPix;
__arm_2d_rgb565_unpack(*(&Colour), &tSrcPix);
#ifdef USE_MVE_INTRINSICS
RGB565_COLOUR_FILLING_MASK_MVE(RGB565_TRGT_LOAD, _,
RGB565_SCAL_OPACITY_NONE, _, pchAlpha, 1);
#else
/* RGB565 pack/unpack Masks */
/* use memory rather than vmov to optimize Helium operations interleaving */
uint16x8_t scratch[4];
// Unpacking Mask Red
vst1q((uint16_t*)&scratch[0], vdupq_n_u16(0x1f));
// Unpacking Mask Green
vst1q((uint16_t*)&scratch[1], vdupq_n_u16(0x3f));
// packing Mask Green
vst1q((uint16_t*)&scratch[2], vdupq_n_u16(0xfc));
// packing Mask Blue
vst1q((uint16_t*)&scratch[3], vdupq_n_u16(0xf8));
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = pchAlpha;
uint16_t *pCurTarget = pTarget;
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile (
".p2align 2 \n"
/* load scheduling */
" vldrh.u16 q0, [%[pTarget]] \n"
" vmov.i16 q7, #0x0100 \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
// vecAlpha
" vsub.i16 q2, q7, q1 \n"
/* RGB565 unpack */
/* vecAlpha * 4 for G channel upscale */
" vmul.i16 q2, q2, %[four] \n"
/* G channel extract */
" vshr.u16 q5, q0, #5 \n"
/* load Unpacking Mask for R channel */
" vldrh.u16 q7, [%[scratch], #(0*16)] \n"
" vand q4, q0, q7 \n"
/* load Unpacking Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(1*16)] \n"
" vand q5, q5, q7 \n"
/* scale G vector with alpha vector */
" vmul.u16 q5, q5, q2 \n"
/* B channel */
" vshr.u16 q6, q0, #11 \n"
/* blend G vector with input G color*/
" vmla.u16 q5, q1, %[G] \n"
/* vecAlpha * 8 for R & B upscale */
" vshl.i16 q2, q2, #1 \n"
/* scale R vector with alpha vec */
" vmul.u16 q4, q4, q2 \n"
" vshr.u16 q5, q5, #8 \n"
/* blend R vector with input R color*/
" vmla.u16 q4, q1, %[R] \n"
/* load packing Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(2*16)] \n"
/* scale B vector with alpha vector */
" vmul.u16 q6, q6, q2 \n"
" vand q5, q5, q7 \n"
/* blend B vector with input B color*/
" vmla.u16 q6, q1, %[B] \n"
/* load packing Mask for B channel */
" vldrh.u16 q7, [%[scratch], #(3*16)] \n"
" vshr.u16 q6, q6, #8 \n"
/* RGB 565 pack */
/* (G & 0x00fc), 8) */
" vmul.i16 q5, q5, %[eight] \n"
/* (B & 0x00f8) */
" vand q6, q6, q7 \n"
/* load next alpha vector */
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmov.i16 q7, #0x0100 \n"
/* pack G & B */
" vmla.u16 q5, q6, %[twofiftysix] \n"
/* combined (R >> 8) >> 3 */
" vshr.u16 q4, q4, #11 \n"
/* load next target */
" vldrh.u16 q0, [%[pTarget], #16] \n"
/* pack R */
" vorr q4, q4, q5 \n"
" vstrh.16 q4, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[pTarget]"+r"(pCurTarget),[pAlpha] "+r"(pAlpha),[loopCnt] "+r"(blkCnt)
:[Colour] "r"(Colour), [eight] "r" (8), [four] "r" (4),
[R] "r" (tSrcPix.R), [G] "r" (tSrcPix.G), [B] "r" (tSrcPix.B),
[twofiftysix] "r" (256), [scratch] "r" (scratch)
:"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory");
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_colour_filling_mask_opacity(uint16_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint16_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
__arm_2d_color_fast_rgb_t tSrcPix;
__arm_2d_rgb565_unpack(*(&Colour), &tSrcPix);
#ifdef USE_MVE_INTRINSICS
RGB565_COLOUR_FILLING_MASK_MVE(RGB565_TRGT_LOAD, _,
RGB565_SCAL_OPACITY, vOpacity, pchAlpha, 1);
#else
/* RGB565 pack/unpack Masks + opacity */
/* use memory rather than vmov to optimize Helium operations interleaving */
uint16x8_t scratch[5];
// Unpacking Mask Red
vst1q((uint16_t*)&scratch[0], vdupq_n_u16(0x1f));
// Unpacking Mask Green
vst1q((uint16_t*)&scratch[1], vdupq_n_u16(0x3f));
// packing Mask Green
vst1q((uint16_t*)&scratch[2], vdupq_n_u16(0xfc));
// packing Mask Blue
vst1q((uint16_t*)&scratch[3], vdupq_n_u16(0xf8));
// opacity
vst1q((uint16_t*)&scratch[4], (uint16x8_t)vOpacity);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = pchAlpha;
uint16_t *pCurTarget = pTarget;
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile (
".p2align 2 \n"
/* load scheduling */
" vldrh.u16 q0, [%[pTarget]] \n"
" vmov.i16 q7, #0x0100 \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
/* opacity vector */
" vldrh.u16 q6, [%[scratch], #(4*16)] \n"
" vmulh.u8 q1, q1, q6 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
// vecAlpha
" vsub.i16 q2, q7, q1 \n"
/* RGB565 unpack */
/* vecAlpha * 4 for G channel upscale */
" vmul.i16 q2, q2, %[four] \n"
/* G channel extract */
" vshr.u16 q5, q0, #5 \n"
/* load Unpacking Mask for R channel */
" vldrh.u16 q7, [%[scratch], #(0*16)] \n"
" vand q4, q0, q7 \n"
/* load Unpacking Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(1*16)] \n"
" vand q5, q5, q7 \n"
/* scale G vector with alpha vector */
" vmul.u16 q5, q5, q2 \n"
/* B channel */
" vshr.u16 q6, q0, #11 \n"
/* blend G vector with input G color*/
" vmla.u16 q5, q1, %[G] \n"
/* vecAlpha * 8 for R & B upscale */
" vshl.i16 q2, q2, #1 \n"
/* scale R vector with alpha vec */
" vmul.u16 q4, q4, q2 \n"
" vshr.u16 q5, q5, #8 \n"
/* blend R vector with input R color*/
" vmla.u16 q4, q1, %[R] \n"
/* load packing Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(2*16)] \n"
/* scale B vector with alpha vector */
" vmul.u16 q6, q6, q2 \n"
" vand q5, q5, q7 \n"
/* blend B vector with input B color*/
" vmla.u16 q6, q1, %[B] \n"
/* load packing Mask for B channel */
" vldrh.u16 q7, [%[scratch], #(3*16)] \n"
" vshr.u16 q6, q6, #8 \n"
/* RGB 565 pack */
/* (G & 0x00fc), 8) */
" vmul.i16 q5, q5, %[eight] \n"
/* (B & 0x00f8) */
" vand q6, q6, q7 \n"
/* load next alpha vector */
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmov.i16 q7, #0x0100 \n"
/* pack G & B */
" vmla.u16 q5, q6, %[twofiftysix] \n"
/* reload opacity and scale alpha */
" vldrh.u16 q6, [%[scratch], #(4*16)] \n"
" vmulh.u8 q1, q1, q6 \n"
/* combined (R >> 8) >> 3 */
" vshr.u16 q4, q4, #11 \n"
/* load next target */
" vldrh.u16 q0, [%[pTarget], #16] \n"
/* pack R */
" vorr q4, q4, q5 \n"
" vstrh.16 q4, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[pTarget]"+r"(pCurTarget),[pAlpha] "+r"(pAlpha),[loopCnt] "+r"(blkCnt)
:[Colour] "r"(Colour), [eight] "r" (8), [four] "r" (4),
[R] "r" (tSrcPix.R), [G] "r" (tSrcPix.G), [B] "r" (tSrcPix.B),
[twofiftysix] "r" (256), [scratch] "r" (scratch)
:"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory");
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_colour_filling_channel_mask(uint16_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint16_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
__arm_2d_color_fast_rgb_t tSrcPix;
__arm_2d_rgb565_unpack(*(&Colour), &tSrcPix);
#ifdef USE_MVE_INTRINSICS
RGB565_COLOUR_FILLING_MASK_MVE(RGB565_TRGT_LOAD_STRIDE, vStride4Offs,
RGB565_SCAL_OPACITY_NONE, _, pwAlpha, 4);
#else
/* RGB565 pack/unpack Masks */
/* use memory rather than vmov to optimize Helium operations interleaving */
uint16x8_t scratch[4];
// Unpacking Mask Red
vst1q((uint16_t*)&scratch[0], vdupq_n_u16(0x1f));
// Unpacking Mask Green
vst1q((uint16_t*)&scratch[1], vdupq_n_u16(0x3f));
// packing Mask Green
vst1q((uint16_t*)&scratch[2], vdupq_n_u16(0xfc));
// packing Mask Blue
vst1q((uint16_t*)&scratch[3], vdupq_n_u16(0xf8));
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint32_t *pAlpha = pwAlpha;
uint16_t *pCurTarget = pTarget;
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile (
".p2align 2 \n"
/* load scheduling */
" vldrh.u16 q0, [%[pTarget]] \n"
" vmov.i16 q7, #0x0100 \n"
" vldrb.u16 q1, [%[pAlpha],%[str4Offs]]\n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" add %[pAlpha], %[pAlpha],#(8*4)\n"
// vecAlpha
" vsub.i16 q2, q7, q1 \n"
/* RGB565 unpack */
/* vecAlpha * 4 for G channel upscale */
" vmul.i16 q2, q2, %[four] \n"
/* G channel extract */
" vshr.u16 q5, q0, #5 \n"
/* load Unpacking Mask for R channel */
" vldrh.u16 q7, [%[scratch], #(0*16)] \n"
" vand q4, q0, q7 \n"
/* load Unpacking Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(1*16)] \n"
" vand q5, q5, q7 \n"
/* scale G vector with alpha vector */
" vmul.u16 q5, q5, q2 \n"
/* B channel */
" vshr.u16 q6, q0, #11 \n"
/* blend G vector with input G color*/
" vmla.u16 q5, q1, %[G] \n"
/* vecAlpha * 8 for R & B upscale */
" vshl.i16 q2, q2, #1 \n"
/* scale R vector with alpha vec */
" vmul.u16 q4, q4, q2 \n"
" vshr.u16 q5, q5, #8 \n"
/* blend R vector with input R color*/
" vmla.u16 q4, q1, %[R] \n"
/* load packing Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(2*16)] \n"
/* scale B vector with alpha vector */
" vmul.u16 q6, q6, q2 \n"
" vand q5, q5, q7 \n"
/* blend B vector with input B color*/
" vmla.u16 q6, q1, %[B] \n"
/* load packing Mask for B channel */
" vldrh.u16 q7, [%[scratch], #(3*16)] \n"
" vshr.u16 q6, q6, #8 \n"
/* RGB 565 pack */
/* (G & 0x00fc), 8) */
" vmul.i16 q5, q5, %[eight] \n"
/* (B & 0x00f8) */
" vand q6, q6, q7 \n"
/* load next alpha vector */
" vldrb.u16 q1, [%[pAlpha],%[str4Offs]]\n"
" vmov.i16 q7, #0x0100 \n"
/* pack G & B */
" vmla.u16 q5, q6, %[twofiftysix] \n"
/* combined (R >> 8) >> 3 */
" vshr.u16 q4, q4, #11 \n"
/* load next target */
" vldrh.u16 q0, [%[pTarget], #16] \n"
/* pack R */
" vorr q4, q4, q5 \n"
" vstrh.16 q4, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[pTarget]"+r"(pCurTarget),[pAlpha] "+r"(pAlpha),[loopCnt] "+r"(blkCnt)
:[Colour] "r"(Colour), [eight] "r" (8), [four] "r" (4),
[R] "r" (tSrcPix.R), [G] "r" (tSrcPix.G), [B] "r" (tSrcPix.B),
[twofiftysix] "r" (256), [scratch] "r" (scratch), [str4Offs] "t"(vStride4Offs)
:"q0", "q1", "q2", "q4", "q5", "q6", "q7", "memory");
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_colour_filling_channel_mask_opacity(uint16_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint16_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
__arm_2d_color_fast_rgb_t tSrcPix;
__arm_2d_rgb565_unpack(*(&Colour), &tSrcPix);
#ifdef USE_MVE_INTRINSICS
RGB565_COLOUR_FILLING_MASK_MVE(RGB565_TRGT_LOAD_STRIDE, vStride4Offs,
RGB565_SCAL_OPACITY, vOpacity, pwAlpha, 4);
#else
/* RGB565 pack/unpack Masks + opacity */
/* use memory rather than vmov to optimize Helium operations interleaving */
uint16x8_t scratch[5];
// Unpacking Mask Red
vst1q((uint16_t*)&scratch[0], vdupq_n_u16(0x1f));
// Unpacking Mask Green
vst1q((uint16_t*)&scratch[1], vdupq_n_u16(0x3f));
// packing Mask Green
vst1q((uint16_t*)&scratch[2], vdupq_n_u16(0xfc));
// packing Mask Blue
vst1q((uint16_t*)&scratch[3], vdupq_n_u16(0xf8));
// opacity
vst1q((uint16_t*)&scratch[4], (uint16x8_t)vOpacity);
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint32_t *pAlpha = pwAlpha;
uint16_t *pCurTarget = pTarget;
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile (
".p2align 2 \n"
/* load scheduling */
" vldrh.u16 q0, [%[pTarget]] \n"
" vmov.i16 q7, #0x0100 \n"
" vldrb.u16 q1, [%[pAlpha],%[str4Offs]]\n"
/* opacity vector */
" vldrh.u16 q6, [%[scratch], #(4*16)] \n"
" vmulh.u8 q1, q1, q6 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" add %[pAlpha], %[pAlpha],#(8*4)\n"
// vecAlpha
" vsub.i16 q2, q7, q1 \n"
/* RGB565 unpack */
/* vecAlpha * 4 for G channel upscale */
" vmul.i16 q2, q2, %[four] \n"
/* G channel extract */
" vshr.u16 q5, q0, #5 \n"
/* load Unpacking Mask for R channel */
" vldrh.u16 q7, [%[scratch], #(0*16)] \n"
" vand q4, q0, q7 \n"
/* load Unpacking Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(1*16)] \n"
" vand q5, q5, q7 \n"
/* scale G vector with alpha vector */
" vmul.u16 q5, q5, q2 \n"
/* B channel */
" vshr.u16 q6, q0, #11 \n"
/* blend G vector with input G color*/
" vmla.u16 q5, q1, %[G] \n"
/* vecAlpha * 8 for R & B upscale */
" vshl.i16 q2, q2, #1 \n"
/* scale R vector with alpha vec */
" vmul.u16 q4, q4, q2 \n"
" vshr.u16 q5, q5, #8 \n"
/* blend R vector with input R color*/
" vmla.u16 q4, q1, %[R] \n"
/* load packing Mask for G channel */
" vldrh.u16 q7, [%[scratch], #(2*16)] \n"
/* scale B vector with alpha vector */
" vmul.u16 q6, q6, q2 \n"
" vand q5, q5, q7 \n"
/* blend B vector with input B color*/
" vmla.u16 q6, q1, %[B] \n"
/* load packing Mask for B channel */
" vldrh.u16 q7, [%[scratch], #(3*16)] \n"
" vshr.u16 q6, q6, #8 \n"
/* RGB 565 pack */
/* (G & 0x00fc), 8) */
" vmul.i16 q5, q5, %[eight] \n"
/* (B & 0x00f8) */
" vand q6, q6, q7 \n"
/* load next alpha vector */
" vldrb.u16 q1, [%[pAlpha],%[str4Offs]]\n"
" vmov.i16 q7, #0x0100 \n"
/* pack G & B */
" vmla.u16 q5, q6, %[twofiftysix] \n"
/* combined (R >> 8) >> 3 */
" vldrh.u16 q6, [%[scratch], #(4*16)] \n"
" vmulh.u8 q1, q1, q6 \n"
" vshr.u16 q4, q4, #11 \n"
/* load next target */
" vldrh.u16 q0, [%[pTarget], #16] \n"
/* pack R */
" vorr q4, q4, q5 \n"
" vstrh.16 q4, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[pTarget]"+r"(pCurTarget),[pAlpha] "+r"(pAlpha),[loopCnt] "+r"(blkCnt)
:[Colour] "r"(Colour), [eight] "r" (8), [four] "r" (4),
[R] "r" (tSrcPix.R), [G] "r" (tSrcPix.G), [B] "r" (tSrcPix.B),
[twofiftysix] "r" (256), [scratch] "r" (scratch), [str4Offs] "t"(vStride4Offs)
:"q0", "q1", "q2", "q4", "q5", "q6", "q7", "memory");
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
#endif
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_colour_filling_mask(uint32_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint32_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
uint16_t c0, c1, c2;
c0 = Colour & 0xff;
c1 = (Colour >> 8) & 0xff;
c2 = (Colour >> 16) & 0xff;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t * pAlpha = pchAlpha;
uint8_t * pTargetCh0 = (uint8_t*)pTarget;
uint8_t * pTargetCh1 = pTargetCh0 + 1;
uint8_t * pTargetCh2 = pTargetCh0 + 2;
#ifdef USE_MVE_INTRINSICS
CCCN888_COLOUR_FILLING_MASK_INNER_MVE(CCCN888_TRGT_LOAD, _,
CCCN888_SCAL_OPACITY_NONE, _, 1);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
/* expand chan0 */
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
/* scale ch0 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan1 */
" vldrb.u16 q0, [%[pTargetCh1], %[str4Offs]] \n"
/* blend ch0 vector with input ch0 color*/
" vmla.u16 q3, q1, %[c0] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh0], %[str4Offs]] \n"
/* scale ch1 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan2 */
" vldrb.u16 q0, [%[pTargetCh2], %[str4Offs]] \n"
/* blend ch1 vector with input ch1 color*/
" vmla.u16 q3, q1, %[c1] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh1], %[str4Offs]] \n"
" adds %[pTargetCh0], #32 \n"
" adds %[pTargetCh1], #32 \n"
/* scale ch2 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
/* blend ch2 vector with input ch2 color*/
" vmla.u16 q3, q1, %[c2] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh2], %[str4Offs]] \n"
" add.w %[pTargetCh2], %[pTargetCh2], #32 \n"
" letp lr, 2b \n"
"1: \n"
:[pTargetCh0] "+r"(pTargetCh0), [pTargetCh1] "+r"(pTargetCh1),
[pTargetCh2] "+r"(pTargetCh2), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[str4Offs] "t" (vStride4Offs),
[c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2)
:"q0", "q1", "q2", "q3", "memory");
#endif
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_colour_filling_mask_opacity(uint32_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint8_t * __RESTRICT pchAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint32_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
uint16_t c0, c1, c2;
c0 = Colour & 0xff;
c1 = (Colour >> 8) & 0xff;
c2 = (Colour >> 16) & 0xff;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t * pAlpha = pchAlpha;
uint8_t * pTargetCh0 = (uint8_t*)pTarget;
uint8_t * pTargetCh1 = pTargetCh0 + 1;
uint8_t * pTargetCh2 = pTargetCh0 + 2;
#ifdef USE_MVE_INTRINSICS
CCCN888_COLOUR_FILLING_MASK_INNER_MVE(CCCN888_TRGT_LOAD, _,
CCCN888_SCAL_OPACITY, vOpacity, 1);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
/* expand chan0 */
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
/* scale ch0 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan1 */
" vldrb.u16 q0, [%[pTargetCh1], %[str4Offs]] \n"
/* blend ch0 vector with input ch0 color*/
" vmla.u16 q3, q1, %[c0] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh0], %[str4Offs]] \n"
/* scale ch1 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan2 */
" vldrb.u16 q0, [%[pTargetCh2], %[str4Offs]] \n"
/* blend ch1 vector with input ch1 color*/
" vmla.u16 q3, q1, %[c1] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh1], %[str4Offs]] \n"
" adds %[pTargetCh0], #32 \n"
" adds %[pTargetCh1], #32 \n"
/* scale ch2 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
/* blend ch2 vector with input ch2 color*/
" vmla.u16 q3, q1, %[c2] \n"
" vldrb.u16 q1, [%[pAlpha]], #8 \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh2], %[str4Offs]] \n"
" add.w %[pTargetCh2], %[pTargetCh2], #32 \n"
" letp lr, 2b \n"
"1: \n"
:[pTargetCh0] "+r"(pTargetCh0), [pTargetCh1] "+r"(pTargetCh1),
[pTargetCh2] "+r"(pTargetCh2), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[str4Offs] "t" (vStride4Offs),
[vOpacity] "t"(vOpacity),
[c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2)
:"q0", "q1", "q2", "q3", "memory");
#endif
pchAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_colour_filling_channel_mask(uint32_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t * __RESTRICT ptCopySize,
uint32_t Colour)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
uint16_t c0, c1, c2;
c0 = Colour & 0xff;
c1 = (Colour >> 8) & 0xff;
c2 = (Colour >> 16) & 0xff;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = (const uint8_t *)pwAlpha;
uint8_t * pTargetCh0 = (uint8_t*)pTarget;
uint8_t * pTargetCh1 = pTargetCh0 + 1;
uint8_t * pTargetCh2 = pTargetCh0 + 2;
#ifdef USE_MVE_INTRINSICS
CCCN888_COLOUR_FILLING_MASK_INNER_MVE(CCCN888_TRGT_LOAD_STRIDE, vStride4Offs,
CCCN888_SCAL_OPACITY_NONE, _, 4);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
/* expand chan0 */
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
/* scale ch0 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan1 */
" vldrb.u16 q0, [%[pTargetCh1], %[str4Offs]] \n"
/* blend ch0 vector with input ch0 color*/
" vmla.u16 q3, q1, %[c0] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh0], %[str4Offs]] \n"
/* scale ch1 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan2 */
" vldrb.u16 q0, [%[pTargetCh2], %[str4Offs]] \n"
/* blend ch1 vector with input ch1 color*/
" vmla.u16 q3, q1, %[c1] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh1], %[str4Offs]] \n"
" adds %[pAlpha], #32 \n"
" adds %[pTargetCh0], #32 \n"
/* scale ch2 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
/* blend ch2 vector with input ch2 color*/
" vmla.u16 q3, q1, %[c2] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh2], %[str4Offs]] \n"
" adds %[pTargetCh1], #32 \n"
" adds %[pTargetCh2], #32 \n"
" letp lr, 2b \n"
"1: \n"
:[pTargetCh0] "+r"(pTargetCh0), [pTargetCh1] "+r"(pTargetCh1),
[pTargetCh2] "+r"(pTargetCh2), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[str4Offs] "t" (vStride4Offs),
[c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2)
:"q0", "q1", "q2", "q3", "memory");
#endif
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_colour_filling_channel_mask_opacity(uint32_t * __RESTRICT pTarget,
int16_t iTargetStride,
uint32_t * __RESTRICT pwAlpha,
int16_t iAlphaStride,
arm_2d_size_t *
__RESTRICT ptCopySize,
uint32_t Colour, uint8_t chOpacity)
{
int_fast16_t iHeight = ptCopySize->iHeight;
int_fast16_t iWidth = ptCopySize->iWidth;
uint16x8_t v256 = vdupq_n_u16(256);
uint16x8_t vStride4Offs = vidupq_n_u16(0, 4);
uint8x16_t vOpacity = vdupq_n_u8(chOpacity);
uint16_t c0, c1, c2;
c0 = Colour & 0xff;
c1 = (Colour >> 8) & 0xff;
c2 = (Colour >> 16) & 0xff;
for (int_fast16_t y = 0; y < iHeight; y++) {
const uint8_t *pAlpha = (const uint8_t *)pwAlpha;
uint8_t * pTargetCh0 = (uint8_t*)pTarget;
uint8_t * pTargetCh1 = pTargetCh0 + 1;
uint8_t * pTargetCh2 = pTargetCh0 + 2;
#ifdef USE_MVE_INTRINSICS
CCCN888_COLOUR_FILLING_MASK_INNER_MVE(CCCN888_TRGT_LOAD_STRIDE, vStride4Offs,
CCCN888_SCAL_OPACITY, vOpacity, 4);
#else
register unsigned blkCnt __asm("lr");
blkCnt = iWidth;
__asm volatile(
".p2align 2 \n"
/* expand chan0 */
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
" vsub.i16 q2, %[vec256], q1 \n"
/* scale ch0 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan1 */
" vldrb.u16 q0, [%[pTargetCh1], %[str4Offs]] \n"
/* blend ch0 vector with input ch0 color*/
" vmla.u16 q3, q1, %[c0] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh0], %[str4Offs]] \n"
/* scale ch1 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
/* expand chan2 */
" vldrb.u16 q0, [%[pTargetCh2], %[str4Offs]] \n"
/* blend ch1 vector with input ch1 color*/
" vmla.u16 q3, q1, %[c1] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh1], %[str4Offs]] \n"
" adds %[pAlpha], #32 \n"
" adds %[pTargetCh0], #32 \n"
/* scale ch2 vector with alpha vector */
" vmul.u16 q3, q0, q2 \n"
" vldrb.u16 q0, [%[pTargetCh0], %[str4Offs]] \n"
/* blend ch2 vector with input ch2 color*/
" vmla.u16 q3, q1, %[c2] \n"
" vldrb.u16 q1, [%[pAlpha], %[str4Offs]] \n"
" vmulh.u8 q1, q1, %[vOpacity] \n"
" vshr.u16 q3, q3, #8 \n"
" vstrb.u16 q3, [%[pTargetCh2], %[str4Offs]] \n"
" adds %[pTargetCh1], #32 \n"
" adds %[pTargetCh2], #32 \n"
" letp lr, 2b \n"
"1: \n"
:[pTargetCh0] "+r"(pTargetCh0), [pTargetCh1] "+r"(pTargetCh1),
[pTargetCh2] "+r"(pTargetCh2), [pAlpha] "+r" (pAlpha), [loopCnt] "+r"(blkCnt)
:[vec256] "t" (v256),[str4Offs] "t" (vStride4Offs), [vOpacity] "t"(vOpacity),
[c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2)
:"q0", "q1", "q2", "q3", "memory");
#endif
pwAlpha += (iAlphaStride);
pTarget += (iTargetStride);
}
}
/*----------------------------------------------------------------------------*
* Convert Colour format *
*----------------------------------------------------------------------------*/
__OVERRIDE_WEAK
void __arm_2d_impl_cccn888_to_rgb565(uint32_t *__RESTRICT pwSourceBase,
int16_t iSourceStride,
uint16_t *__RESTRICT phwTargetBase,
int16_t iTargetStride,
arm_2d_size_t *__RESTRICT ptCopySize)
{
int32_t blkCnt;
uint32x4_t maskR = vdupq_n_u32(0x001f);
uint32x4_t maskG = vdupq_n_u32(0x07e0);
uint32x4_t maskB = vdupq_n_u32(0xf800);
for (int_fast16_t y = 0; y < ptCopySize->iHeight; y++) {
const uint32_t *pSource = pwSourceBase;
uint16_t *pTarget = phwTargetBase;
blkCnt = ptCopySize->iWidth;
#ifdef USE_MVE_INTRINSICS
do {
mve_pred16_t tailPred = vctp32q(blkCnt);
/* load a vector of 4 cccn888 pixels */
uint32x4_t vecIn = vld1q_z(pSource, tailPred);
/* extract individual channels and place them according bit position */
uint32x4_t vecR = (vecIn >> 3) & maskR;
uint32x4_t vecG = (vecIn >> 5) & maskG;
uint32x4_t vecB = (vecIn >> 8) & maskB;
/* merge */
uint32x4_t vOut = vecR | vecG | vecB;
/* store a vector of 4 rgb565 pixels */
vstrhq_p_u32(pTarget, vOut, tailPred);
pSource += 4;
pTarget += 4;
blkCnt -= 4;
}
while (blkCnt > 0);
#else
const int32_t inv_2pow3 = 1 << (31-3); /*1/2^3 in Q.31 */
const int32_t inv_2pow5 = 1 << (31-5); /*1/2^5 in Q.31 */
const int32_t inv_2pow8 = 1 << (31-8); /*1/2^8 in Q.31 */
__asm volatile(
" wlstp.32 lr, %[loopCnt], 1f \n"
/* precompute for allowing filling stalls in the inner loop */
/* use vqdmulh to replace shifts to allow overlap with 'AND' */
/* load a vector of 4 cccn888 pixels */
" vldrw.u32 q0, [%[pSource]], #16 \n"
/* mimic right shift by 3 */
" vqdmulh.s32 q1, q0, %[inv_2pow3] \n"
".p2align 2 \n"
"2: \n"
" vand q1, q1, %[maskR] \n"
/* mimic right shift by 5 */
" vqdmulh.s32 q2, q0, %[inv_2pow5] \n"
" vand q2, q2, %[maskG] \n"
/* mimic right shift by 8 */
" vqdmulh.s32 q3, q0, %[inv_2pow8] \n"
/* accumulate R & G */
" vorr q2, q1, q2 \n"
/* load next vector of 4 cccn888 pixels */
" vldrw.u32 q0, [%[pSource]], #16 \n"
" vand q3, q3, %[maskB] \n"
/* mimic right shift by 3 */
" vqdmulh.s32 q1, q0, %[inv_2pow3] \n"
/* accumulate B */
" vorr q2, q2, q3 \n"
/* store a vector of 4 rgb565 pixels */
" vstrh.32 q2, [%[pTarget]], #8 \n"
" letp lr, 2b \n"
"1: \n"
: [pSource] "+r"(pSource), [pTarget] "+r" (pTarget)
: [loopCnt] "r"(blkCnt), [inv_2pow3] "r" (inv_2pow3),
[inv_2pow5] "r" (inv_2pow5), [inv_2pow8] "r" (inv_2pow8),
[maskR] "t" (maskR),[maskG] "t" (maskG),[maskB] "t" (maskB)
: "q0", "q1", "q2", "q3", "memory", "r14" );
#endif
pwSourceBase += iSourceStride;
phwTargetBase += iTargetStride;
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_to_cccn888(uint16_t *__RESTRICT phwSourceBase,
int16_t iSourceStride,
uint32_t *__RESTRICT pwTargetBase,
int16_t iTargetStride,
arm_2d_size_t *__RESTRICT ptCopySize)
{
int32_t blkCnt;
uint32x4_t maskRB = vdupq_n_u32(0xf8);
uint32x4_t maskG = vdupq_n_u32(0xfc00);
for (int_fast16_t y = 0; y < ptCopySize->iHeight; y++) {
const uint16_t *__RESTRICT phwSource = phwSourceBase;
uint32_t *__RESTRICT pwTarget = pwTargetBase;
blkCnt = ptCopySize->iWidth;
#ifdef USE_MVE_INTRINSICS
do {
mve_pred16_t tailPred = vctp32q(blkCnt);
/* load a vector of 4 rgb565 pixels */
uint32x4_t vecIn = vldrhq_z_u32(phwSource, tailPred);
/* extract individual channels and place them according position */
uint32x4_t vecR = (vecIn << 3) & maskRB;
uint32x4_t vecG = (vecIn << 5) & maskG;
uint32x4_t vecB = ((vecIn >> 8) & maskRB) << 16;
/* merge and set n channel to 0xff */
uint32x4_t vOut = 0xff000000 | vecR | vecG | vecB;
/* store a vector of 4 cccn888 pixels */
vst1q_p(pwTarget, vOut, tailPred);
phwSource += 4;
pwTarget += 4;
blkCnt -= 4;
}
while (blkCnt > 0);
#else
__asm volatile(
" wlstp.32 lr, %[loopCnt], 1f \n"
/* precompute for allowing filling stalls in the inner loop */
/* use vqdmulh & vmul to replace shifts to allow overlap with 'AND' */
/* load a vector of 4 rgb565 pixels */
" vldrh.u32 q0, [%[pSource]], #8 \n"
/* mimic left shift by 3 */
" vmul.u32 q1, q0, %[two_pow3] \n"
".p2align 2 \n"
"2: \n"
/* mimic left shift by 5 */
" vmul.u32 q2, q0, %[two_pow5] \n"
" vand q1, q1, %[maskRB] \n"
/* mimic right shift by 8 */
" vqdmulh.s32 q3, q0, %[inv_2pow8] \n"
" vand q2, q2, %[maskG] \n"
/* accumulate G & R, use vmla instead of vorr for best overlap */
" vmla.u32 q2, q1, %[one] \n"
" vand q3, q3, %[maskRB] \n"
/* accumulate B + left shift by 16 */
" vmla.u32 q2, q3, %[two_pow16] \n"
/* load next vector of 4 rgb565 pixels */
" vldrh.u32 q0, [%[pSource]], #8 \n"
/* merge and set n channel to 0xff */
" vorr.i32 q2, #0xff000000 \n"
/* mimic left shift by 3 */
" vmul.u32 q1, q0, %[two_pow3] \n"
/* store a vector of 4 cccn888 pixels */
" vstrw.32 q2, [%[pTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
: [pSource] "+r"(phwSource), [pTarget] "+r" (pwTarget)
: [loopCnt] "r"(blkCnt),[two_pow3] "r" (1<<3), [two_pow5] "r" (1<<5),
[two_pow16] "r" (1<<16),[inv_2pow8] "r" (1 << (31-8)),
[maskRB] "t" (maskRB),[maskG] "t" (maskG), [one] "r" (1)
: "q0", "q1", "q2", "q3", "memory", "r14" );
#endif
phwSourceBase += iSourceStride;
pwTargetBase += iTargetStride;
}
}
void __arm_2d_impl_rgb565_masks_fill(uint16_t * __RESTRICT ptSourceBase,
int16_t iSourceStride,
arm_2d_size_t * __RESTRICT ptSourceSize,
uint8_t * __RESTRICT pchSourceMaskBase,
int16_t iSourceMaskStride,
arm_2d_size_t * __RESTRICT ptSourceMaskSize,
uint16_t * __RESTRICT ptTargetBase,
int16_t iTargetStride,
arm_2d_size_t * __RESTRICT ptTargetSize,
uint8_t * __RESTRICT pchTargetMaskBase,
int16_t iTargetMaskStride,
arm_2d_size_t * __RESTRICT ptTargetMaskSize)
{
uint8_t *__RESTRICT pchTargetMaskLineBase = pchTargetMaskBase;
uint16x8_t v256 = vdupq_n_u16(256);
#ifndef USE_MVE_INTRINSICS
uint16x8_t scratch[5];
/* vector of 256 avoiding use of vdup to increase overlap efficiency*/
vst1q((uint16_t *) & scratch[0], v256);
/* scratch[1] is temporary for blended Red chan. vector */
/* Unpacking Mask Red */
vst1q((uint16_t *) & scratch[2], vdupq_n_u16(0x00fc));
/* B channel packing mask */
vst1q((uint16_t *) & scratch[3], vdupq_n_u16(0xf800));
/* G channel packing Mask */
vst1q((uint16_t *) & scratch[4], vdupq_n_u16(0x07e0));
/* use of fixed point mult instead of vshr to increase overlap efficiency */
const int16_t inv_2pow3 = 1 << (15 - 3); /* 1/(2^3) in Q.15 */
#endif
for (int_fast16_t iTargetY = 0; iTargetY < ptTargetSize->iHeight;) {
uint16_t *__RESTRICT ptSource = ptSourceBase;
uint8_t *pchSourceMask = pchSourceMaskBase;
#if __API_CAFWM_CFG_SUPPORT_SRC_MSK_WRAPING
int_fast16_t iSourceMaskY = 0;
#endif
for (int_fast16_t iSourceY = 0; iSourceY < ptSourceSize->iHeight; iSourceY++) {
uint16_t *__RESTRICT ptTarget = ptTargetBase;
uint8_t *__RESTRICT pchTargetMask = pchTargetMaskLineBase;
uint_fast32_t wLengthLeft = ptTargetSize->iWidth;
do {
uint_fast32_t wLength = MIN(wLengthLeft, ptSourceSize->iWidth);
uint16_t *__RESTRICT ptSrc = ptSource;
uint8_t *__RESTRICT pchSrcMsk = pchSourceMask;
uint16_t *__RESTRICT ptTargetCur = ptTarget;
uint8_t *__RESTRICT pchTargetMaskCur = pchTargetMask;
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt = wLength;
do {
uint16x8_t vecTarget = vld1q(ptTargetCur);
uint16x8_t vecSource = vld1q(ptSrc);
uint16x8_t vecSrcMsk = vldrbq_u16(pchSrcMsk);
uint16x8_t vecTargetMask = vldrbq_u16(pchTargetMaskCur);
uint16x8_t vecHwOpacity =
vsubq_u16(v256, (vecSrcMsk * vecTargetMask) >> 8);
vecTarget = __arm_2d_rgb565_blending_opacity_single_vec(
vecTarget, vecSource, vecHwOpacity);
/* tail predication */
vst1q_p_u16(ptTargetCur, vecTarget, vctp16q(blkCnt));
pchSrcMsk += 8;
pchTargetMaskCur += 8;
ptTargetCur += 8;
ptSrc += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else
register unsigned blkCnt __asm("lr");
blkCnt = wLength;
__asm volatile (
/* R & B mask */
"vecRBUnpackMask .req q7 \n"
"vecAlpha .req q5 \n"
"vecHwOpacity .req q3 \n"
/* preload */
" vldrb.u16 q0, [%[pchSrcMsk]], #8 \n"
" vmov.i16 vecRBUnpackMask, #0x00f8 \n"
" vldrb.u16 q5, [%[pchTargetMask]], #8 \n"
".p2align 2 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
/* vecSrcMsk * vecTargetMask */
" vmul.i16 q0, q5, q0 \n"
" vldrh.u16 q6, [%[ptTarget]] \n"
" vshr.u16 vecAlpha, q0, #8 \n"
/* 256-dup vector */
" vldrh.u16 q1, [%[scratch], #(16*0)] \n"
/* vecHwOpacity =
vsubq_u16(v256, (vecSrcMsk * vecTargetMask) >> 8) */
" vsub.i16 vecHwOpacity, q1, vecAlpha \n"
" vldrh.u16 q1, [%[ptSrc]], #16 \n"
/* mimic vshl #3 */
" vshl.u16 q0, q6, #3 \n"
" vmul.i16 q4, q1, %[eight] \n"
/* vecR extract and scale */
" vand q0, q0, vecRBUnpackMask \n"
" vmul.i16 q0, vecHwOpacity, q0 \n"
/* vecSrcR extract and scale */
" vand q4, q4, vecRBUnpackMask \n"
" vmul.i16 q4, vecAlpha, q4 \n"
/* 0xfc G-mask */
" vldrw.u32 q2, [%[scratch], #(16*2)] \n"
" vadd.i16 q4, q0, q4 \n"
/* push blended R */
" vstrw.32 q4, [%[scratch], #(16*1)] \n"
/* mimic vshr.u16 q4, q6, #3 */
" vqdmulh.s16 q4, q6, %[inv_2pow3] \n"
" vshr.u16 q0, q1, #3 \n"
/* vecG extract and scale */
" vand q4, q4, q2 \n"
" vmul.i16 q4, vecHwOpacity, q4 \n"
/* vecSrcG extract and scale */
" vand q0, q0, q2 \n"
" vmul.i16 q2, vecAlpha, q0 \n"
" vshr.u16 q0, q1, #8 \n"
/* blended G */
/* vadd.i16 q2, q4, q2
addition using vmla for more efficient overlap */
" vmla.u16 q2, q4, %[one] \n"
/* vecB extract and scale */
" vshr.u16 q4, q6, #8 \n"
" vand q4, q4, vecRBUnpackMask \n"
" vmul.i16 q4, vecHwOpacity, q4 \n"
/* vecSrcB extract and scale */
" vand q0, q0, vecRBUnpackMask \n"
" vmul.i16 q0, vecAlpha, q0 \n"
".unreq vecAlpha \n"
".unreq vecHwOpacity \n"
".unreq vecRBUnpackMask \n"
/* reload blended R */
" vldrw.u32 q1, [%[scratch], #(16*1)] \n"
/* blended B
vadd.i16 q0, q4, q0
addition using vmla for more efficient overlap */
" vmla.u16 q0, q4, %[one] \n"
/* pack R */
" vshr.u16 q3, q1, #11 \n"
/* B channel packing mask 0xf800 */
" vldrw.u32 q4, [%[scratch], #(16*3)] \n"
" vand q0, q0, q4 \n"
/* accumulate R & B */
" vorr q4, q0, q3 \n"
/* G channel packing mask 0x07e0 */
" vldrw.u32 q3, [%[scratch], #(16*4)] \n"
" vshr.u16 q2, q2, #5 \n"
/* load next source mask */
" vldrb.u16 q0, [%[pchSrcMsk]], #8 \n"
/* G channel masking */
" vand q2, q2, q3 \n"
/* load next target mask */
" vldrb.u16 q5, [%[pchTargetMask]], #8 \n"
/* pack G with R.B */
" vorr q4, q4, q2 \n"
" vstrh.16 q4, [%[ptTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[ptTarget] "+r"(ptTargetCur),[ptSrc] "+r"(ptSrc),
[pchTargetMask] "+l"(pchTargetMaskCur),[pchSrcMsk] "+l"(pchSrcMsk),
[loopCnt] "+r"(blkCnt)
:[scratch] "r" (scratch),[eight] "r"(8),[inv_2pow3] "r"(inv_2pow3),
[one] "r" (1)
:"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory");
#endif
ptTarget += wLength;
pchTargetMask += wLength;
wLengthLeft -= wLength;
} while (wLengthLeft);
ptSource += iSourceStride;
ptTargetBase += iTargetStride;
#if __API_CAFWM_CFG_SUPPORT_SRC_MSK_WRAPING
iSourceMaskY++;
//! handle source mask
if ( (iSourceMaskY >= ptSourceMaskSize->iHeight)
|| (iSourceMaskY >= ptSourceSize->iHeight)) {
pchSourceMask = pchSourceMaskBase;
iSourceMaskY = 0;
} else {
pchSourceMask += iSourceMaskStride;
}
#else
pchSourceMask += iSourceMaskStride;
#endif
pchTargetMaskLineBase += iTargetMaskStride;
iTargetY++;
if (iTargetY >= ptTargetSize->iHeight) {
break;
}
}
}
}
__OVERRIDE_WEAK
void __arm_2d_impl_rgb565_src_msk_1h_des_msk_fill(
uint16_t * __RESTRICT ptSourceBase,
int16_t iSourceStride,
arm_2d_size_t * __RESTRICT ptSourceSize,
uint8_t * __RESTRICT pchSourceMaskBase,
int16_t iSourceMaskStride,
arm_2d_size_t * __RESTRICT ptSourceMaskSize,
uint16_t * __RESTRICT ptTargetBase,
int16_t iTargetStride,
arm_2d_size_t * __RESTRICT ptTargetSize,
uint8_t * __RESTRICT pchTargetMaskBase,
int16_t iTargetMaskStride,
arm_2d_size_t * __RESTRICT ptTargetMaskSize)
{
uint8_t *__RESTRICT pchTargetMaskLineBase = pchTargetMaskBase;
uint16x8_t v256 = vdupq_n_u16(256);
#ifndef USE_MVE_INTRINSICS
uint16x8_t scratch[5];
/* vector of 256 avoiding use of vdup to increase overlap efficiency*/
vst1q((uint16_t *) & scratch[0], v256);
/* scratch[1] is temporary for blended Red chan. vector */
/* Unpacking Mask Red */
vst1q((uint16_t *) & scratch[2], vdupq_n_u16(0x00fc));
/* B channel packing mask */
vst1q((uint16_t *) & scratch[3], vdupq_n_u16(0xf800));
/* G channel packing Mask */
vst1q((uint16_t *) & scratch[4], vdupq_n_u16(0x07e0));
/* use of fixed point mult instead of vshr to increase overlap efficiency */
const int16_t inv_2pow3 = 1 << (15 - 3); /* 1/(2^3) in Q.15 */
#endif
for (int_fast16_t iTargetY = 0; iTargetY < ptTargetSize->iHeight;) {
uint16_t *__RESTRICT ptSource = ptSourceBase;
uint8_t *pchSourceMask = pchSourceMaskBase;
#if __API_CAFWM_CFG_SUPPORT_SRC_MSK_WRAPING
int_fast16_t iSourceMaskY = 0;
#endif
for (int_fast16_t iSourceY = 0; iSourceY < ptSourceSize->iHeight; iSourceY++) {
uint16_t *__RESTRICT ptTarget = ptTargetBase;
uint8_t *__RESTRICT pchTargetMask = pchTargetMaskLineBase;
uint_fast32_t wLengthLeft = ptTargetSize->iWidth;
do {
uint_fast32_t wLength = MIN(wLengthLeft, ptSourceSize->iWidth);
uint16_t *__RESTRICT ptSrc = ptSource;
uint8_t *__RESTRICT pchSrcMsk = pchSourceMask;
uint16_t *__RESTRICT ptTargetCur = ptTarget;
uint8_t *__RESTRICT pchTargetMaskCur = pchTargetMask;
#ifdef USE_MVE_INTRINSICS
int32_t blkCnt = wLength;
do {
uint16x8_t vecTarget = vld1q(ptTargetCur);
uint16x8_t vecSource = vld1q(ptSrc);
uint16x8_t vecSrcMsk = vldrbq_u16(pchSrcMsk);
uint16x8_t vecTargetMask = vldrbq_u16(pchTargetMaskCur);
uint16x8_t vecHwOpacity =
vsubq_u16(v256, (vecSrcMsk * vecTargetMask) >> 8);
vecTarget = __arm_2d_rgb565_blending_opacity_single_vec(
vecTarget, vecSource, vecHwOpacity);
/* tail predication */
vst1q_p_u16(ptTargetCur, vecTarget, vctp16q(blkCnt));
pchSrcMsk += 8;
pchTargetMaskCur += 8;
ptTargetCur += 8;
ptSrc += 8;
blkCnt -= 8;
}
while (blkCnt > 0);
#else
register unsigned blkCnt __asm("lr");
blkCnt = wLength;
__asm volatile (
/* R & B mask */
"vecRBUnpackMask .req q7 \n"
"vecAlpha .req q5 \n"
"vecHwOpacity .req q3 \n"
/* preload */
" vldrb.u16 q0, [%[pchSrcMsk]], #8 \n"
" vmov.i16 vecRBUnpackMask, #0x00f8 \n"
" vldrb.u16 q5, [%[pchTargetMask]], #8 \n"
".p2align 2 \n"
" wlstp.16 lr, %[loopCnt], 1f \n"
"2: \n"
/* vecSrcMsk * vecTargetMask */
" vmul.i16 q0, q5, q0 \n"
" vldrh.u16 q6, [%[ptTarget]] \n"
" vshr.u16 vecAlpha, q0, #8 \n"
/* 256-dup vector */
" vldrh.u16 q1, [%[scratch], #(16*0)] \n"
/* vecHwOpacity =
vsubq_u16(v256, (vecSrcMsk * vecTargetMask) >> 8) */
" vsub.i16 vecHwOpacity, q1, vecAlpha \n"
" vldrh.u16 q1, [%[ptSrc]], #16 \n"
/* mimic vshl #3 */
" vshl.u16 q0, q6, #3 \n"
" vmul.i16 q4, q1, %[eight] \n"
/* vecR extract and scale */
" vand q0, q0, vecRBUnpackMask \n"
" vmul.i16 q0, vecHwOpacity, q0 \n"
/* vecSrcR extract and scale */
" vand q4, q4, vecRBUnpackMask \n"
" vmul.i16 q4, vecAlpha, q4 \n"
/* 0xfc G-mask */
" vldrw.u32 q2, [%[scratch], #(16*2)] \n"
" vadd.i16 q4, q0, q4 \n"
/* push blended R */
" vstrw.32 q4, [%[scratch], #(16*1)] \n"
/* mimic vshr.u16 q4, q6, #3 */
" vqdmulh.s16 q4, q6, %[inv_2pow3] \n"
" vshr.u16 q0, q1, #3 \n"
/* vecG extract and scale */
" vand q4, q4, q2 \n"
" vmul.i16 q4, vecHwOpacity, q4 \n"
/* vecSrcG extract and scale */
" vand q0, q0, q2 \n"
" vmul.i16 q2, vecAlpha, q0 \n"
" vshr.u16 q0, q1, #8 \n"
/* blended G */
/* vadd.i16 q2, q4, q2
addition using vmla for more efficient overlap */
" vmla.u16 q2, q4, %[one] \n"
/* vecB extract and scale */
" vshr.u16 q4, q6, #8 \n"
" vand q4, q4, vecRBUnpackMask \n"
" vmul.i16 q4, vecHwOpacity, q4 \n"
/* vecSrcB extract and scale */
" vand q0, q0, vecRBUnpackMask \n"
" vmul.i16 q0, vecAlpha, q0 \n"
".unreq vecAlpha \n"
".unreq vecHwOpacity \n"
".unreq vecRBUnpackMask \n"
/* reload blended R */
" vldrw.u32 q1, [%[scratch], #(16*1)] \n"
/* blended B
vadd.i16 q0, q4, q0
addition using vmla for more efficient overlap */
" vmla.u16 q0, q4, %[one] \n"
/* pack R */
" vshr.u16 q3, q1, #11 \n"
/* B channel packing mask 0xf800 */
" vldrw.u32 q4, [%[scratch], #(16*3)] \n"
" vand q0, q0, q4 \n"
/* accumulate R & B */
" vorr q4, q0, q3 \n"
/* G channel packing mask 0x07e0 */
" vldrw.u32 q3, [%[scratch], #(16*4)] \n"
" vshr.u16 q2, q2, #5 \n"
/* load next source mask */
" vldrb.u16 q0, [%[pchSrcMsk]], #8 \n"
/* G channel masking */
" vand q2, q2, q3 \n"
/* load next target mask */
" vldrb.u16 q5, [%[pchTargetMask]], #8 \n"
/* pack G with R.B */
" vorr q4, q4, q2 \n"
" vstrh.16 q4, [%[ptTarget]], #16 \n"
" letp lr, 2b \n"
"1: \n"
:[ptTarget] "+r"(ptTargetCur),[ptSrc] "+r"(ptSrc),
[pchTargetMask] "+l"(pchTargetMaskCur),[pchSrcMsk] "+l"(pchSrcMsk),
[loopCnt] "+r"(blkCnt)
:[scratch] "r" (scratch),[eight] "r"(8),[inv_2pow3] "r"(inv_2pow3),
[one] "r" (1)
:"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory");
#endif
ptTarget += wLength;
pchTargetMask += wLength;
wLengthLeft -= wLength;
} while (wLengthLeft);
ptSource += iSourceStride;
ptTargetBase += iTargetStride;
#if __API_CAFWM_CFG_SUPPORT_SRC_MSK_WRAPING
iSourceMaskY++;
//! handle source mask
if ( (iSourceMaskY >= ptSourceMaskSize->iHeight)
|| (iSourceMaskY >= ptSourceSize->iHeight)) {
pchSourceMask = pchSourceMaskBase;
iSourceMaskY = 0;
} else {
pchSourceMask += iSourceMaskStride;
}
#else
pchSourceMask += iSourceMaskStride;
#endif
pchTargetMaskLineBase = pchTargetMaskBase;
iTargetY++;
if (iTargetY >= ptTargetSize->iHeight) {
break;
}
}
}
}
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
#ifdef __cplusplus
}
#endif
#endif // __ARM_2D_HAS_HELIUM__