Krita Source Code Documentation
Loading...
Searching...
No Matches
KisVisualEllipticalSelectorShape.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
7
8#include <QColor>
9#include <QPainter>
10#include <QRect>
11#include <QLineF>
12#include <QtMath>
13
14#include "kis_debug.h"
15#include "kis_global.h"
16
18
19#define KVESS_MARGIN 2
20
22 Dimensions dimension,
23 int channel1, int channel2,
24 int barWidth,
26 : KisVisualColorSelectorShape(parent, dimension, channel1, channel2)
27{
28 //qDebug() << "creating KisVisualEllipticalSelectorShape" << this;
29 m_type = d;
30 m_barWidth = barWidth;
32}
33
35{
36 //qDebug() << "deleting KisVisualEllipticalSelectorShape" << this;
37}
38
40{
41 m_barWidth = width;
43 update();
44}
45
47{
48 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(geom.contains(geometry()), geom);
49 int sizeValue = qMin(width(), height());
50 QRectF b(0, 0, sizeValue, sizeValue);
51 QLineF radius(b.center(), QPointF(b.left() + m_barWidth - 1, b.center().y()) );
52 radius.setAngle(135);
53 QPointF tl(qFloor(radius.p2().x()), qFloor(radius.p2().y()));
54 QPointF br = b.bottomRight() - tl;
55 // QRect interprets bottomRight differently (unsuitable) for "historical reasons",
56 // so construct a QRectF and convert to QRect
57 QRect r = QRectF(tl, br).toRect();
58 r.translate(pos());
59 return r;
60}
61
63{
64 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(geom.contains(geometry()), geom);
65 int sizeValue = qMin(width(), height());
66 QRect b(0, 0, sizeValue, sizeValue);
67 QPointF tl = QPointF (b.topLeft().x()+m_barWidth, b.topLeft().y()+m_barWidth);
68 QPointF br = QPointF (b.bottomRight().x()-m_barWidth, b.bottomRight().y()-m_barWidth);
69 QRect r(tl.toPoint(), br.toPoint());
70 r.translate(pos());
71 return r;
72}
73
75{
76 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(geom.contains(geometry()), geom);
77 int sizeValue = qMin(width(), height());
78 QPointF center(0.5 * width(), 0.5 * height());
79 qreal radius = 0.5 * sizeValue - (m_barWidth + 4);
80 QLineF rLine(center, QPointF(center.x() + radius, center.y()));
81 rLine.setAngle(330);
82 QPoint br(rLine.p2().toPoint());
83 QPoint tl(width() - br.x(), m_barWidth + 4);
84 // can't use QRect(tl, br) constructor because it interprets br unsuitably for "historical reasons"
85 QRect bound(tl, QSize(br.x() - tl.x(), br.y() - tl.y()));
86 bound.adjust(-5, -5, 5, 5);
87 bound.translate(pos());
88 return bound;
89}
90
95
97{
98 if (supportsGamutMask()) {
101 if (mask) {
104 }
105 update();
106 }
107}
108
110{
111 qreal offset = 7.0;
112 qreal a = (qreal)width()*0.5;
113 QPointF center(a, a);
114 QLineF line(center, QPoint((m_barWidth*0.5),a));
115 qreal angle = coordinate.x()*360.0;
116 angle = 360.0 - fmod(angle+180.0, 360.0);
118 angle = (coordinate.x()/2)*360.0;
119 angle = fmod((angle+270.0), 360.0);
120 }
121 line.setAngle(angle);
123 line.setLength(qMin(coordinate.y()*(a-offset), a-offset));
124 }
125 return line.p2();
126}
127
129{
130 //default implementation:
131 qreal x = 0.5;
132 qreal y = 1.0;
133 qreal offset = 7.0;
134 QPointF center = QRectF(QPointF(0.0, 0.0), this->size()).center();
135 qreal a = (qreal(this->width()) / qreal(2));
136 qreal xRel = center.x()-coordinate.x();
137 qreal yRel = center.y()-coordinate.y();
138 qreal radius = sqrt(xRel*xRel+yRel*yRel);
139
141 qreal angle = atan2(yRel, xRel);
142 angle = kisRadiansToDegrees(angle);
143 angle = fmod(angle+360, 360.0);
144 x = angle/360.0;
146 y = qBound(0.0,radius/(a-offset), 1.0);
147 }
148
149 } else {
150 qreal angle = atan2(xRel, yRel);
151 angle = kisRadiansToDegrees(angle);
152 angle = fmod(angle+180, 360.0);
153 if (angle>180.0) {
154 angle = 360.0-angle;
155 }
156 x = (angle/360.0)*2;
158 y = qBound(0.0,(radius+offset)/a, 1.0);
159 }
160 }
161
162 return QPointF(x, y);
163}
164
165QPointF KisVisualEllipticalSelectorShape::mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const
166{
167 QPointF pos2(pos);
169 qreal h_center = width()/2.0;
170 bool start_left = dragStart.x() < h_center;
171 bool cursor_left = pos.x() < h_center;
172 if (start_left != cursor_left) {
173 pos2.setX(h_center);
174 }
175 }
178 if (mask) {
179 QPointF maskPoint = m_gamutMaskTransform.map(pos);
180 if (!mask->coordIsClear(maskPoint, true)) {
181 // Ideally we try to find the closest point on the mask border, possibly
182 // depending on dragStart. Currently just returns old position.
183 return getCursorPosition();
184 }
185 }
186 }
188}
189
191{
192 QRegion mask = QRegion(0,0,width(),height(), QRegion::Ellipse);
194 mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2), QRegion::Ellipse));
195 }
196 return mask;
197}
198
199QImage KisVisualEllipticalSelectorShape::renderAlphaMaskImpl(qreal outerBorder, qreal innerBorder) const
200{
201 // Hi-DPI aware rendering requires that we determine the device pixel dimension;
202 // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller...
203 const int deviceWidth = qCeil(width() * devicePixelRatioF());
204 const int deviceHeight = qCeil(height() * devicePixelRatioF());
205
206 QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8);
207 alphaMask.fill(0);
208 alphaMask.setDevicePixelRatio(devicePixelRatioF());
209 QPainter painter(&alphaMask);
210 painter.setRenderHint(QPainter::Antialiasing);
211 painter.setBrush(Qt::white);
212 painter.setPen(Qt::NoPen);
213 QRectF circle(outerBorder, outerBorder, width() - 2*outerBorder, height() - 2*outerBorder);
214 painter.drawEllipse(circle);
215
216 //painter.setBrush(Qt::black);
217 if (innerBorder > outerBorder) {
218 circle = QRectF(innerBorder, innerBorder, width() - 2*innerBorder, height() - 2*innerBorder);
219 painter.setCompositionMode(QPainter::CompositionMode_Clear);
220 painter.drawEllipse(circle);
221 }
222 return alphaMask;
223}
224
226{
229 return QImage();
230 }
231 qreal outerBorder = KVESS_MARGIN;
232 qreal innerBorder = -1;
234 outerBorder += 0.25 * (m_barWidth - 4);
235 }
237 innerBorder = m_barWidth - 2;
238 }
239 return renderAlphaMaskImpl(outerBorder, innerBorder);
240}
241
243{
246 return QImage();
247 }
248 qreal innerBorder = m_barWidth - 2;
250 innerBorder = KVESS_MARGIN + 1 + 0.25 * (m_barWidth - 4);
251 }
252 return renderAlphaMaskImpl(KVESS_MARGIN, innerBorder);
253}
254
256{
258
259 if (!mask) {
260 m_gamutMaskImage = QImage();
261 return;
262 }
263 const int deviceWidth = qCeil(width() * devicePixelRatioF());
264 const int deviceHeight = qCeil(height() * devicePixelRatioF());
265
266 if (m_gamutMaskImage.size() != QSize(deviceWidth, deviceHeight)) {
267 m_gamutMaskImage = QImage(deviceWidth, deviceHeight, QImage::Format_ARGB32_Premultiplied);
268 m_gamutMaskImage.setDevicePixelRatio(devicePixelRatioF());
269 }
270 m_gamutMaskImage.fill(0);
271
272 QPainter painter(&m_gamutMaskImage);
273 QPen pen(Qt::white);
274 painter.setRenderHint(QPainter::Antialiasing, true);
275 painter.translate(KVESS_MARGIN, KVESS_MARGIN);
276 painter.setBrush(QColor(0, 0, 0, 128));
277 painter.setPen(pen);
278
279 painter.drawEllipse(QRectF(0, 0, width() - 2*KVESS_MARGIN, height() - 2*KVESS_MARGIN));
280
281 painter.setTransform(mask->maskToViewTransform(width() - 2*KVESS_MARGIN), true);
282 painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
283 mask->paint(painter, true);
284
285 // TODO: implement a way to render gamut mask outline with custom pen
286 // determine how many units 1 pixel is now:
287 //QLineF measure = painter.transform().map(QLineF(0.0, 0.0, 1.0, 0.0));
288 //pen.setWidthF(1.0 / measure.length());
289 //painter.setPen(pen);
290 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
291 mask->paintStroke(painter, true);
292
294}
295
297{
298 //qDebug() << this << "KisVisualEllipticalSelectorShape::drawCursor: image needs update" << imagesNeedUpdate();
301 QBrush fill(Qt::SolidPattern);
302
303 int cursorwidth = 5;
304
306 painter.setPen(Qt::white);
307 fill.setColor(Qt::white);
308 painter.setBrush(fill);
309 painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
310 QPointF mirror(width() - cursorPoint.x(), cursorPoint.y());
311 painter.drawEllipse(mirror, cursorwidth, cursorwidth);
312 fill.setColor(col);
313 painter.setPen(Qt::black);
314 painter.setBrush(fill);
315 painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1);
316 painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1);
317
318 } else {
319 painter.setPen(Qt::white);
320 fill.setColor(Qt::white);
321 painter.setBrush(fill);
322 painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
323 fill.setColor(col);
324 painter.setPen(Qt::black);
325 painter.setBrush(fill);
326 painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0);
327 }
328}
329
331{
334 }
335 painter.drawImage(0, 0, m_gamutMaskImage);
336}
The KisVisualColorSelectorShape class A 2d widget can represent at maximum 2 coordinates....
KisVisualColorSelector * colorSelector() const
void forceImageUpdate()
forceImageUpdate force the image to recache.
QPointF getCursorPosition() const
getCursorPosition
QColor getColorFromConverter(KoColor c)
getColorFromConverter
Dimensions getDimensions() const
getDimensions
Dimensions
The Dimensions enum Whether or not the shape is single or two dimensional.
The KisVisualColorSelector class.
KoGamutMask * activeGamutMask() const
QImage renderAlphaMask() const override
render the alpha mask for the widget background the returned image is expected to be QImage::Format_A...
void setBorderWidth(int width) override
setBorderWidth set the border of the single dimensional selector.
void updateGamutMask() override
Notify shape that the gamut mask changed.
QPointF convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const override
convertWidgetCoordinateToShapeCoordinate Convert a coordinate in the widget's height/width to a shape...
QImage renderAlphaMaskImpl(qreal outerBorder, qreal innerBorder) const
KisVisualEllipticalSelectorShape(KisVisualColorSelector *parent, Dimensions dimension, int channel1, int channel2, int barWidth=20, KisVisualEllipticalSelectorShape::singelDTypes d=KisVisualEllipticalSelectorShape::border)
QRect getSpaceForSquare(QRect geom) override
getSpaceForSquare
QPointF mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const override
default implementation just calls convertWidgetCoordinateToShapeCoordinate(pos)
QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override
convertShapeCoordinateToWidgetCoordinate
The resource type for gamut masks used by the artistic color selector.
Definition KoGamutMask.h:44
void paint(QPainter &painter, bool preview)
QTransform viewToMaskTransform(qreal viewSize)
QTransform maskToViewTransform(qreal viewSize)
void paintStroke(QPainter &painter, bool preview)
bool coordIsClear(const QPointF &coord, bool preview)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181