Commit 4f16ff7c authored by Agata Cacko's avatar Agata Cacko

Implement SAI-like Luminosity blending mode

It was requested to make a new lighten mode that behaves
just like a Luminosity/Shine mode in SAI. This commit contains
the code that adds it to the program.

BUG:409627
parent 4cd4bce1
......@@ -96,6 +96,7 @@ KoCompositeOpRegistry::KoCompositeOpRegistry()
m_map.insert(m_categories[3], KoID(COMPOSITE_TINT_IFS_ILLUSIONS, i18n("Tint (IFS Illusions)")));
m_map.insert(m_categories[3], KoID(COMPOSITE_FOG_LIGHTEN_IFS_ILLUSIONS, i18n("Fog Lighten (IFS Illusions)")));
m_map.insert(m_categories[3], KoID(COMPOSITE_EASY_DODGE , i18n("Easy Dodge")));
m_map.insert(m_categories[3], KoID(COMPOSITE_LUMINOSITY_SAI , i18n("Luminosity (SAI)")));
m_map.insert(m_categories[4], KoID(COMPOSITE_MOD , i18n("Modulo")));
m_map.insert(m_categories[4], KoID(COMPOSITE_MOD_CON , i18n("Modulo - Continuous")));
......
......@@ -120,6 +120,8 @@ const QString COMPOSITE_SUPER_LIGHT = "super_light";
const QString COMPOSITE_TINT_IFS_ILLUSIONS = "tint_ifs_illusions";
const QString COMPOSITE_FOG_LIGHTEN_IFS_ILLUSIONS = "fog_lighten_ifs_illusions";
const QString COMPOSITE_EASY_DODGE = "easy dodge";
const QString COMPOSITE_LUMINOSITY_SAI = "luminosity_sai";
const QString COMPOSITE_HUE = "hue";
const QString COMPOSITE_COLOR = "color";
......@@ -209,8 +211,9 @@ public:
KoIDList list;
for(; begin!=end; ++begin){
if( colorSpaceHasCompositeOp(colorSpace, *begin) == removeInvaliOps)
if (colorSpaceHasCompositeOp(colorSpace, *begin) == removeInvaliOps) {
list.push_back(*begin);
}
}
return list;
......
......@@ -933,4 +933,20 @@ inline T cfFlatLight(T src, T dst) {
return clamp<T>(cfHardMixPhotoshop(inv(src),dst)==unitValue<T>() ? cfPenumbraB(src,dst) : cfPenumbraA(src,dst));
}
template<class HSXType, class TReal>
inline void cfAdditionSAI(TReal src, TReal sa, TReal& dst, TReal& da)
{
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<TReal>::compositetype composite_type;
Q_UNUSED(da);
composite_type newsrc;
newsrc = mul(src, sa);
dst = clamp<TReal>(newsrc + dst);
}
#endif // KOCOMPOSITEOP_FUNCTIONS_H_
......@@ -110,60 +110,133 @@ public:
channels_type* dst, channels_type dstAlpha, channels_type maskAlpha,
channels_type opacity, const QBitArray& channelFlags) {
using namespace Arithmetic;
srcAlpha = mul(srcAlpha, maskAlpha, opacity);
if(alphaLocked) {
if(dstAlpha != zeroValue<channels_type>()) {
float srcR = scale<float>(src[red_pos]);
float srcG = scale<float>(src[green_pos]);
float srcB = scale<float>(src[blue_pos]);
float dstR = scale<float>(dst[red_pos]);
float dstG = scale<float>(dst[green_pos]);
float dstB = scale<float>(dst[blue_pos]);
compositeFunc(srcR, srcG, srcB, dstR, dstG, dstB);
if(allChannelFlags || channelFlags.testBit(red_pos))
dst[red_pos] = lerp(dst[red_pos], scale<channels_type>(dstR), srcAlpha);
if(allChannelFlags || channelFlags.testBit(green_pos))
dst[green_pos] = lerp(dst[green_pos], scale<channels_type>(dstG), srcAlpha);
if(allChannelFlags || channelFlags.testBit(blue_pos))
dst[blue_pos] = lerp(dst[blue_pos], scale<channels_type>(dstB), srcAlpha);
}
return dstAlpha;
}
else {
channels_type newDstAlpha = unionShapeOpacity(srcAlpha, dstAlpha);
if(newDstAlpha != zeroValue<channels_type>()) {
float srcR = scale<float>(src[red_pos]);
float srcG = scale<float>(src[green_pos]);
float srcB = scale<float>(src[blue_pos]);
float dstR = scale<float>(dst[red_pos]);
float dstG = scale<float>(dst[green_pos]);
float dstB = scale<float>(dst[blue_pos]);
compositeFunc(srcR, srcG, srcB, dstR, dstG, dstB);
if(allChannelFlags || channelFlags.testBit(red_pos))
dst[red_pos] = div(blend(src[red_pos], srcAlpha, dst[red_pos], dstAlpha, scale<channels_type>(dstR)), newDstAlpha);
if(allChannelFlags || channelFlags.testBit(green_pos))
dst[green_pos] = div(blend(src[green_pos], srcAlpha, dst[green_pos], dstAlpha, scale<channels_type>(dstG)), newDstAlpha);
if(allChannelFlags || channelFlags.testBit(blue_pos))
dst[blue_pos] = div(blend(src[blue_pos], srcAlpha, dst[blue_pos], dstAlpha, scale<channels_type>(dstB)), newDstAlpha);
}
return newDstAlpha;
}
}
};
/**
* Generic CompositeOp for separable channel + alpha compositing functions
*
* A template to generate a KoCompositeOp class by just specifying a
* blending/compositing function. This template works with compositing functions
* for separable channels (means each channel of a pixel can be processed separately)
* with taking apha into consideration.
* Note that because of special treating of alpha, any composite op function
* needs to make alpha blending itself - the value of color that is written onto the projection
* is the same that the composite function gives (compare with KoCompositeOpGenericHSL and KoCompositeOpGenericSC).
*/
template<class Traits, void compositeFunc(float, float, float&, float&)>
class KoCompositeOpGenericSCAlpha: public KoCompositeOpBase< Traits, KoCompositeOpGenericSCAlpha<Traits,compositeFunc> >
{
typedef KoCompositeOpBase< Traits, KoCompositeOpGenericSCAlpha<Traits,compositeFunc> > base_class;
typedef typename Traits::channels_type channels_type;
static const qint32 channels_nb = Traits::channels_nb;
static const qint32 alpha_pos = Traits::alpha_pos;
public:
KoCompositeOpGenericSCAlpha(const KoColorSpace* cs, const QString& id, const QString& description, const QString& category)
: base_class(cs, id, description, category) { }
public:
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;
srcAlpha = mul(srcAlpha, maskAlpha, opacity);
if(alphaLocked) {
channels_type oldAlpha = dstAlpha;
if(dstAlpha != zeroValue<channels_type>()) {
for(qint32 i=0; i <channels_nb; i++) {
if(i != alpha_pos && (allChannelFlags || channelFlags.testBit(i))) {
float dstValueFloat = scale<float>(dst[i]);
float dstAlphaFloat = scale<float>(oldAlpha);
compositeFunc(scale<float>(src[i]), scale<float>(srcAlpha), dstValueFloat, dstAlphaFloat);
dst[i] = scale<channels_type>(dstValueFloat);
}
}
}
return dstAlpha;
}
else {
channels_type oldAlpha = dstAlpha;
channels_type newDstAlpha = unionShapeOpacity(srcAlpha, dstAlpha);
if(newDstAlpha != zeroValue<channels_type>()) {
for(qint32 i=0; i <channels_nb; i++) {
if(i != alpha_pos && (allChannelFlags || channelFlags.testBit(i))) {
float dstFloat = scale<float>(dst[i]);
float dstAlphaFloat = scale<float>(oldAlpha);
compositeFunc(scale<float>(src[i]), scale<float>(srcAlpha), dstFloat, dstAlphaFloat);
dst[i] = scale<channels_type>(dstFloat);
}
}
}
return newDstAlpha;
}
}
};
#endif // _KOCOMPOSITEOP_GENERIC_H_
......@@ -296,8 +296,46 @@ struct AddRGBOps<Traits, true>
}
};
template<class Traits, bool flag>
struct AddGeneralAlphaOps
{
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct AddGeneralAlphaOps<Traits, true>
{
typedef float Arg;
static const qint32 alpha_pos = Traits::alpha_pos;
template<void compositeFunc(Arg, Arg, Arg&, Arg&)>
static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category)
{
cs->addCompositeOp(new KoCompositeOpGenericSCAlpha<Traits, compositeFunc>(cs, id, description, category));
}
static void add(KoColorSpace* cs)
{
add<&cfAdditionSAI <HSVType,Arg> >(cs, COMPOSITE_LUMINOSITY_SAI , i18n("Luminosity (SAI)") , KoCompositeOp::categoryHSV());
}
};
}
/**
* This function add to the colorspace all the composite ops defined by
* the pigment library.
......@@ -311,8 +349,10 @@ void addStandardCompositeOps(KoColorSpace* cs)
static const bool useRGBOps = (boost::is_base_of<KoBgrTraits<channels_type>, _Traits_>::value
|| boost::is_base_of<KoRgbTraits<channels_type>, _Traits_>::value);
_Private::AddGeneralOps<_Traits_, useGeneralOps>::add(cs);
_Private::AddRGBOps <_Traits_, useRGBOps >::add(cs);
_Private::AddGeneralOps <_Traits_, useGeneralOps>::add(cs);
_Private::AddRGBOps <_Traits_, useRGBOps >::add(cs);
_Private::AddGeneralAlphaOps <_Traits_, useGeneralOps>::add(cs);
}
template<class _Traits_>
......
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