Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_my_paint_shade_selector.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Adam Celarek <kdedev at xibo dot at>
3 * SPDX-FileCopyrightText: 2008 Martin Renold <martinxyz@gmx.ch>
4 * SPDX-FileCopyrightText: 2009 Ilya Portnov <nomail>
5 *
6 * This class is based on "lib/colorchanger.hpp" from MyPaint (mypaint.intilinux.com)
7 *
8 * SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10
12
13#include <cmath>
14#include <cstdlib>
15
16#include <QImage>
17#include <QColor>
18#include <QPainter>
19#include <QMouseEvent>
20#include <QApplication>
21#include <QScreen>
22#include <QtGlobal>
23#include <QTimer>
24
25#include <kconfig.h>
26#include <kconfiggroup.h>
27#include <ksharedconfig.h>
28
29#include "KoColorSpace.h"
31#include "KoColor.h"
33
34#include "kis_paint_device.h"
35#include "kis_painter.h"
37
38inline int sqr(int x);
39inline qreal sqr2(qreal x);
40inline int signedSqr(int x);
41
42
45 m_updateTimer(new QTimer(this))
46{
47 setAcceptDrops(true);
48
50
51 setMinimumSize(80, 80);
52 setColor(KoColor(Qt::red, colorSpace()));
53
54 m_updateTimer->setInterval(1);
55 m_updateTimer->setSingleShot(true);
56 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
57}
58
60 // Hint to the casual reader: some of the calculation here do not
61 // what Martin Renold originally intended. Not everything here will make sense.
62 // It does not matter in the end, as long as the result looks good.
63
64 // This selector was ported from MyPaint in 2010
69 }
70 else {
73 }
74 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
75 QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
76
77 int sizeHD = qMin(width(), height())*devicePixelRatioF();
78 int s_radiusHD = sizeHD/2.6;
79
80 int widthHD = width()*devicePixelRatioF();
81 int heightHD = height()*devicePixelRatioF();
82
83 const int pixelSize = m_realPixelCache->colorSpace()->pixelSize();
84 const int borderPixelSize = m_realCircleBorder->colorSpace()->pixelSize();
85
86 QRect pickRectHighDPI = QRect(QPoint(0, 0), size()*devicePixelRatioF());
87 KisSequentialIterator it(m_realPixelCache, pickRectHighDPI);
88 KisSequentialIterator borderIt(m_realCircleBorder, pickRectHighDPI);
89
90
91 while(it.nextPixel()) {
92 borderIt.nextPixel();
93
94 int x = it.x();
95 int y = it.y();
96
97 float v_factor = 0.6f;
98 float s_factor = 0.6f;
99 float v_factor2 = 0.013f;
100 float s_factor2 = 0.013f;
101
102 int stripe_width = 15*sizeHD/255.;
103
104 float h = 0;
105 float s = 0;
106 float v = 0;
107
108 int dx = x-widthHD/2;
109 int dy = y-heightHD/2;
110 int diag = sqrt(2.0)*sizeHD/2;
111
112 int dxs, dys;
113 if (dx > 0)
114 dxs = dx - stripe_width;
115 else
116 dxs = dx + stripe_width;
117 if (dy > 0)
118 dys = dy - stripe_width;
119 else
120 dys = dy + stripe_width;
121
122 qreal r = std::sqrt(qreal(sqr(dxs)+sqr(dys)));
123
124 if (qMin(abs(dx), abs(dy)) < stripe_width) {
125 // horizontal and vertical lines
126 bool horizontal = std::abs(dx) > std::abs(dy);
127 dx = (dx/qreal(sizeHD))*255;
128 dy = (dy/qreal(sizeHD))*255;
129
130 h = 0;
131 // x-axis = value, y-axis = saturation
132 v = dx*v_factor + signedSqr(dx)*v_factor2;
133 s = - (dy*s_factor + signedSqr(dy)*s_factor2);
134 // but not both at once
135 if (horizontal) {
136 // horizontal stripe
137 s = 0.0;
138 } else {
139 // vertical stripe
140 v = 0.0;
141 }
142 }
143 else if (std::min(std::abs(dx - dy), std::abs(dx + dy)) < stripe_width) {
144
145 dx = (dx/qreal(sizeHD))*255;
146 dy = (dy/qreal(sizeHD))*255;
147
148 h = 0;
149 // x-axis = value, y-axis = saturation
150 v = dx*v_factor + signedSqr(dx)*v_factor2;
151 s = - (dy*s_factor + signedSqr(dy)*s_factor2);
152 // both at once
153 }
154 else if (r < s_radiusHD+1) {
155 // hue
156 if (dx > 0)
157 h = 90*sqr2(r/s_radiusHD);
158 else
159 h = 360 - 90*sqr2(r/s_radiusHD);
160 s = 256*(atan2f(std::abs(dxs),dys)/M_PI) - 128;
161
162 if (r > s_radiusHD) {
163 // antialiasing boarder
164 qreal aaFactor = r-floor(r); // part after the decimal point
165 aaFactor = 1-aaFactor;
166
167 qreal fh = m_colorH + h/360.0;
168 qreal fs = m_colorS + s/255.0;
169 qreal fv = m_colorV + v/255.0;
170
171 fh -= floor(fh);
172 fs = qBound(qreal(0.0), fs, qreal(1.0));
173 fv = qBound(qreal(0.01), fv, qreal(1.0));
174 KoColor color;
175 //KoColor color = converter()->fromHsvF(fh, fs, fv);
176 if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
177 else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
178 else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
179 else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv, R, G, B);}
180 else{dbgKrita<<"MyPaint Color selector don't work right.";
181 color = converter()->fromHsvF(fh, fs, fv);}
182//dbgKrita<<color->toQcolor();
183 color.setOpacity(aaFactor);
184 Acs::setColorWithIterator(borderIt, color, borderPixelSize);
185
186 h = 180 + 180*atan2f(dys,-dxs)/M_PI;
187 v = 255*(r-s_radiusHD)/(diag-s_radiusHD) - 128;
188 s = 0; // overwrite the s value that was meant for the inside of the circle
189 // here we already have drawn the inside, and the value left should be just the background value
190
191 }
192 }
193 else {
194 // background (hue+darkness gradient)
195 h = 180 + 180*atan2f(dys,-dxs)/M_PI;
196 v = 255*(r-s_radiusHD)/(diag-s_radiusHD) - 128;
197 }
198
199 qreal fh = m_colorH + h/360.0;
200 qreal fs = m_colorS + s/255.0;
201 qreal fv = m_colorV + v/255.0;
202
203 fh -= floor(fh);
204 fs = qBound(qreal(0.0), fs, qreal(1.0));
205 fv = qBound(qreal(0.01), fv, qreal(1.0));
206 KoColor color;
207 //KoColor color = converter()->fromHsvF(fh, fs, fv);
208 if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
209 else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
210 else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
211 else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv);}
212 else{dbgKrita<<"MyPaint Color selector don't work right.";
213 color = converter()->fromHsvF(fh, fs, fv);}
214
215 Acs::setColorWithIterator(it, color, pixelSize);
216 }
217
219 gc.bitBlt(QPoint(0,0), m_realCircleBorder, QRect(rect().topLeft(), rect().size()*devicePixelRatioF()));
220
221 QPainter painter(this);
222 if (!isEnabled()) {
223 painter.setOpacity(0.2);
224 }
225 QImage renderedImage = converter()->toQImage(m_realPixelCache);
226 renderedImage.setDevicePixelRatio(devicePixelRatioF());
227
228 painter.drawImage(0, 0, renderedImage);
229}
230
231
233{
234 e->setAccepted(false);
236
237 if (!e->isAccepted()) {
238 if(rect().contains(e->pos())) {
239 KoColor color(Acs::sampleColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
240 this->updateColorPreview(color);
242 }
243 }
244}
245
247{
248 if(rect().contains(e->pos())) {
249 KoColor color(Acs::sampleColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
250 this->updateColorPreview(color);
251 }
253}
254
256{
257 e->setAccepted(false);
259
260 if(!e->isAccepted()) {
261 KoColor color(Acs::sampleColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
262
263 Acs::ColorRole role = Acs::buttonToRole(e->button());
264
265 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
266
267 bool onRightClick = cfg.readEntry("shadeSelectorUpdateOnRightClick", false);
268 bool onLeftClick = cfg.readEntry("shadeSelectorUpdateOnLeftClick", false);
269
270 bool explicitColorReset =
271 (e->button() == Qt::LeftButton && onLeftClick) ||
272 (e->button() == Qt::RightButton && onRightClick);
273
274 this->updateColor(color, role, explicitColorReset);
276 e->accept();
277 }
278}
279
286
288
289 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
290 QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
291
292 R = cfg.readEntry("lumaR", 0.2126);
293 G = cfg.readEntry("lumaG", 0.7152);
294 B = cfg.readEntry("lumaB", 0.0722);
295
296 if(shadeMyPaintType=="HSV"){this->converter()->getHsvF(color, &m_colorH, &m_colorS, &m_colorV);}
297 if(shadeMyPaintType=="HSL"){this->converter()->getHslF(color, &m_colorH, &m_colorS, &m_colorV);}
298 if(shadeMyPaintType=="HSI"){this->converter()->getHsiF(color, &m_colorH, &m_colorS, &m_colorV);}
299 if(shadeMyPaintType=="HSY"){this->converter()->getHsyF(color, &m_colorH, &m_colorS, &m_colorV, R, G, B);}
300 m_lastRealColor = color;
301 this->updateColorPreview(color);
302
303 m_updateTimer->start();
304}
305
307{
308 if(m_colorUpdateAllowed==false)
309 return;
310
311 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
312
313 bool onForeground = cfg.readEntry("shadeSelectorUpdateOnForeground", false);
314 bool onBackground = cfg.readEntry("shadeSelectorUpdateOnBackground", true);
315
316 if ((key == KoCanvasResource::ForegroundColor && onForeground) ||
317 (key == KoCanvasResource::BackgroundColor && onBackground)) {
318
319 setColor(v.value<KoColor>());
320 }
321}
322
323inline int sqr(int x) {
324 return x*x;
325}
326
327inline qreal sqr2(qreal x) {
328 return (x*x)/2+x/2;
329}
330
331inline int signedSqr(int x) {
332 int sign = x>0?1:-1;
333 return x*x*sign;
334}
qreal v
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
Base class for all color selectors, that should support color management and zooming.
void updateColorPreview(const KoColor &color)
KisDisplayColorConverter * converter() const
const KoColorSpace * colorSpace() const
virtual void setColor(const KoColor &color)
void mouseReleaseEvent(QMouseEvent *) override
void mousePressEvent(QMouseEvent *) override
void mouseMoveEvent(QMouseEvent *event) override
void updateBaseColorPreview(const KoColor &color)
void updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset)
void getHslF(const KoColor &srcColor, qreal *h, qreal *s, qreal *l, qreal *a=0)
KoColor fromHsiF(qreal h, qreal s, qreal i)
void getHsiF(const KoColor &srcColor, qreal *h, qreal *s, qreal *i)
QImage toQImage(KisPaintDeviceSP srcDevice, bool proofPaintColors=false) const
void getHsyF(const KoColor &srcColor, qreal *h, qreal *s, qreal *y, qreal R=0.2126, qreal G=0.7152, qreal B=0.0722, qreal gamma=2.2)
KoColor fromHslF(qreal h, qreal s, qreal l, qreal a=1.0)
void getHsvF(const KoColor &srcColor, qreal *h, qreal *s, qreal *v, qreal *a=0)
KoColor fromHsyF(qreal h, qreal s, qreal y, qreal R=0.2126, qreal G=0.7152, qreal B=0.0722, qreal gamma=2.2)
KoColor fromHsvF(qreal h, qreal s, qreal v, qreal a=1.0)
KisColorSelectorBase * createPopup() const override
void mouseMoveEvent(QMouseEvent *) override
void paintEvent(QPaintEvent *) override
void mousePressEvent(QMouseEvent *) override
const KoColorSpace * m_cachedColorSpace
void canvasResourceChanged(int key, const QVariant &v) override
void mouseReleaseEvent(QMouseEvent *) override
void setColor(const KoColor &color) override
virtual void clear()
const KoColorSpace * colorSpace() const
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
ALWAYS_INLINE int x() const
ALWAYS_INLINE int y() const
virtual quint32 pixelSize() const =0
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
#define dbgKrita
Definition kis_debug.h:45
#define M_PI
Definition kis_global.h:111
int signedSqr(int x)
qreal sqr2(qreal x)
ColorRole buttonToRole(Qt::MouseButton button)
void setColorWithIterator(Iterator &it, const KoColor &color, const int pixelSize)
KoColor sampleColor(PaintDeviceSP device, const QPoint &pt)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.