Commit 888070bb authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Greater-alpha blend mode

This adds a blend mode that uses a sigmoid blending function
to take the greater alpha of either the current layer or the brush.
The result of this is that making multiple passes across the
same area but with different strokes will not further increase
the opacity past the brush setting or stylus pressure. This
can help with consistently shading a region across multiple
separate strokes without creating spurious or accidental
sharp edges due to edge-overlaps with existing brush strokes.

The patch was created by Nicholas Guttenberg

Differential revision:
parent 54ae7585
......@@ -78,6 +78,7 @@ KoCompositeOpRegistry::KoCompositeOpRegistry()
m_map.insert(m_categories[4], KoID(COMPOSITE_OVER , i18n("Normal")));
m_map.insert(m_categories[4], KoID(COMPOSITE_BEHIND , i18n("Behind")));
m_map.insert(m_categories[4], KoID(COMPOSITE_GREATER , i18n("Greater")));
m_map.insert(m_categories[4], KoID(COMPOSITE_OVERLAY , i18n("Overlay")));
m_map.insert(m_categories[4], KoID(COMPOSITE_ERASE , i18n("Erase")));
m_map.insert(m_categories[4], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken")));
......@@ -61,6 +61,7 @@ const QString COMPOSITE_EXCLUSION = "exclusion";
const QString COMPOSITE_HARD_MIX = "hard mix";
const QString COMPOSITE_OVERLAY = "overlay";
const QString COMPOSITE_BEHIND = "behind";
const QString COMPOSITE_GREATER = "greater";
const QString COMPOSITE_DARKEN = "darken";
const QString COMPOSITE_BURN = "burn";//this is also known as 'color burn'.
* Copyright (c) 2014 Nicholas Guttenberg <>
* Based on KoCompositeOpBehind.h,
* Copyright (c) 2012 José Luis Vergara <>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
#include "KoCompositeOpBase.h"
* Greater-than compositor - uses the greater of two alpha values to determine the color
template<class CS_Traits>
class KoCompositeOpGreater : public KoCompositeOpBase<CS_Traits, KoCompositeOpGreater<CS_Traits> >
typedef KoCompositeOpBase<CS_Traits, KoCompositeOpGreater<CS_Traits> > base_class;
typedef typename CS_Traits::channels_type channels_type;
typedef typename KoColorSpaceMathsTraits<typename CS_Traits::channels_type>::compositetype composite_type;
static const qint8 channels_nb = CS_Traits::channels_nb;
static const qint8 alpha_pos = CS_Traits::alpha_pos;
KoCompositeOpGreater(const KoColorSpace * cs)
: base_class(cs, COMPOSITE_GREATER, i18n("Greater"), KoCompositeOp::categoryMix()) { }
template<bool alphaLocked, bool allChannelFlags>
inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
channels_type* dst, channels_type dstAlpha,
channels_type maskAlpha, channels_type opacity,
const QBitArray& channelFlags ) {
using namespace Arithmetic;
if (dstAlpha == unitValue<channels_type>()) return dstAlpha;
channels_type appliedAlpha = mul(maskAlpha, srcAlpha, opacity);
if (appliedAlpha == zeroValue<channels_type>()) return dstAlpha;
channels_type newDstAlpha;
float dA = scale<float>(dstAlpha);
float w = 1.0/(1.0+exp(-40.0*(dA - scale<float>(appliedAlpha))));
float a = dA*w + scale<float>(appliedAlpha)*(1.0-w);
if (a<0.0f) a=0.0f; if (a>1.0f) a=1.0f;
// For a standard Over, the resulting alpha is: a = opacity*dstAlpha + (1-opacity)*srcAlpha
// Let us assume we're blending with a color with srcAlpha = 1 here
// Therefore, opacity = (1.0 - a)/(1.0 - dstAlpha)
if (a<dA) a = dA;
float fakeOpacity = 1.0 - (1.0f - a)/(1.0f - dA + 1e-16);
if (dstAlpha != zeroValue<channels_type>()) {
for (qint8 channel = 0; channel < channels_nb; ++channel)
if(channel != alpha_pos && (allChannelFlags || channelFlags.testBit(channel)))
typedef typename KoColorSpaceMathsTraits<channels_type>::compositetype composite_type;
channels_type dstMult = mul(dst[channel], dstAlpha);
channels_type srcMult = mul(src[channel], unitValue<channels_type>());
channels_type blendedValue = lerp(dstMult, srcMult, scale<channels_type>(fakeOpacity));
composite_type normedValue = KoColorSpaceMaths<channels_type>::divide(blendedValue, newDstAlpha);
dst[channel] = KoColorSpaceMaths<channels_type>::clampAfterScale(normedValue);
else {
// don't blend if the color of the destination is undefined (has zero opacity)
// copy the source channel instead
for (qint8 channel = 0; channel < channels_nb; ++channel)
if(channel != alpha_pos && (allChannelFlags || channelFlags.testBit(channel)))
dst[channel] = src[channel];
return newDstAlpha;
......@@ -35,6 +35,7 @@
#include "compositeops/KoCompositeOpCopy2.h"
#include "compositeops/KoCompositeOpDissolve.h"
#include "compositeops/KoCompositeOpBehind.h"
#include "compositeops/KoCompositeOpGreater.h"
#include "KoOptimizedCompositeOpFactory.h"
......@@ -108,6 +109,7 @@ struct AddGeneralOps<Traits, true>
cs->addCompositeOp(new KoCompositeOpCopy2<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpErase<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpBehind<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpGreater<Traits>(cs));
add<&cfOverlay<Arg> >(cs, COMPOSITE_OVERLAY , i18n("Overlay") , KoCompositeOp::categoryMix());
add<&cfGrainMerge<Arg> >(cs, COMPOSITE_GRAIN_MERGE , i18n("Grain Merge") , KoCompositeOp::categoryMix());
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