Krita Source Code Documentation
Loading...
Searching...
No Matches
KisHandlePainterHelper.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QPainter>
10#include <QPainterPath>
11#include "kis_algebra_2d.h"
12#include "kis_painting_tweaks.h"
13
15
16KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, qreal handleRadius, int decorationThickness)
17 : m_painter(_painter),
18 m_originalPainterTransform(m_painter->transform()),
19 m_painterTransform(m_painter->transform()),
20 m_handleRadius(handleRadius),
21 m_decorationThickness(decorationThickness),
22 m_decomposedMatrix(m_painterTransform)
23{
24 init();
25}
26
27KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, const QTransform &originalPainterTransform, qreal handleRadius, int decorationThickness)
28 : m_painter(_painter),
29 m_originalPainterTransform(originalPainterTransform),
30 m_painterTransform(m_painter->transform()),
31 m_handleRadius(handleRadius),
32 m_decorationThickness(decorationThickness),
33 m_decomposedMatrix(m_painterTransform)
34{
35 init();
36}
37
39 : m_painter(rhs.m_painter),
40 m_originalPainterTransform(rhs.m_originalPainterTransform),
41 m_painterTransform(rhs.m_painterTransform),
42 m_handleRadius(rhs.m_handleRadius),
43 m_decorationThickness(rhs.m_decorationThickness),
44 m_decomposedMatrix(rhs.m_decomposedMatrix),
45 m_handleTransform(rhs.m_handleTransform),
46 m_handlePolygon(rhs.m_handlePolygon),
47 m_handleStyle(rhs.m_handleStyle)
48{
49 // disable the source helper
50 rhs.m_painter = 0;
51}
52
54{
56
57 m_painter->setTransform(QTransform());
59
60 if (m_handleRadius > 0.0) {
61 const QRectF handleRect(-m_handleRadius, -m_handleRadius, 2 * m_handleRadius, 2 * m_handleRadius);
62 m_handlePolygon = m_handleTransform.map(QPolygonF(handleRect));
63 }
64}
65
71
76
77void KisHandlePainterHelper::drawHandleRect(const QPointF &center, qreal radius, QPoint offset = QPoint(0,0))
78{
80
81 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius);
82 QPolygonF handlePolygon = m_handleTransform.map(QPolygonF(handleRect));
83 handlePolygon.translate(m_painterTransform.map(center));
84
85 handlePolygon.translate(offset);
86
87 const QPen originalPen = m_painter->pen();
88
89 // temporarily set the pen width to 2 to avoid pixel shifting dropping pixels the border
90 QPen *tempPen = new QPen(m_painter->pen());
91 tempPen->setCosmetic(true);
92 tempPen->setWidth(4 * m_decorationThickness);
93 const QPen customPen = *tempPen;
94 m_painter->setPen(customPen);
95
96
98 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
99 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
100 m_painter->drawPolygon(handlePolygon);
101 }
102
103 m_painter->setPen(originalPen);
104}
105
106void KisHandlePainterHelper::drawHandleCircle(const QPointF &center, qreal radius) {
108
109 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius);
110 handleRect.translate(m_painterTransform.map(center));
111
113 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
114 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
115 m_painter->drawEllipse(handleRect);
116 }
117}
118
120{
122}
123
125{
126 drawHandleCircle(center, 0.7 * m_handleRadius);
127}
128
129void KisHandlePainterHelper::drawHandleLine(const QLineF &line, qreal width, QVector<qreal> dashPattern, qreal dashOffset)
130{
132
133 QPainterPath p;
134 p.moveTo(m_painterTransform.map(line.p1()));
135 p.lineTo(m_painterTransform.map(line.p2()));
136 QPainterPathStroker s;
137 s.setWidth(width);
138 if (!dashPattern.isEmpty()) {
139 s.setDashPattern(dashPattern);
140 s.setDashOffset(dashOffset);
141 }
142 s.setCapStyle(Qt::RoundCap);
143 s.setJoinStyle(Qt::RoundJoin);
144 p = s.createStroke(p);
145
147 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
148 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
149 m_painter->strokePath(p, m_painter->pen());
150 m_painter->fillPath(p, m_painter->brush());
151 }
152}
153
154void KisHandlePainterHelper::drawHandleRect(const QPointF &center) {
156 QPolygonF paintingPolygon = m_handlePolygon.translated(m_painterTransform.map(center));
157
159 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
160 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
161 m_painter->drawPolygon(paintingPolygon);
162 }
163}
164
165void KisHandlePainterHelper::drawGradientHandle(const QPointF &center, qreal radius) {
167
168 QPolygonF handlePolygon;
169
170 handlePolygon << QPointF(-radius, 0);
171 handlePolygon << QPointF(0, radius);
172 handlePolygon << QPointF(radius, 0);
173 handlePolygon << QPointF(0, -radius);
174
175 handlePolygon = m_handleTransform.map(handlePolygon);
176 handlePolygon.translate(m_painterTransform.map(center));
177
179 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
180 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
181 m_painter->drawPolygon(handlePolygon);
182 }
183}
184
186{
187 drawGradientHandle(center, 1.41 * m_handleRadius);
188}
189
190void KisHandlePainterHelper::drawGradientCrossHandle(const QPointF &center, qreal radius) {
192
193 { // Draw a cross
194 QPainterPath p;
195 p.moveTo(-radius, -radius);
196 p.lineTo(radius, radius);
197 p.moveTo(radius, -radius);
198 p.lineTo(-radius, radius);
199
200 p = m_handleTransform.map(p);
201 p.translate(m_painterTransform.map(center));
202
204 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
205 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
206 m_painter->drawPath(p);
207 }
208 }
209
210 { // Draw a square
211 const qreal halfRadius = 0.5 * radius;
212
213 QPolygonF handlePolygon;
214 handlePolygon << QPointF(-halfRadius, 0);
215 handlePolygon << QPointF(0, halfRadius);
216 handlePolygon << QPointF(halfRadius, 0);
217 handlePolygon << QPointF(0, -halfRadius);
218
219 handlePolygon = m_handleTransform.map(handlePolygon);
220 handlePolygon.translate(m_painterTransform.map(center));
221
223 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
224 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
225 m_painter->drawPolygon(handlePolygon);
226 }
227 }
228}
229
230void KisHandlePainterHelper::drawArrow(const QPointF &pos, const QPointF &from, qreal radius)
231{
233
234 QPainterPath p;
235
236 QLineF line(pos, from);
237 line.setLength(radius);
238
239 QPointF norm = KisAlgebra2D::leftUnitNormal(pos - from);
240 norm *= 0.34 * radius;
241
242 p.moveTo(line.p2() + norm);
243 p.lineTo(line.p1());
244 p.lineTo(line.p2() - norm);
245
246 p.translate(-pos);
247
248 p = m_handleTransform.map(p).translated(m_painterTransform.map(pos));
249
251 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
252 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
253 m_painter->drawPath(p);
254 }
255}
256
257void KisHandlePainterHelper::drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius)
258{
260
261 QPainterPath p;
262 p.moveTo(start);
263 p.lineTo(end);
264 p = m_painterTransform.map(p);
265
267 it.stylePair.first.setWidthF(it.stylePair.first.widthF()*m_decorationThickness);
268 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
269 m_painter->drawPath(p);
270 }
271
272 const qreal length = kisDistance(start, end);
273 const QPointF diff = end - start;
274
275 if (length > 5 * radius) {
276 drawArrow(start + 0.33 * diff, start, radius);
277 drawArrow(start + 0.66 * diff, start, radius);
278 } else if (length > 3 * radius) {
279 drawArrow(start + 0.5 * diff, start, radius);
280 }
281}
282
283void KisHandlePainterHelper::drawRubberLine(const QPolygonF &poly) {
285
286 QPolygonF paintingPolygon = m_painterTransform.map(poly);
287
289 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
290 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
291 m_painter->drawPolygon(paintingPolygon);
292 }
293}
294
296{
297 drawConnectionLine(line.p1(), line.p2());
298}
299
300void KisHandlePainterHelper::drawConnectionLine(const QPointF &p1, const QPointF &p2)
301{
303
304 QPointF realP1 = m_painterTransform.map(p1);
305 QPointF realP2 = m_painterTransform.map(p2);
306
308 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
309 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
310 m_painter->drawLine(realP1, realP2);
311 }
312}
313
314void KisHandlePainterHelper::drawPath(const QPainterPath &path)
315{
316 const QPainterPath realPath = m_painterTransform.map(path);
317
319 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
320 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
321 m_painter->drawPath(realPath);
322 }
323}
324
325void KisHandlePainterHelper::drawPixmap(const QPixmap &pixmap, QPointF position, int size, QRectF sourceRect)
326{
327 QPointF handlePolygon = m_painterTransform.map(position);
328
329 QPoint offsetPosition(0, 40);
330 handlePolygon += offsetPosition;
331
332 handlePolygon -= QPointF(size*0.5,size*0.5);
333
334 m_painter->drawPixmap(QRect(handlePolygon.x(), handlePolygon.y(),
335 size, size),
336 pixmap,
337 sourceRect);
338}
339
340void KisHandlePainterHelper::fillHandleRect(const QPointF &center, qreal radius, QColor fillColor, QPoint offset = QPoint(0,0))
341{
343
344 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius);
345 QPolygonF handlePolygon = m_handleTransform.map(QPolygonF(handleRect));
346 handlePolygon.translate(m_painterTransform.map(center));
347
348 QPainterPath painterPath;
349 painterPath.addPolygon(handlePolygon);
350
351 // offset that happens after zoom transform. This means the offset will be the same, no matter the zoom level
352 // this is good for UI elements that need to be below the bounding box
353 painterPath.translate(offset);
354
355 const QPainterPath pathToSend = painterPath;
356 const QBrush brushStyle(fillColor);
357 m_painter->fillPath(pathToSend, brushStyle);
358}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
const Params2D p
QPointF p2
QPointF p1
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawHandleSmallCircle(const QPointF &center)
void drawPath(const QPainterPath &path)
void drawArrow(const QPointF &pos, const QPointF &from, qreal radius)
void drawGradientCrossHandle(const QPointF &center, qreal radius)
void drawHandleRect(const QPointF &center, qreal radius)
void drawConnectionLine(const QLineF &line)
void fillHandleRect(const QPointF &center, qreal radius, QColor fillColor, QPoint offset)
KisHandlePainterHelper(QPainter *_painter, qreal handleRadius=0.0, int decorationThickness=1)
KisAlgebra2D::DecomposedMatrix m_decomposedMatrix
void setHandleStyle(const KisHandleStyle &style)
void drawGradientHandle(const QPointF &center, qreal radius)
void drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius)
void drawRubberLine(const QPolygonF &poly)
void drawPixmap(const QPixmap &pixmap, QPointF position, int size, QRectF sourceRect)
void drawHandleLine(const QLineF &line, qreal width=1.0, QVector< qreal > dashPattern={}, qreal dashOffset=0.0)
void drawHandleCircle(const QPointF &center, qreal radius)
static KisHandleStyle & inheritStyle()
QVector< IterationStyle > lineIterations
QVector< IterationStyle > handleIterations
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T leftUnitNormal(const T &a)
QTransform shearTransform() const
QTransform rotateTransform() const
QPair< QPen, QBrush > stylePair