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

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)")));
  • Maybe this should also have the "Shine" word, so people can go and look for it?

  • Like, "Luminosity/Shine SAI"? Or just "Shine (SAI)"? If I understood correctly, first it was Luminosity, then Shine in SAI 2 - Shine can be seen as generally better (since it adds shine ;) ) especially for users that didn't use SAI, but on the other hand, people looking for Luminosity from SAI won't find it. On the third hand, we can just add it in Krita Manual in the "Coming from SAI" page, so it shouldn't be an issue and it could be called just "Shine" then.

    Please tell how you think it should be called exactly and I'll change it accordingly.

  • I think I would prefer to have it called "Luminosity/Shine (SAI)" That covers all bases.

  • Done in 3ffa4bdf.

Please register or sign in to reply
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