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