diff --git a/libs/libkis/Selection.cpp b/libs/libkis/Selection.cpp index 3626391c29..7c17819288 100644 --- a/libs/libkis/Selection.cpp +++ b/libs/libkis/Selection.cpp @@ -1,328 +1,334 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Selection.h" #include #include "kis_iterator_ng.h" #include #include #include #include #include #include #include #include struct Selection::Private { Private() {} KisSelectionSP selection; }; Selection::Selection(KisSelectionSP selection, QObject *parent) : QObject(parent) , d(new Private) { d->selection = selection; } Selection::Selection(QObject *parent) : QObject(parent) , d(new Private) { d->selection = new KisSelection(); } Selection::~Selection() { delete d; } bool Selection::operator==(const Selection &other) const { return (d->selection == other.d->selection); } bool Selection::operator!=(const Selection &other) const { return !(operator==(other)); } +Selection *Selection::duplicate() const +{ + return new Selection(d->selection ? new KisSelection(*d->selection) + : new KisSelection()); +} + int Selection::width() const { if (!d->selection) return 0; return d->selection->selectedExactRect().width(); } int Selection::height() const { if (!d->selection) return 0; return d->selection->selectedExactRect().height(); } int Selection::x() const { if (!d->selection) return 0; int xPos = d->selection->x(); if (d->selection->hasPixelSelection()) { xPos = d->selection->selectedExactRect().x(); } return xPos; } int Selection::y() const { if (!d->selection) return 0; int yPos = d->selection->y(); if (d->selection->hasPixelSelection()) { yPos = d->selection->selectedExactRect().y(); } return yPos; } void Selection::move(int x, int y) { if (!d->selection) return; d->selection->pixelSelection()->moveTo(QPoint(x, y)); } void Selection::clear() { if (!d->selection) return; d->selection->clear(); } void Selection::contract(int value) { if (!d->selection) return; d->selection->pixelSelection()->select(QRect(x(), y(), width() - value, height() - value)); } void Selection::copy(Node *node) { if (!node) return; if (!d->selection) return; if (node->node()->exactBounds().isEmpty()) return; if (!node->node()->hasEditablePaintDevice()) return; KisPaintDeviceSP dev = node->node()->paintDevice(); KisPaintDeviceSP clip = new KisPaintDevice(dev->colorSpace()); KisPaintDeviceSP selectionProjection = d->selection->projection(); const KoColorSpace *cs = clip->colorSpace(); const KoColorSpace *selCs = d->selection->projection()->colorSpace(); QRect rc = d->selection->selectedExactRect(); KisPainter::copyAreaOptimized(QPoint(), dev, clip, rc); KisHLineIteratorSP layerIt = clip->createHLineIteratorNG(0, 0, rc.width()); KisHLineConstIteratorSP selectionIt = selectionProjection->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); for (qint32 y = 0; y < rc.height(); y++) { for (qint32 x = 0; x < rc.width(); x++) { qreal dstAlpha = cs->opacityF(layerIt->rawData()); qreal sel = selCs->opacityF(selectionIt->oldRawData()); qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha); float mask = newAlpha / dstAlpha; cs->applyAlphaNormedFloatMask(layerIt->rawData(), &mask, 1); layerIt->nextPixel(); selectionIt->nextPixel(); } layerIt->nextRow(); selectionIt->nextRow(); } KisClipboard::instance()->setClip(clip, rc.topLeft()); } void Selection::cut(Node* node) { if (!node) return; if (!d->selection) return; if (node->node()->exactBounds().isEmpty()) return; if (!node->node()->hasEditablePaintDevice()) return; KisPaintDeviceSP dev = node->node()->paintDevice(); copy(node); dev->clearSelection(d->selection); node->node()->setDirty(d->selection->selectedExactRect()); } void Selection::paste(Node *destination, int x, int y) { if (!destination) return; if (!d->selection) return; if (!KisClipboard::instance()->hasClip()) return; KisPaintDeviceSP src = KisClipboard::instance()->clip(QRect(), false); KisPaintDeviceSP dst = destination->node()->paintDevice(); if (!dst) return; KisPainter::copyAreaOptimized(QPoint(x, y), src, dst, src->exactBounds(), d->selection); destination->node()->setDirty(); } void Selection::erode() { if (!d->selection) return; KisErodeSelectionFilter esf; QRect rc = esf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); esf.process(d->selection->pixelSelection(), rc); } void Selection::dilate() { if (!d->selection) return; KisDilateSelectionFilter dsf; QRect rc = dsf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); dsf.process(d->selection->pixelSelection(), rc); } void Selection::border(int xRadius, int yRadius) { if (!d->selection) return; KisBorderSelectionFilter sf(xRadius, yRadius); QRect rc = sf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); sf.process(d->selection->pixelSelection(), rc); } void Selection::feather(int radius) { if (!d->selection) return; KisFeatherSelectionFilter fsf(radius); QRect rc = fsf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); fsf.process(d->selection->pixelSelection(), rc); } void Selection::grow(int xradius, int yradius) { if (!d->selection) return; KisGrowSelectionFilter gsf(xradius, yradius); QRect rc = gsf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); gsf.process(d->selection->pixelSelection(), rc); } void Selection::shrink(int xRadius, int yRadius, bool edgeLock) { if (!d->selection) return; KisShrinkSelectionFilter sf(xRadius, yRadius, edgeLock); QRect rc = sf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); sf.process(d->selection->pixelSelection(), rc); } void Selection::smooth() { if (!d->selection) return; KisSmoothSelectionFilter sf; QRect rc = sf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); sf.process(d->selection->pixelSelection(), rc); } void Selection::invert() { if (!d->selection) return; KisInvertSelectionFilter sf; QRect rc = sf.changeRect(d->selection->selectedExactRect(), d->selection->pixelSelection()->defaultBounds()); sf.process(d->selection->pixelSelection(), rc); } void Selection::resize(int w, int h) { if (!d->selection) return; d->selection->pixelSelection()->select(QRect(x(), y(), w, h)); } void Selection::select(int x, int y, int w, int h, int value) { if (!d->selection) return; d->selection->pixelSelection()->select(QRect(x, y, w, h), value); } void Selection::selectAll(Node *node, int value) { if (!d->selection) return; d->selection->pixelSelection()->select(node->node()->exactBounds(), value); } void Selection::replace(Selection *selection) { if (!d->selection) return; d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_REPLACE); } void Selection::add(Selection *selection) { if (!d->selection) return; d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_ADD); } void Selection::subtract(Selection *selection) { if (!d->selection) return; d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_SUBTRACT); } void Selection::intersect(Selection *selection) { if (!d->selection) return; d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_INTERSECT); } void Selection::symmetricdifference(Selection *selection) { if (!d->selection) return; d->selection->pixelSelection()->applySelection(selection->selection()->pixelSelection(), SELECTION_SYMMETRICDIFFERENCE); } QByteArray Selection::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->selection) return ba; KisPaintDeviceSP dev = d->selection->projection(); quint8 *data = new quint8[w * h]; dev->readBytes(data, x, y, w, h); ba = QByteArray((const char*)data, (int)(w * h)); delete[] data; return ba; } void Selection::setPixelData(QByteArray value, int x, int y, int w, int h) { if (!d->selection) return; KisPixelSelectionSP dev = d->selection->pixelSelection(); if (!dev) return; dev->writeBytes((const quint8*)value.constData(), x, y, w, h); } KisSelectionSP Selection::selection() const { return d->selection; } diff --git a/libs/libkis/Selection.h b/libs/libkis/Selection.h index ffbf96e2a4..c5665ebe94 100644 --- a/libs/libkis/Selection.h +++ b/libs/libkis/Selection.h @@ -1,250 +1,255 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_SELECTION_H #define LIBKIS_SELECTION_H #include #include "kritalibkis_export.h" #include "libkis.h" #include /** * Selection represents a selection on Krita. A selection is * not necessarily associated with a particular Node or Image. * * @code * from krita import * * * d = Application.activeDocument() * n = d.activeNode() * r = n.bounds() * s = Selection() * s.select(r.width() / 3, r.height() / 3, r.width() / 3, r.height() / 3, 255) * s.cut(n) * @endcode */ class KRITALIBKIS_EXPORT Selection : public QObject { Q_OBJECT public: /** * For internal use only. */ Selection(KisSelectionSP selection, QObject *parent = 0); /** * Create a new, empty selection object. */ explicit Selection(QObject *parent = 0); ~Selection() override; bool operator==(const Selection &other) const; bool operator!=(const Selection &other) const; public Q_SLOTS: + /** + * @return a duplicate of the seletion + */ + Selection *duplicate() const; + /** * @return the width of the selection */ int width() const; /** * @return the height of the selection */ int height() const; /** * @return the left-hand position of the selection. */ int x() const; /** * @return the top position of the selection. */ int y() const; /** * Move the selection's top-left corner to the given coordinates. */ void move(int x, int y); /** * Make the selection entirely unselected. */ void clear(); /** * Make the selection's width and height smaller by the given value. * This will not move the selection's top-left position. */ void contract(int value); /** * @brief copy copies the area defined by the selection from the node to the clipboard. * @param node the node from where the pixels will be copied. */ void copy(Node *node); /** * @brief cut erases the area defined by the selection from the node and puts a copy on the clipboard. * @param node the node from which the selection will be cut. */ void cut(Node *node); /** * @brief paste pastes the content of the clipboard to the given node, limited by the area of the current * selection. * @param destination the node where the pixels will be written * @param x: the x position at which the clip will be written * @param y: the y position at which the clip will be written */ void paste(Node *destination, int x, int y); /** * Erode the selection with a radius of 1 pixel. */ void erode(); /** * Dilate the selection with a radius of 1 pixel. */ void dilate(); /** * Border the selection with the given radius. */ void border(int xRadius, int yRadius); /** * Feather the selection with the given radius. */ void feather(int radius); /** * Grow the selection with the given radius. */ void grow(int xradius, int yradius); /** * Shrink the selection with the given radius. */ void shrink(int xRadius, int yRadius, bool edgeLock); /** * Smooth the selection. */ void smooth(); /** * Invert the selection. */ void invert(); /** * Resize the selection to the given width and height. The top-left position will not be moved. */ void resize(int w, int h); /** * Select the given area. The value can be between 0 and 255; 0 is * totally unselected, 255 is totally selected. */ void select(int x, int y, int w, int h, int value); /** * Select all pixels in the given node. The value can be between 0 and 255; 0 is * totally unselected, 255 is totally selected. */ void selectAll(Node *node, int value); /** * Replace the current selection's selection with the one of the given selection. */ void replace(Selection *selection); /** * Add the given selection's selected pixels to the current selection. */ void add(Selection *selection); /** * Subtract the given selection's selected pixels from the current selection. */ void subtract(Selection *selection); /** * Intersect the given selection with this selection. */ void intersect(Selection *selection); /** * Intersect with the inverse of the given selection with this selection. */ void symmetricdifference(Selection *selection); /** * @brief pixelData reads the given rectangle from the Selection's mask and returns it as a * byte array. The pixel data starts top-left, and is ordered row-first. * * The byte array will contain one byte for every pixel, representing the selectedness. 0 * is totally unselected, 255 is fully selected. * * You can read outside the Selection's boundaries; those pixels will be unselected. * * The byte array is a copy of the original selection data. * @param x x position from where to start reading * @param y y position from where to start reading * @param w row length to read * @param h number of rows to read * @return a QByteArray with the pixel data. The byte array may be empty. */ QByteArray pixelData(int x, int y, int w, int h) const; /** * @brief setPixelData writes the given bytes, of which there must be enough, into the * Selection. * * @param value the byte array representing the pixels. There must be enough bytes available. * Krita will take the raw pointer from the QByteArray and start reading, not stopping before * (w * h) bytes are read. * * @param x the x position to start writing from * @param y the y position to start writing from * @param w the width of each row * @param h the number of rows to write */ void setPixelData(QByteArray value, int x, int y, int w, int h); private: friend class Document; friend class FilterLayer; friend class FillLayer; friend class SelectionMask; KisSelectionSP selection() const; struct Private; Private *const d; }; #endif // LIBKIS_SELECTION_H diff --git a/plugins/extensions/pykrita/sip/krita/Selection.sip b/plugins/extensions/pykrita/sip/krita/Selection.sip index 7ea9cbca44..f4562edefa 100644 --- a/plugins/extensions/pykrita/sip/krita/Selection.sip +++ b/plugins/extensions/pykrita/sip/krita/Selection.sip @@ -1,42 +1,43 @@ class Selection : QObject { %TypeHeaderCode #include "Selection.h" %End Selection(const Selection & __0); public: Selection(QObject* parent /TransferThis/ = 0); virtual ~Selection(); bool operator==(const Selection &other) const; bool operator!=(const Selection &other) const; public Q_SLOTS: + Selection *duplicate() /Factory/; int width() const; int height() const; int x() const; int y() const; void move(int x, int y); void clear(); void contract(int value); void copy(Node *node); void cut(Node *node); void paste(Node *destination, int x, int y); void erode(); void dilate(); void border(int xRadius, int yRadius); void feather(int radius); void grow(int xradius, int yradius); void shrink(int xRadius, int yRadius, bool edgeLock); void smooth(); void invert(); void resize(int w, int h); void select(int x, int y, int w, int h, int value); void selectAll(Node* node, int value); void replace(Selection *selection); void add(Selection *selection); void subtract(Selection *selection); void intersect(Selection *selection); void symmetricdifference(Selection *selection); QByteArray pixelData(int x, int y, int w, int h) const; void setPixelData(QByteArray value, int x, int y, int w, int h); private: };