1 /*
2  * Copyright (c) 2016 Thorsten Zachmann <zachmann@kde.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
21 #define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
22 
23 #include "KoCompositeOpBase.h"
24 #include "KoCompositeOpRegistry.h"
25 #include "KoStreamedMath.h"
26 #include "KoAlphaDarkenParamsWrapper.h"
27 
28 
29 template<typename channels_type, typename _ParamsWrapper>
30 struct AlphaDarkenCompositor128 {
31     using ParamsWrapper = _ParamsWrapper;
32 
33     struct Pixel {
34         channels_type red;
35         channels_type green;
36         channels_type blue;
37         channels_type alpha;
38     };
39 
40     /**
41      * This is a vector equivalent of compositeOnePixelScalar(). It is considered
42      * to process Vc::float_v::size() pixels in a single pass.
43      *
44      * o the \p haveMask parameter points whether the real (non-null) mask
45      *   pointer is passed to the function.
46      * o the \p src pointer may be aligned to vector boundary or may be
47      *   not. In case not, it must be pointed with a special parameter
48      *   \p src_aligned.
49      * o the \p dst pointer must always(!) be aligned to the boundary
50      *   of a streaming vector. Unaligned writes are really expensive.
51      * o This function is *never* used if HAVE_VC is not present
52      */
53     template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
compositeVectorAlphaDarkenCompositor12854     static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
55     {
56         const Pixel *sp = reinterpret_cast<const Pixel*>(src);
57         Pixel *dp = reinterpret_cast<Pixel*>(dst);
58 
59         Vc::float_v src_c1;
60         Vc::float_v src_c2;
61         Vc::float_v src_c3;
62         Vc::float_v src_alpha;
63 
64         const Vc::float_v::IndexType indexes(Vc::IndexesFromZero);
65         Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> data(const_cast<Pixel*>(sp));
66         tie(src_c1, src_c2, src_c3, src_alpha) = data[indexes];
67 
68         Vc::float_v msk_norm_alpha;
69         if (haveMask) {
70             const Vc::float_v uint8Rec1((float)1.0 / 255.0);
71             Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
72             msk_norm_alpha = mask_vec * uint8Rec1 * src_alpha;
73         }
74         else {
75             msk_norm_alpha = src_alpha;
76         }
77 
78         // we don't use directly passed value
79         Q_UNUSED(opacity);
80 
81         // instead we use value calculated by ParamsWrapper
82         opacity = oparams.opacity;
83         Vc::float_v opacity_vec(opacity);
84 
85         src_alpha = msk_norm_alpha * opacity_vec;
86 
87         const Vc::float_v zeroValue(KoColorSpaceMathsTraits<channels_type>::zeroValue);
88 
89         Vc::float_v dst_c1;
90         Vc::float_v dst_c2;
91         Vc::float_v dst_c3;
92         Vc::float_v dst_alpha;
93 
94         Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> dataDest(dp);
95         tie(dst_c1, dst_c2, dst_c3, dst_alpha) = dataDest[indexes];
96 
97         Vc::float_m empty_dst_pixels_mask = dst_alpha == zeroValue;
98 
99         if (!empty_dst_pixels_mask.isFull()) {
100             if (empty_dst_pixels_mask.isEmpty()) {
101                 dst_c1 = (src_c1 - dst_c1) * src_alpha + dst_c1;
102                 dst_c2 = (src_c2 - dst_c2) * src_alpha + dst_c2;
103                 dst_c3 = (src_c3 - dst_c3) * src_alpha + dst_c3;
104             }
105             else {
106                 dst_c1(empty_dst_pixels_mask) = src_c1;
107                 dst_c2(empty_dst_pixels_mask) = src_c2;
108                 dst_c3(empty_dst_pixels_mask) = src_c3;
109                 Vc::float_m not_empty_dst_pixels_mask = !empty_dst_pixels_mask;
110                 dst_c1(not_empty_dst_pixels_mask) = (src_c1 - dst_c1) * src_alpha + dst_c1;
111                 dst_c2(not_empty_dst_pixels_mask) = (src_c2 - dst_c2) * src_alpha + dst_c2;
112                 dst_c3(not_empty_dst_pixels_mask) = (src_c3 - dst_c3) * src_alpha + dst_c3;
113             }
114         }
115         else {
116             dst_c1 = src_c1;
117             dst_c2 = src_c2;
118             dst_c3 = src_c3;
119         }
120 
121         Vc::float_v fullFlowAlpha(dst_alpha);
122 
123         if (oparams.averageOpacity > opacity) {
124             Vc::float_v average_opacity_vec(oparams.averageOpacity);
125             Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
126             fullFlowAlpha(fullFlowAlpha_mask) = (average_opacity_vec - src_alpha) * (dst_alpha / average_opacity_vec) + src_alpha;
127         }
128         else {
129             Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
130             fullFlowAlpha(fullFlowAlpha_mask) = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
131         }
132 
133         if (oparams.flow == 1.0) {
134             dst_alpha = fullFlowAlpha;
135         }
136         else {
137             Vc::float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha);
138             Vc::float_v flow_norm_vec(oparams.flow);
139             dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
140         }
141         dataDest[indexes] = tie(dst_c1, dst_c2, dst_c3, dst_alpha);
142     }
143 
144     /**
145      * Composes one pixel of the source into the destination
146      */
147     template <bool haveMask, Vc::Implementation _impl>
compositeOnePixelScalarAlphaDarkenCompositor128148     static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
149     {
150         using namespace Arithmetic;
151         const qint32 alpha_pos = 3;
152 
153         const channels_type *src = reinterpret_cast<const channels_type*>(s);
154         channels_type *dst = reinterpret_cast<channels_type*>(d);
155 
156         float dstAlphaNorm = dst[alpha_pos];
157 
158         const float uint8Rec1 = 1.0 / 255.0;
159         float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos];
160 
161         Q_UNUSED(opacity);
162         opacity = oparams.opacity;
163 
164         float srcAlphaNorm = mskAlphaNorm * opacity;
165 
166         if (dstAlphaNorm != 0) {
167             dst[0] = lerp(dst[0], src[0], srcAlphaNorm);
168             dst[1] = lerp(dst[1], src[1], srcAlphaNorm);
169             dst[2] = lerp(dst[2], src[2], srcAlphaNorm);
170         } else {
171             const Pixel *s = reinterpret_cast<const Pixel*>(src);
172             Pixel *d = reinterpret_cast<Pixel*>(dst);
173             *d = *s;
174         }
175 
176         float flow = oparams.flow;
177         float averageOpacity = oparams.averageOpacity;
178 
179         float fullFlowAlpha;
180 
181         if (averageOpacity > opacity) {
182             fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
183         } else {
184             fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
185         }
186 
187         if (flow == 1.0) {
188             dst[alpha_pos] = fullFlowAlpha;
189         } else {
190             float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
191             dst[alpha_pos] = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
192         }
193     }
194 };
195 
196 /**
197  * An optimized version of a composite op for the use in 16 byte
198  * colorspaces with alpha channel placed at the last byte of
199  * the pixel: C1_C2_C3_A.
200  */
201 template<Vc::Implementation _impl, typename ParamsWrapper>
202 class KoOptimizedCompositeOpAlphaDarken128Impl : public KoCompositeOp
203 {
204 public:
KoOptimizedCompositeOpAlphaDarken128Impl(const KoColorSpace * cs)205     KoOptimizedCompositeOpAlphaDarken128Impl(const KoColorSpace* cs)
206         : KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
207 
208     using KoCompositeOp::composite;
209 
composite(const KoCompositeOp::ParameterInfo & params)210     virtual void composite(const KoCompositeOp::ParameterInfo& params) const
211     {
212         if(params.maskRowStart) {
213             KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params);
214         } else {
215             KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params);
216         }
217     }
218 };
219 
220 template<Vc::Implementation _impl>
221 class KoOptimizedCompositeOpAlphaDarkenHard128
222     : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>
223 {
224 public:
KoOptimizedCompositeOpAlphaDarkenHard128(const KoColorSpace * cs)225     KoOptimizedCompositeOpAlphaDarkenHard128(const KoColorSpace* cs)
226         : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {}
227 };
228 
229 template<Vc::Implementation _impl>
230 class KoOptimizedCompositeOpAlphaDarkenCreamy128
231     : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>
232 {
233 public:
KoOptimizedCompositeOpAlphaDarkenCreamy128(const KoColorSpace * cs)234     KoOptimizedCompositeOpAlphaDarkenCreamy128(const KoColorSpace* cs)
235         : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {}
236 };
237 
238 #endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
239