Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_shape_selection.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Sven Langkamp <sven.langkamp@gmail.com>
3 * SPDX-FileCopyrightText: 2011 Jan Hambrecht <jaham@gmx.net>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10
11#include <QPainter>
12#include <kundo2command.h>
13#include <QMimeData>
14#include <QApplication>
15#include <QThread>
16
17#include <KoShapeStroke.h>
18#include <KoPathShape.h>
19#include <KoShapeGroup.h>
20#include <KoCompositeOp.h>
21#include <KoShapeManager.h>
22#include <KisDocument.h>
23
24#include <KoXmlNS.h>
25#include <KoShapeRegistry.h>
27#include <KoXmlWriter.h>
28#include <KoStore.h>
29#include <KoShapeController.h>
31#include <KoStoreDevice.h>
33
34#include <kis_painter.h>
35#include <kis_paint_device.h>
36#include <kis_image.h>
37#include <kis_iterator_ng.h>
38#include <kis_selection.h>
39
44#include "kis_shape_layer.h"
45#include "kis_lod_transform.h"
46
47#include <kis_debug.h>
48
49
51 : KoShapeLayer(new KisShapeSelectionModel(selection->resolutionProxy(), selection, this)),
52 m_model(static_cast<KisShapeSelectionModel*>(this->model())),
53 m_resolutionProxy(m_model->resolutionProxy())
54{
55 init(m_resolutionProxy, shapeControllerBase);
56}
57
59 : KoShapeLayer(new KisShapeSelectionModel(selection->resolutionProxy(), selection, this)),
60 m_model(static_cast<KisShapeSelectionModel*>(this->model())),
61 m_resolutionProxy(m_model->resolutionProxy())
62{
64
65 // TODO: refactor shape selection to pass signals
66 // via KoShapeManager, not via the model
69
71 Q_FOREACH (KoShape *shape, rhs.shapes()) {
72 KoShape *clonedShape = shape->cloneShape();
73 KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; }
74 this->addShape(clonedShape);
75 }
76
79}
80
87
89{
90 KIS_SAFE_ASSERT_RECOVER_RETURN(resolutionProxy);
91 KIS_SAFE_ASSERT_RECOVER_RETURN(shapeControllerBase);
92
93 m_shapeControllerBase = shapeControllerBase;
94
95 setShapeId("KisShapeSelection");
96 setSelectable(false);
97 m_converter = new KisImageViewConverter(resolutionProxy);
98 m_canvas = new KisShapeSelectionCanvas(shapeControllerBase);
100
101 m_model->setObjectName("KisShapeSelectionModel");
102 m_model->moveToThread(qApp->thread());
103 m_canvas->setObjectName("KisShapeSelectionCanvas");
104 m_canvas->moveToThread(qApp->thread());
105
106 connect(this, SIGNAL(sigMoveShapes(QPointF)), SLOT(slotMoveShapes(QPointF)));
107}
108
110{
111 return new KisShapeSelection(*this, selection);
112}
113
114bool KisShapeSelection::saveSelection(KoStore * store, const QRect &imageRect) const
115{
116 const QSizeF sizeInPx = imageRect.size();
117 const QSizeF sizeInPt(sizeInPx.width() / m_resolutionProxy->xRes(), sizeInPx.height() / m_resolutionProxy->yRes());
118
119 return KisShapeLayer::saveShapesToStore(store, this->shapes(), sizeInPt);
120}
121
122bool KisShapeSelection::loadSelection(KoStore* store, const QRect &imageRect)
123{
124 QSizeF fragmentSize; // unused!
125
126 // FIXME: we handle xRes() only!
128 const qreal resolutionPPI = 72.0 * m_resolutionProxy->xRes();
129
131
132 if (store->open("content.svg")) {
133 KoStoreDevice storeDev(store);
134 storeDev.open(QIODevice::ReadOnly);
135
137 "", imageRect,
138 resolutionPPI, m_canvas->shapeController()->resourceManager(),
139 true,
140 &fragmentSize);
141
142 store->close();
143
144 Q_FOREACH (KoShape *shape, shapes) {
145 addShape(shape);
146 }
147
148 return true;
149 }
150
151 return false;
152}
153
155{
156 m_model->setUpdatesEnabled(enabled);
157}
158
160{
161 return m_model->updatesEnabled();
162}
163
165{
166 return new KisTakeAllShapesCommand(this, true, false);
167}
168
170{
171 return !m_model->count();
172}
173
175{
176 return m_outline;
177}
178
180{
181 return true;
182}
183
185{
186 QTransform resolutionMatrix;
187 resolutionMatrix.scale(m_resolutionProxy->xRes(), m_resolutionProxy->yRes());
188
189 QList<KoShape*> shapesList = shapes();
190
191 QPainterPath outline;
192 Q_FOREACH (KoShape * shape, shapesList) {
201 QTransform shapeMatrix = shape->absoluteTransformation();
202 outline = outline.united(resolutionMatrix.map(shapeMatrix.map(shape->outline())));
203 }
204
206}
207
208void KisShapeSelection::paintComponent(QPainter& painter) const
209{
210 Q_UNUSED(painter);
211}
212
214{
215 Q_ASSERT(projection);
216
217 QRectF boundingRect = outlineCache().boundingRect();
218 renderSelection(projection, boundingRect.toAlignedRect());
219}
220
222{
223 Q_ASSERT(projection);
224 renderSelection(projection, r);
225}
226
227void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect)
228{
230
231 const qint32 MASK_IMAGE_WIDTH = 256;
232 const qint32 MASK_IMAGE_HEIGHT = 256;
233
234 QPainterPath selectionOutline = outlineCache();
235
236 if (projection->defaultBounds()->currentLevelOfDetail() > 0) {
237 KisLodTransform t(projection);
238 selectionOutline = t.map(selectionOutline);
239 }
240
241 if (*projection->defaultPixel().data() == OPACITY_TRANSPARENT_U8) {
242 projection->clear(requestedRect);
243 } else {
244 KoColor transparentColor = KoColor::createTransparent(projection->colorSpace());
245 projection->fill(requestedRect, transparentColor);
246 }
247 const QRect r = requestedRect & selectionOutline.boundingRect().toAlignedRect();
248
249 QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32);
250 QPainter maskPainter(&polygonMaskImage);
251 maskPainter.setRenderHint(QPainter::Antialiasing, true);
252
253 // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
254 for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) {
255 for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) {
256
257 maskPainter.fillRect(polygonMaskImage.rect(), Qt::black);
258 maskPainter.translate(-x, -y);
259 maskPainter.fillPath(selectionOutline, Qt::white);
260 maskPainter.translate(x, y);
261
262 qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH);
263 qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT);
264
265 KisSequentialIterator it(projection, QRect(x, y, rectWidth, rectHeight));
266 while (it.nextPixel()) {
267 (*it.rawData()) = qRed(polygonMaskImage.pixel(it.x() - x, it.y() - y));
268 }
269 }
270 }
271}
272
277
279 : KoShapeFactoryBase("KisShapeSelection", "selection shape container")
280{
281 setHidden(true);
282}
283
285{
286 const QPointF diff(x / m_resolutionProxy->xRes(), 0);
287 Q_EMIT sigMoveShapes(diff);
288}
289
291{
292 const QPointF diff(0, y / m_resolutionProxy->yRes());
293 Q_EMIT sigMoveShapes(diff);
294}
295
296void KisShapeSelection::slotMoveShapes(const QPointF &diff)
297{
298 Q_FOREACH (KoShape* shape, shapeManager()->shapes()) {
299 if (shape != this) {
300 QPointF pos = shape->position();
301 shape->setPosition(pos + diff);
302 }
303 }
304}
305
306// TODO same code as in vector layer, refactor!
307KUndo2Command* KisShapeSelection::transform(const QTransform &transform) {
309 if(shapes.isEmpty()) return 0;
310
311 QTransform realTransform = m_converter->documentToView() *
313
314 QList<QTransform> oldTransformations;
315 QList<QTransform> newTransformations;
316
317 // this code won't work if there are shapes, that inherit the transformation from the parent container.
318 // the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that.
319 Q_FOREACH (const KoShape* shape, shapes) {
320 QTransform oldTransform = shape->transformation();
321 oldTransformations.append(oldTransform);
322
323 // don't transform the container
324 if (dynamic_cast<const KoShapeGroup *>(shape) || !shape->parent()) {
325 newTransformations.append(oldTransform);
326 } else {
327 QTransform globalTransform = shape->absoluteTransformation();
328 QTransform localTransform = globalTransform * realTransform * globalTransform.inverted();
329 newTransformations.append(localTransform*oldTransform);
330 }
331 }
332
333 return new KoShapeTransformCommand(shapes, oldTransformations, newTransformations);
334}
335
337{
338 m_resolutionProxy = resolutionProxy;
339
340 // the model will automatically request update for the
341 // canvas and recalculation of the outline
342 m_model->setResolutionProxy(resolutionProxy);
343}
const quint8 OPACITY_TRANSPARENT_U8
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
virtual int currentLevelOfDetail() const =0
KisPaintInformation map(KisPaintInformation pi) const
virtual void clear()
void fill(const QRect &rc, const KoColor &color)
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE int x() const
ALWAYS_INLINE int y() const
static QList< KoShape * > createShapesFromSvg(QIODevice *device, const QString &baseXmlDir, const QRectF &rectInPixels, qreal resolutionPPI, KoDocumentResourceManager *resourceManager, bool loadingFromKra, QSizeF *fragmentSize, QStringList *warnings=0, QStringList *errors=0)
static bool saveShapesToStore(KoStore *store, QList< KoShape * > shapes, const QSizeF &sizeInPt)
KoShapeManager * shapeManager() const override
void setShapeSelection(KisShapeSelection *selection)
void setResolutionProxy(KisImageResolutionProxySP newResolutionProxy)
friend class KisTakeAllShapesCommand
void setUpdatesEnabled(bool enabled)
void setResolutionProxy(KisImageResolutionProxySP resolutionProxy) override
KUndo2Command * resetToEmpty() override
void moveY(qint32 y) override
void moveX(qint32 x) override
void init(KisImageResolutionProxySP resolutionProxy, KoShapeControllerBase *shapeControllerBase)
bool saveSelection(KoStore *store, const QRect &imageRect) const
void renderSelection(KisPaintDeviceSP projection, const QRect &requestedRect)
void slotMoveShapes(const QPointF &diff)
void recalculateOutlineCache() override
KisSelectionComponent * clone(KisSelection *selection) override
bool outlineCacheValid() const override
KisImageViewConverter * m_converter
KisShapeSelectionCanvas * m_canvas
KisShapeSelectionModel * m_model
KisShapeSelection(const KisShapeSelection &rhs)
QPainterPath outlineCache() const override
KoShapeManager * shapeManager() const
KoShapeControllerBase * m_shapeControllerBase
void paintComponent(QPainter &painter) const override
Paint the component Implement this method to allow the shape to paint itself, just like the KoShape::...
bool loadSelection(KoStore *store, const QRect &imageRect)
KisImageResolutionProxySP m_resolutionProxy
void renderToProjection(KisPaintDeviceSP projection) override
void sigMoveShapes(const QPointF &diff)
bool isEmpty() const override
QPointer< KoShapeController > shapeController
static KoColor createTransparent(const KoColorSpace *cs)
Definition KoColor.cpp:681
quint8 * data()
Definition KoColor.h:144
QList< KoShape * > shapes() const
void addShape(KoShape *shape)
void setHidden(bool hidden)
QRectF boundingRect() const override
Get the bounding box of the shape.
QList< KoShape * > shapes
void setUpdatesBlocked(bool value)
void addShape(KoShape *shape, KoShapeManager::Repaint repaint=PaintShapeOnAdd)
virtual QPainterPath outline() const
Definition KoShape.cpp:630
void setSelectable(bool selectable)
Definition KoShape.cpp:1009
KoShapeContainer * parent() const
Definition KoShape.cpp:1039
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
Definition KoShape.cpp:200
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
virtual void setPosition(const QPointF &position)
Set the position of the shape in pt.
Definition KoShape.cpp:295
void setShapeId(const QString &id)
Definition KoShape.cpp:1062
QTransform transform() const
return the current matrix that contains the rotation/scale/position of this shape
Definition KoShape.cpp:1145
QPointF position() const
Get the position of the shape in pt.
Definition KoShape.cpp:825
bool open(OpenMode m) override
bool close()
Definition KoStore.cpp:156
bool open(const QString &name)
Definition KoStore.cpp:109
virtual QPointF viewToDocument(const QPointF &viewPoint) const
virtual QPointF documentToView(const QPointF &documentPoint) const
static bool qFuzzyCompare(half p1, half p2)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130