KoOptimizedCompositeOpOver128.h 9.48 KB
Newer Older
1
/*
2
 * SPDX-FileCopyrightText: 2015 Thorsten Zachmann <zachmann@kde.org>
3
 *
Samuel Gaist's avatar
Samuel Gaist committed
4
 * SPDX-License-Identifier: LGPL-2.0-or-later
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 */

#ifndef KOOPTIMIZEDCOMPOSITEOPOVER128_H_
#define KOOPTIMIZEDCOMPOSITEOPOVER128_H_

#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include "KoStreamedMath.h"

#define NATIVE_OPACITY_OPAQUE KoColorSpaceMathsTraits<channels_type>::unitValue
#define NATIVE_OPACITY_TRANSPARENT KoColorSpaceMathsTraits<channels_type>::zeroValue

#define INFO_DEBUG 0

19
template<typename channels_type, bool alphaLocked, bool allChannelsFlag>
20
struct OverCompositor128 {
21 22
    struct ParamsWrapper {
        ParamsWrapper(const KoCompositeOp::ParameterInfo& params)
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
            : channelFlags(params.channelFlags)
        {
        }
        const QBitArray &channelFlags;
    };

    struct Pixel {
        channels_type red;
        channels_type green;
        channels_type blue;
        channels_type alpha;
    };

    // \see docs in AlphaDarkenCompositor32
    template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
38
    static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
39 40 41 42 43 44 45 46 47
    {
#if INFO_DEBUG
        static quint32 countTotal = 0;
        static quint32 countOne = 0;
        static quint32 countTwo = 0;
        static quint32 countThree = 0;
        static quint32 countFour = 0;

        if (++countTotal % 250000 == 0) {
Halla Rempt's avatar
Halla Rempt committed
48
            qInfo() << "count" << countOne << countTwo << countThree << countFour << countTotal << opacity;
49 50 51 52 53 54 55 56 57 58 59 60 61 62
        }
#endif
        Q_UNUSED(oparams);

        const Pixel *sp = reinterpret_cast<const Pixel*>(src);
        Pixel *dp = reinterpret_cast<Pixel*>(dst);

        Vc::float_v src_alpha;
        Vc::float_v dst_alpha;

        Vc::float_v src_c1;
        Vc::float_v src_c2;
        Vc::float_v src_c3;

Michael Abrahams's avatar
Michael Abrahams committed
63
        const Vc::float_v::IndexType indexes(Vc::IndexesFromZero);
64
        Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> data(const_cast<Pixel*>(sp));
65
        tie(src_c1, src_c2, src_c3, src_alpha) = data[indexes];
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

        //bool haveOpacity = opacity != 1.0;
        const Vc::float_v opacity_norm_vec(opacity);
        src_alpha *= opacity_norm_vec;

        if (haveMask) {
            const Vc::float_v uint8MaxRec1((float)1.0 / 255);
            Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
            src_alpha *= mask_vec * uint8MaxRec1;
        }

        const Vc::float_v zeroValue(NATIVE_OPACITY_TRANSPARENT);
        // The source cannot change the colors in the destination,
        // since its fully transparent
        if ((src_alpha == zeroValue).isFull()) {
#if INFO_DEBUG
            countFour++;
#endif
            return;
        }

        Vc::float_v dst_c1;
        Vc::float_v dst_c2;
        Vc::float_v dst_c3;

        Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> dataDest(dp);
92
        tie(dst_c1, dst_c2, dst_c3, dst_alpha) = dataDest[indexes];
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

        Vc::float_v src_blend;
        Vc::float_v new_alpha;

        const Vc::float_v oneValue(NATIVE_OPACITY_OPAQUE);
        if ((dst_alpha == oneValue).isFull()) {
            new_alpha = dst_alpha;
            src_blend = src_alpha;
        } else if ((dst_alpha == zeroValue).isFull()) {
            new_alpha = src_alpha;
            src_blend = oneValue;
        } else {
            /**
             * The value of new_alpha can have *some* zero values,
             * which will result in NaN values while division.
             */
            new_alpha = dst_alpha + (oneValue - dst_alpha) * src_alpha;
            Vc::float_m mask = (new_alpha == zeroValue);
            src_blend = src_alpha / new_alpha;
            src_blend.setZero(mask);
        }

        if (!(src_blend == oneValue).isFull()) {
#if INFO_DEBUG
            ++countOne;
#endif

            dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1;
            dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2;
            dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3;

124
            dataDest[indexes] = tie(dst_c1, dst_c2, dst_c3, new_alpha);
125 126 127 128
        } else {
#if INFO_DEBUG
                ++countTwo;
#endif
129
                dataDest[indexes] = tie(src_c1, src_c2, src_c3, new_alpha);
130 131 132 133
        }
    }

    template <bool haveMask, Vc::Implementation _impl>
134
    static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    {
        using namespace Arithmetic;
        const qint32 alpha_pos = 3;

        const channels_type *s = reinterpret_cast<const channels_type*>(src);
        channels_type *d = reinterpret_cast<channels_type*>(dst);

        float srcAlpha = s[alpha_pos];
        srcAlpha *= opacity;

        if (haveMask) {
            const float uint8Rec1 = 1.0 / 255;
            srcAlpha *= float(*mask) * uint8Rec1;
        }

#if INFO_DEBUG
        static int xx = 0;
        bool display = xx > 45 && xx < 50;
        if (display) {
Halla Rempt's avatar
Halla Rempt committed
154
            qInfo() << "O" << s[alpha_pos] << srcAlpha << haveMask << opacity;
155 156 157 158 159 160 161 162
        }
#endif

        if (srcAlpha != NATIVE_OPACITY_TRANSPARENT) {

            float dstAlpha = d[alpha_pos];
            float srcBlendNorm;

163
            if (alphaLocked || dstAlpha == NATIVE_OPACITY_OPAQUE) {
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
                srcBlendNorm = srcAlpha;
            } else if (dstAlpha == NATIVE_OPACITY_TRANSPARENT) {
                dstAlpha = srcAlpha;
                srcBlendNorm = NATIVE_OPACITY_OPAQUE;

                if (!allChannelsFlag) {
                    KoStreamedMathFunctions::clearPixel<16>(dst);
                }
            } else {
                dstAlpha += (NATIVE_OPACITY_OPAQUE - dstAlpha) * srcAlpha;
                srcBlendNorm = srcAlpha / dstAlpha;
            }

#if INFO_DEBUG
            if (display) {
Halla Rempt's avatar
Halla Rempt committed
179
                qInfo() << "params" << srcBlendNorm << allChannelsFlag << alphaLocked << dstAlpha << haveMask;
180 181 182 183 184 185 186 187 188 189 190 191 192 193
            }
#endif
            if(allChannelsFlag) {
                if (srcBlendNorm == NATIVE_OPACITY_OPAQUE) {
                    if (!alphaLocked) {
                        KoStreamedMathFunctions::copyPixel<16>(src, dst);
                    } else {
                        d[0] = s[0];
                        d[1] = s[1];
                        d[2] = s[2];
                    }
                } else if (srcBlendNorm != 0.0){
#if INFO_DEBUG
                    if (display) {
Halla Rempt's avatar
Halla Rempt committed
194
                        qInfo() << "calc" << s[0] << d[0] << srcBlendNorm * (s[0] - d[0]) + d[0] << s[0] - d[0] << srcBlendNorm * (s[0] - d[0]) << srcBlendNorm;
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
                    }
#endif
                    d[0] = srcBlendNorm * (s[0] - d[0]) + d[0];
                    d[1] = srcBlendNorm * (s[1] - d[1]) + d[1];
                    d[2] = srcBlendNorm * (s[2] - d[2]) + d[2];
                }
            } else {
                const QBitArray &channelFlags = oparams.channelFlags;

                if (srcBlendNorm == NATIVE_OPACITY_OPAQUE) {
                    if(channelFlags.at(0)) d[0] = s[0];
                    if(channelFlags.at(1)) d[1] = s[1];
                    if(channelFlags.at(2)) d[2] = s[2];
                } else if (srcBlendNorm != 0.0) {
                    if(channelFlags.at(0)) d[0] = srcBlendNorm * (s[0] - d[0]) + d[0];
                    if(channelFlags.at(1)) d[1] = srcBlendNorm * (s[1] - d[1]) + d[1];
                    if(channelFlags.at(2)) d[2] = srcBlendNorm * (s[2] - d[2]) + d[2];
                }
            }

            if (!alphaLocked) {
                d[alpha_pos] = dstAlpha;
            }
#if INFO_DEBUG
            if (display) {
Halla Rempt's avatar
Halla Rempt committed
220
                qInfo() << "result" << d[0] << d[1] << d[2] << d[3];
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
            }
            ++xx;
#endif
        }
    }
};

/**
 * An optimized version of a composite op for the use in 16 byte
 * colorspaces with alpha channel placed at the last byte of
 * the pixel: C1_C2_C3_A.
 */
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpOver128 : public KoCompositeOp
{
public:
    KoOptimizedCompositeOpOver128(const KoColorSpace* cs)
        : KoCompositeOp(cs, COMPOSITE_OVER, i18n("Normal"), KoCompositeOp::categoryMix()) {}

    using KoCompositeOp::composite;

    virtual void composite(const KoCompositeOp::ParameterInfo& params) const
    {
        if(params.maskRowStart) {
            composite<true>(params);
        } else {
            composite<false>(params);
        }
    }

    template <bool haveMask>
    inline void composite(const KoCompositeOp::ParameterInfo& params) const {
        if (params.channelFlags.isEmpty() ||
            params.channelFlags == QBitArray(4, true)) {

256
            KoStreamedMath<_impl>::template genericComposite128<haveMask, false, OverCompositor128<float, false, true> >(params);
257 258 259 260 261 262 263 264 265 266
        } else {
            const bool allChannelsFlag =
                params.channelFlags.at(0) &&
                params.channelFlags.at(1) &&
                params.channelFlags.at(2);

            const bool alphaLocked =
                !params.channelFlags.at(3);

            if (allChannelsFlag && alphaLocked) {
267
                KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, true> >(params);
268
            } else if (!allChannelsFlag && !alphaLocked) {
269
                KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, false, false> >(params);
270
            } else /*if (!allChannelsFlag && alphaLocked) */{
271
                KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, false> >(params);
272 273 274 275 276 277
            }
        }
    }
};

#endif // KOOPTIMIZEDCOMPOSITEOPOVER128_H_