Krita Source Code Documentation
Loading...
Searching...
No Matches
KoMarker.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2011 Thorsten Zachmann <zachmann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "KoMarker.h"
8
9#include <KoXmlNS.h>
10#include "KoPathShape.h"
11#include "KoPathShapeLoader.h"
14#include "KoShapePainter.h"
15#include <KoShapeStroke.h>
17#include <KoColorBackground.h>
18
19
20#include <QString>
21#include <QUrl>
22#include <QPainterPath>
23#include <QPainter>
24
25#include "kis_global.h"
26#include "kis_algebra_2d.h"
27
28class Q_DECL_HIDDEN KoMarker::Private
29{
30public:
32 : coordinateSystem(StrokeWidth),
33 referenceSize(3,3),
34 hasAutoOrientation(false),
35 explicitOrientation(0)
36 {}
37
39 // shape manager that is stored in the painter should be destroyed
40 // before the shapes themselves
41 shapePainter.reset();
42 qDeleteAll(shapes);
43 }
44
45 bool operator==(const KoMarker::Private &other) const
46 {
47 // WARNING: comparison of shapes is extremely fuzzy! Don't
48 // trust it in life-critical cases!
49
50 return name == other.name &&
51 coordinateSystem == other.coordinateSystem &&
52 referencePoint == other.referencePoint &&
53 referenceSize == other.referenceSize &&
54 hasAutoOrientation == other.hasAutoOrientation &&
55 explicitOrientation == other.explicitOrientation &&
56 compareShapesTo(other.shapes);
57 }
58
59 Private(const Private &rhs)
60 : name(rhs.name),
61 coordinateSystem(rhs.coordinateSystem),
62 referencePoint(rhs.referencePoint),
63 referenceSize(rhs.referenceSize),
64 hasAutoOrientation(rhs.hasAutoOrientation),
65 explicitOrientation(rhs.explicitOrientation)
66 {
67 Q_FOREACH (KoShape *shape, rhs.shapes) {
68 shapes << shape->cloneShape();
69 }
70 }
71
72 QString name;
76
79
81 QScopedPointer<KoShapePainter> shapePainter;
82
83 bool compareShapesTo(const QList<KoShape*> other) const {
84 if (shapes.size() != other.size()) return false;
85
86 for (int i = 0; i < shapes.size(); i++) {
87 if (shapes[i]->outline() != other[i]->outline() ||
88 shapes[i]->absoluteTransformation() != other[i]->absoluteTransformation()) {
89
90 return false;
91 }
92 }
93
94 return true;
95 }
96
97 QTransform markerTransform(qreal strokeWidth, qreal nodeAngle, const QPointF &pos = QPointF()) {
98 const QTransform translate = QTransform::fromTranslate(-referencePoint.x(), -referencePoint.y());
99
100 QTransform t = translate;
101
102 if (coordinateSystem == StrokeWidth) {
103 t *= QTransform::fromScale(strokeWidth, strokeWidth);
104 }
105
106 const qreal angle = hasAutoOrientation ? nodeAngle : explicitOrientation;
107 if (angle != 0.0) {
108 QTransform r;
109 r.rotateRadians(angle);
110 t *= r;
111 }
112
113 t *= QTransform::fromTranslate(pos.x(), pos.y());
114
115 return t;
116 }
117};
118
120: d(new Private())
121{
122}
123
125{
126 delete d;
127}
128
129QString KoMarker::name() const
130{
131 return d->name;
132}
133
135 : QSharedData(rhs),
136 d(new Private(*rhs.d))
137{
138}
139
140bool KoMarker::operator==(const KoMarker &other) const
141{
142 return *d == *other.d;
143}
144
149
151{
152 return d->coordinateSystem;
153}
154
156{
158
159 if (value == "userSpaceOnUse") {
160 result = UserSpaceOnUse;
161 }
162
163 return result;
164}
165
167{
168 return
169 value == StrokeWidth ?
170 "strokeWidth" :
171 "userSpaceOnUse";
172}
173
175{
176 d->referencePoint = value;
177}
178
179QPointF KoMarker::referencePoint() const
180{
181 return d->referencePoint;
182}
183
184void KoMarker::setReferenceSize(const QSizeF &size)
185{
186 d->referenceSize = size;
187}
188
189QSizeF KoMarker::referenceSize() const
190{
191 return d->referenceSize;
192}
193
195{
196 return d->hasAutoOrientation;
197}
198
200{
201 d->hasAutoOrientation = value;
202}
203
205{
206 return d->explicitOrientation;
207}
208
210{
211 d->explicitOrientation = value;
212}
213
215{
216 d->shapes = shapes;
217
218 if (d->shapePainter) {
219 d->shapePainter->setShapes(shapes);
220 }
221}
222
224{
225 return d->shapes;
226}
227
228void KoMarker::paintAtPosition(QPainter *painter, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
229{
230 QTransform oldTransform = painter->transform();
231
232 if (!d->shapePainter) {
233 d->shapePainter.reset(new KoShapePainter());
234 d->shapePainter->setShapes(d->shapes);
235 }
236
237 painter->setTransform(d->markerTransform(strokeWidth, nodeAngle, pos), true);
238 d->shapePainter->paint(*painter);
239
240 painter->setTransform(oldTransform);
241}
242
243qreal KoMarker::maxInset(qreal strokeWidth) const
244{
245 QRectF shapesBounds = boundingRect(strokeWidth, 0.0); // normalized to 0,0
246 qreal result = 0.0;
247
248 result = qMax(KisAlgebra2D::norm(shapesBounds.topLeft()), result);
249 result = qMax(KisAlgebra2D::norm(shapesBounds.topRight()), result);
250 result = qMax(KisAlgebra2D::norm(shapesBounds.bottomLeft()), result);
251 result = qMax(KisAlgebra2D::norm(shapesBounds.bottomRight()), result);
252
253 return result;
254}
255
256QRectF KoMarker::boundingRect(qreal strokeWidth, qreal nodeAngle) const
257{
258 QRectF shapesBounds = KoShape::boundingRect(d->shapes);
259
260 const QTransform t = d->markerTransform(strokeWidth, nodeAngle);
261
262 if (!t.isIdentity()) {
263 shapesBounds = t.mapRect(shapesBounds);
264 }
265
266 return shapesBounds;
267}
268
269QPainterPath KoMarker::outline(qreal strokeWidth, qreal nodeAngle) const
270{
271 QPainterPath outline;
272 Q_FOREACH (KoShape *shape, d->shapes) {
273 outline |= shape->absoluteTransformation().map(shape->outline());
274 }
275
276 const QTransform t = d->markerTransform(strokeWidth, nodeAngle);
277
278 if (!t.isIdentity()) {
279 outline = t.map(outline);
280 }
281
282 return outline;
283}
284
285void KoMarker::drawPreview(QPainter *painter, const QRectF &previewRect, const QPen &pen, KoFlake::MarkerPosition position)
286{
287 const QRectF outlineRect = outline(pen.widthF(), 0).boundingRect(); // normalized to 0,0
288 QPointF marker;
289 QPointF start;
290 QPointF end;
291
292 if (position == KoFlake::StartMarker) {
293 marker = QPointF(-outlineRect.left() + previewRect.left(), previewRect.center().y());
294 start = marker;
295 end = QPointF(previewRect.right(), start.y());
296 } else if (position == KoFlake::MidMarker) {
297 start = QPointF(previewRect.left(), previewRect.center().y());
298 marker = QPointF(-outlineRect.center().x() + previewRect.center().x(), start.y());
299 end = QPointF(previewRect.right(), start.y());
300 } else if (position == KoFlake::EndMarker) {
301 start = QPointF(previewRect.left(), previewRect.center().y());
302 marker = QPointF(-outlineRect.right() + previewRect.right(), start.y());
303 end = marker;
304 }
305
306 painter->save();
307 painter->setPen(pen);
308 painter->setClipRect(previewRect);
309
310 painter->drawLine(start, end);
311 paintAtPosition(painter, marker, pen.widthF(), 0);
312
313 painter->restore();
314}
315
316void KoMarker::applyShapeStroke(const KoShape *parentShape, KoShapeStroke *stroke, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
317{
318 const QGradient *originalGradient = stroke->lineBrush().gradient();
319
320 if (!originalGradient) {
321 QList<KoShape*> linearizedShapes = KoShape::linearizeSubtree(d->shapes);
322 Q_FOREACH(KoShape *shape, linearizedShapes) {
323 // update the stroke
324 KoShapeStrokeSP shapeStroke = shape->stroke() ?
325 qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke()) :
327
328 if (shapeStroke) {
329 shapeStroke = toQShared(new KoShapeStroke(*shapeStroke));
330
331 shapeStroke->setLineBrush(QBrush());
332 shapeStroke->setColor(stroke->color());
333
334 shape->setStroke(shapeStroke);
335 }
336
337 // update the background
338 if (shape->background()) {
340 shape->setBackground(bg);
341 }
342 }
343 } else {
344 QScopedPointer<QGradient> g(KoFlake::cloneGradient(originalGradient));
346
347 const QTransform markerTransformInverted =
348 d->markerTransform(strokeWidth, nodeAngle, pos).inverted();
349
350 QTransform gradientToUser;
351
352 // Unwrap the gradient to work in global mode
353 if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
354 QRectF boundingRect =
355 parentShape ?
356 parentShape->outline().boundingRect() :
357 this->boundingRect(strokeWidth, nodeAngle);
358
360
361 gradientToUser = QTransform(boundingRect.width(), 0, 0, boundingRect.height(),
362 boundingRect.x(), boundingRect.y());
363
364 g->setCoordinateMode(QGradient::LogicalMode);
365 }
366
367 QList<KoShape*> linearizedShapes = KoShape::linearizeSubtree(d->shapes);
368 Q_FOREACH(KoShape *shape, linearizedShapes) {
369 // shape-unwinding transform
370 QTransform t = gradientToUser * markerTransformInverted * shape->absoluteTransformation().inverted();
371
372 // update the stroke
373 KoShapeStrokeSP shapeStroke = shape->stroke() ?
374 qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke()) :
376
377 if (shapeStroke) {
378 shapeStroke = toQShared(new KoShapeStroke(*shapeStroke));
379
380 QBrush brush(*g);
381 brush.setTransform(t);
382 shapeStroke->setLineBrush(brush);
383 shapeStroke->setColor(Qt::transparent);
384 shape->setStroke(shapeStroke);
385 }
386
387 // update the background
388 if (shape->background()) {
389
391 shape->setBackground(bg);
392 }
393 }
394 }
395}
float value(const T *src, size_t ch)
QSharedPointer< KoShapeStroke > KoShapeStrokeSP
A simple solid color shape background.
A gradient shape background.
bool compareShapesTo(const QList< KoShape * > other) const
Definition KoMarker.cpp:83
Private(const Private &rhs)
Definition KoMarker.cpp:59
Private *const d
Definition KoMarker.h:105
void setCoordinateSystem(MarkerCoordinateSystem value)
Definition KoMarker.cpp:145
static MarkerCoordinateSystem coordinateSystemFromString(const QString &value)
Definition KoMarker.cpp:155
QRectF boundingRect(qreal strokeWidth, qreal nodeAngle) const
Definition KoMarker.cpp:256
bool operator==(const KoMarker::Private &other) const
Definition KoMarker.cpp:45
qreal explicitOrientation
Definition KoMarker.cpp:78
void paintAtPosition(QPainter *painter, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
paintAtOrigin paints the marker at the position pos. Scales and rotates the marker if needed.
Definition KoMarker.cpp:228
bool hasAutoOrientation
Definition KoMarker.cpp:77
void setAutoOrientation(bool value)
Definition KoMarker.cpp:199
MarkerCoordinateSystem coordinateSystem
Definition KoMarker.cpp:73
void applyShapeStroke(const KoShape *shape, KoShapeStroke *stroke, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
Definition KoMarker.cpp:316
static QString coordinateSystemToString(MarkerCoordinateSystem value)
Definition KoMarker.cpp:166
QPointF referencePoint
Definition KoMarker.cpp:74
void setReferenceSize(const QSizeF &size)
Definition KoMarker.cpp:184
QTransform markerTransform(qreal strokeWidth, qreal nodeAngle, const QPointF &pos=QPointF())
Definition KoMarker.cpp:97
QString name
Definition KoMarker.cpp:72
void setShapes(const QList< KoShape * > &shapes)
Definition KoMarker.cpp:214
void setReferencePoint(const QPointF &value)
Definition KoMarker.cpp:174
QScopedPointer< KoShapePainter > shapePainter
Definition KoMarker.cpp:81
void setExplicitOrientation(qreal value)
Definition KoMarker.cpp:209
void drawPreview(QPainter *painter, const QRectF &previewRect, const QPen &pen, KoFlake::MarkerPosition position)
Definition KoMarker.cpp:285
QList< KoShape * > shapes
Definition KoMarker.cpp:80
QPainterPath outline(qreal strokeWidth, qreal nodeAngle) const
Definition KoMarker.cpp:269
QSizeF referenceSize
Definition KoMarker.cpp:75
MarkerCoordinateSystem
Definition KoMarker.h:42
@ StrokeWidth
Definition KoMarker.h:43
@ UserSpaceOnUse
Definition KoMarker.h:44
qreal maxInset(qreal strokeWidth) const
Definition KoMarker.cpp:243
QBrush lineBrush() const
Returns the strokes brush.
virtual QPainterPath outline() const
Definition KoShape.cpp:630
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:335
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:1081
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:918
static QList< KoShape * > linearizeSubtree(const QList< KoShape * > &shapes)
Definition KoShape.cpp:1381
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
Definition KoShape.cpp:200
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
QSharedPointer< T > toQShared(T *ptr)
Rect ensureRectNotSmaller(Rect rc, const decltype(Rect().size()) &size)
qreal norm(const T &a)
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
Definition KoFlake.cpp:17
MarkerPosition
Definition KoFlake.h:41
@ EndMarker
Definition KoFlake.h:44
@ StartMarker
Definition KoFlake.h:42
@ MidMarker
Definition KoFlake.h:43