Commit dc05c8fc authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Implement "Neutral Point" feature for lightness overlay brush

Neutral point maps specific brush pixel value to "neutral" value,
which doesn't change the selected color. Pixles lighter than
neutral value make brush color lighet, pixels darker --- darker.
parent 3153d479
......@@ -78,10 +78,9 @@ KisBrushSP KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefini
* WARNING: see comment in KisGbrBrush::setUseColorAsMask()
*/
colorfulBrush->setUseColorAsMask((bool)brushDefinition.attribute("ColorAsMask").toInt());
colorfulBrush->setAdjustmentMidPoint(brushDefinition.attribute("AdjustmentMidPoint", "127").toInt());
colorfulBrush->setBrightnessAdjustment(brushDefinition.attribute("BrightnessAdjustment").toDouble());
colorfulBrush->setContrastAdjustment(brushDefinition.attribute("ContrastAdjustment").toDouble());
ENTER_FUNCTION() << ppVar(colorfulBrush->brightnessAdjustment()) << ppVar(colorfulBrush->contrastAdjustment());
}
return brush;
......
......@@ -31,6 +31,7 @@ KisScalingSizeBrush::KisScalingSizeBrush(const QString &filename)
KisScalingSizeBrush::KisScalingSizeBrush(const KisScalingSizeBrush &rhs)
: KisBrush(rhs),
m_useColorAsMask(rhs.m_useColorAsMask),
m_adjustmentMidPoint(rhs.m_adjustmentMidPoint),
m_brightnessAdjustment(rhs.m_brightnessAdjustment),
m_contrastAdjustment(rhs.m_contrastAdjustment)
{
......@@ -80,17 +81,31 @@ QImage KisScalingSizeBrush::brushTipImage() const
{
QImage image = KisBrush::brushTipImage();
if (hasColor() && useColorAsMask()) {
if (!qFuzzyIsNull(m_brightnessAdjustment) || !qFuzzyIsNull(m_contrastAdjustment)) {
if (m_adjustmentMidPoint != 127 ||
!qFuzzyIsNull(m_brightnessAdjustment) ||
!qFuzzyIsNull(m_contrastAdjustment)) {
const int half = KoColorSpaceMathsTraits<quint8>::halfValue;
const int unit = KoColorSpaceMathsTraits<quint8>::unitValue;
const qreal midPoint = half * (1.0 + 0.5 * m_brightnessAdjustment);
const qreal loA = 2 * midPoint / (1.0 - m_contrastAdjustment) / unit;
const qreal hiA = 2 * (unit - midPoint) / (1.0 - m_contrastAdjustment) / unit;
const qreal midX = m_adjustmentMidPoint;
const qreal midY = m_brightnessAdjustment > 0 ?
KoColorSpaceMaths<qreal>::blend(unit, half, m_brightnessAdjustment) :
KoColorSpaceMaths<qreal>::blend(0, half, -m_brightnessAdjustment);
const qreal loB = midPoint - half * loA;
const qreal hiB = midPoint - half * hiA;
qreal loA = 0.0;
qreal hiA = 0.0;
qreal loB = 0.0;
qreal hiB = 255.0;
if (!qFuzzyCompare(m_contrastAdjustment, 1.0)) {
loA = midY / (1.0 - m_contrastAdjustment) / midX;
hiA = (unit - midY) / (1.0 - m_contrastAdjustment) / (unit - midX);
loB = midY - midX * loA;
hiB = midY - midX * hiA;
}
for (int y = 0; y < image.height(); y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
......@@ -99,7 +114,7 @@ QImage KisScalingSizeBrush::brushTipImage() const
int v = qGray(c);
if (v >= half) {
if (v >= midX) {
v = qMin(unit, qRound(hiA * v + hiB));
} else {
v = qMax(0, qRound(loA * v + loB));
......@@ -123,6 +138,11 @@ QImage KisScalingSizeBrush::brushTipImage() const
return image;
}
void KisScalingSizeBrush::setAdjustmentMidPoint(quint8 value)
{
m_adjustmentMidPoint = value;
}
void KisScalingSizeBrush::setBrightnessAdjustment(qreal value)
{
m_brightnessAdjustment = value;
......@@ -133,6 +153,11 @@ void KisScalingSizeBrush::setContrastAdjustment(qreal value)
m_contrastAdjustment = value;
}
quint8 KisScalingSizeBrush::adjustmentMidPoint() const
{
return m_adjustmentMidPoint;
}
qreal KisScalingSizeBrush::brightnessAdjustment() const
{
return m_brightnessAdjustment;
......@@ -148,6 +173,7 @@ qreal KisScalingSizeBrush::contrastAdjustment() const
void KisScalingSizeBrush::toXML(QDomDocument& d, QDomElement& e) const
{
e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask()));
e.setAttribute("AdjustmentMidPoint", QString::number(m_adjustmentMidPoint));
e.setAttribute("BrightnessAdjustment", QString::number(m_brightnessAdjustment));
e.setAttribute("ContrastAdjustment", QString::number(m_contrastAdjustment));
KisBrush::toXML(d, e);
......
......@@ -43,9 +43,11 @@ public:
QImage brushTipImage() const override;
void setAdjustmentMidPoint(quint8 value);
void setBrightnessAdjustment(qreal value);
void setContrastAdjustment(qreal value);
quint8 adjustmentMidPoint() const;
qreal brightnessAdjustment() const;
qreal contrastAdjustment() const;
......@@ -53,6 +55,7 @@ public:
private:
bool m_useColorAsMask = false;
quint8 m_adjustmentMidPoint = 127;
qreal m_brightnessAdjustment = 0.0;
qreal m_contrastAdjustment = 0.0;
};
......
......@@ -37,6 +37,8 @@
#include "KoConvolutionOp.h"
#include "KoCompositeOpRegistry.h"
#include "KoColorSpaceEngine.h"
#include <KoColorSpaceTraits.h>
#include <KoColorSpacePreserveLightnessUtils.h>
#include <QThreadStorage>
#include <QByteArray>
......@@ -815,8 +817,6 @@ bool KoColorSpace::preferCompositionInSourceColorSpace() const
return false;
}
#include <KoColorSpaceTraits.h>
void KoColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const
{
/// Fallback implementation. All RGB color spaces have their own
......
......@@ -861,38 +861,4 @@ inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat)
else r = g = b = TReal(0.0);
}
#include "kis_global.h"
template<typename CSTraits>
inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qint32 nPixels) {
using RGBPixel = typename CSTraits::Pixel;
using channels_type = typename CSTraits::channels_type;
static const quint32 pixelSize = CSTraits::pixelSize;
const RGBPixel *brushColorRGB = reinterpret_cast<const RGBPixel*>(brushColor);
const float brushColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->red);
const float brushColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->green);
const float brushColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->blue);
for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {
RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);
const float brushMaskL = qRed(*brush) / 255.0f;
const float lightOffset = (2 * brushMaskL - 1);
float pixelR = brushColorR;
float pixelG = brushColorG;
float pixelB = brushColorB;
addLightness<HSLType, float>(pixelR, pixelG, pixelB, lightOffset);
pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(qAlpha(*brush)));
}
}
#endif
#ifndef KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
#define KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
#include <KoColorSpaceMaths.h>
template<typename CSTraits>
inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qint32 nPixels) {
using RGBPixel = typename CSTraits::Pixel;
using channels_type = typename CSTraits::channels_type;
static const quint32 pixelSize = CSTraits::pixelSize;
const RGBPixel *brushColorRGB = reinterpret_cast<const RGBPixel*>(brushColor);
const float brushColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->red);
const float brushColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->green);
const float brushColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->blue);
const float brushColorL = getLightness<HSLType, float>(brushColorR, brushColorG, brushColorB);
for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {
RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);
const float brushMaskL = qRed(*brush) / 255.0f;
const float lightOffset = 2.0 * brushMaskL - 1.0;
// const float lightBlend = qAbs(2.0 * brushMaskL - 1.0);
// const float lightOffset = (brushMaskL - brushColorL) * lightBlend;
float pixelR = brushColorR;
float pixelG = brushColorG;
float pixelB = brushColorB;
addLightness<HSLType, float>(pixelR, pixelG, pixelB, lightOffset);
pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(qAlpha(*brush)));
}
}
#endif // KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
......@@ -32,6 +32,7 @@
#include "KoIntegerMaths.h"
#include "KoColorConversions.h"
#include <KoColorSpacePreserveLightnessUtils.h>
KoRgbU16ColorSpace::KoRgbU16ColorSpace() :
KoSimpleColorSpace<KoBgrU16Traits>(colorSpaceId(),
......
......@@ -32,6 +32,7 @@
#include "compositeops/KoCompositeOps.h"
#include "KoColorConversions.h"
#include <KoColorSpacePreserveLightnessUtils.h>
KoRgbU8ColorSpace::KoRgbU8ColorSpace() :
......
......@@ -29,6 +29,7 @@
#include "compositeops/RgbCompositeOpOut.h"
#include "compositeops/RgbCompositeOpBumpmap.h"
#include <kis_dom_utils.h>
#include <KoColorSpacePreserveLightnessUtils.h>
RgbF16ColorSpace::RgbF16ColorSpace(const QString &name, KoColorProfile *p) :
LcmsColorSpace<KoRgbF16Traits>(colorSpaceId(), name, TYPE_RGBA_HALF_FLT, cmsSigRgbData, p)
......
......@@ -29,6 +29,7 @@
#include <kis_dom_utils.h>
#include <KoColorConversions.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpacePreserveLightnessUtils.h>
RgbF32ColorSpace::RgbF32ColorSpace(const QString &name, KoColorProfile *p) :
LcmsColorSpace<KoRgbF32Traits>(colorSpaceId(), name, TYPE_RGBA_FLT, cmsSigRgbData, p)
......
......@@ -27,6 +27,7 @@
#include "compositeops/RgbCompositeOps.h"
#include "kis_dom_utils.h"
#include <KoColorConversions.h>
#include <KoColorSpacePreserveLightnessUtils.h>
RgbU16ColorSpace::RgbU16ColorSpace(const QString &name, KoColorProfile *p) :
LcmsColorSpace<KoBgrU16Traits>(colorSpaceId(), name, TYPE_BGRA_16, cmsSigRgbData, p)
......
......@@ -30,6 +30,7 @@
#include "compositeops/KoCompositeOps.h"
#include "compositeops/RgbCompositeOps.h"
#include <kis_dom_utils.h>
#include <KoColorSpacePreserveLightnessUtils.h>
#define downscale(quantum) (quantum) //((unsigned char) ((quantum)/257UL))
#define upscale(value) (value) // ((quint8) (257UL*(value)))
......
......@@ -263,6 +263,9 @@
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="KisSliderSpinBox" name="intAdjustmentMidPoint" native="true"/>
</item>
<item>
<widget class="KisSliderSpinBox" name="intBrightnessAdjustment" native="true"/>
</item>
......
......@@ -175,6 +175,11 @@ KisPredefinedBrushChooser::KisPredefinedBrushChooser(QWidget *parent, const char
resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0"));
connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush()));
intAdjustmentMidPoint->setRange(0, 255);
intAdjustmentMidPoint->setPageStep(10);
intAdjustmentMidPoint->setSingleStep(1);
intAdjustmentMidPoint->setPrefix(i18nc("@label:slider", "Neutral point: "));
intBrightnessAdjustment->setRange(-100, 100);
intBrightnessAdjustment->setPageStep(10);
intBrightnessAdjustment->setSingleStep(1);
......@@ -195,6 +200,7 @@ KisPredefinedBrushChooser::KisPredefinedBrushChooser(QWidget *parent, const char
connect(btnColorMode, SIGNAL(toggled(bool)), SLOT(slotWriteBrushMode()));
connect(btnLightnessMode, SIGNAL(toggled(bool)), SLOT(slotWriteBrushMode()));
connect(intAdjustmentMidPoint, SIGNAL(valueChanged(int)), SLOT(slotUpdateBrushAdjustments()));
connect(intBrightnessAdjustment, SIGNAL(valueChanged(int)), SLOT(slotUpdateBrushAdjustments()));
connect(intContrastAdjustment, SIGNAL(valueChanged(int)), SLOT(slotUpdateBrushAdjustments()));
......@@ -404,9 +410,11 @@ void KisPredefinedBrushChooser::slotUpdateBrushModeButtonsState()
btnColorMode->setChecked(true);
}
intAdjustmentMidPoint->setValue(colorfulBrush->adjustmentMidPoint());
intBrightnessAdjustment->setValue(qRound(colorfulBrush->brightnessAdjustment() * 100.0));
intContrastAdjustment->setValue(qRound(colorfulBrush->contrastAdjustment() * 100.0));
} else {
intAdjustmentMidPoint->setValue(127);
intBrightnessAdjustment->setValue(0);
intContrastAdjustment->setValue(0);
btnMaskMode->setChecked(true);
......@@ -419,6 +427,7 @@ void KisPredefinedBrushChooser::slotUpdateBrushModeButtonsState()
void KisPredefinedBrushChooser::slotWriteBrushAdjustmentsState()
{
intAdjustmentMidPoint->setEnabled(btnLightnessMode->isEnabled() && btnLightnessMode->isChecked());
intBrightnessAdjustment->setEnabled(btnLightnessMode->isEnabled() && btnLightnessMode->isChecked());
intContrastAdjustment->setEnabled(btnLightnessMode->isEnabled() && btnLightnessMode->isChecked());
}
......@@ -451,6 +460,7 @@ void KisPredefinedBrushChooser::slotUpdateBrushAdjustments()
// sliders must not be user-accessible for non-colorful brushes
KIS_SAFE_ASSERT_RECOVER_RETURN(colorfulBrush);
colorfulBrush->setAdjustmentMidPoint(quint8(intAdjustmentMidPoint->value()));
colorfulBrush->setBrightnessAdjustment(intBrightnessAdjustment->value() / 100.0);
colorfulBrush->setContrastAdjustment(intContrastAdjustment->value() / 100.0);
......
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