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 customPen = m_painter->pen();
91 customPen.setCosmetic(true);
92 customPen.setWidth(4);
93 m_painter->setPen(customPen);
94
96 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
97 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
98 m_painter->drawPolygon(handlePolygon);
99 }
100
101 m_painter->setPen(originalPen);
102}
103
104void KisHandlePainterHelper::drawHandleCircle(const QPointF &center, qreal radius) {
106
107 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius);
108 handleRect.translate(m_painterTransform.map(center));
109
111 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
112 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
113 m_painter->drawEllipse(handleRect);
114 }
115}
116
118{
120}
121
123{
124 drawHandleCircle(center, 0.7 * m_handleRadius);
125}
126
127void KisHandlePainterHelper::drawHandleLine(const QLineF &line, qreal width, QVector<qreal> dashPattern, qreal dashOffset)
128{
130
131 QPainterPath p;
132 p.moveTo(m_painterTransform.map(line.p1()));
133 p.lineTo(m_painterTransform.map(line.p2()));
134 QPainterPathStroker s;
135 s.setWidth(width);
136 if (!dashPattern.isEmpty()) {
137 s.setDashPattern(dashPattern);
138 s.setDashOffset(dashOffset);
139 }
140 s.setCapStyle(Qt::RoundCap);
141 s.setJoinStyle(Qt::RoundJoin);
142 p = s.createStroke(p);
143
145 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
146 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
147 m_painter->strokePath(p, m_painter->pen());
148 m_painter->fillPath(p, m_painter->brush());
149 }
150}
151
152void KisHandlePainterHelper::drawHandleRect(const QPointF &center) {
154 QPolygonF paintingPolygon = m_handlePolygon.translated(m_painterTransform.map(center));
155
157 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
158 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
159 m_painter->drawPolygon(paintingPolygon);
160 }
161}
162
163void KisHandlePainterHelper::drawGradientHandle(const QPointF &center, qreal radius) {
165
166 QPolygonF handlePolygon;
167
168 handlePolygon << QPointF(-radius, 0);
169 handlePolygon << QPointF(0, radius);
170 handlePolygon << QPointF(radius, 0);
171 handlePolygon << QPointF(0, -radius);
172
173 handlePolygon = m_handleTransform.map(handlePolygon);
174 handlePolygon.translate(m_painterTransform.map(center));
175
177 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
178 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
179 m_painter->drawPolygon(handlePolygon);
180 }
181}
182
184{
185 drawGradientHandle(center, 1.41 * m_handleRadius);
186}
187
188void KisHandlePainterHelper::drawGradientCrossHandle(const QPointF &center, qreal radius) {
190
191 { // Draw a cross
192 QPainterPath p;
193 p.moveTo(-radius, -radius);
194 p.lineTo(radius, radius);
195 p.moveTo(radius, -radius);
196 p.lineTo(-radius, radius);
197
198 p = m_handleTransform.map(p);
199 p.translate(m_painterTransform.map(center));
200
202 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
203 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
204 m_painter->drawPath(p);
205 }
206 }
207
208 { // Draw a square
209 const qreal halfRadius = 0.5 * radius;
210
211 QPolygonF handlePolygon;
212 handlePolygon << QPointF(-halfRadius, 0);
213 handlePolygon << QPointF(0, halfRadius);
214 handlePolygon << QPointF(halfRadius, 0);
215 handlePolygon << QPointF(0, -halfRadius);
216
217 handlePolygon = m_handleTransform.map(handlePolygon);
218 handlePolygon.translate(m_painterTransform.map(center));
219
221 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
222 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
223 m_painter->drawPolygon(handlePolygon);
224 }
225 }
226}
227
228void KisHandlePainterHelper::drawArrow(const QPointF &pos, const QPointF &from, qreal radius)
229{
231
232 QPainterPath p;
233
234 QLineF line(pos, from);
235 line.setLength(radius);
236
237 QPointF norm = KisAlgebra2D::leftUnitNormal(pos - from);
238 norm *= 0.34 * radius;
239
240 p.moveTo(line.p2() + norm);
241 p.lineTo(line.p1());
242 p.lineTo(line.p2() - norm);
243
244 p.translate(-pos);
245
246 p = m_handleTransform.map(p).translated(m_painterTransform.map(pos));
247
249 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
250 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
251 m_painter->drawPath(p);
252 }
253}
254
255void KisHandlePainterHelper::drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius)
256{
258
259 QPainterPath p;
260 p.moveTo(start);
261 p.lineTo(end);
262 p = m_painterTransform.map(p);
263
265 it.stylePair.first.setWidthF(it.stylePair.first.widthF()*m_decorationThickness);
266 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
267 m_painter->drawPath(p);
268 }
269
270 const qreal length = kisDistance(start, end);
271 const QPointF diff = end - start;
272
273 if (length > 5 * radius) {
274 drawArrow(start + 0.33 * diff, start, radius);
275 drawArrow(start + 0.66 * diff, start, radius);
276 } else if (length > 3 * radius) {
277 drawArrow(start + 0.5 * diff, start, radius);
278 }
279}
280
281void KisHandlePainterHelper::drawRubberLine(const QPolygonF &poly) {
283
284 QPolygonF paintingPolygon = m_painterTransform.map(poly);
285
287 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
288 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
289 m_painter->drawPolygon(paintingPolygon);
290 }
291}
292
294{
295 drawConnectionLine(line.p1(), line.p2());
296}
297
298void KisHandlePainterHelper::drawConnectionLine(const QPointF &p1, const QPointF &p2)
299{
301
302 QPointF realP1 = m_painterTransform.map(p1);
303 QPointF realP2 = m_painterTransform.map(p2);
304
306 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
307 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
308 m_painter->drawLine(realP1, realP2);
309 }
310}
311
312void KisHandlePainterHelper::drawPath(const QPainterPath &path)
313{
314 const QPainterPath realPath = m_painterTransform.map(path);
315
317 it.stylePair.first.setWidthF(it.stylePair.first.widthF() * m_decorationThickness);
318 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop);
319 m_painter->drawPath(realPath);
320 }
321}
322
323void KisHandlePainterHelper::drawPixmap(const QPixmap &pixmap, QPointF position, int size, QRectF sourceRect)
324{
325 QPointF handlePolygon = m_painterTransform.map(position);
326
327 QPoint offsetPosition(0, 40);
328 handlePolygon += offsetPosition;
329
330 handlePolygon -= QPointF(size*0.5,size*0.5);
331
332 m_painter->drawPixmap(QRect(handlePolygon.x(), handlePolygon.y(),
333 size, size),
334 pixmap,
335 sourceRect);
336}
337
338void KisHandlePainterHelper::fillHandleRect(const QPointF &center, qreal radius, QColor fillColor, QPoint offset = QPoint(0,0))
339{
341
342 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius);
343 QPolygonF handlePolygon = m_handleTransform.map(QPolygonF(handleRect));
344 handlePolygon.translate(m_painterTransform.map(center));
345
346 QPainterPath painterPath;
347 painterPath.addPolygon(handlePolygon);
348
349 // offset that happens after zoom transform. This means the offset will be the same, no matter the zoom level
350 // this is good for UI elements that need to be below the bounding box
351 painterPath.translate(offset);
352
353 const QPainterPath pathToSend = painterPath;
354 const QBrush brushStyle(fillColor);
355 m_painter->fillPath(pathToSend, brushStyle);
356}
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