1 /*
2 * Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
3 * Copyright (c) 2008 Martin Renold <martinxyz@gmx.ch>
4 * Copyright (c) 2009 Ilya Portnov <nomail>
5 *
6 * This class is based on "lib/colorchanger.hpp" from MyPaint (mypaint.intilinux.com)
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 #include "kis_my_paint_shade_selector.h"
24
25 #include <cmath>
26 #include <cstdlib>
27
28 #include <QImage>
29 #include <QColor>
30 #include <QPainter>
31 #include <QMouseEvent>
32 #include <QApplication>
33 #include <QDesktopWidget>
34 #include <QtGlobal>
35 #include <QTimer>
36
37 #include <kconfig.h>
38 #include <kconfiggroup.h>
39 #include <ksharedconfig.h>
40
41 #include "KoColorSpace.h"
42 #include "KoColorSpaceRegistry.h"
43 #include "KoColor.h"
44 #include "KoCanvasResourceProvider.h"
45
46 #include "kis_paint_device.h"
47 #include "kis_painter.h"
48 #include "kis_display_color_converter.h"
49
50 inline int sqr(int x);
51 inline qreal sqr2(qreal x);
52 inline int signedSqr(int x);
53
54
KisMyPaintShadeSelector(QWidget * parent)55 KisMyPaintShadeSelector::KisMyPaintShadeSelector(QWidget *parent) :
56 KisColorSelectorBase(parent),
57 m_updateTimer(new QTimer(this))
58 {
59 setAcceptDrops(true);
60
61 updateSettings();
62
63 setMinimumSize(80, 80);
64 setColor(KoColor(Qt::red, colorSpace()));
65
66 m_updateTimer->setInterval(1);
67 m_updateTimer->setSingleShot(true);
68 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
69 }
70
paintEvent(QPaintEvent *)71 void KisMyPaintShadeSelector::paintEvent(QPaintEvent *) {
72 // Hint to the casual reader: some of the calculation here do not
73 // what Martin Renold originally intended. Not everything here will make sense.
74 // It does not matter in the end, as long as the result looks good.
75
76 // This selector was ported from MyPaint in 2010
77 if (m_cachedColorSpace != colorSpace()) {
78 m_realPixelCache = new KisPaintDevice(colorSpace());
79 m_realCircleBorder = new KisPaintDevice(colorSpace());
80 m_cachedColorSpace = colorSpace();
81 }
82 else {
83 m_realPixelCache->clear();
84 m_realCircleBorder->clear();
85 }
86 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
87 QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
88
89 int sizeHD = qMin(width(), height())*devicePixelRatioF();
90 int s_radiusHD = sizeHD/2.6;
91
92 int widthHD = width()*devicePixelRatioF();
93 int heightHD = height()*devicePixelRatioF();
94
95 const int pixelSize = m_realPixelCache->colorSpace()->pixelSize();
96 const int borderPixelSize = m_realCircleBorder->colorSpace()->pixelSize();
97
98 QRect pickRectHighDPI = QRect(QPoint(0, 0), size()*devicePixelRatioF());
99 KisSequentialIterator it(m_realPixelCache, pickRectHighDPI);
100 KisSequentialIterator borderIt(m_realCircleBorder, pickRectHighDPI);
101
102
103 while(it.nextPixel()) {
104 borderIt.nextPixel();
105
106 int x = it.x();
107 int y = it.y();
108
109 float v_factor = 0.6f;
110 float s_factor = 0.6f;
111 float v_factor2 = 0.013f;
112 float s_factor2 = 0.013f;
113
114 int stripe_width = 15*sizeHD/255.;
115
116 float h = 0;
117 float s = 0;
118 float v = 0;
119
120 int dx = x-widthHD/2;
121 int dy = y-heightHD/2;
122 int diag = sqrt(2.0)*sizeHD/2;
123
124 int dxs, dys;
125 if (dx > 0)
126 dxs = dx - stripe_width;
127 else
128 dxs = dx + stripe_width;
129 if (dy > 0)
130 dys = dy - stripe_width;
131 else
132 dys = dy + stripe_width;
133
134 qreal r = std::sqrt(qreal(sqr(dxs)+sqr(dys)));
135
136 if (qMin(abs(dx), abs(dy)) < stripe_width) {
137 // horizontal and vertical lines
138 bool horizontal = std::abs(dx) > std::abs(dy);
139 dx = (dx/qreal(sizeHD))*255;
140 dy = (dy/qreal(sizeHD))*255;
141
142 h = 0;
143 // x-axis = value, y-axis = saturation
144 v = dx*v_factor + signedSqr(dx)*v_factor2;
145 s = - (dy*s_factor + signedSqr(dy)*s_factor2);
146 // but not both at once
147 if (horizontal) {
148 // horizontal stripe
149 s = 0.0;
150 } else {
151 // vertical stripe
152 v = 0.0;
153 }
154 }
155 else if (std::min(std::abs(dx - dy), std::abs(dx + dy)) < stripe_width) {
156
157 dx = (dx/qreal(sizeHD))*255;
158 dy = (dy/qreal(sizeHD))*255;
159
160 h = 0;
161 // x-axis = value, y-axis = saturation
162 v = dx*v_factor + signedSqr(dx)*v_factor2;
163 s = - (dy*s_factor + signedSqr(dy)*s_factor2);
164 // both at once
165 }
166 else if (r < s_radiusHD+1) {
167 // hue
168 if (dx > 0)
169 h = 90*sqr2(r/s_radiusHD);
170 else
171 h = 360 - 90*sqr2(r/s_radiusHD);
172 s = 256*(atan2f(std::abs(dxs),dys)/M_PI) - 128;
173
174 if (r > s_radiusHD) {
175 // antialiasing boarder
176 qreal aaFactor = r-floor(r); // part after the decimal point
177 aaFactor = 1-aaFactor;
178
179 qreal fh = m_colorH + h/360.0;
180 qreal fs = m_colorS + s/255.0;
181 qreal fv = m_colorV + v/255.0;
182
183 fh -= floor(fh);
184 fs = qBound(qreal(0.0), fs, qreal(1.0));
185 fv = qBound(qreal(0.01), fv, qreal(1.0));
186 KoColor color;
187 //KoColor color = converter()->fromHsvF(fh, fs, fv);
188 if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
189 else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
190 else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
191 else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv, R, G, B);}
192 else{dbgKrita<<"MyPaint Color selector don't work right.";
193 color = converter()->fromHsvF(fh, fs, fv);}
194 //dbgKrita<<color->toQcolor();
195 color.setOpacity(aaFactor);
196 Acs::setColorWithIterator(borderIt, color, borderPixelSize);
197
198 h = 180 + 180*atan2f(dys,-dxs)/M_PI;
199 v = 255*(r-s_radiusHD)/(diag-s_radiusHD) - 128;
200 s = 0; // overwrite the s value that was meant for the inside of the circle
201 // here we already have drawn the inside, and the value left should be just the background value
202
203 }
204 }
205 else {
206 // background (hue+darkness gradient)
207 h = 180 + 180*atan2f(dys,-dxs)/M_PI;
208 v = 255*(r-s_radiusHD)/(diag-s_radiusHD) - 128;
209 }
210
211 qreal fh = m_colorH + h/360.0;
212 qreal fs = m_colorS + s/255.0;
213 qreal fv = m_colorV + v/255.0;
214
215 fh -= floor(fh);
216 fs = qBound(qreal(0.0), fs, qreal(1.0));
217 fv = qBound(qreal(0.01), fv, qreal(1.0));
218 KoColor color;
219 //KoColor color = converter()->fromHsvF(fh, fs, fv);
220 if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
221 else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
222 else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
223 else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv);}
224 else{dbgKrita<<"MyPaint Color selector don't work right.";
225 color = converter()->fromHsvF(fh, fs, fv);}
226
227 Acs::setColorWithIterator(it, color, pixelSize);
228 }
229
230 KisPainter gc(m_realPixelCache);
231 gc.bitBlt(QPoint(0,0), m_realCircleBorder, QRect(rect().topLeft(), rect().size()*devicePixelRatioF()));
232
233 QPainter painter(this);
234 QImage renderedImage = converter()->toQImage(m_realPixelCache);
235 renderedImage.setDevicePixelRatio(devicePixelRatioF());
236
237 painter.drawImage(0, 0, renderedImage);
238 }
239
240
mousePressEvent(QMouseEvent * e)241 void KisMyPaintShadeSelector::mousePressEvent(QMouseEvent* e)
242 {
243 e->setAccepted(false);
244 KisColorSelectorBase::mousePressEvent(e);
245
246 if (!e->isAccepted()) {
247 if(rect().contains(e->pos())) {
248 KoColor color(Acs::pickColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
249 this->updateColorPreview(color);
250 updatePreviousColorPreview();
251 }
252 }
253 }
254
mouseMoveEvent(QMouseEvent * e)255 void KisMyPaintShadeSelector::mouseMoveEvent(QMouseEvent *e)
256 {
257 if(rect().contains(e->pos())) {
258 KoColor color(Acs::pickColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
259 this->updateColorPreview(color);
260 }
261 KisColorSelectorBase::mouseMoveEvent(e);
262 }
263
mouseReleaseEvent(QMouseEvent * e)264 void KisMyPaintShadeSelector::mouseReleaseEvent(QMouseEvent *e)
265 {
266 e->setAccepted(false);
267 KisColorSelectorBase::mouseReleaseEvent(e);
268
269 if(!e->isAccepted()) {
270 KoColor color(Acs::pickColor(m_realPixelCache, e->pos()*devicePixelRatioF()));
271
272 Acs::ColorRole role = Acs::buttonToRole(e->button());
273
274 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
275
276 bool onRightClick = cfg.readEntry("shadeSelectorUpdateOnRightClick", false);
277 bool onLeftClick = cfg.readEntry("shadeSelectorUpdateOnLeftClick", false);
278
279 bool explicitColorReset =
280 (e->button() == Qt::LeftButton && onLeftClick) ||
281 (e->button() == Qt::RightButton && onRightClick);
282
283 this->updateColor(color, role, explicitColorReset);
284 updateBaseColorPreview(color);
285 e->accept();
286 }
287 }
288
createPopup() const289 KisColorSelectorBase* KisMyPaintShadeSelector::createPopup() const
290 {
291 KisColorSelectorBase* popup = new KisMyPaintShadeSelector(0);
292 popup->setColor(m_lastRealColor);
293 return popup;
294 }
295
setColor(const KoColor & color)296 void KisMyPaintShadeSelector::setColor(const KoColor &color) {
297
298 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
299 QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
300
301 R = cfg.readEntry("lumaR", 0.2126);
302 G = cfg.readEntry("lumaG", 0.7152);
303 B = cfg.readEntry("lumaB", 0.0722);
304
305 if(shadeMyPaintType=="HSV"){this->converter()->getHsvF(color, &m_colorH, &m_colorS, &m_colorV);}
306 if(shadeMyPaintType=="HSL"){this->converter()->getHslF(color, &m_colorH, &m_colorS, &m_colorV);}
307 if(shadeMyPaintType=="HSI"){this->converter()->getHsiF(color, &m_colorH, &m_colorS, &m_colorV);}
308 if(shadeMyPaintType=="HSY"){this->converter()->getHsyF(color, &m_colorH, &m_colorS, &m_colorV, R, G, B);}
309 m_lastRealColor = color;
310 this->updateColorPreview(color);
311
312 m_updateTimer->start();
313 }
314
canvasResourceChanged(int key,const QVariant & v)315 void KisMyPaintShadeSelector::canvasResourceChanged(int key, const QVariant &v)
316 {
317 if(m_colorUpdateAllowed==false)
318 return;
319
320 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
321
322 bool onForeground = cfg.readEntry("shadeSelectorUpdateOnForeground", false);
323 bool onBackground = cfg.readEntry("shadeSelectorUpdateOnBackground", true);
324
325 if ((key == KoCanvasResourceProvider::ForegroundColor && onForeground) ||
326 (key == KoCanvasResourceProvider::BackgroundColor && onBackground)) {
327
328 setColor(v.value<KoColor>());
329 }
330 }
331
sqr(int x)332 inline int sqr(int x) {
333 return x*x;
334 }
335
sqr2(qreal x)336 inline qreal sqr2(qreal x) {
337 return (x*x)/2+x/2;
338 }
339
signedSqr(int x)340 inline int signedSqr(int x) {
341 int sign = x>0?1:-1;
342 return x*x*sign;
343 }
344