File indexing completed on 2024-05-12 15:58:08

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef __KIS_ANTIALIASING_FADE_MAKER_H
0009 #define __KIS_ANTIALIASING_FADE_MAKER_H
0010 
0011 #include <kis_global.h>
0012 
0013 #include <xsimd_extensions/xsimd.hpp>
0014 
0015 template<class BaseFade>
0016 class KisAntialiasingFadeMaker1D
0017 {
0018 public:
0019     KisAntialiasingFadeMaker1D(const BaseFade &baseFade, bool enableAntialiasing)
0020         : m_radius(0.0)
0021         , m_fadeStartValue(0)
0022         , m_antialiasingFadeStart(0)
0023         , m_antialiasingFadeCoeff(0)
0024         , m_enableAntialiasing(enableAntialiasing)
0025         , m_baseFade(baseFade)
0026     {
0027     }
0028 
0029     KisAntialiasingFadeMaker1D(const KisAntialiasingFadeMaker1D &rhs, const BaseFade &baseFade)
0030         : m_radius(rhs.m_radius)
0031         , m_fadeStartValue(rhs.m_fadeStartValue)
0032         , m_antialiasingFadeStart(rhs.m_antialiasingFadeStart)
0033         , m_antialiasingFadeCoeff(rhs.m_antialiasingFadeCoeff)
0034         , m_enableAntialiasing(rhs.m_enableAntialiasing)
0035         , m_baseFade(baseFade)
0036     {
0037     }
0038 
0039     void setSquareNormCoeffs(qreal xcoeff, qreal ycoeff)
0040     {
0041         m_radius = 1.0;
0042 
0043         const qreal xf = qMax(0.0, ((1.0 / xcoeff) - 1.0) * xcoeff);
0044         const qreal yf = qMax(0.0, ((1.0 / ycoeff) - 1.0) * ycoeff);
0045 
0046         m_antialiasingFadeStart = pow2(0.5 * (xf + yf));
0047 
0048         m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart);
0049         m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart);
0050     }
0051 
0052     void setRadius(qreal radius)
0053     {
0054         m_radius = radius;
0055         m_antialiasingFadeStart = qMax(0.0, m_radius - 1.0);
0056 
0057         m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart);
0058         m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart);
0059     }
0060 
0061     inline bool needFade(qreal dist, quint8 *value)
0062     {
0063         if (dist > m_radius) {
0064             *value = 255;
0065             return true;
0066         }
0067 
0068         if (!m_enableAntialiasing) {
0069             return false;
0070         }
0071 
0072         if (dist > m_antialiasingFadeStart) {
0073             *value = m_fadeStartValue + (dist - m_antialiasingFadeStart) * m_antialiasingFadeCoeff;
0074             return true;
0075         }
0076 
0077         return false;
0078     }
0079 
0080 #if defined(HAVE_XSIMD) && !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
0081     template<typename A>
0082     xsimd::batch_bool<float, A> needFade(xsimd::batch<float, A> &dist)
0083     {
0084         using float_v = xsimd::batch<float, A>;
0085         using float_m = typename float_v::batch_bool_type;
0086 
0087         const float_v vOne(1);
0088         const float_v vValMax(255.f);
0089 
0090         const float_v vRadius(m_radius);
0091         const float_v vFadeStartValue(m_fadeStartValue);
0092         const float_v vAntialiasingFadeStart(m_antialiasingFadeStart);
0093         const float_v vAntialiasingFadeCoeff(m_antialiasingFadeCoeff);
0094 
0095         const float_m outsideMask = dist > vRadius;
0096         dist = xsimd::set_one(dist, outsideMask);
0097 
0098         float_m fadeStartMask(false);
0099 
0100         if (m_enableAntialiasing) {
0101             fadeStartMask = dist > vAntialiasingFadeStart;
0102             dist = xsimd::select((outsideMask ^ fadeStartMask) & fadeStartMask,
0103                                  (vFadeStartValue + (dist - vAntialiasingFadeStart) * vAntialiasingFadeCoeff) / vValMax,
0104                                  dist);
0105         }
0106         return (outsideMask | fadeStartMask);
0107     }
0108 
0109 #endif /* defined HAVE_XSIMD */
0110 
0111 private:
0112     qreal m_radius;
0113     quint8 m_fadeStartValue;
0114     qreal m_antialiasingFadeStart;
0115     qreal m_antialiasingFadeCoeff;
0116     bool m_enableAntialiasing;
0117     const BaseFade &m_baseFade;
0118 };
0119 
0120 template<class BaseFade>
0121 class KisAntialiasingFadeMaker2D
0122 {
0123 public:
0124     KisAntialiasingFadeMaker2D(const BaseFade &baseFade, bool enableAntialiasing)
0125         : m_xLimit(0)
0126         , m_yLimit(0)
0127         , m_xFadeLimitStart(0)
0128         , m_yFadeLimitStart(0)
0129         , m_xFadeCoeff(0)
0130         , m_yFadeCoeff(0)
0131         , m_enableAntialiasing(enableAntialiasing)
0132         , m_baseFade(baseFade)
0133     {
0134     }
0135 
0136     KisAntialiasingFadeMaker2D(const KisAntialiasingFadeMaker2D &rhs, const BaseFade &baseFade)
0137         : m_xLimit(rhs.m_xLimit)
0138         , m_yLimit(rhs.m_yLimit)
0139         , m_xFadeLimitStart(rhs.m_xFadeLimitStart)
0140         , m_yFadeLimitStart(rhs.m_yFadeLimitStart)
0141         , m_xFadeCoeff(rhs.m_xFadeCoeff)
0142         , m_yFadeCoeff(rhs.m_yFadeCoeff)
0143         , m_enableAntialiasing(rhs.m_enableAntialiasing)
0144         , m_baseFade(baseFade)
0145     {
0146     }
0147 
0148     void setLimits(qreal halfWidth, qreal halfHeight)
0149     {
0150         m_xLimit = halfWidth;
0151         m_yLimit = halfHeight;
0152 
0153         m_xFadeLimitStart = m_xLimit - 1.0;
0154         m_yFadeLimitStart = m_yLimit - 1.0;
0155 
0156         m_xFadeCoeff = 1.0 / (m_xLimit - m_xFadeLimitStart);
0157         m_yFadeCoeff = 1.0 / (m_yLimit - m_yFadeLimitStart);
0158     }
0159 
0160     inline bool needFade(qreal x, qreal y, quint8 *value)
0161     {
0162         x = qAbs(x);
0163         y = qAbs(y);
0164 
0165         if (x > m_xLimit) {
0166             *value = 255;
0167             return true;
0168         }
0169 
0170         if (y > m_yLimit) {
0171             *value = 255;
0172             return true;
0173         }
0174 
0175         if (!m_enableAntialiasing) {
0176             return false;
0177         }
0178 
0179         if (x > m_xFadeLimitStart) {
0180             quint8 baseValue = m_baseFade.value(x, y);
0181             *value = baseValue + (255.0 - baseValue) * (x - m_xFadeLimitStart) * m_xFadeCoeff;
0182 
0183             if (y > m_yFadeLimitStart && *value < 255) {
0184                 *value += (255.0 - *value) * (y - m_yFadeLimitStart) * m_yFadeCoeff;
0185             }
0186 
0187             return true;
0188         }
0189 
0190         if (y > m_yFadeLimitStart) {
0191             quint8 baseValue = m_baseFade.value(x, y);
0192             *value = baseValue + (255.0 - baseValue) * (y - m_yFadeLimitStart) * m_yFadeCoeff;
0193 
0194             if (x > m_xFadeLimitStart && *value < 255) {
0195                 *value += (255.0 - *value) * (x - m_xFadeLimitStart) * m_xFadeCoeff;
0196             }
0197 
0198             return true;
0199         }
0200 
0201         return false;
0202     }
0203 
0204 #if defined(HAVE_XSIMD) && !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
0205     template<typename A>
0206     xsimd::batch_bool<float, A> needFade(xsimd::batch<float, A> &xr, xsimd::batch<float, A> &yr) const
0207     {
0208         using float_v = xsimd::batch<float, A>;
0209         using float_m = typename float_v::batch_bool_type;
0210 
0211         const float_v vXLimit(m_xLimit);
0212         const float_v vYLimit(m_yLimit);
0213 
0214         const float_m outXMask = xsimd::abs(xr) > vXLimit;
0215         const float_m outYMask = xsimd::abs(yr) > vYLimit;
0216 
0217         return (outXMask | outYMask);
0218     }
0219 
0220     // Apply fader separately to avoid calculating vValue twice.
0221     template<typename A>
0222     void apply2DFader(xsimd::batch<float, A> &vValue, xsimd::batch_bool<float, A> &excludeMask, xsimd::batch<float, A> &xr, xsimd::batch<float, A> &yr) const
0223     {
0224         using float_v = xsimd::batch<float, A>;
0225         using float_m = typename float_v::batch_bool_type;
0226 
0227         const float_v vValMax(255.f);
0228 
0229         if (m_enableAntialiasing) {
0230             const float_v vXFadeLimitStart(m_xFadeLimitStart);
0231             const float_v vYFadeLimitStart(m_yFadeLimitStart);
0232             const float_v vXFadeCoeff(m_xFadeCoeff);
0233             const float_v vYFadeCoeff(m_yFadeCoeff);
0234 
0235             const float_v xra = xsimd::abs(xr);
0236             float_m fadeXStartMask(false);
0237             float_m fadeYStartMask(false);
0238 
0239             float_v fadeValue(0);
0240             const float_v vBaseValue =
0241                 xsimd::truncate_to_type<uint16_t>(vValue);
0242 
0243             fadeXStartMask = xra > vXFadeLimitStart;
0244             fadeXStartMask = (fadeXStartMask ^ excludeMask) & fadeXStartMask;
0245             if (!xsimd::all(fadeXStartMask)) {
0246                 fadeValue = vBaseValue + (vValMax - vBaseValue) * (xra - vXFadeLimitStart) * vXFadeCoeff;
0247                 fadeValue = xsimd::select(fadeXStartMask & ((yr > vYFadeLimitStart) & (fadeValue < vValMax)),
0248                                           fadeValue + (vValMax - fadeValue) * (yr - vYFadeLimitStart) * vYFadeCoeff,
0249                                           fadeValue);
0250                 vValue = xsimd::select(fadeXStartMask, fadeValue, vValue);
0251             }
0252 
0253             fadeYStartMask = yr > vYFadeLimitStart;
0254             fadeYStartMask = (fadeYStartMask ^ fadeXStartMask) & fadeYStartMask;
0255             if (!xsimd::all(fadeYStartMask)) {
0256                 fadeValue = vBaseValue + (vValMax - vBaseValue) * (yr - vYFadeLimitStart) * vYFadeCoeff;
0257                 fadeValue = xsimd::select(fadeYStartMask & ((xra > vXFadeLimitStart) & (fadeValue < vValMax)),
0258                                           fadeValue + (vValMax - fadeValue) * (xra - vXFadeLimitStart) * vXFadeCoeff,
0259                                           fadeValue);
0260                 vValue = xsimd::select(fadeYStartMask, fadeValue, vValue);
0261             }
0262         }
0263     }
0264 
0265 #endif /* defined HAVE_XSIMD */
0266 
0267 private:
0268     qreal m_xLimit;
0269     qreal m_yLimit;
0270 
0271     qreal m_xFadeLimitStart;
0272     qreal m_yFadeLimitStart;
0273 
0274     qreal m_xFadeCoeff;
0275     qreal m_yFadeCoeff;
0276 
0277     bool m_enableAntialiasing;
0278 
0279     const BaseFade &m_baseFade;
0280 };
0281 
0282 #endif /* __KIS_ANTIALIASING_FADE_MAKER_H */