Krita Source Code Documentation
Loading...
Searching...
No Matches
KoShapeFillWrapper.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <KoShape.h>
10#include <QList>
11#include <QBrush>
12#include <KoColorBackground.h>
14#include <KoPatternBackground.h>
16#include <KoShapeStroke.h>
19#include <KoStopGradient.h>
20
21#include "kis_assert.h"
22#include "kis_debug.h"
23#include "kis_global.h"
24
25#include <KoFlakeUtils.h>
26
28{
30
33 return shape->background();
34 }
35 static Type type(KoShape *shape) {
37 QSharedPointer<KoColorBackground> colorBackground = qSharedPointerDynamicCast<KoColorBackground>(background);
38 QSharedPointer<KoGradientBackground> gradientBackground = qSharedPointerDynamicCast<KoGradientBackground>(background);
39 QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(background);
40 QSharedPointer<KoMeshGradientBackground> meshgradientBackground = qSharedPointerDynamicCast<KoMeshGradientBackground>(background);
41
42
43 if(gradientBackground) {
44 return Type::Gradient;
45 }
46
47 if (patternBackground) {
48 return Type::Pattern;
49 }
50
51 if (colorBackground) {
52 return Type::Solid;
53 }
54
55 if (meshgradientBackground) {
56 return Type::MeshGradient;
57 }
58
59 return Type::None;
60 }
61
62 static QColor color(KoShape *shape) {
63 QSharedPointer<KoColorBackground> colorBackground = qSharedPointerDynamicCast<KoColorBackground>(shape->background());
64 return colorBackground ? colorBackground->color() : QColor();
65 }
66
67 static const QGradient* gradient(KoShape *shape) {
68 QSharedPointer<KoGradientBackground> gradientBackground = qSharedPointerDynamicCast<KoGradientBackground>(shape->background());
69 return gradientBackground ? gradientBackground->gradient() : 0;
70 }
71
72 static QTransform gradientTransform(KoShape *shape) {
73 QSharedPointer<KoGradientBackground> gradientBackground = qSharedPointerDynamicCast<KoGradientBackground>(shape->background());
74 return gradientBackground ? gradientBackground->transform() : QTransform();
75 }
76
77 static const SvgMeshGradient* meshgradient(KoShape *shape) {
78 QSharedPointer<KoMeshGradientBackground> meshgradientBackground = qSharedPointerDynamicCast<KoMeshGradientBackground>(shape->background());
79 return meshgradientBackground ? meshgradientBackground->gradient() : nullptr;
80 }
81
82 static QTransform meshgradientTransform(KoShape *shape) {
83 QSharedPointer<KoMeshGradientBackground> meshgradientBackground = qSharedPointerDynamicCast<KoMeshGradientBackground>(shape->background());
84 return meshgradientBackground ? meshgradientBackground->transform() : QTransform();
85 }
86
88 return p1->compareTo(p2.data());
89 }
90};
91
93{
95
98 return shape->stroke();
99 }
100 static Type type(KoShape *shape) {
101 KoShapeStrokeSP stroke = qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke());
102 if (!stroke) return Type::None;
103
104 // Pattern type not implemented yet, so that logic will have to be added here later
105 if (stroke->lineBrush().gradient()) {
106 return Type::Gradient;
107 } else {
108
109 // strokes without any width are none
110 if (stroke->color().isValid() && stroke->lineWidth() != 0.0) {
111 return Type::Solid;
112 }
113
114 return Type::None;
115 }
116 }
117
118 static QColor color(KoShape *shape) {
119 KoShapeStrokeSP stroke = qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke());
120 return stroke ? stroke->color() : QColor();
121 }
122
123 static const QGradient* gradient(KoShape *shape) {
124 KoShapeStrokeSP stroke = qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke());
125 return stroke ? stroke->lineBrush().gradient() : 0;
126 }
127
128 static QTransform gradientTransform(KoShape *shape) {
129 KoShapeStrokeSP stroke = qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke());
130 return stroke ? stroke->lineBrush().transform() : QTransform();
131 }
132
134 return p1->compareFillTo(p2.data());
135 }
136};
137
138
139template <class Policy>
141{
142 if (shapes.size() == 1) return true;
143
144 typename Policy::PointerType bg =
145 Policy::getBackground(shapes.first());
146
147 Q_FOREACH (KoShape *shape, shapes) {
148 if (
149 !(
150 (!bg && !Policy::getBackground(shape)) ||
151 (bg && Policy::compareTo(bg, Policy::getBackground(shape)))
152 )) {
153
154 return false;
155 }
156 }
157
158 return true;
159}
160
161/******************************************************************************/
162/* KoShapeFillWrapper::Private */
163/******************************************************************************/
164
173
175{
176 QGradientStops stops = stopGradient->stops();
177
178 if (!shape || !stops.count()) {
180 }
181
182 KoGradientBackground *newGradient = 0;
183 QSharedPointer<KoGradientBackground> oldGradient = qSharedPointerDynamicCast<KoGradientBackground>(shape->background());
184 if (oldGradient) {
185 // just copy the gradient and set the new stops
186 QGradient *g = KoFlake::mergeGradient(oldGradient->gradient(), stopGradient);
187 newGradient = new KoGradientBackground(g);
188 newGradient->setTransform(oldGradient->transform());
189 }
190 else {
191 // No gradient yet, so create a new one.
192 QScopedPointer<QLinearGradient> fakeShapeGradient(new QLinearGradient(QPointF(0, 0), QPointF(1, 1)));
193 fakeShapeGradient->setCoordinateMode(QGradient::ObjectBoundingMode);
194
195 QGradient *g = KoFlake::mergeGradient(fakeShapeGradient.data(), stopGradient);
196 newGradient = new KoGradientBackground(g);
197 }
198 return QSharedPointer<KoGradientBackground>(newGradient);
199}
200
201void KoShapeFillWrapper::Private::applyFillGradientStops(KoShapeStrokeSP shapeStroke, const QGradient *stopGradient)
202{
203 QGradientStops stops = stopGradient->stops();
204 if (!stops.count()) return;
205
206 QLinearGradient fakeShapeGradient(QPointF(0, 0), QPointF(1, 1));
207 fakeShapeGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
208 QTransform gradientTransform;
209 const QGradient *shapeGradient = 0;
210
211 {
212 QBrush brush = shapeStroke->lineBrush();
213 gradientTransform = brush.transform();
214 shapeGradient = brush.gradient() ? brush.gradient() : &fakeShapeGradient;
215 }
216
217 {
218 QScopedPointer<QGradient> g(KoFlake::mergeGradient(shapeGradient, stopGradient));
219 QBrush newBrush = *g;
220 newBrush.setTransform(gradientTransform);
221 shapeStroke->setLineBrush(newBrush);
222 }
223}
224
225/******************************************************************************/
226/* KoShapeFillWrapper */
227/******************************************************************************/
228
230 : m_d(new Private())
231{
233 m_d->shapes << shape;
234 m_d->fillVariant= fillVariant;
235}
236
237
239 : m_d(new Private())
240{
241 KIS_SAFE_ASSERT_RECOVER_RETURN(!shapes.isEmpty());
242 m_d->shapes = shapes;
243 m_d->fillVariant= fillVariant;
244}
245
249
251{
252 if (m_d->shapes.isEmpty()) return false;
253
254 return m_d->fillVariant == KoFlake::Fill ?
255 !compareBackgrounds<ShapeBackgroundFetchPolicy>(m_d->shapes) :
256 !compareBackgrounds<ShapeStrokeFillFetchPolicy>(m_d->shapes);
257}
258
260{
261 if (m_d->shapes.isEmpty() || isMixedFill()) return KoFlake::None;
262
263 KoShape *shape = m_d->shapes.first();
265
266 KoFlake::FillType fillType;
267 if (m_d->fillVariant == KoFlake::Fill) {
268 // fill property of vector object
269 fillType = ShapeBackgroundFetchPolicy::type(shape);
270 } else {
271 // stroke property of vector object
272 fillType = ShapeStrokeFillFetchPolicy::type(shape);
273 }
274
275 return fillType;
276}
277
279{
280 // this check guarantees that the shapes list is not empty and
281 // the fill is not mixed!
282 if (type() != KoFlake::Solid) return QColor();
283
284 KoShape *shape = m_d->shapes.first();
286
287 return m_d->fillVariant == KoFlake::Fill ?
290}
291
292const QGradient* KoShapeFillWrapper::gradient() const
293{
294 // this check guarantees that the shapes list is not empty and
295 // the fill is not mixed!
296 if (type() != KoFlake::Gradient) return 0;
297
298 KoShape *shape = m_d->shapes.first();
300
301 return m_d->fillVariant == KoFlake::Fill ?
304}
305
307{
308 // this check guarantees that the shapes list is not empty and
309 // the fill is not mixed!
310 if (type() != KoFlake::Gradient) return QTransform();
311
312 KoShape *shape = m_d->shapes.first();
313 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, QTransform());
314
315 return m_d->fillVariant == KoFlake::Fill ?
318}
319
321{
322 if (type() != KoFlake::MeshGradient) return nullptr;
323
324 KoShape *shape = m_d->shapes.first();
326
327 return m_d->fillVariant == KoFlake::Fill ?
329 nullptr;
330}
331
333{
334 KUndo2Command *command = 0;
335
336 if (m_d->fillVariant == KoFlake::Fill) {
338
339 if (color.isValid()) {
341 }
342
344 command = new KoShapeBackgroundCommand(m_d->shapes, fill);
345 } else {
346 command = KoFlake::modifyShapesStrokes(m_d->shapes,
347 [color] (KoShapeStrokeSP stroke) {
348 stroke->setLineBrush(Qt::NoBrush);
349 stroke->setColor(color);
350
351 });
352 }
353
354 return command;
355}
356
358{
359 KUndo2Command *command = 0;
360
361 command = KoFlake::modifyShapesStrokes(m_d->shapes, [lineWidth](KoShapeStrokeSP stroke) {
362 stroke->setColor(Qt::transparent);
363 stroke->setLineWidth(lineWidth);
364
365 });
366
367 return command;
368}
369
370
372{
373 KoShape *shape = m_d->shapes.first();
374 if (!shape) return false;
375 if (m_d->fillVariant == KoFlake::Fill) return false;
376
377 // this check is useful to determine if
378 KoShapeStrokeSP stroke = qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke());
379 if (!stroke) return false;
380
381 if ( stroke->lineWidth() == 0.0) {
382 return true;
383 }
384
385 return false;
386}
387
388
389KUndo2Command *KoShapeFillWrapper::setGradient(const QGradient *gradient, const QTransform &transform)
390{
391 KUndo2Command *command = 0;
392
393 if (m_d->fillVariant == KoFlake::Fill) {
395
396 foreach (KoShape *shape, m_d->shapes) {
397 Q_UNUSED(shape);
398
400 newGradient->setTransform(transform);
401 newBackgrounds << toQShared(newGradient);
402 }
403
404 command = new KoShapeBackgroundCommand(m_d->shapes, newBackgrounds);
405
406 } else {
407 command = KoFlake::modifyShapesStrokes(m_d->shapes,
408 [gradient, transform] (KoShapeStrokeSP stroke) {
409 QBrush newBrush = *gradient;
410 newBrush.setTransform(transform);
411
412 stroke->setLineBrush(newBrush);
413 stroke->setColor(Qt::transparent);
414 });
415 }
416
417 return command;
418}
419
421{
423}
424
426{
427 KUndo2Command *command = 0;
428
429 if (m_d->fillVariant == KoFlake::Fill) {
431
432 foreach (KoShape *shape, m_d->shapes) {
433 newBackgrounds << m_d->applyFillGradientStops(shape, gradient);
434 }
435
436 command = new KoShapeBackgroundCommand(m_d->shapes, newBackgrounds);
437
438 } else {
439 command = KoFlake::modifyShapesStrokes(m_d->shapes,
440 [this, gradient] (KoShapeStrokeSP stroke) {
441 m_d->applyFillGradientStops(stroke, gradient);
442 });
443 }
444
445 return command;
446}
447
449 const QTransform &transform)
450{
451 KUndo2Command *command = nullptr;
452 if (m_d->fillVariant == KoFlake::Fill) {
454
455 for (const auto &shape: m_d->shapes) {
456 Q_UNUSED(shape);
457 KoMeshGradientBackground *newBackground =
458 new KoMeshGradientBackground(gradient, transform);
459
460 newBackgrounds << toQShared(newBackground);
461 }
462 command = new KoShapeBackgroundCommand(m_d->shapes, newBackgrounds);
463 }
464 // TODO: for strokes!!
465 return command;
466}
QPointF p2
QPointF p1
bool compareBackgrounds(const QList< KoShape * > shapes)
A simple solid color shape background.
A gradient shape background.
void setTransform(const QTransform &matrix)
Sets the transform matrix.
The undo / redo command for setting the shape background.
KoShapeFillWrapper(KoShape *shape, KoFlake::FillVariant fillVariant)
const QScopedPointer< Private > m_d
KoFlake::FillType type() const
KUndo2Command * applyGradientStopsOnly(const QGradient *gradient)
QTransform gradientTransform() const
const SvgMeshGradient * meshgradient() const
KUndo2Command * setMeshGradient(const SvgMeshGradient *gradient, const QTransform &transform)
const QGradient * gradient() const
KUndo2Command * setLineWidth(const float &lineWidth)
KUndo2Command * setGradient(const QGradient *gradient, const QTransform &transform)
KUndo2Command * applyGradient(const QGradient *gradient)
KUndo2Command * setColor(const QColor &color)
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
QSharedPointer< T > toQShared(T *ptr)
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
Definition KoFlake.cpp:17
KRITAFLAKE_EXPORT QGradient * mergeGradient(const QGradient *coordsSource, const QGradient *fillSource)
Definition KoFlake.cpp:54
auto modifyShapesStrokes(QList< KoShape * > shapes, ModifyFunction modifyFunction) -> decltype(modifyFunction(KoShapeStrokeSP()),(KUndo2Command *)(0))
@ None
Definition KoFlake.h:34
@ Solid
Definition KoFlake.h:35
@ MeshGradient
Definition KoFlake.h:38
@ Gradient
Definition KoFlake.h:36
FillVariant
Definition KoFlake.h:28
@ Fill
Definition KoFlake.h:29
QSharedPointer< KoShapeBackground > applyFillGradientStops(KoShape *shape, const QGradient *srcQGradient)
static const QGradient * gradient(KoShape *shape)
static QTransform meshgradientTransform(KoShape *shape)
static QColor color(KoShape *shape)
static PointerType getBackground(KoShape *shape)
static const SvgMeshGradient * meshgradient(KoShape *shape)
static bool compareTo(PointerType p1, PointerType p2)
static QTransform gradientTransform(KoShape *shape)
QSharedPointer< KoShapeBackground > PointerType
static Type type(KoShape *shape)
static bool compareTo(PointerType p1, PointerType p2)
static PointerType getBackground(KoShape *shape)
static QTransform gradientTransform(KoShape *shape)
static Type type(KoShape *shape)
static QColor color(KoShape *shape)
static const QGradient * gradient(KoShape *shape)
KoShapeStrokeModelSP PointerType