Commit 665dcfd0 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Revert "Revert "Merge branch 'krita-testing-kazakov'""

This reverts commit 224877d25720d6582821d2e0d13d0142717983b1.

Now we are ready to have it again ;)
parent c5a9e3f7
......@@ -349,7 +349,7 @@ void benchmarkCompositeOp(const KoCompositeOp *op, const QString &postfix)
#ifdef HAVE_VC
template<class Compositor>
void checkRounding()
void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1)
{
QVector<Tile> tiles =
generateTiles(2, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
......@@ -366,11 +366,29 @@ void checkRounding()
quint8 *dst2 = tiles[1].dst;
quint8 *msk2 = tiles[1].mask;
KoCompositeOp::ParameterInfo params;
params.opacity = opacity;
params.flow = flow;
if (averageOpacity >= 0.0) {
params._lastOpacityData = averageOpacity;
params.lastOpacity = &params._lastOpacityData;
}
params.channelFlags = QBitArray();
typename Compositor::OptionalParams optionalParams(params);
for (int i = 0; i < numBlocks; i++) {
Compositor::template compositeVector<true,true, VC_IMPL>(src1, dst1, msk1, 0.5, 0.3);
Compositor::template compositeVector<true,true, VC_IMPL>(src1, dst1, msk1, params.opacity, optionalParams);
for (int j = 0; j < vecSize; j++) {
Compositor::template compositeOnePixelScalar<true, VC_IMPL>(src2, dst2, msk2, 0.5, 0.3, QBitArray());
//if (8 * i + j == 7080) {
// qDebug() << "src: " << src2[0] << src2[1] << src2[2] << src2[3];
// qDebug() << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
// qDebug() << "msk:" << msk2[0];
//}
Compositor::template compositeOnePixelScalar<true, VC_IMPL>(src2, dst2, msk2, params.opacity, optionalParams);
if(!comparePixels(dst1, dst2, 0)) {
qDebug() << "Wrong rounding in pixel:" << 8 * i + j;
......@@ -398,17 +416,45 @@ void checkRounding()
#endif
void KisCompositionBenchmark::checkRoundingAlphaDarken()
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_03()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_05()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.5);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_07()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.7);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10_08()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >();
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0,0.8);
#endif
}
void KisCompositionBenchmark::checkRoundingOver()
{
#ifdef HAVE_VC
checkRounding<OverCompositor32<quint8, quint32, false, true> >();
checkRounding<OverCompositor32<quint8, quint32, false, true> >(0.5, 0.3);
#endif
}
......
......@@ -25,7 +25,12 @@ class KisCompositionBenchmark : public QObject
{
Q_OBJECT
private slots:
void checkRoundingAlphaDarken();
void checkRoundingAlphaDarken_05_03();
void checkRoundingAlphaDarken_05_05();
void checkRoundingAlphaDarken_05_07();
void checkRoundingAlphaDarken_05_10();
void checkRoundingAlphaDarken_05_10_08();
void checkRoundingOver();
void compareAlphaDarkenOps();
......
......@@ -162,8 +162,7 @@ void KisPainter::init()
d->maskImageHeight = 255;
d->mirrorHorizontaly = false;
d->mirrorVerticaly = false;
d->paramInfo.opacity = 1.0f;
d->paramInfo.flow = 1.0f;
d->paramInfo = KoCompositeOp::ParameterInfo();
d->renderingIntent = KoColorConversionTransformation::InternalRenderingIntent;
d->conversionFlags = KoColorConversionTransformation::InternalConversionFlags;
}
......@@ -2354,6 +2353,11 @@ quint8 KisPainter::flow() const
return quint8(d->paramInfo.flow * 255.0f);
}
void KisPainter::setOpacityUpdateAverage(quint8 opacity)
{
d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f);
}
void KisPainter::setOpacity(quint8 opacity)
{
d->paramInfo.opacity = float(opacity) / 255.0f;
......
......@@ -677,6 +677,13 @@ public:
quint8 flow() const;
/**
* Sets the opacity of the painting and recalculates the
* mean opacity of the stroke. This mean value is used to
* make ALPHA_DARKEN painting look correct
*/
void setOpacityUpdateAverage(quint8 opacity);
/// Set the opacity which is used in painting (like filling polygons)
void setOpacity(quint8 opacity);
......
......@@ -82,9 +82,9 @@ void KisFlowOpacityOption::setOpacity(qreal opacity)
void KisFlowOpacityOption::apply(KisPainter* painter, const KisPaintInformation& info)
{
if(m_paintActionType == WASH && m_nodeHasIndirectPaintingSupport)
painter->setOpacity(quint8(getDynamicOpacity(info) * 255.0));
painter->setOpacityUpdateAverage(quint8(getDynamicOpacity(info) * 255.0));
else
painter->setOpacity(quint8(getStaticOpacity() * getDynamicOpacity(info) * 255.0));
painter->setOpacityUpdateAverage(quint8(getStaticOpacity() * getDynamicOpacity(info) * 255.0));
painter->setFlow(quint8(getFlow() * 255.0));
}
......@@ -63,7 +63,7 @@ quint8 KisPressureOpacityOption::apply(KisPainter * painter, const KisPaintInfor
qreal opacity = (qreal)(origOpacity * computeValue(info));
quint8 opacity2 = (quint8)qRound(qBound<qreal>(OPACITY_TRANSPARENT_U8, opacity, OPACITY_OPAQUE_U8));
painter->setOpacity(opacity2);
painter->setOpacityUpdateAverage(opacity2);
return origOpacity;
}
......
......@@ -42,6 +42,19 @@ QString KoCompositeOp::categoryHSV() { return i18n("HSV"); }
QString KoCompositeOp::categoryMix() { return i18n("Mix"); }
QString KoCompositeOp::categoryMisc() { return i18n("Misc"); }
void KoCompositeOp::ParameterInfo::updateOpacityAndAverage(float value) {
const float exponent = 0.1;
opacity = value;
if (*lastOpacity < opacity) {
lastOpacity = &opacity;
} else {
_lastOpacityData = exponent * opacity + (1.0 - exponent) * (*lastOpacity);
lastOpacity = &_lastOpacityData;
}
}
struct KoCompositeOp::Private {
const KoColorSpace * colorSpace;
QString id;
......
......@@ -54,8 +54,15 @@ public:
static QString categoryMix();
static QString categoryMisc();
struct ParameterInfo
struct PIGMENTCMS_EXPORT ParameterInfo
{
ParameterInfo()
: opacity(1.0f),
flow(1.0f),
lastOpacity(&opacity)
{
}
quint8* dstRowStart;
qint32 dstRowStride;
const quint8* srcRowStart;
......@@ -66,7 +73,11 @@ public:
qint32 cols;
float opacity;
float flow;
float _lastOpacityData;
float* lastOpacity;
QBitArray channelFlags;
void updateOpacityAndAverage(float value);
};
public:
......
......@@ -87,9 +87,24 @@ public:
}
if(alpha_pos != -1) {
channels_type alpha1 = unionShapeOpacity(srcAlpha, dstAlpha); // alpha with 0% flow
channels_type alpha2 = (opacity > dstAlpha) ? lerp(dstAlpha, opacity, mskAlpha) : dstAlpha; // alpha with 100% flow
dst[alpha_pos] = lerp(alpha1, alpha2, flow);
channels_type fullFlowAlpha;
channels_type averageOpacity = mul(flow, scale<channels_type>(*params.lastOpacity));
if (averageOpacity > opacity) {
channels_type reverseBlend = KoColorSpaceMaths<channels_type>::divide(dstAlpha, averageOpacity);
fullFlowAlpha = averageOpacity > dstAlpha ? lerp(srcAlpha, averageOpacity, reverseBlend) : dstAlpha;
} else {
fullFlowAlpha = opacity > dstAlpha ? lerp(dstAlpha, opacity, mskAlpha) : dstAlpha;
}
if (params.flow == 1.0) {
dstAlpha = fullFlowAlpha;
} else {
channels_type zeroFlowAlpha = unionShapeOpacity(srcAlpha, dstAlpha);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
}
dst[alpha_pos] = dstAlpha;
}
src += srcInc;
......
......@@ -27,6 +27,18 @@
template<typename channels_type, typename pixel_type>
struct AlphaDarkenCompositor32 {
struct OptionalParams {
OptionalParams(const KoCompositeOp::ParameterInfo& params)
: flow(params.flow),
averageOpacity(*params.lastOpacity * params.flow),
premultipliedOpacity(params.opacity * params.flow)
{
}
float flow;
float averageOpacity;
float premultipliedOpacity;
};
/**
* This is a vector equivalent of compositeOnePixelScalar(). It is considered
* to process Vc::float_v::Size pixels in a single pass.
......@@ -40,16 +52,15 @@ struct AlphaDarkenCompositor32 {
* of a streaming vector. Unaligned writes are really expensive.
* 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, float flow)
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
{
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
Vc::float_v opacity_vec(255.0 * opacity * flow);
Vc::float_v flow_norm_vec(flow);
Vc::float_v opacity_vec(255.0 * oparams.premultipliedOpacity);
Vc::float_v average_opacity_vec(255.0 * oparams.averageOpacity);
Vc::float_v flow_norm_vec(oparams.flow);
Vc::float_v uint8MaxRec2((float)1.0 / (255.0 * 255.0));
......@@ -124,15 +135,38 @@ struct AlphaDarkenCompositor32 {
dst_c3(not_empty_dst_pixels_mask) = dst_blend * (src_c3 - dst_c3) + dst_c3;
}
Vc::float_v alpha1 = src_alpha + dst_alpha -
dst_blend * dst_alpha;
Vc::float_v fullFlowAlpha;
if (oparams.averageOpacity > opacity) {
Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
if (fullFlowAlpha_mask.isEmpty()) {
fullFlowAlpha = dst_alpha;
} else {
Vc::float_v reverse_blend = dst_alpha / average_opacity_vec;
Vc::float_v opt1 = (average_opacity_vec - src_alpha) * reverse_blend + src_alpha;
fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = opt1;
}
} else {
Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
if (fullFlowAlpha_mask.isEmpty()) {
fullFlowAlpha = dst_alpha;
} else {
Vc::float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = opt1;
}
}
Vc::float_m alpha2_mask = opacity_vec > dst_alpha;
Vc::float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
Vc::float_v alpha2;
alpha2(!alpha2_mask) = dst_alpha;
alpha2(alpha2_mask) = opt1;
dst_alpha = (alpha2 - alpha1) * flow_norm_vec + alpha1;
if (oparams.flow == 1.0) {
dst_alpha = fullFlowAlpha;
} else {
Vc::float_v zeroFlowAlpha = src_alpha + dst_alpha -
dst_blend * dst_alpha;
dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
}
KoStreamedMath<_impl>::write_channels_32(dst, dst_alpha, dst_c1, dst_c2, dst_c3);
}
......@@ -141,10 +175,8 @@ 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, float flow, const QBitArray &channelFlags)
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
{
Q_UNUSED(channelFlags);
using namespace Arithmetic;
const qint32 alpha_pos = 3;
......@@ -157,11 +189,7 @@ struct AlphaDarkenCompositor32 {
float srcAlphaNorm;
float mskAlphaNorm;
/**
* FIXME: precalculate this value on a higher level for
* not doing it on every cycle
*/
opacity *= flow;
opacity = oparams.premultipliedOpacity;
if (haveMask) {
mskAlphaNorm = float(*mask) * uint8Rec2 * src[alpha_pos];
......@@ -181,9 +209,28 @@ struct AlphaDarkenCompositor32 {
*d = *s;
}
float alpha1 = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm); // alpha with 0% flow
float alpha2 = (opacity > dstAlphaNorm) ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm; // alpha with 100% flow
dst[alpha_pos] = quint8(lerp(alpha1, alpha2, flow) * uint8Max);
float flow = oparams.flow;
float averageOpacity = oparams.averageOpacity;
float fullFlowAlpha;
if (averageOpacity > opacity) {
fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
} else {
fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
}
float dstAlpha;
if (flow == 1.0) {
dstAlpha = fullFlowAlpha * uint8Max;
} else {
float zeroFlowAlpha = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow) * uint8Max;
}
dst[alpha_pos] = quint8(dstAlpha);
}
};
......
......@@ -28,13 +28,19 @@
template<typename channels_type, typename pixel_type, bool alphaLocked, bool allChannelsFlag>
struct OverCompositor32 {
// \see docs in AlphaDarkenCompositor32
struct OptionalParams {
OptionalParams(const KoCompositeOp::ParameterInfo& params)
: channelFlags(params.channelFlags)
{
}
const QBitArray &channelFlags;
};
// \see docs in AlphaDarkenCompositor32
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, float flow)
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
{
Q_UNUSED(flow);
Q_UNUSED(oparams);
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
......@@ -118,10 +124,8 @@ struct OverCompositor32 {
}
template <bool haveMask, Vc::Implementation _impl>
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, float flow, const QBitArray &channelFlags)
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
{
Q_UNUSED(flow);
using namespace Arithmetic;
const qint32 alpha_pos = 3;
......@@ -172,6 +176,8 @@ struct OverCompositor32 {
dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm);
}
} else {
const QBitArray &channelFlags = oparams.channelFlags;
if (srcBlendNorm == 1.0) {
if(channelFlags.at(0)) dst[0] = src[0];
if(channelFlags.at(1)) dst[1] = src[1];
......
......@@ -51,6 +51,7 @@ template<bool useMask, bool useFlow, class Compositor>
quint8* dstRowStart = params.dstRowStart;
const quint8* maskRowStart = params.maskRowStart;
const quint8* srcRowStart = params.srcRowStart;
typename Compositor::OptionalParams optionalParams(params);
for(quint32 r=params.rows; r>0; --r) {
const quint8 *mask = maskRowStart;
......@@ -60,7 +61,7 @@ template<bool useMask, bool useFlow, class Compositor>
int blockRest = params.cols;
for(int i = 0; i < blockRest; i++) {
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, params.flow, params.channelFlags);
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
src += srcLinearInc;
dst += linearInc;
......@@ -197,6 +198,7 @@ template<bool useMask, bool useFlow, class Compositor>
quint8* dstRowStart = params.dstRowStart;
const quint8* maskRowStart = params.maskRowStart;
const quint8* srcRowStart = params.srcRowStart;
typename Compositor::OptionalParams optionalParams(params);
if (!params.srcRowStride) {
quint32 *buf = Vc::malloc<quint32, Vc::AlignOnVector>(vectorSize);
......@@ -244,7 +246,7 @@ template<bool useMask, bool useFlow, class Compositor>
}
for(int i = 0; i < blockAlign; i++) {
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, params.flow, params.channelFlags);
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
src += srcLinearInc;
dst += linearInc;
......@@ -254,7 +256,7 @@ template<bool useMask, bool useFlow, class Compositor>
}
for (int i = 0; i < blockAlignedVector; i++) {
Compositor::template compositeVector<useMask, true, _impl>(src, dst, mask, params.opacity, params.flow);
Compositor::template compositeVector<useMask, true, _impl>(src, dst, mask, params.opacity, optionalParams);
src += srcVectorInc;
dst += vectorInc;
......@@ -264,7 +266,7 @@ template<bool useMask, bool useFlow, class Compositor>
}
for (int i = 0; i < blockUnalignedVector; i++) {
Compositor::template compositeVector<useMask, false, _impl>(src, dst, mask, params.opacity, params.flow);
Compositor::template compositeVector<useMask, false, _impl>(src, dst, mask, params.opacity, optionalParams);
src += srcVectorInc;
dst += vectorInc;
......@@ -275,7 +277,7 @@ template<bool useMask, bool useFlow, class Compositor>
for(int i = 0; i < blockRest; i++) {
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, params.flow, params.channelFlags);
Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
src += srcLinearInc;
dst += linearInc;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment