Commit 52d75243 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Implement selection of Flow mode: Creamy (new) and Hard (old)

The option is global and located at:

Preferences->General->Tools->Flow Mode

After changing the option value, Krita should be restarted.
parent d4a0c1f9
......@@ -494,12 +494,12 @@ void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1, quint32
}
params.channelFlags = QBitArray();
typename Compositor::OptionalParams optionalParams(params);
typename Compositor::ParamsWrapper paramsWrapper(params);
// The error count is needed as 38.5 gets rounded to 38 instead of 39 in the vc version.
int errorcount = 0;
for (int i = 0; i < numBlocks; i++) {
Compositor::template compositeVector<true,true, Vc::CurrentImplementation::current()>(src1, dst1, msk1, params.opacity, optionalParams);
Compositor::template compositeVector<true,true, Vc::CurrentImplementation::current()>(src1, dst1, msk1, params.opacity, paramsWrapper);
for (int j = 0; j < vecSize; j++) {
//if (8 * i + j == 7080) {
......@@ -508,7 +508,7 @@ void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1, quint32
// dbgKrita << "msk:" << msk2[0];
//}
Compositor::template compositeOnePixelScalar<true, Vc::CurrentImplementation::current()>(src2, dst2, msk2, params.opacity, optionalParams);
Compositor::template compositeOnePixelScalar<true, Vc::CurrentImplementation::current()>(src2, dst2, msk2, params.opacity, paramsWrapper);
bool compareResult = true;
if (pixelSize == 4) {
......@@ -642,7 +642,7 @@ void KisCompositionBenchmark::compareAlphaDarkenOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
......@@ -654,7 +654,7 @@ void KisCompositionBenchmark::compareRgbF32AlphaDarkenOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
......@@ -666,7 +666,7 @@ void KisCompositionBenchmark::compareAlphaDarkenOpsNoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
......@@ -713,7 +713,7 @@ void KisCompositionBenchmark::compareRgbF32OverOps()
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
......@@ -745,7 +745,7 @@ void KisCompositionBenchmark::testRgb8CompositeOverOptimized()
void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
......
......@@ -72,6 +72,7 @@ set(kritapigment_SRCS
colorspaces/KoSimpleColorSpaceEngine.cpp
compositeops/KoOptimizedCompositeOpFactory.cpp
compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
compositeops/KoAlphaDarkenParamsWrapper.cpp
${__per_arch_factory_objs}
colorprofiles/KoDummyColorProfile.cpp
resources/KoAbstractGradient.cpp
......
......@@ -30,8 +30,8 @@
const int TILE_WIDTH = 64;
const int TILE_HEIGHT = 64;
const int IMG_WIDTH = 4096;
const int IMG_HEIGHT = 4096;
const int IMG_WIDTH = 2048;
const int IMG_HEIGHT = 2048;
const quint8 OPACITY_HALF = 128;
......@@ -41,10 +41,12 @@ const int TILES_IN_HEIGHT = IMG_HEIGHT / TILE_HEIGHT;
#define COMPOSITE_BENCHMARK \
for (int y = 0; y < TILES_IN_HEIGHT; y++){ \
for (int x = 0; x < TILES_IN_WIDTH; x++){ \
compositeOp->composite(m_dstBuffer, TILE_WIDTH * KoBgrU16Traits::pixelSize, \
m_srcBuffer, TILE_WIDTH * KoBgrU16Traits::pixelSize, \
0, 0, \
for (int x = 0; x < TILES_IN_WIDTH; x++) { \
const int rowStride = IMG_WIDTH * KoBgrU8Traits::pixelSize; \
const int bufOffset = y * rowStride + x * TILE_WIDTH * KoBgrU8Traits::pixelSize; \
compositeOp->composite(m_dstBuffer + bufOffset, rowStride, \
m_srcBuffer + bufOffset, rowStride, \
m_mskBuffer + bufOffset, rowStride, \
TILE_WIDTH, TILE_HEIGHT, \
OPACITY_HALF); \
} \
......@@ -52,15 +54,25 @@ const int TILES_IN_HEIGHT = IMG_HEIGHT / TILE_HEIGHT;
void KoCompositeOpsBenchmark::initTestCase()
{
m_dstBuffer = new quint8[ TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize ];
m_srcBuffer = new quint8[ TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize ];
const int bufLen = IMG_HEIGHT * IMG_WIDTH * KoBgrU8Traits::pixelSize;
m_dstBuffer = new quint8[bufLen];
m_srcBuffer = new quint8[bufLen];
m_mskBuffer = new quint8[bufLen];
}
// this is called before every benchmark
void KoCompositeOpsBenchmark::init()
{
memset(m_dstBuffer, 42 , TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize);
memset(m_srcBuffer, 42 , TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize);
qsrand(42);
for (int i = 0; i < int(IMG_WIDTH * IMG_HEIGHT * KoBgrU8Traits::pixelSize); i++) {
const int randVal = qrand();
m_srcBuffer[i] = randVal & 0x0000FF;
m_dstBuffer[i] = (randVal & 0x00FF000) >> 8;
m_mskBuffer[i] = (randVal & 0xFF0000) >> 16;
}
}
......@@ -68,20 +80,29 @@ void KoCompositeOpsBenchmark::cleanupTestCase()
{
delete [] m_dstBuffer;
delete [] m_srcBuffer;
delete [] m_mskBuffer;
}
void KoCompositeOpsBenchmark::benchmarkCompositeOver()
{
KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createOverOp32(KoColorSpaceRegistry::instance()->rgb16());
KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createOverOp32(KoColorSpaceRegistry::instance()->rgb8());
QBENCHMARK{
COMPOSITE_BENCHMARK
}
}
void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarken()
void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarkenHard()
{
KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(KoColorSpaceRegistry::instance()->rgb8());
QBENCHMARK{
COMPOSITE_BENCHMARK
}
}
void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarkenCreamy()
{
//KoCompositeOpAlphaDarken<KoBgrU16Traits> compositeOp(0);
KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(KoColorSpaceRegistry::instance()->rgb16());
KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(KoColorSpaceRegistry::instance()->rgb8());
QBENCHMARK{
COMPOSITE_BENCHMARK
}
......
......@@ -31,11 +31,13 @@ private Q_SLOTS:
void cleanupTestCase();
void benchmarkCompositeOver();
void benchmarkCompositeAlphaDarken();
void benchmarkCompositeAlphaDarkenHard();
void benchmarkCompositeAlphaDarkenCreamy();
private:
quint8 * m_dstBuffer;
quint8 * m_srcBuffer;
quint8 * m_mskBuffer;
};
......
......@@ -35,6 +35,7 @@
#include "KoCompositeOpCopy2.h"
#include "KoCompositeOpAlphaDarken.h"
#include "KoCompositeOpBase.h"
#include "KoCompositeOps.h"
#include <colorprofiles/KoDummyColorProfile.h>
namespace {
......@@ -91,7 +92,7 @@ KoAlphaColorSpaceImpl<_CSTrait>::KoAlphaColorSpaceImpl()
m_compositeOps << new KoCompositeOpOver<_CSTrait>(this)
<< new KoCompositeOpErase<_CSTrait>(this)
<< new KoCompositeOpCopy2<_CSTrait>(this)
<< new KoCompositeOpAlphaDarken<_CSTrait>(this)
<< createAlphaDarkenCompositeOp<_CSTrait>(this)
<< new AlphaColorSpaceMultiplyOp<_CSTrait>(this);
Q_FOREACH (KoCompositeOp *op, m_compositeOps) {
......
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KoAlphaDarkenParamsWrapper.h"
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include "kis_debug.h"
bool useCreamyAlphaDarken()
{
static bool isConfigInitialized = false;
static bool useCreamyAlphaDarken = true;
if (!isConfigInitialized) {
KConfigGroup cfg = KSharedConfig::openConfig()->group("");
useCreamyAlphaDarken = cfg.readEntry("useCreamyAlphaDarken", true);
isConfigInitialized = true;
}
if (!useCreamyAlphaDarken) {
qInfo() << "INFO: requested old version of AlphaDarken composite op. Switching...";
}
return useCreamyAlphaDarken;
}
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KOALPHADARKENPARAMSWRAPPER_H
#define KOALPHADARKENPARAMSWRAPPER_H
#include <KoCompositeOp.h>
#include "KoColorSpaceMaths.h"
bool KRITAPIGMENT_EXPORT useCreamyAlphaDarken();
struct KoAlphaDarkenParamsWrapperHard {
KoAlphaDarkenParamsWrapperHard(const KoCompositeOp::ParameterInfo& params)
: opacity(params.flow * params.opacity),
flow(params.flow),
averageOpacity(params.flow * (*params.lastOpacity))
{
}
float opacity;
float flow;
float averageOpacity;
template <typename T>
static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha, T normCoeff) {
return srcAlpha + dstAlpha - srcAlpha * dstAlpha * normCoeff;
}
template <typename T>
static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha) {
return srcAlpha + dstAlpha - srcAlpha * dstAlpha;
}
template<typename channels_type>
static inline channels_type calculateZeroFlowAlphaLegacy(channels_type srcAlpha, channels_type dstAlpha) {
return Arithmetic::unionShapeOpacity(srcAlpha, dstAlpha);
}
};
struct KoAlphaDarkenParamsWrapperCreamy {
KoAlphaDarkenParamsWrapperCreamy(const KoCompositeOp::ParameterInfo& params)
: opacity(params.opacity),
flow(params.flow),
averageOpacity(*params.lastOpacity)
{
}
float opacity;
float flow;
float averageOpacity;
template <typename T>
static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha, T normCoeff) {
Q_UNUSED(srcAlpha);
Q_UNUSED(normCoeff);
return dstAlpha;
}
template <typename T>
static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha) {
Q_UNUSED(srcAlpha);
return dstAlpha;
}
template<typename channels_type>
static inline channels_type calculateZeroFlowAlphaLegacy(channels_type srcAlpha, channels_type dstAlpha) {
Q_UNUSED(srcAlpha);
return dstAlpha;
}
};
#endif // KOALPHADARKENPARAMSWRAPPER_H
......@@ -21,14 +21,14 @@
#ifndef KOCOMPOSITEOPALPHADARKEN_H_
#define KOCOMPOSITEOPALPHADARKEN_H_
#include "KoCompositeOpFunctions.h"
#include "KoColorSpaceMaths.h"
#include "KoCompositeOpBase.h"
#include <KoCompositeOpRegistry.h>
/**
* A template version of the alphadarken composite operation to use in colorspaces
*/
template<class Traits>
template<class Traits, class ParamsWrapper>
class KoCompositeOpAlphaDarken: public KoCompositeOp
{
typedef typename Traits::channels_type channels_type;
......@@ -55,9 +55,11 @@ public:
{
using namespace Arithmetic;
ParamsWrapper paramsWrapper(params);
qint32 srcInc = (params.srcRowStride == 0) ? 0 : channels_nb;
channels_type flow = scale<channels_type>(params.flow);
channels_type opacity = scale<channels_type>(params.opacity);
channels_type flow = scale<channels_type>(paramsWrapper.flow);
channels_type opacity = scale<channels_type>(paramsWrapper.opacity);
quint8* dstRowStart = params.dstRowStart;
const quint8* srcRowStart = params.srcRowStart;
const quint8* maskRowStart = params.maskRowStart;
......@@ -89,9 +91,26 @@ public:
if(alpha_pos != -1) {
channels_type fullFlowAlpha;
channels_type averageOpacity = scale<channels_type>(*params.lastOpacity);
channels_type averageOpacity = scale<channels_type>(paramsWrapper.averageOpacity);
/**
* Here we calculate fullFlowAlpha, which shuold strive either to
* averageOpacity or opacity (whichever is the greater) or just keep old dstAlpha
* value, if both opacity values are not bit enough
*/
if (averageOpacity > opacity) {
/**
* This crypty code is basically an optimized version of the folowing:
* fullFlowAlpha = averageOpacity *
* unionShapeOpacity(srcAlpha / averageOpacity,
* dstAlpha / averageOpacity);
*
* The main idea is: fullFlowAlpha should be as near to averageOpacity as
* maximum of srcAlpha and dstAlpha and a bit more. So that in consequent
* applications of the blending operation alpha channel would aim to
* averageOpacity.
*/
channels_type reverseBlend = KoColorSpaceMaths<channels_type>::divide(dstAlpha, averageOpacity);
fullFlowAlpha = averageOpacity > dstAlpha ? lerp(srcAlpha, averageOpacity, reverseBlend) : dstAlpha;
} else {
......@@ -101,10 +120,8 @@ public:
if (params.flow == 1.0) {
dstAlpha = fullFlowAlpha;
} else {
channels_type zeroFlowAlpha = dstAlpha;
channels_type zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlphaLegacy(srcAlpha, dstAlpha);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
}
dst[alpha_pos] = dstAlpha;
......
......@@ -38,7 +38,7 @@
#include "compositeops/KoCompositeOpDestinationIn.h"
#include "compositeops/KoCompositeOpDestinationAtop.h"
#include "compositeops/KoCompositeOpGreater.h"
#include "compositeops/KoAlphaDarkenParamsWrapper.h"
#include "KoOptimizedCompositeOpFactory.h"
namespace _Private {
......@@ -53,7 +53,11 @@ template<class Traits>
struct OptimizedOpsSelector
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return new KoCompositeOpAlphaDarken<Traits>(cs);
if (useCreamyAlphaDarken()) {
return new KoCompositeOpAlphaDarken<Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
} else {
return new KoCompositeOpAlphaDarken<Traits, KoAlphaDarkenParamsWrapperHard>(cs);
}
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return new KoCompositeOpOver<Traits>(cs);
......@@ -64,7 +68,9 @@ template<>
struct OptimizedOpsSelector<KoBgrU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
return useCreamyAlphaDarken() ?
KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(cs) :
KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
......@@ -75,7 +81,9 @@ template<>
struct OptimizedOpsSelector<KoLabU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
return useCreamyAlphaDarken() ?
KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(cs) :
KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
......@@ -86,7 +94,9 @@ template<>
struct OptimizedOpsSelector<KoRgbF32Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
return useCreamyAlphaDarken() ?
KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy128(cs) :
KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard128(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp128(cs);
......@@ -251,4 +261,10 @@ void addStandardCompositeOps(KoColorSpace* cs)
_Private::AddRGBOps <_Traits_, useRGBOps >::add(cs);
}
template<class _Traits_>
KoCompositeOp* createAlphaDarkenCompositeOp(const KoColorSpace *cs)
{
return _Private::OptimizedOpsSelector<_Traits_>::createAlphaDarkenOp(cs);
}
#endif
......@@ -23,18 +23,11 @@
#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include "KoStreamedMath.h"
#include "KoAlphaDarkenParamsWrapper.h"
template<typename channels_type, typename pixel_type>
template<typename channels_type, typename pixel_type, typename _ParamsWrapper>
struct AlphaDarkenCompositor128 {
struct OptionalParams {
OptionalParams(const KoCompositeOp::ParameterInfo& params)
: flow(params.flow)
, averageOpacity(*params.lastOpacity)
{
}
float flow;
float averageOpacity;
};
using ParamsWrapper = _ParamsWrapper;
struct Pixel {
channels_type red;
......@@ -57,7 +50,7 @@ struct AlphaDarkenCompositor128 {
* o This function is *never* used if HAVE_VC is not present
*/
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
const Pixel *sp = reinterpret_cast<const Pixel*>(src);
Pixel *dp = reinterpret_cast<Pixel*>(dst);
......@@ -81,6 +74,11 @@ struct AlphaDarkenCompositor128 {
msk_norm_alpha = src_alpha;
}
// we don't use directly passed value
Q_UNUSED(opacity);
// instead we use value calculated by ParamsWrapper
opacity = oparams.opacity;
Vc::float_v opacity_vec(opacity);
src_alpha = msk_norm_alpha * opacity_vec;
......@@ -135,7 +133,7 @@ struct AlphaDarkenCompositor128 {
dst_alpha = fullFlowAlpha;
}
else {
Vc::float_v zeroFlowAlpha = dst_alpha;
Vc::float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha);
Vc::float_v flow_norm_vec(oparams.flow);
dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
}
......@@ -146,7 +144,7 @@ struct AlphaDarkenCompositor128 {
* Composes one pixel of the source into the destination
*/
template <bool haveMask, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const OptionalParams &oparams)
static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
......@@ -159,6 +157,9 @@ struct AlphaDarkenCompositor128 {
const float uint8Rec1 = 1.0 / 255.0;
float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos];
Q_UNUSED(opacity);
opacity = oparams.opacity;
float srcAlphaNorm = mskAlphaNorm * opacity;
if (dstAlphaNorm != 0) {
......@@ -185,7 +186,7 @@ struct AlphaDarkenCompositor128 {
if (flow == 1.0) {
dst[alpha_pos] = fullFlowAlpha;
} else {
float zeroFlowAlpha = dstAlphaNorm;
float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
dst[alpha_pos] = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
}
}
......@@ -196,11 +197,11 @@ struct AlphaDarkenCompositor128 {
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpAlphaDarken128 : public KoCompositeOp
template<Vc::Implementation _impl, typename ParamsWrapper>
class KoOptimizedCompositeOpAlphaDarken128Impl : public KoCompositeOp
{
public:
KoOptimizedCompositeOpAlphaDarken128(const KoColorSpace* cs)
KoOptimizedCompositeOpAlphaDarken128Impl(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
......@@ -208,11 +209,27 @@ public:
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, quint32> >(params);
KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, quint32, ParamsWrapper> >(params);
} else {
KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, quint32> >(params);
KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, quint32, ParamsWrapper> >(params);
}
}
};
template<Vc::Implementation _impl>
struct KoOptimizedCompositeOpAlphaDarkenHard128
: public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>
{
KoOptimizedCompositeOpAlphaDarkenHard128(const KoColorSpace* cs)
: KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {}
};
template<Vc::Implementation _impl>
struct KoOptimizedCompositeOpAlphaDarkenCreamy128
: public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>
{
KoOptimizedCompositeOpAlphaDarkenCreamy128(const KoColorSpace* cs)
: KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {}
};
#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
......@@ -25,18 +25,11 @@
#include "KoCompositeOpRegistry.h"
#include <klocalizedstring.h>
#include "KoStreamedMath.h"
#include <KoAlphaDarkenParamsWrapper.h>
template<typename channels_type, typename pixel_type>
template<typename channels_type, typename pixel_type, typename _ParamsWrapper>
struct AlphaDarkenCompositor32 {
struct OptionalParams {
OptionalParams(const KoCompositeOp::ParameterInfo& params)
: flow(params.flow),
averageOpacity(*params.lastOpacity)
{
}
float flow;
float averageOpacity;
};
using ParamsWrapper = _ParamsWrapper;
/**
* This is a vector equivalent of compositeOnePixelScalar(). It is considered
......@@ -52,11 +45,16 @@ struct AlphaDarkenCompositor32 {
* o This function is *never* used if HAVE_VC is not present
*/
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
// we don't use directly passed value
Q_UNUSED(opacity);
// instead we use value calculated by ParamsWrapper
opacity = oparams.opacity;
Vc::float_v opacity_vec(255.0 * opacity);
Vc::float_v average_opacity_vec(255.0 * oparams.averageOpacity);
......@@ -163,7 +161,7 @@ struct AlphaDarkenCompositor32 {
if (oparams.flow == 1.0) {
dst_alpha = fullFlowAlpha;
} else {
Vc::float_v zeroFlowAlpha = dst_alpha;
Vc::float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha, uint8MaxRec1);
dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
}
......@@ -174,7 +172,7 @@ struct AlphaDarkenCompositor32 {
* Composes one pixel of the source into the destination
*/
template <bool haveMask, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
......@@ -188,6 +186,9 @@ struct AlphaDarkenCompositor32 {
float srcAlphaNorm;
float mskAlphaNorm;
Q_UNUSED(opacity);
opacity = oparams.opacity;
if (haveMask) {
mskAlphaNorm = float(*mask) * uint8Rec2 * src[alpha_pos];
srcAlphaNorm = mskAlphaNorm * opacity;
......@@ -223,7 +224,7 @@ struct AlphaDarkenCompositor32 {
if (flow == 1.0) {
dstAlpha = fullFlowAlpha * uint8Max;
} else {
float zeroFlowAlpha = dstAlphaNorm;
float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow) * uint8Max;
}
......@@ -236,11 +237,11 @@ struct AlphaDarkenCompositor32 {
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpAlphaDarken32 : public KoCompositeOp
template<Vc::Implementation _impl, class ParamsWrapper>
class KoOptimizedCompositeOpAlphaDarken32Impl : public KoCompositeOp
{
public:
KoOptimizedCompositeOpAlphaDarken32(const KoColorSpace* cs)
KoOptimizedCompositeOpAlphaDarken32Impl(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
......@@ -248,11 +249,30 @@ public:
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
KoStreamedMath<_impl>::template genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
KoStreamedMath<_impl>::template genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
} else {
KoStreamedMath<_impl>::template genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
KoStreamedMath<_impl>::template genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
}
}
};
template<Vc::Implementation _impl>
struct KoOptimizedCompositeOpAlphaDarkenHard32 :
public KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperHard>
{
KoOptimizedCompositeOpAlphaDarkenHard32(const KoColorSpace *cs)
: KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {
}