diff --git a/libs/libkis/CMakeLists.txt b/libs/libkis/CMakeLists.txt index 20226c368c..67633ffc52 100644 --- a/libs/libkis/CMakeLists.txt +++ b/libs/libkis/CMakeLists.txt @@ -1,37 +1,48 @@ set(kritalibkis_LIB_SRCS Action.cpp Canvas.cpp Channel.cpp DockWidget.cpp DockWidgetFactoryBase.cpp Document.cpp Filter.cpp InfoObject.cpp Krita.cpp ManagedColor.cpp Node.cpp Notifier.cpp PresetChooser Palette.cpp PaletteView.cpp Resource.cpp Selection.cpp View.cpp Extension.cpp Window.cpp + GroupLayer.cpp + CloneLayer.cpp + FileLayer.cpp + FilterLayer.cpp + FillLayer.cpp + VectorLayer.cpp + FilterMask.cpp + SelectionMask.cpp + + Shape.cpp + GroupShape.cpp ) add_library(kritalibkis SHARED ${kritalibkis_LIB_SRCS} ) generate_export_header(kritalibkis) target_link_libraries(kritalibkis kritaui kritaimage kritaversion) target_link_libraries(kritalibkis LINK_INTERFACE_LIBRARIES kritaimage kritaui) set_target_properties(kritalibkis PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritalibkis ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(tests) diff --git a/libs/libkis/CloneLayer.cpp b/libs/libkis/CloneLayer.cpp new file mode 100644 index 0000000000..b6c8b65bbf --- /dev/null +++ b/libs/libkis/CloneLayer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "CloneLayer.h" +#include +#include +#include + +CloneLayer::CloneLayer(KisImageSP image, QString name, KisLayerSP source, QObject *parent) : + Node(image, new KisCloneLayer(source, image, name, OPACITY_OPAQUE_U8), parent) +{ + +} + +CloneLayer::CloneLayer(KisCloneLayerSP layer, QObject *parent): + Node(layer->image(), layer, parent) +{ + +} + +CloneLayer::~CloneLayer() +{ + +} + +QString CloneLayer::type() const +{ + return "clonelayer"; +} diff --git a/libs/libkis/CloneLayer.h b/libs/libkis/CloneLayer.h new file mode 100644 index 0000000000..7d523a8cbe --- /dev/null +++ b/libs/libkis/CloneLayer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_CLONELAYER_H +#define LIBKIS_CLONELAYER_H + +#include +#include "Node.h" + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +/** + * @brief The CloneLayer class + * A clone layer is a layer that takes a reference inside the image + * and shows the exact same pixeldata. + * + * If the original is updated, the clone layer will update too. + */ + +class KRITALIBKIS_EXPORT CloneLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(CloneLayer) + +public: + explicit CloneLayer(KisImageSP image, QString name, KisLayerSP source, QObject *parent = 0); + + /** + * @brief CloneLayer + * function for wrapping a preexisting node into a clonelayer object. + * @param clone + * @param parent + */ + explicit CloneLayer(KisCloneLayerSP layer, QObject *parent = 0); + ~CloneLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return clonelayer + */ + virtual QString type() const override; +}; + +#endif // LIBKIS_PAINTLAYER_H + diff --git a/libs/libkis/Document.cpp b/libs/libkis/Document.cpp index c20bb06818..d34d5c04da 100644 --- a/libs/libkis/Document.cpp +++ b/libs/libkis/Document.cpp @@ -1,713 +1,794 @@ /* * 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 "Document.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Document::Private { Private() {} QPointer document; }; Document::Document(KisDocument *document, QObject *parent) : QObject(parent) , d(new Private) { d->document = document; } Document::~Document() { delete d; } bool Document::operator==(const Document &other) const { return (d->document == other.d->document); } bool Document::operator!=(const Document &other) const { return !(operator==(other)); } bool Document::batchmode() const { if (!d->document) return false; return d->document->fileBatchMode(); } void Document::setBatchmode(bool value) { if (!d->document) return; d->document->setFileBatchMode(value); } Node *Document::activeNode() const { QList activeNodes; Q_FOREACH(QPointer view, KisPart::instance()->views()) { if (view && view->document() == d->document) { activeNodes << view->currentNode(); } } if (activeNodes.size() > 0) { return new Node(d->document->image(), activeNodes.first()); } return new Node(d->document->image(), d->document->image()->root()->firstChild()); } void Document::setActiveNode(Node* value) { if (!value->node()) return; KisMainWindow *mainWin = KisPart::instance()->currentMainwindow(); if (!mainWin) return; KisViewManager *viewManager = mainWin->viewManager(); if (!viewManager) return; if (viewManager->document() != d->document) return; KisNodeManager *nodeManager = viewManager->nodeManager(); if (!nodeManager) return; KisNodeSelectionAdapter *selectionAdapter = nodeManager->nodeSelectionAdapter(); if (!selectionAdapter) return; selectionAdapter->setActiveNode(value->node()); } QList Document::topLevelNodes() const { if (!d->document) return QList(); Node n(d->document->image(), d->document->image()->rootLayer()); return n.childNodes(); } Node *Document::nodeByName(const QString &name) const { if (!d->document) return 0; KisNodeSP node = d->document->image()->rootLayer()->findChildByName(name); return new Node(d->document->image(), node); } QString Document::colorDepth() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorDepthId().id(); } QString Document::colorModel() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorModelId().id(); } QString Document::colorProfile() const { if (!d->document) return ""; return d->document->image()->colorSpace()->profile()->name(); } bool Document::setColorProfile(const QString &value) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(value); if (!profile) return false; bool retval = d->document->image()->assignImageProfile(profile); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return retval; } bool Document::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, colorProfile); if (!colorSpace) return false; d->document->image()->convertImageColorSpace(colorSpace, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::HighQuality | KoColorConversionTransformation::NoOptimization); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return true; } QString Document::documentInfo() const { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->document->documentInfo()->save(doc); return doc.toString(); } void Document::setDocumentInfo(const QString &document) { KoXmlDocument doc; QString errorMsg; int errorLine, errorColumn; doc.setContent(document, &errorMsg, &errorLine, &errorColumn); d->document->documentInfo()->load(doc); } QString Document::fileName() const { if (!d->document) return QString(); return d->document->url().toLocalFile(); } void Document::setFileName(QString value) { if (!d->document) return; QString mimeType = KisMimeDatabase::mimeTypeForFile(value, false); d->document->setMimeType(mimeType.toLatin1()); d->document->setUrl(QUrl::fromLocalFile(value)); } int Document::height() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->height(); } void Document::setHeight(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), d->document->image()->width(), value); } QString Document::name() const { if (!d->document) return ""; return d->document->documentInfo()->aboutInfo("title"); } void Document::setName(QString value) { if (!d->document) return; d->document->documentInfo()->setAboutInfo("title", value); } int Document::resolution() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return qRound(d->document->image()->xRes() * 72); } void Document::setResolution(int value) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; d->document->image()->setResolution(value / 72.0, value / 72.0); } Node *Document::rootNode() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return new Node(image, image->root()); } Selection *Document::selection() const { if (!d->document) return 0; if (!d->document->image()) return 0; if (!d->document->image()->globalSelection()) return 0; return new Selection(d->document->image()->globalSelection()); } void Document::setSelection(Selection* value) { if (!d->document) return; if (!d->document->image()) return; if (value) { d->document->image()->setGlobalSelection(value->selection()); } else { d->document->image()->setGlobalSelection(0); } } int Document::width() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->width(); } void Document::setWidth(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), value, d->document->image()->height()); } int Document::xOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().x(); } void Document::setXOffset(int x) { if (!d->document) return; if (!d->document->image()) return; resizeImage(x, d->document->image()->bounds().y(), d->document->image()->width(), d->document->image()->height()); } int Document::yOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().y(); } void Document::setYOffset(int y) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), y, d->document->image()->width(), d->document->image()->height()); } double Document::xRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->xRes(); } void Document::setXRes(double xRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(xRes, d->document->image()->yRes()); } double Document::yRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->yRes(); } void Document::setYRes(double yRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(d->document->image()->xRes(), yRes); } QByteArray Document::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->document) return ba; KisImageSP image = d->document->image(); if (!image) return ba; KisPaintDeviceSP dev = image->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } bool Document::close() { bool retval = d->document->closeUrl(false); Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == d->document) { view->close(); view->deleteLater(); } } KisPart::instance()->removeDocument(d->document); d->document = 0; return retval; } void Document::crop(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc(x, y, w, h); image->cropImage(rc); } bool Document::exportImage(const QString &filename, const InfoObject &exportConfiguration) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false); const QByteArray outputFormat = outputFormatString.toLatin1(); return d->document->exportDocumentSync(QUrl::fromLocalFile(filename), outputFormat, exportConfiguration.configuration()); } void Document::flatten() { if (!d->document) return; if (!d->document->image()) return; d->document->image()->flatten(); } void Document::resizeImage(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc; rc.setX(x); rc.setY(y); rc.setWidth(w); rc.setHeight(h); image->resizeImage(rc); } void Document::scaleImage(int w, int h, int xres, int yres, QString strategy) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc = image->bounds(); rc.setWidth(w); rc.setHeight(h); KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy); if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic"); image->scaleImage(rc.size(), xres, yres, actualStrategy); } void Document::rotateImage(double radians) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->rotateImage(radians); } void Document::shearImage(double angleX, double angleY) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->shear(angleX, angleY); } bool Document::save() { if (!d->document) return false; if (d->document->url().isEmpty()) return false; bool retval = d->document->save(true, 0); d->document->waitForSavingToComplete(); return retval; } bool Document::saveAs(const QString &filename) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false); const QByteArray outputFormat = outputFormatString.toLatin1(); bool retval = d->document->saveAs(QUrl::fromLocalFile(filename), outputFormat, true); d->document->waitForSavingToComplete(); return retval; } Node* Document::createNode(const QString &name, const QString &nodeType) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); Node *node = 0; if (nodeType == "paintlayer") { node = new Node(image, new KisPaintLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "grouplayer") { node = new Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filelayer") { node = new Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filterlayer") { node = new Node(image, new KisAdjustmentLayer(image, name, 0, 0)); } else if (nodeType == "filllayer") { node = new Node(image, new KisGeneratorLayer(image, name, 0, 0)); } else if (nodeType == "clonelayer") { node = new Node(image, new KisCloneLayer(0, image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "vectorlayer") { node = new Node(image, new KisShapeLayer(d->document->shapeController(), image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "transparencymask") { node = new Node(image, new KisTransparencyMask()); } else if (nodeType == "filtermask") { node = new Node(image, new KisFilterMask()); } else if (nodeType == "transformmask") { node = new Node(image, new KisTransformMask()); } else if (nodeType == "selectionmask") { node = new Node(image, new KisSelectionMask(image)); } return node; } +GroupLayer *Document::createGroupLayer(const QString &name) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new GroupLayer(image, name); +} + +FileLayer *Document::createFileLayer(const QString &name, const QString FileName, const QString ScalingMethod) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new FileLayer(image, name, fileName(), FileName, ScalingMethod); +} + +FilterLayer *Document::createFilterLayer(const QString &name, Filter &filter, Selection &selection) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new FilterLayer(image, name, filter, selection); +} + +FillLayer *Document::createFillLayer(const QString &name, const QString filterName, InfoObject &configuration, Selection &selection) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + KisFilterConfigurationSP config = KisGeneratorRegistry::instance()->value(filterName)->defaultConfiguration(); + Q_FOREACH(const QString property, configuration.properties().keys()) { + config->setProperty(property, configuration.property(property)); + } + + return new FillLayer(image, name, config, selection); +} + +CloneLayer *Document::createCloneLayer(const QString &name, const Node *source) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + KisLayerSP layer = qobject_cast(source->node().data()); + + return new CloneLayer(image, name, layer); +} + +VectorLayer *Document::createVectorLayer(const QString &name) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new VectorLayer(d->document->shapeController(), image, name); +} + +FilterMask *Document::createFilterMask(const QString &name, Filter &filter) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new FilterMask(image, name, filter); +} + +SelectionMask *Document::createSelectionMask(const QString &name) +{ + if (!d->document) return 0; + if (!d->document->image()) return 0; + KisImageSP image = d->document->image(); + + return new SelectionMask(image, name); +} + + QImage Document::projection(int x, int y, int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->image()->convertToQImage(x, y, w, h, 0); } QImage Document::thumbnail(int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->generatePreview(QSize(w, h)).toImage(); } void Document::lock() { if (!d->document || !d->document->image()) return; d->document->image()->barrierLock(); } void Document::unlock() { if (!d->document || !d->document->image()) return; d->document->image()->unlock(); } void Document::waitForDone() { if (!d->document || !d->document->image()) return; d->document->image()->waitForDone(); } bool Document::tryBarrierLock() { if (!d->document || !d->document->image()) return false; return d->document->image()->tryBarrierLock(); } bool Document::isIdle() { if (!d->document || !d->document->image()) return false; return d->document->image()->isIdle(); } void Document::refreshProjection() { if (!d->document || !d->document->image()) return; d->document->image()->refreshGraph(); } QList Document::horizontalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().horizontalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).x()); } return lines; } QList Document::verticalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().verticalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).y()); } return lines; } bool Document::guidesVisible() const { return d->document->guidesConfig().lockGuides(); } bool Document::guidesLocked() const { return d->document->guidesConfig().showGuides(); } Document *Document::clone() const { if (!d->document) return 0; QPointer clone = d->document->clone(); Document * d = new Document(clone); clone->setParent(d); // It's owned by the document, not KisPart return d; } void Document::setHorizontalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).x()); } config.setHorizontalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setVerticalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).y()); } config.setVerticalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setGuidesVisible(bool visible) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setShowGuides(visible); d->document->setGuidesConfig(config); } void Document::setGuidesLocked(bool locked) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setLockGuides(locked); d->document->setGuidesConfig(config); } QPointer Document::document() const { return d->document; } diff --git a/libs/libkis/Document.h b/libs/libkis/Document.h index cadb6bc645..e28fce7a98 100644 --- a/libs/libkis/Document.h +++ b/libs/libkis/Document.h @@ -1,628 +1,708 @@ /* * 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_DOCUMENT_H #define LIBKIS_DOCUMENT_H #include #include "kritalibkis_export.h" #include "libkis.h" +#include "GroupLayer.h" +#include "CloneLayer.h" +#include "FileLayer.h" +#include "FilterLayer.h" +#include "FillLayer.h" +#include "VectorLayer.h" +#include "FilterMask.h" +#include "SelectionMask.h" + class KisDocument; /** * The Document class encapsulates a Krita Document/Image. A Krita document is an Image with * a filename. Libkis does not differentiate between a document and an image, like Krita does * internally. */ class KRITALIBKIS_EXPORT Document : public QObject { Q_OBJECT Q_DISABLE_COPY(Document) public: explicit Document(KisDocument *document, QObject *parent = 0); ~Document() override; bool operator==(const Document &other) const; bool operator!=(const Document &other) const; /** * @brief horizontalGuides * The horizontal guides. * @return a list of the horizontal positions of guides. */ QList horizontalGuides() const; /** * @brief verticalGuides * The vertical guide lines. * @return a list of vertical guides. */ QList verticalGuides() const; /** * @brief guidesVisible * Returns guide visiiblity. * @return whether the guides are visible. */ bool guidesVisible() const; /** * @brief guidesLocked * Returns guide lockedness. * @return whether the guides are locked. */ bool guidesLocked() const; public Q_SLOTS: /** * @brief clone create a shallow clone of this document. * @return a new Document that should be identical to this one in every respect. */ Document *clone() const; /** * Batchmode means that no actions on the document should show dialogs or popups. * @return true if the document is in batchmode. */ bool batchmode() const; /** * Set batchmode to @param value. If batchmode is true, then there should be no popups * or dialogs shown to the user. */ void setBatchmode(bool value); /** * @brief activeNode retrieve the node that is currently active in the currently active window * @return the active node. If there is no active window, the first child node is returned. */ Node* activeNode() const; /** * @brief setActiveNode make the given node active in the currently active view and window * @param value the node to make active. */ void setActiveNode(Node* value); /** * @brief toplevelNodes return a list with all top level nodes in the image graph */ QList topLevelNodes() const; /** * @brief nodeByName searches the node tree for a node with the given name and returns it * @param name the name of the node * @return the first node with the given name or 0 if no node is found */ Node *nodeByName(const QString &name) const; /** * colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @return the color depth. */ QString colorDepth() const; /** * @brief colorModel retrieve the current color model of this document: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @return the internal color model string. */ QString colorModel() const; /** * @return the name of the current color profile */ QString colorProfile() const; /** * @brief setColorProfile set the color profile of the image to the given profile. The profile has to * be registered with krita and be compatible with the current color model and depth; the image data * is not converted. * @param colorProfile * @return false if the colorProfile name does not correspond to to a registered profile or if assigning * the profile failed. */ bool setColorProfile(const QString &colorProfile); /** * @brief setColorSpace convert the nodes and the image to the given colorspace. The conversion is * done with Perceptual as intent, High Quality and No LCMS Optimizations as flags and no blackpoint * compensation. * * @param colorModel A string describing the color model of the image: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @param colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @param colorProfile a valid color profile for this color model and color depth combination. * @return false the combination of these arguments does not correspond to a colorspace. */ bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile); /** * @brief documentInfo creates and XML document representing document and author information. * @return a string containing a valid XML document with the right information about the document * and author. The DTD can be found here: * * https://phabricator.kde.org/source/krita/browse/master/krita/dtd/ * * @code * * * * * My Document * * * * * Unknown * 1 * 35 * 2017-02-27T20:15:09 * 2017-02-27T20:14:33 * * * * Boudewijn Rempt * * * * * * * * * * * * * * * @endcode * */ QString documentInfo() const; /** * @brief setDocumentInfo set the Document information to the information contained in document * @param document A string containing a valid XML document that conforms to the document-info DTD * that can be found here: * * https://phabricator.kde.org/source/krita/browse/master/krita/dtd/ */ void setDocumentInfo(const QString &document); /** * @return the full path to the document, if it has been set. */ QString fileName() const; /** * @brief setFileName set the full path of the document to @param value */ void setFileName(QString value); /** * @return the height of the image in pixels */ int height() const; /** * @brief setHeight resize the document to @param value height. This is a canvas resize, not a scale. */ void setHeight(int value); /** * @return the name of the document. This is the title field in the @see documentInfo */ QString name() const; /** * @brief setName sets the name of the document to @param value. This is the title field in the @see documentInfo */ void setName(QString value); /** * @return the resolution in pixels per inch */ int resolution() const; /** * @brief setResolution set the resolution of the image; this does not scale the image * @param value the resolution in pixels per inch */ void setResolution(int value); /** * @brief rootNode the root node is the invisible group layer that contains the entire node * hierarchy. * @return the root of the image */ Node* rootNode() const; /** * @brief selection Create a Selection object around the global selection, if there is one. * @return the global selection or None if there is no global selection. */ Selection* selection() const; /** * @brief setSelection set or replace the global selection * @param value a valid selection object. */ void setSelection(Selection* value); /** * @return the width of the image in pixels. */ int width() const; /** * @brief setWidth resize the document to @param value width. This is a canvas resize, not a scale. */ void setWidth(int value); /** * @return the left edge of the canvas in pixels. */ int xOffset() const; /** * @brief setXOffset sets the left edge of the canvas to @param x. */ void setXOffset(int x); /** * @return the top edge of the canvas in pixels. */ int yOffset() const; /** * @brief setYOffset sets the top edge of the canvas to @param y. */ void setYOffset(int y); /** * @return xRes the horizontal resolution of the image in pixels per pt (there are 72 pts to an inch) */ double xRes() const; /** * @brief setXRes set the horizontal resolution of the image to xRes in pixels per pt. (there are 72 pts to an inch) */ void setXRes(double xRes) const; /** * @return yRes the vertical resolution of the image in pixels per pt (there are 72 pts to an inch) */ double yRes() const; /** * @brief setYRes set the vertical resolution of the image to yRes in pixels per pt. (there are 72 pts to an inch) */ void setYRes(double yRes) const; /** * @brief pixelData reads the given rectangle from the image projection and returns it as a byte * array. The pixel data starts top-left, and is ordered row-first. * * The byte array can be interpreted as follows: 8 bits images have one byte per channel, * and as many bytes as there are channels. 16 bits integer images have two bytes per channel, * representing an unsigned short. 16 bits float images have two bytes per channel, representing * a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a * float. * * You can read outside the image boundaries; those pixels will be transparent black. * * The order of channels is: * *
    *
  • Integer RGBA: Blue, Green, Red, Alpha *
  • Float RGBA: Red, Green, Blue, Alpha *
  • LabA: L, a, b, Alpha *
  • CMYKA: Cyan, Magenta, Yellow, Key, Alpha *
  • XYZA: X, Y, Z, A *
  • YCbCrA: Y, Cb, Cr, Alpha *
* * The byte array is a copy of the original image data. In Python, you can use bytes, bytearray * and the struct module to interpret the data and construct, for instance, a Pillow Image object. * * @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 close Close the document: remove it from Krita's internal list of documents and * close all views. If the document is modified, you should save it first. There will be * no prompt for saving. * @return true if the document is closed. */ bool close(); /** * @brief crop the image to rectangle described by @param x, @param y, * @param w and @param h */ void crop(int x, int y, int w, int h); /** * @brief exportImage export the image, without changing its URL to the given path. * @param filename the full path to which the image is to be saved * @param exportConfiguration a configuration object appropriate to the file format. * An InfoObject will used to that configuration. * * The supported formats have specific configurations that must be used when in * batchmode. They are described below: * *\b png *
    *
  • alpha: bool (True or False) *
  • compression: int (1 to 9) *
  • forceSRGB: bool (True or False) *
  • indexed: bool (True or False) *
  • interlaced: bool (True or False) *
  • saveSRGBProfile: bool (True or False) *
  • transparencyFillcolor: rgb (Ex:[255,255,255]) *
* *\b jpeg *
    *
  • baseline: bool (True or False) *
  • exif: bool (True or False) *
  • filters: bool (['ToolInfo', 'Anonymizer']) *
  • forceSRGB: bool (True or False) *
  • iptc: bool (True or False) *
  • is_sRGB: bool (True or False) *
  • optimize: bool (True or False) *
  • progressive: bool (True or False) *
  • quality: int (0 to 100) *
  • saveProfile: bool (True or False) *
  • smoothing: int (0 to 100) *
  • subsampling: int (0 to 3) *
  • transparencyFillcolor: rgb (Ex:[255,255,255]) *
  • xmp: bool (True or False) *
* @return true if the export succeeded, false if it failed. */ bool exportImage(const QString &filename, const InfoObject &exportConfiguration); /** * @brief flatten all layers in the image */ void flatten(); /** * @brief resizeImage resizes the canvas to the given left edge, top edge, width and height. * Note: This doesn't scale, use scale image for that. * @param x the new left edge * @param y the new top edge * @param w the new width * @param h the new height */ void resizeImage(int x, int y, int w, int h); /** * @brief scaleImage * @param w the new width * @param h the new height * @param xres the new xres * @param yres the new yres * @param strategy the scaling strategy. There's several ones amongst these that aren't available in the regular UI. * The list of filters is extensible and can be retrieved with Krita::filter *
    *
  • Hermite
  • *
  • Bicubic - Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.
  • *
  • Box - Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.
  • *
  • Bilinear - Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.
  • *
  • Bell
  • *
  • BSpline
  • *
  • Kanczos3 - Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.
  • *
  • Mitchell
  • *
*/ void scaleImage(int w, int h, int xres, int yres, QString strategy); /** * @brief rotateImage * Rotate the image by the given radians. * @param radians the amount you wish to rotate the image in radians */ void rotateImage(double radians); /** * @brief shearImage shear the whole image. * @param angleX the X-angle in degrees to shear by * @param angleY the Y-angle in degrees to shear by */ void shearImage(double angleX, double angleY); /** * @brief save the image to its currently set path. The modified flag of the * document will be reset * @return true if saving succeeded, false otherwise. */ bool save(); /** * @brief saveAs save the document under the @param filename. The document's * filename will be reset to @param filename. * @param filename the new filename (full path) for the document * @return true if saving succeeded, false otherwise. */ bool saveAs(const QString &filename); /** * @brief createNode create a new node of the given type. The node is not added * to the node hierarchy; you need to do that by finding the right parent node, * getting its list of child nodes and adding the node in the right place, then * calling Node::SetChildNodes * * @param name The name of the node * * @param nodeType The type of the node. Valid types are: *
    *
  • paintlayer *
  • grouplayer *
  • filelayer *
  • filterlayer *
  • filllayer *
  • clonelayer *
  • vectorlayer *
  • transparencymask *
  • filtermask *
  • transformmask *
  • selectionmask *
* * When relevant, the new Node will have the colorspace of the image by default; * that can be changed with Node::setColorSpace. * * The settings and selections for relevant layer and mask types can also be set * after the Node has been created. * * @return the new Node. */ Node* createNode(const QString &name, const QString &nodeType); + /** + * @brief createGroupLayer + * Returns a grouplayer object. Grouplayers are nodes that can have + * other layers as children and have the passthrough mode. + * @param name the name of the layer. + * @return a GroupLayer object. + */ + GroupLayer* createGroupLayer(const QString &name); + /** + * @brief createFileLayer returns a layer that shows an external image. + * @param name name of the file layer. + * @param FileName the absolute filename of the file referenced. Symlinks will be resolved. + * @param ScalingMethod how the dimensions of the file are interpreted + * can be either "None", "ImageToSize" or "ImageToPPI" + * @return a FileLayer + */ + FileLayer* createFileLayer(const QString &name, const QString FileName, const QString ScalingMethod); + + /** + * @brief createFilterLayer creates a filter layer, which is a layer that represents a filter + * applied non-destructively. + * @param name name of the filterLayer + * @param filter the filter that this filter layer will us. + * @param selection the selection. + * @return a filter layer object. + */ + FilterLayer* createFilterLayer(const QString &name, Filter &filter, Selection &selection); + + /** + * @brief createFillLayer creates a fill layer object, which is a layer + * @param name + * @param filterName - name of the generation filter. + * @param configuration - the configuration for the generation filter. + * @param selection - the selection. + * @return a filllayer object. + */ + FillLayer* createFillLayer(const QString &name, const QString filterName, InfoObject &configuration, Selection &selection); + + /** + * @brief createCloneLayer + * @param name + * @param source + * @return + */ + CloneLayer* createCloneLayer(const QString &name, const Node* source); + + /** + * @brief createVectorLayer + * Creates a vector layer that can contain vector shapes. + * @param name the name of this layer. + * @return a VectorLayer. + */ + VectorLayer* createVectorLayer(const QString &name); + + /** + * @brief createFilterMask + * Creates a filter mask object that much like a filterlayer can apply a filter non-destructively. + * @param name the name of the layer. + * @param filter the filter assigned. + * @return a FilterMask + */ + FilterMask* createFilterMask(const QString &name, Filter &filter); + + /** + * @brief createSelectionMask + * Creates a selection mask, which can be used to store selections. + * @param name - the name of the layer. + * @return a SelectionMask + */ + SelectionMask* createSelectionMask(const QString &name); + /** * @brief projection creates a QImage from the rendered image or * a cutout rectangle. */ QImage projection(int x = 0, int y = 0, int w = 0, int h = 0) const; /** * @brief thumbnail create a thumbnail of the given dimensions. * * If the requested size is too big a null QImage is created. * * @return a QImage representing the layer contents. */ QImage thumbnail(int w, int h) const; /** * Why this should be used, When it should be used, How it should be used, * and warnings about when not. */ void lock(); /** * Why this should be used, When it should be used, How it should be used, * and warnings about when not. */ void unlock(); /** * Why this should be used, When it should be used, How it should be used, * and warnings about when not. */ void waitForDone(); /** * Why this should be used, When it should be used, How it should be used, * and warnings about when not. */ bool tryBarrierLock(); /** * Why this should be used, When it should be used, How it should be used, * and warnings about when not. */ bool isIdle(); /** * Starts a synchronous recomposition of the projection: everything will * wait until the image is fully recomputed. */ void refreshProjection(); /** * @brief setHorizontalGuides * replace all existing horizontal guides with the entries in the list. * @param list a list of floats containing the new guides. */ void setHorizontalGuides(const QList &lines); /** * @brief setVerticalGuides * replace all existing horizontal guides with the entries in the list. * @param list a list of floats containing the new guides. */ void setVerticalGuides(const QList &lines); /** * @brief setGuidesVisible * set guides visible on this document. * @param visible whether or not the guides are visible. */ void setGuidesVisible(bool visible); /** * @brief setGuidesLocked * set guides locked on this document * @param locked whether or not to lock the guides on this document. */ void setGuidesLocked(bool locked); private: friend class Krita; friend class Window; friend class Filter; QPointer document() const; private: struct Private; Private *const d; }; #endif // LIBKIS_DOCUMENT_H diff --git a/libs/libkis/FileLayer.cpp b/libs/libkis/FileLayer.cpp new file mode 100644 index 0000000000..13fefcbfeb --- /dev/null +++ b/libs/libkis/FileLayer.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "FileLayer.h" +#include +#include +#include +#include + +FileLayer::FileLayer(KisImageSP image, const QString name, const QString baseName, const QString fileName, const QString scalingMethod, QObject *parent) + : Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8), parent) +{ + KisFileLayer *file = dynamic_cast(this->node().data()); + KisFileLayer::ScalingMethod sm = KisFileLayer::None; + if (scalingMethod=="ToImageSize") { + sm= KisFileLayer::ToImageSize; + } else if (scalingMethod=="ToImagePPI") { + sm= KisFileLayer::ToImagePPI; + } + file->setScalingMethod(sm); + file->setFileName(baseName, getFileNameFromAbsolute(baseName, fileName)); +} + +FileLayer::FileLayer(KisFileLayerSP layer, QObject *parent) + : Node(layer->image(), layer, parent) +{ + +} + +FileLayer::~FileLayer() +{ + +} + +QString FileLayer::type() const +{ + return "filelayer"; +} + +void FileLayer::setProperties(QString FileName, QString ScalingMethod) +{ + KisFileLayer *file = dynamic_cast(this->node().data()); + KisFileLayer::ScalingMethod sm = KisFileLayer::None; + if (ScalingMethod=="ToImageSize") { + sm= KisFileLayer::ToImageSize; + } else if (ScalingMethod=="ToImagePPI") { + sm= KisFileLayer::ToImagePPI; + } + file->setScalingMethod(sm); + file->setFileName(QFileInfo(file->path()).baseName(), getFileNameFromAbsolute(QFileInfo(file->path()).baseName(), FileName)); +} + +QString FileLayer::path() const +{ + const KisFileLayer *file = qobject_cast(this->node()); + return file->path(); +} + +QString FileLayer::scalingMethod() const +{ + const KisFileLayer *file = qobject_cast(this->node()); + KisFileLayer::ScalingMethod sm = file->scalingMethod(); + QString method = "None"; + + if (sm==KisFileLayer::ToImageSize) { + method = "ToImageSize"; + } else if (sm==KisFileLayer::ToImagePPI) { + method = "ToImagePPI"; + } + return method; +} + +QString FileLayer::getFileNameFromAbsolute(QString baseName, QString absolutePath) +{ + QFileInfo fi(absolutePath); + if (fi.isSymLink()) { + absolutePath = fi.symLinkTarget(); + fi = QFileInfo(absolutePath); + } + if (!baseName.isEmpty() && fi.isAbsolute()) { + QDir directory(baseName); + absolutePath = directory.relativeFilePath(absolutePath); + } + return absolutePath; +} + + diff --git a/libs/libkis/FileLayer.h b/libs/libkis/FileLayer.h new file mode 100644 index 0000000000..59f7b5562a --- /dev/null +++ b/libs/libkis/FileLayer.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_FILELAYER_H +#define LIBKIS_FILELAYER_H + +#include +#include "Node.h" + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +/** + * @brief The FileLayer class + * A file layer is a layer that can reference an external image + * and show said reference in the layer stack. + * + * If the external image is updated, Krita will try to update the + * file layer image as well. + */ + +class KRITALIBKIS_EXPORT FileLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(FileLayer) + +public: + explicit FileLayer(KisImageSP image, + const QString name = QString(), + const QString baseName=QString(), + const QString fileName=QString(), + const QString scalingMethod=QString(), + QObject *parent = 0); + explicit FileLayer(KisFileLayerSP layer, QObject *parent = 0); + ~FileLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return "filelayer" + */ + QString type() const override; + + /** + * @brief setProperties + * Change the properties of the file layer. + * @param FileName - A String containing the absolute file name. + * @param ScalingMethod - a string with the scaling method, defaults to "None", + * other options are "ToImageSize" and "ToImagePPI" + */ + void setProperties(QString FileName, QString ScalingMethod = QString("None")); + + /** + * @brief path + * @return A QString with the full path of the referenced image. + */ + QString path() const; + + /** + * @brief scalingMethod + * returns how the file referenced is scaled. + * @return one of the following: + *
  • None - The file is not scaled in any way. + *
  • ToImageSize - The file is scaled to the full image size; + *
  • ToImagePPI - The file is scaled by the PPI of the image. This keep the physical dimensions the same. + */ + QString scalingMethod() const; + +private: + /** + * @brief getFileNameFromAbsolute + * referenced from the fileLayer dialog, this will jumps through all the hoops + * to ensure that an appropriate filename will be gotten. + * @param baseName the location of the document. + * @param absolutePath the absolute location of the file referenced. + * @return the appropriate relative path. + */ + QString getFileNameFromAbsolute(QString baseName, QString absolutePath); + QString m_baseName; +}; + +#endif // LIBKIS_FILELAYER_H + diff --git a/libs/libkis/FillLayer.cpp b/libs/libkis/FillLayer.cpp new file mode 100644 index 0000000000..501dae5e58 --- /dev/null +++ b/libs/libkis/FillLayer.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "FillLayer.h" +#include +#include +#include +#include +#include +#include + +FillLayer::FillLayer(KisImageSP image, QString name, KisFilterConfigurationSP filter, Selection &selection, QObject *parent) : + Node(image, new KisGeneratorLayer(image, name, filter, selection.selection()), parent) +{ + +} + +FillLayer::FillLayer(KisGeneratorLayerSP layer, QObject *parent): + Node(layer->image(), layer, parent) +{ + +} + +FillLayer::~FillLayer() +{ + +} + +QString FillLayer::filterName() +{ + const KisGeneratorLayer *layer = qobject_cast(this->node()); + return layer->filter()->name(); +} + +InfoObject * FillLayer::filterConfig() +{ + const KisGeneratorLayer *layer = qobject_cast(this->node()); + return new InfoObject(layer->filter()); +} + +QString FillLayer::type() const +{ + return "filllayer"; +} + +void FillLayer::setFilter(QString &filterName, InfoObject *config) +{ + KisGeneratorLayer *layer = dynamic_cast(this->node().data()); + //getting the default configuration here avoids trouble with versioning. + KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(filterName); + KisFilterConfigurationSP cfg = generator->defaultConfiguration(); + Q_FOREACH(const QString property, config->properties().keys()) { + cfg->setProperty(property, config->property(property)); + } + layer->setFilter(cfg); +} diff --git a/libs/libkis/FillLayer.h b/libs/libkis/FillLayer.h new file mode 100644 index 0000000000..ff66f370b3 --- /dev/null +++ b/libs/libkis/FillLayer.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_FILLLAYER_H +#define LIBKIS_FILLLAYER_H + +#include +#include "Node.h" +#include +#include + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" +/** + * @brief The FillLayer class + * A fill layer is much like a filter layer in that it takes a name + * and filter. It however specialises in filters that fill the whole canvas, + * such as a pattern or full color fill. + */ +class KRITALIBKIS_EXPORT FillLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(FillLayer) + +public: + explicit FillLayer(KisImageSP image, QString name,KisFilterConfigurationSP filter, Selection &selection, QObject *parent = 0); + explicit FillLayer(KisGeneratorLayerSP layer, QObject *parent = 0); + ~FillLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return The type of the node. Valid types are: + *
      + *
    • paintlayer + *
    • grouplayer + *
    • filelayer + *
    • filterlayer + *
    • filllayer + *
    • clonelayer + *
    • vectorlayer + *
    • transparencymask + *
    • filtermask + *
    • transformmask + *
    • selectionmask + *
    • colorizemask + *
    + * + * If the Node object isn't wrapping a valid Krita layer or mask object, and + * empty string is returned. + */ + virtual QString type() const override; + + void setFilter(QString &filterName, InfoObject *filterConfig); + + QString filterName(); + InfoObject * filterConfig(); +}; + +#endif // LIBKIS_FILLLAYER_H diff --git a/libs/libkis/Filter.cpp b/libs/libkis/Filter.cpp index 56c1423af6..48ec4ff903 100644 --- a/libs/libkis/Filter.cpp +++ b/libs/libkis/Filter.cpp @@ -1,165 +1,174 @@ /* * 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 "Filter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Krita.h" #include "Document.h" #include "InfoObject.h" #include "Node.h" struct Filter::Private { Private() {} QString name; InfoObject *configuration {0}; }; Filter::Filter() : QObject(0) , d(new Private) { } Filter::~Filter() { delete d->configuration; delete d; } bool Filter::operator==(const Filter &other) const { return (d->name == other.d->name && d->configuration == other.d->configuration); } bool Filter::operator!=(const Filter &other) const { return !(operator==(other)); } QString Filter::name() const { return d->name; } void Filter::setName(const QString &name) { d->name = name; delete d->configuration; KisFilterSP filter = KisFilterRegistry::instance()->value(d->name); d->configuration = new InfoObject(filter->defaultConfiguration()); } InfoObject* Filter::configuration() const { return d->configuration; } void Filter::setConfiguration(InfoObject* value) { d->configuration = value; } bool Filter::apply(Node *node, int x, int y, int w, int h) { if (node->locked()) return false; KisFilterSP filter = KisFilterRegistry::instance()->value(d->name); if (!filter) return false; KisPaintDeviceSP dev = node->paintDevice(); if (!dev) return false; QRect applyRect = QRect(x, y, w, h); KisFilterConfigurationSP config = static_cast(d->configuration->configuration().data()); filter->process(dev, applyRect, config); return true; } bool Filter::startFilter(Node *node, int x, int y, int w, int h) { if (node->locked()) return false; KisFilterSP filter = KisFilterRegistry::instance()->value(d->name); if (!filter) return false; KisImageWSP image = node->image(); if (!image) return false; KisFilterConfigurationSP filterConfig = static_cast(d->configuration->configuration().data()); image->waitForDone(); QRect initialApplyRect = QRect(x, y, w, h); QRect applyRect = initialApplyRect; KisPaintDeviceSP paintDevice = node->paintDevice(); if (paintDevice && filter->needsTransparentPixels(filterConfig.data(), paintDevice->colorSpace())) { applyRect |= image->bounds(); } KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, node->node()); Document *document = Krita::instance()->activeDocument(); if (document && KisPart::instance()->viewCount(document->document()) > 0) { Q_FOREACH (QPointer view, KisPart::instance()->views()) { if (view && view->document() == document->document()) { resources = new KisResourcesSnapshot(image, node->node(), view->resourceProvider()->resourceManager()); break; } } } delete document; KisStrokeId currentStrokeId = image->startStroke(new KisFilterStrokeStrategy(filter, KisFilterConfigurationSP(filterConfig), resources)); QRect processRect = filter->changedRect(applyRect, filterConfig.data(), 0); processRect &= image->bounds(); if (filter->supportsThreading()) { QSize size = KritaUtils::optimalPatchSize(); QVector rects = KritaUtils::splitRectIntoPatches(processRect, size); Q_FOREACH (const QRect &rc, rects) { image->addJob(currentStrokeId, new KisFilterStrokeStrategy::Data(rc, true)); } } else { image->addJob(currentStrokeId, new KisFilterStrokeStrategy::Data(processRect, false)); } image->endStroke(currentStrokeId); return true; } + +KisFilterConfigurationSP Filter::filterConfig() +{ + KisFilterConfigurationSP config = KisFilterRegistry::instance()->get(d->name)->defaultConfiguration(); + Q_FOREACH(const QString property, d->configuration->properties().keys()) { + config->setProperty(property, d->configuration->property(property)); + } + return config; +} diff --git a/libs/libkis/Filter.h b/libs/libkis/Filter.h index 76f7b92c16..a81aaa39cf 100644 --- a/libs/libkis/Filter.h +++ b/libs/libkis/Filter.h @@ -1,108 +1,114 @@ /* * 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_FILTER_H #define LIBKIS_FILTER_H #include #include "kritalibkis_export.h" #include "libkis.h" +#include /** * Filter: represents a filter and its configuration. A filter is identified by * an internal name. The configuration for each filter is defined as an InfoObject: * a map of name and value pairs. * * Currently available filters are: * * 'autocontrast', 'blur', 'bottom edge detections', 'brightnesscontrast', 'burn', 'colorbalance', 'colortoalpha', 'colortransfer', * 'desaturate', 'dodge', 'emboss', 'emboss all directions', 'emboss horizontal and vertical', 'emboss horizontal only', * 'emboss laplascian', 'emboss vertical only', 'gaussian blur', 'gaussiannoisereducer', 'gradientmap', 'halftone', 'hsvadjustment', * 'indexcolors', 'invert', 'left edge detections', 'lens blur', 'levels', 'maximize', 'mean removal', 'minimize', 'motion blur', * 'noise', 'normalize', 'oilpaint', 'perchannel', 'phongbumpmap', 'pixelize', 'posterize', 'raindrops', 'randompick', * 'right edge detections', 'roundcorners', 'sharpen', 'smalltiles', 'sobel', 'threshold', 'top edge detections', 'unsharp', * 'wave', 'waveletnoisereducer'] */ class KRITALIBKIS_EXPORT Filter : public QObject { Q_OBJECT Q_DISABLE_COPY(Filter) public: /** * @brief Filter: create an empty filter object. Until a name is set, the filter cannot * be applied. */ explicit Filter(); ~Filter() override; bool operator==(const Filter &other) const; bool operator!=(const Filter &other) const; public Q_SLOTS: /** * @brief name the internal name of this filter. * @return the name. */ QString name() const; /** * @brief setName set the filter's name to the given name. */ void setName(const QString &name); /** * @return the configuration object for the filter */ InfoObject* configuration() const; /** * @brief setConfiguration set the configuration object for the filter */ void setConfiguration(InfoObject* value); /** * @brief Apply the filter to the given node. * @param node the node to apply the filter to * @params x, y, w, h: describe the rectangle the filter should be apply. * This is always in image pixel coordinates and not relative to the x, y * of the node. * @return true if the filter was applied successfully, or * false if the filter could not be applied because the node is locked or * does not have an editable paint device. */ bool apply(Node *node, int x, int y, int w, int h); /** * @brief startFilter starts the given filter on the given node. * * @param node the node to apply the filter to * @params x, y, w, h: describe the rectangle the filter should be apply. * This is always in image pixel coordinates and not relative to the x, y * of the node. */ bool startFilter(Node *node, int x, int y, int w, int h); private: + friend class FilterLayer; + friend class FilterMask; + struct Private; Private *const d; + KisFilterConfigurationSP filterConfig(); + }; #endif // LIBKIS_FILTER_H diff --git a/libs/libkis/FilterLayer.cpp b/libs/libkis/FilterLayer.cpp new file mode 100644 index 0000000000..fdffed5742 --- /dev/null +++ b/libs/libkis/FilterLayer.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "FilterLayer.h" +#include +#include +#include +#include +#include +#include + +FilterLayer::FilterLayer(KisImageSP image, QString name, Filter &filter, Selection &selection, QObject *parent) : + Node(image, new KisAdjustmentLayer(image, name, filter.filterConfig(), selection.selection()), parent) +{ + +} + +FilterLayer::FilterLayer(KisAdjustmentLayerSP layer, QObject *parent): + Node(layer->image(), layer, parent) +{ + +} + +FilterLayer::~FilterLayer() +{ + +} + +QString FilterLayer::type() const +{ + return "filterlayer"; +} + +void FilterLayer::setFilter(Filter &filter) +{ + KisAdjustmentLayer *layer = dynamic_cast(this->node().data()); + //getting the default configuration here avoids trouble with versioning. + layer->setFilter(filter.filterConfig()); +} + +Filter * FilterLayer::filter() +{ + Filter* filter = new Filter(); + const KisAdjustmentLayer *layer = qobject_cast(this->node()); + filter->setName(layer->filter()->name()); + filter->setConfiguration(new InfoObject(layer->filter())); + return filter; +} + diff --git a/libs/libkis/FilterLayer.h b/libs/libkis/FilterLayer.h new file mode 100644 index 0000000000..e4d0f4ad7b --- /dev/null +++ b/libs/libkis/FilterLayer.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_FILTERLAYER_H +#define LIBKIS_FILTERLAYER_H + +#include +#include "Node.h" +#include +#include + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" +/** + * @brief The FilterLayer class + * A filter layer will, when compositing, take the composited + * image up to the point of the loction of the filter layer + * in the stack, create a copy and apply a filter. + * + * This means you can use blending modes on the filter layers, + * which will be used to blend the filtered image with the original. + * + * Similarly, you can activate things like alpha inheritance, or + * you can set grayscale pixeldata on the filter layer to act as + * a mask. + * + * Filter layers can be animated. + */ +class KRITALIBKIS_EXPORT FilterLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(FilterLayer) + +public: + explicit FilterLayer(KisImageSP image, QString name, Filter &filter, Selection &selection, QObject *parent = 0); + explicit FilterLayer(KisAdjustmentLayerSP layer, QObject *parent = 0); + ~FilterLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return "filterlayer" + */ + QString type() const override; + + void setFilter(Filter &filter); + + Filter * filter(); +}; + +#endif // LIBKIS_FILTERLAYER_H + diff --git a/libs/libkis/FilterMask.cpp b/libs/libkis/FilterMask.cpp new file mode 100644 index 0000000000..70b151fad2 --- /dev/null +++ b/libs/libkis/FilterMask.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "FilterMask.h" +#include +#include +#include +#include +#include + +FilterMask::FilterMask(KisImageSP image, QString name, Filter &filter, QObject *parent) : + Node(image, new KisFilterMask(), parent) +{ + this->node()->setName(name); + KisFilterMask *mask = dynamic_cast(this->node().data()); + mask->setFilter(filter.filterConfig()); +} + +FilterMask::FilterMask(KisImageSP image, KisFilterMaskSP mask, QObject *parent): + Node(image, mask, parent) +{ + +} + +FilterMask::~FilterMask() +{ + +} + +QString FilterMask::type() const +{ + return "filtermask"; +} + +void FilterMask::setFilter(Filter &filter) +{ + KisFilterMask *mask = dynamic_cast(this->node().data()); + mask->setFilter(filter.filterConfig()); +} + +Filter * FilterMask::filter() +{ + Filter* filter = new Filter(); + const KisFilterMask *mask = qobject_cast(this->node()); + filter->setName(mask->filter()->name()); + filter->setConfiguration(new InfoObject(mask->filter())); + return filter; +} + diff --git a/libs/libkis/FilterMask.h b/libs/libkis/FilterMask.h new file mode 100644 index 0000000000..9cf2fe9e62 --- /dev/null +++ b/libs/libkis/FilterMask.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_FILTERMASK_H +#define LIBKIS_FILTERMASK_H + +#include +#include "Node.h" +#include "Filter.h" + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +/** + * @brief The FilterMask class + * A filter mask, unlike a filter layer, will add a non-desctructive filter + * to the composited image of the node it is attached to. + * + * You can set grayscale pixeldata on the filter mask to adjust where the filter is applied. + * + * Filtermasks can be animated. + */ + +class KRITALIBKIS_EXPORT FilterMask : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(FilterMask) + +public: + explicit FilterMask(KisImageSP image, QString name, Filter &filter, QObject *parent = 0); + explicit FilterMask(KisImageSP image, KisFilterMaskSP mask, QObject *parent=0); + ~FilterMask() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return The type of the node. Valid types are: + *
      + *
    • paintlayer + *
    • grouplayer + *
    • filelayer + *
    • filterlayer + *
    • filllayer + *
    • clonelayer + *
    • vectorlayer + *
    • transparencymask + *
    • filtermask + *
    • transformmask + *
    • selectionmask + *
    • colorizemask + *
    + * + * If the Node object isn't wrapping a valid Krita layer or mask object, and + * empty string is returned. + */ + QString type() const override; + + void setFilter(Filter &filter); + Filter *filter(); +}; + +#endif // LIBKIS_FILTERMASK_H + + diff --git a/libs/libkis/GroupLayer.cpp b/libs/libkis/GroupLayer.cpp new file mode 100644 index 0000000000..24d3ca5f3f --- /dev/null +++ b/libs/libkis/GroupLayer.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "GroupLayer.h" +#include +#include + +GroupLayer::GroupLayer(KisImageSP image, QString name, QObject *parent) : + Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8), parent) +{ + +} + +GroupLayer::GroupLayer(KisGroupLayerSP layer, QObject *parent): + Node(layer->image(), layer, parent) +{ + +} + +GroupLayer::~GroupLayer() +{ + +} + +void GroupLayer::setPassThroughMode(bool passthrough) +{ + KisGroupLayer *group = dynamic_cast(this->node().data()); + group->setPassThroughMode(passthrough); +} + +bool GroupLayer::passThroughMode() const +{ + const KisGroupLayer *group = qobject_cast(this->node()); + return group->passThroughMode(); +} + +QString GroupLayer::type() const +{ + return "grouplayer"; +} diff --git a/libs/libkis/GroupLayer.h b/libs/libkis/GroupLayer.h new file mode 100644 index 0000000000..64bd867ea5 --- /dev/null +++ b/libs/libkis/GroupLayer.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_GROUPLAYER_H +#define LIBKIS_GROUPLAYER_H + +#include +#include "Node.h" + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +/** + * @brief The GroupLayer class + * A group layer is a layer that can contain other layers. + * In Krita, layers within a group layer are composited + * first before they are added into the composition code for where + * the group is in the stack. This has a significant effect on how + * it is interpreted for blending modes. + * + * PassThrough changes this behaviour. + * + * Group layer cannot be animated, but can contain animated layers or masks. + */ +class KRITALIBKIS_EXPORT GroupLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(GroupLayer) + +public: + explicit GroupLayer(KisImageSP image, QString name, QObject *parent = 0); + explicit GroupLayer(KisGroupLayerSP layer, QObject *parent = 0); + ~GroupLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return grouplayer + */ + virtual QString type() const override; + + /** + * @brief setPassThroughMode + * This changes the way how compositing works. + * Instead of compositing all the layers before compositing it with the rest of the image, + * the group layer becomes a sort of formal way to organise everything. + * + * Passthrough mode is the same as it is in photoshop, + * and the inverse of SVG's isolation attribute(with passthrough=false being the same as + * isolation="isolate"). + * + * @param passthrough whether or not to set the layer to passthrough. + */ + void setPassThroughMode(bool passthrough); + + /** + * @brief passThroughMode + * @return returns whether or not this layer is in passthrough mode. @see setPassThroughMode + */ + bool passThroughMode() const; +}; + +#endif // LIBKIS_GROUPLAYER_H + diff --git a/libs/libkis/GroupShape.cpp b/libs/libkis/GroupShape.cpp new file mode 100644 index 0000000000..036319a8f9 --- /dev/null +++ b/libs/libkis/GroupShape.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "GroupShape.h" +#include + +GroupShape::GroupShape(QObject *parent) : Shape(new KoShapeGroup(), parent) +{ +} + +GroupShape::~GroupShape() +{ + +} + +QList GroupShape::children() +{ + KoShapeGroup * group = dynamic_cast(this->shape()); + QList shapes; + Q_FOREACH(KoShape* shape, group->shapes()) { + shapes.append(new Shape(shape)); + } + return shapes; +} diff --git a/libs/libkis/GroupShape.h b/libs/libkis/GroupShape.h new file mode 100644 index 0000000000..e286443b19 --- /dev/null +++ b/libs/libkis/GroupShape.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_GROUPSHAPE_H +#define LIBKIS_GROUPSHAPE_H + +#include "kritalibkis_export.h" +#include "libkis.h" +#include "Shape.h" +#include + +/** + * @brief The GroupShape class + * A group shape is a vector object with child shapes. + */ + +class KRITALIBKIS_EXPORT GroupShape : public Shape +{ + Q_OBJECT + +public: + explicit GroupShape(QObject *parent = 0); + ~GroupShape(); +public Q_SLOTS: + /** + * @brief children + * @return the child shapes of this group shape. + */ + QList children(); +}; + +#endif // LIBKIS_GROUPSHAPE_H diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp index 50c0b32cca..719a8ae350 100644 --- a/libs/libkis/Krita.cpp +++ b/libs/libkis/Krita.cpp @@ -1,425 +1,427 @@ /* * 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 "Krita.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "View.h" #include "Document.h" #include "Window.h" #include "Extension.h" #include "DockWidgetFactoryBase.h" #include "Filter.h" #include "InfoObject.h" #include "Resource.h" Krita* Krita::s_instance = 0; struct Krita::Private { Private() {} QList extensions; bool batchMode {false}; Notifier *notifier{new Notifier()}; }; Krita::Krita(QObject *parent) : QObject(parent) , d(new Private) { qRegisterMetaType(); } Krita::~Krita() { qDeleteAll(d->extensions); delete d->notifier; delete d; } QList Krita::actions() const { QList actionList; KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return actionList; } KActionCollection *actionCollection = mainWindow->actionCollection(); Q_FOREACH(QAction *action, actionCollection->actions()) { actionList << new Action(action->objectName(), action); } return actionList; } Action *Krita::action(const QString &name) const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KActionCollection *actionCollection = mainWindow->actionCollection(); QAction *action = actionCollection->action(name); if (action) { return new Action(name, action); } return 0; } Document* Krita::activeDocument() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KisView *view = mainWindow->activeView(); if (!view) { return 0; } KisDocument *document = view->document(); return new Document(document); } void Krita::setActiveDocument(Document* value) { Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == value->document().data()) { view->activateWindow(); break; } } } bool Krita::batchmode() const { return d->batchMode; } void Krita::setBatchmode(bool value) { d->batchMode = value; } QList Krita::documents() const { QList ret; foreach(QPointer doc, KisPart::instance()->documents()) { ret << new Document(doc); } return ret; } QStringList Krita::filters() const { QStringList ls = KisFilterRegistry::instance()->keys(); std::sort(ls.begin(), ls.end()); return ls; } Filter *Krita::filter(const QString &name) const { if (!filters().contains(name)) return 0; Filter *filter = new Filter(); filter->setName(name); KisFilterSP f = KisFilterRegistry::instance()->value(name); KisFilterConfigurationSP fc = f->defaultConfiguration(); InfoObject *info = new InfoObject(fc); filter->setConfiguration(info); return filter; } QStringList Krita::colorModels() const { QSet colorModelsIds; QList ids = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorModelsIds << id.id(); } return colorModelsIds.toList();; } QStringList Krita::colorDepths(const QString &colorModel) const { QSet colorDepthsIds; QList ids = KoColorSpaceRegistry::instance()->colorDepthList(colorModel, KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorDepthsIds << id.id(); } return colorDepthsIds.toList();; } QStringList Krita::filterStrategies() const { return KisFilterStrategyRegistry::instance()->keys(); } QStringList Krita::profiles(const QString &colorModel, const QString &colorDepth) const { QSet profileNames; QString id = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth); QList profiles = KoColorSpaceRegistry::instance()->profilesFor(id); Q_FOREACH(const KoColorProfile *profile, profiles) { profileNames << profile->name(); } return profileNames.toList(); } bool Krita::addProfile(const QString &profilePath) { KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); return iccEngine->addProfile(profilePath); } Notifier* Krita::notifier() const { return d->notifier; } QString Krita::version() const { return KritaVersionWrapper::versionString(true); } QList Krita::views() const { QList ret; foreach(QPointer view, KisPart::instance()->views()) { ret << new View(view); } return ret; } Window *Krita::activeWindow() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } return new Window(mainWindow); } QList Krita::windows() const { QList ret; foreach(QPointer mainWin, KisPart::instance()->mainWindows()) { ret << new Window(mainWin); } return ret; } QMap Krita::resources(const QString &type) const { QMap resources = QMap (); if (type == "pattern") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "gradient") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "brush") { KisBrushResourceServer* server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type == "preset") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type == "palette") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "workspace") { KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } return resources; } QStringList Krita::recentDocuments() const { KConfigGroup grp = KSharedConfig::openConfig()->group(QString("RecentFiles")); QStringList keys = grp.keyList(); QStringList recentDocuments; for(int i = 0; i <= keys.filter("File").count(); i++) recentDocuments << grp.readEntry(QString("File%1").arg(i), QString("")); return recentDocuments; } Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile) { KisDocument *document = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(document); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile); Q_ASSERT(cs); QColor qc(Qt::white); qc.setAlpha(0); KoColor bgColor(qc, cs); if (!document->newImage(name, width, height, cs, bgColor, true, 1, "", 100.0)) { qDebug() << "Could not create a new image"; return 0; } Q_ASSERT(document->image()); qDebug() << document->image()->objectName(); return new Document(document); } Document* Krita::openDocument(const QString &filename) { KisDocument *document = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(document); document->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent); return new Document(document); } Window* Krita::openWindow() { KisMainWindow *mw = KisPart::instance()->createMainWindow(); return new Window(mw); } -Action *Krita::createAction(const QString &id, const QString &text) +Action *Krita::createAction(const QString &id, const QString &text, bool addToScriptMenu) { KisAction *action = new KisAction(text, this); action->setObjectName(id); KisActionRegistry *actionRegistry = KisActionRegistry::instance(); actionRegistry->propertizeAction(action->objectName(), action); bool ok; // We will skip this check int activationFlags = actionRegistry->getActionProperty(id, "activationFlags").toInt(&ok, 2); int activationConditions = actionRegistry->getActionProperty(id, "activationConditions").toInt(&ok, 2); action->setActivationFlags((KisAction::ActivationFlags) activationFlags); action->setActivationConditions((KisAction::ActivationConditions) activationConditions); - KisPart::instance()->addScriptAction(action); + if (addToScriptMenu) { + KisPart::instance()->addScriptAction(action); + } return new Action(action->objectName(), action); } void Krita::addExtension(Extension* extension) { d->extensions.append(extension); } QList< Extension* > Krita::extensions() { return d->extensions; } void Krita::writeSetting(const QString &group, const QString &name, const QString &value) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); grp.writeEntry(name, value); } QString Krita::readSetting(const QString &group, const QString &name, const QString &defaultValue) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); return grp.readEntry(name, defaultValue); } void Krita::addDockWidgetFactory(DockWidgetFactoryBase* factory) { KoDockRegistry::instance()->add(factory); } Krita* Krita::instance() { if (!s_instance) { s_instance = new Krita; } return s_instance; } /** * Scripter.fromVariant(variant) * variant is a QVariant * returns instance of QObject-subclass * * This is a helper method for PyQt because PyQt cannot cast a variant to a QObject or QWidget */ QObject *Krita::fromVariant(const QVariant& v) { if (v.canConvert< QWidget* >()) { QObject* obj = qvariant_cast< QWidget* >(v); return obj; } else if (v.canConvert< QObject* >()) { QObject* obj = qvariant_cast< QObject* >(v); return obj; } else return 0; } diff --git a/libs/libkis/Krita.h b/libs/libkis/Krita.h index 389773b7fb..d6412129b9 100644 --- a/libs/libkis/Krita.h +++ b/libs/libkis/Krita.h @@ -1,325 +1,325 @@ /* * 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_KRITA_H #define LIBKIS_KRITA_H #include #include "kritalibkis_export.h" #include "libkis.h" #include "Extension.h" #include "Document.h" #include "Window.h" #include "View.h" #include "Action.h" #include "Notifier.h" class QAction; /** * Krita is a singleton class that offers the root access to the Krita object hierarchy. * * The Krita.instance() is aliased as two builtins: Scripter and Application. */ class KRITALIBKIS_EXPORT Krita : public QObject { Q_OBJECT public: explicit Krita(QObject *parent = 0); ~Krita() override; public Q_SLOTS: /** * @return the currently active document, if there is one. */ Document* activeDocument() const; /** * @brief setActiveDocument activates the first view that shows the given document * @param value the document we want to activate */ void setActiveDocument(Document* value); /** * @brief batchmode determines whether the script is run in batch mode. If batchmode * is true, scripts should now show messageboxes or dialog boxes. * * Note that this separate from Document.setBatchmode(), which determines whether * export/save option dialogs are shown. * * @return true if the script is run in batchmode */ bool batchmode() const; /** * @brief setBatchmode sets the the batchmode to @param value; if true, scripts should * not show dialogs or messageboxes. */ void setBatchmode(bool value); /** * @return return a list of all actions for the currently active mainWindow. */ QList actions() const; /** * @return the action that has been registered under the given name, or 0 if no such action exists. */ Action *action(const QString &name) const; /** * @return a list of all open Documents */ QList documents() const; /** * @brief Filters are identified by an internal name. This function returns a list * of all existing registered filters. * @return a list of all registered filters */ QStringList filters() const; /** * @brief filter construct a Filter object with a default configuration. * @param name the name of the filter. Use Krita.instance().filters() to get * a list of all possible filters. * @return the filter or None if there is no such filter. */ Filter *filter(const QString &name) const; /** * @brief colorModels creates a list with all color models id's registered. * @return a list of all color models or a empty list if there is no such color models. */ QStringList colorModels() const; /** * @brief colorDepths creates a list with the names of all color depths * compatible with the given color model. * @param colorModel the id of a color model. * @return a list of all color depths or a empty list if there is no such * color depths. */ QStringList colorDepths(const QString &colorModel) const; /** * @brief filterStrategies Retrieves all installed filter strategies. A filter * strategy is used when transforming (scaling, shearing, rotating) an image to * calculate the value of the new pixels. You can use th * @return the id's of all available filters. */ QStringList filterStrategies() const; /** * @brief profiles creates a list with the names of all color profiles compatible * with the given color model and color depth. * @param colorModel A string describing the color model of the image: *
      *
    • A: Alpha mask
    • *
    • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
    • *
    • XYZA: XYZ with alpha channel
    • *
    • LABA: LAB with alpha channel
    • *
    • CMYKA: CMYK with alpha channel
    • *
    • GRAYA: Gray with alpha channel
    • *
    • YCbCrA: YCbCr with alpha channel
    • *
    * @param colorDepth A string describing the color depth of the image: *
      *
    • U8: unsigned 8 bits integer, the most common type
    • *
    • U16: unsigned 16 bits integer
    • *
    • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
    • *
    • F32: 32 bits floating point
    • *
    * @return a list with valid names */ QStringList profiles(const QString &colorModel, const QString &colorDepth) const; /** * @brief addProfile load the given profile into the profile registry. * @param profilePath the path to the profile. * @return true if adding the profile succeeded. */ bool addProfile(const QString &profilePath); /** * @brief notifier the Notifier singleton emits signals when documents are opened and * closed, the configuration changes, views are opened and closed or windows are opened. * @return the notifier object */ Notifier *notifier() const; /** * @brief version Determine the version of Krita * * Usage: print(Application.version ()) * * @return the version string including git sha1 if Krita was built from git */ QString version() const; /** * @return a list of all views. A Document can be shown in more than one view. */ QList views() const; /** * @return the currently active window or None if there is no window */ Window *activeWindow() const; /** * @return a list of all windows */ QList windows() const; /** * @brief resources returns a list of Resource objects of the given type * @param type Valid types are: * *
      *
    • pattern
    • *
    • gradient
    • *
    • brush
    • *
    • preset
    • *
    • palette
    • *
    • workspace
    • *
    */ QMap resources(const QString &type) const; /** * @brief return all recent documents registered in the RecentFiles group of the kritarc */ QStringList recentDocuments() const; /** * @brief createDocument creates a new document and image and registers the document with the Krita application. * * The document will have one transparent layer. * * @param width the width in pixels * @param height the height in pixels * @param name the name of the image (not the filename of the document) * @param colorModel A string describing the color model of the image: *
      *
    • A: Alpha mask
    • *
    • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
    • *
    • XYZA: XYZ with alpha channel
    • *
    • LABA: LAB with alpha channel
    • *
    • CMYKA: CMYK with alpha channel
    • *
    • GRAYA: Gray with alpha channel
    • *
    • YCbCrA: YCbCr with alpha channel
    • *
    * @param colorDepth A string describing the color depth of the image: *
      *
    • U8: unsigned 8 bits integer, the most common type
    • *
    • U16: unsigned 16 bits integer
    • *
    • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
    • *
    • F32: 32 bits floating point
    • *
    * @param profile The name of an icc profile that is known to Krita. If an empty string is passed, the default is * taken. * @return the created document. */ Document *createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile); /** * @brief openDocument creates a new Document, registers it with the Krita application and loads the given file. * @param filename the file to open in the document * @return the document */ Document *openDocument(const QString &filename); /** * @brief openWindow create a new main window. The window is not shown by default. */ Window *openWindow(); /** * @brief createAction creates an action with the given text and passes it to Krita. Every newly created * mainwindow will create an instance of this action. This means that actions need to be created in the * setup phase of the plugin, not on the fly. * @param id the unique id for this action * @param text the user-visible text * @return the Action you can connect a slot to. */ - Action *createAction(const QString &name, const QString &text); + Action *createAction(const QString &name, const QString &text, bool addToScriptMenu = true); /** * @brief addExtension add the given plugin to Krita. There will be a single instance of each Extension in the Krita process. * @param extension the extension to add. */ void addExtension(Extension* extension); /** * return a list with all registered extension objects. */ QList extensions(); /** * @brief addDockWidgetFactory Add the given docker factory to the application. For scripts * loaded on startup, this means that every window will have one of the dockers created by the * factory. * @param factory The factory object. */ void addDockWidgetFactory(DockWidgetFactoryBase* factory ); /** * @brief writeSetting write the given setting under the given name to the kritarc file in * the given settings group. * @param group The group the setting belongs to. If empty, then the setting is written in the * general section * @param name The name of the setting * @param value The value of the setting. Script settings are always written as strings. */ void writeSetting(const QString &group, const QString &name, const QString &value); /** * @brief readSetting read the given setting value from the kritarc file. * @param group The group the setting is part of. If empty, then the setting is read from * the general group. * @param name The name of the setting * @param defaultValue The default value of the setting * @return a string representing the setting. */ QString readSetting(const QString &group, const QString &name, const QString &defaultValue); /** * @brief instance retrieve the singleton instance of the Application object. */ static Krita* instance(); // Internal only: for use with mikro.py static QObject *fromVariant(const QVariant& v); private: struct Private; Private *const d; static Krita* s_instance; }; Q_DECLARE_METATYPE(Notifier*); #endif // LIBKIS_KRITA_H diff --git a/libs/libkis/Node.cpp b/libs/libkis/Node.cpp index f55d671f62..755cbaa4fc 100644 --- a/libs/libkis/Node.cpp +++ b/libs/libkis/Node.cpp @@ -1,574 +1,611 @@ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Krita.h" #include "Node.h" #include "Channel.h" #include "Filter.h" #include "Selection.h" +#include "GroupLayer.h" +#include "CloneLayer.h" +#include "FilterLayer.h" +#include "FillLayer.h" +#include "FileLayer.h" +#include "VectorLayer.h" +#include "FilterMask.h" +#include "SelectionMask.h" + + + struct Node::Private { Private() {} KisImageSP image; KisNodeSP node; }; Node::Node(KisImageSP image, KisNodeSP node, QObject *parent) : QObject(parent) , d(new Private) { d->image = image; d->node = node; } Node::~Node() { delete d; } bool Node::operator==(const Node &other) const { return (d->node == other.d->node && d->image == other.d->image); } bool Node::operator!=(const Node &other) const { return !(operator==(other)); } Node *Node::clone() const { KisNodeSP clone = d->node->clone(); Node *node = new Node(0, clone); return node; } bool Node::alphaLocked() const { if (!d->node) return false; KisPaintLayerSP paintLayer = qobject_cast(d->node.data()); if (paintLayer) { return paintLayer->alphaLocked(); } return false; } void Node::setAlphaLocked(bool value) { if (!d->node) return; KisPaintLayerSP paintLayer = qobject_cast(d->node.data()); if (paintLayer) { paintLayer->setAlphaLocked(value); } } QString Node::blendingMode() const { if (!d->node) return QString(); return d->node->compositeOpId(); } void Node::setBlendingMode(QString value) { if (!d->node) return; d->node->setCompositeOpId(value); } QList Node::channels() const { QList channels; if (!d->node) return channels; if (!d->node->inherits("KisLayer")) return channels; Q_FOREACH(KoChannelInfo *info, d->node->colorSpace()->channels()) { Channel *channel = new Channel(d->node, info); channels << channel; } return channels; } QList Node::childNodes() const { QList nodes; if (d->node) { int childCount = d->node->childCount(); for (int i = 0; i < childCount; ++i) { - nodes << new Node(d->image, d->node->at(i)); + if (qobject_cast(d->node->at(i))) { + nodes << new GroupLayer(KisGroupLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new CloneLayer(KisCloneLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new FileLayer(KisFileLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new FilterLayer(KisAdjustmentLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new FillLayer(KisGeneratorLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new VectorLayer(KisShapeLayerSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new FilterMask(d->image, KisFilterMaskSP(dynamic_cast(d->node->at(i).data()))); + + } else if (qobject_cast(d->node->at(i))) { + nodes << new SelectionMask(d->image, KisSelectionMaskSP(dynamic_cast(d->node->at(i).data()))); + + } else { + nodes << new Node(d->image, d->node->at(i)); + } } } return nodes; } bool Node::addChildNode(Node *child, Node *above) { if (!d->node) return false; return d->image->addNode(child->node(), d->node, above->node()); } bool Node::removeChildNode(Node *child) { if (!d->node) return false; return d->image->removeNode(child->node()); } void Node::setChildNodes(QList nodes) { if (!d->node) return; KisNodeSP node = d->node->firstChild(); while (node) { d->image->removeNode(node); node = node->nextSibling(); } Q_FOREACH(Node *node, nodes) { d->image->addNode(node->node(), d->node); } } int Node::colorLabel() const { if (!d->node) return 0; return d->node->colorLabelIndex(); } void Node::setColorLabel(int index) { if (!d->node) return; d->node->setColorLabelIndex(index); } QString Node::colorDepth() const { if (!d->node) return ""; return d->node->colorSpace()->colorDepthId().id(); } QString Node::colorModel() const { if (!d->node) return ""; return d->node->colorSpace()->colorModelId().id(); } QString Node::colorProfile() const { if (!d->node) return ""; return d->node->colorSpace()->profile()->name(); } bool Node::setColorProfile(const QString &colorProfile) { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; KisLayer *layer = qobject_cast(d->node.data()); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile); const KoColorSpace *srcCS = layer->colorSpace(); const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(srcCS->colorModelId().id(), srcCS->colorDepthId().id(), profile); KisChangeProfileVisitor v(srcCS, dstCs); return layer->accept(v); } bool Node::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; KisLayer *layer = qobject_cast(d->node.data()); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile); const KoColorSpace *srcCS = layer->colorSpace(); const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile); KisColorSpaceConvertVisitor v(d->image, srcCS, dstCs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return layer->accept(v); } bool Node::animated() const { if (!d->node) return false; return d->node->isAnimated(); } void Node::enableAnimation() const { if (!d->node) return; d->node->enableAnimation(); } bool Node::collapsed() const { if (!d->node) return false; return d->node->collapsed(); } void Node::setCollapsed(bool collapsed) { if (!d->node) return; d->node->setCollapsed(collapsed); } bool Node::inheritAlpha() const { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; return qobject_cast(d->node)->alphaChannelDisabled(); } void Node::setInheritAlpha(bool value) { if (!d->node) return; if (!d->node->inherits("KisLayer")) return; const_cast(qobject_cast(d->node))->disableAlphaChannel(value); } bool Node::locked() const { if (!d->node) return false; return d->node->userLocked(); } void Node::setLocked(bool value) { if (!d->node) return; d->node->setUserLocked(value); } QString Node::name() const { if (!d->node) return QString(); return d->node->name(); } void Node::setName(QString name) { if (!d->node) return; d->node->setName(name); } int Node::opacity() const { if (!d->node) return 0; return d->node->opacity(); } void Node::setOpacity(int value) { if (!d->node) return; if (value < 0) value = 0; if (value > 255) value = 255; d->node->setOpacity(value); } Node* Node::parentNode() const { if (!d->node) return 0; return new Node(d->image, d->node->parent()); } QString Node::type() const { if (!d->node) return QString(); if (qobject_cast(d->node)) { return "paintlayer"; } else if (qobject_cast(d->node)) { return "grouplayer"; } if (qobject_cast(d->node)) { return "filelayer"; } if (qobject_cast(d->node)) { return "filterlayer"; } if (qobject_cast(d->node)) { return "filllayer"; } if (qobject_cast(d->node)) { return "clonelayer"; } if (qobject_cast(d->node)) { - return "shapelayer"; + return "vectorlayer"; } if (qobject_cast(d->node)) { return "transparencymask"; } if (qobject_cast(d->node)) { return "filtermask"; } if (qobject_cast(d->node)) { return "transformmask"; } if (qobject_cast(d->node)) { return "selectionmask"; } if (qobject_cast(d->node)) { return "colorizemask"; } return QString(); } bool Node::visible() const { if (!d->node) return false; return d->node->visible();; } void Node::setVisible(bool visible) { if (!d->node) return; d->node->setVisible(visible); } QByteArray Node::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->node) return ba; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return ba; ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } QByteArray Node::pixelDataAtTime(int x, int y, int w, int h, int time) const { QByteArray ba; if (!d->node || !d->node->isAnimated()) return ba; // KisRasterKeyframeChannel *rkc = dynamic_cast(d->node->getKeyframeChannel(KisKeyframeChannel::Content.id())); if (!rkc) return ba; KisKeyframeSP frame = rkc->keyframeAt(time); if (!frame) return ba; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return ba; rkc->fetchFrame(frame, dev); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } QByteArray Node::projectionPixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->node) return ba; KisPaintDeviceSP dev = d->node->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } void Node::setPixelData(QByteArray value, int x, int y, int w, int h) { if (!d->node) return; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return; dev->writeBytes((const quint8*)value.constData(), x, y, w, h); } QRect Node::bounds() const { if (!d->node) return QRect(); return d->node->exactBounds(); } void Node::move(int x, int y) { if (!d->node) return; d->node->setX(x); d->node->setY(y); } QPoint Node::position() const { if (!d->node) return QPoint(); return QPoint(d->node->x(), d->node->y()); } bool Node::remove() { if (!d->node) return false; if (!d->node->parent()) return false; return d->image->removeNode(d->node); } Node* Node::duplicate() { if (!d->node) return 0; return new Node(d->image, d->node->clone()); } bool Node::save(const QString &filename, double xRes, double yRes) { if (!d->node) return false; if (filename.isEmpty()) return false; KisPaintDeviceSP projection = d->node->projection(); QRect bounds = d->node->exactBounds(); QString mimeType = KisMimeDatabase::mimeTypeForFile(filename, false); QScopedPointer doc(KisPart::instance()->createDocument()); KisImageSP dst = new KisImage(doc->createUndoStore(), bounds.width(), bounds.height(), projection->compositionSourceColorSpace(), d->node->name()); dst->setResolution(xRes, yRes); doc->setFileBatchMode(Krita::instance()->batchmode()); doc->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", d->node->opacity()); paintLayer->paintDevice()->makeCloneFrom(projection, bounds); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->initialRefreshGraph(); bool r = doc->exportDocumentSync(QUrl::fromLocalFile(filename), mimeType.toLatin1()); if (!r) { qWarning() << doc->errorMessage(); } return r; } Node *Node::mergeDown() { if (!d->node) return 0; if (!qobject_cast(d->node.data())) return 0; if (!d->node->nextSibling()) return 0; if (!d->node->parent()) return 0; int index = d->node->parent()->index(d->node->prevSibling()); d->image->mergeDown(qobject_cast(d->node.data()), KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); d->image->waitForDone(); return new Node(d->image, d->node->parent()->at(index)); } void Node::scaleNode(int width, int height, QString strategy) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy); if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic"); d->image->scaleNode(d->node, width, height, actualStrategy); } void Node::rotateNode(double radians) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; d->image->rotateNode(d->node, radians); } void Node::cropNode(int x, int y, int w, int h) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; QRect rect = QRect(x, y, w, h); d->image->cropNode(d->node, rect); } void Node::shearNode(double angleX, double angleY) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; d->image->shearNode(d->node, angleX, angleY); } QImage Node::thumbnail(int w, int h) { if (!d->node) return QImage(); return d->node->createThumbnail(w, h); } KisPaintDeviceSP Node::paintDevice() const { return d->node->paintDevice(); } KisImageSP Node::image() const { return d->image; } KisNodeSP Node::node() const { return d->node; } diff --git a/libs/libkis/Node.h b/libs/libkis/Node.h index debc8f470b..afd87768ec 100644 --- a/libs/libkis/Node.h +++ b/libs/libkis/Node.h @@ -1,528 +1,536 @@ /* * 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_NODE_H #define LIBKIS_NODE_H #include #include #include "kritalibkis_export.h" #include "libkis.h" /** * Node represents a layer or mask in a Krita image's Node hierarchy. Group layers can contain * other layers and masks; layers can contain masks. * */ class KRITALIBKIS_EXPORT Node : public QObject { Q_OBJECT Q_DISABLE_COPY(Node) public: explicit Node(KisImageSP image, KisNodeSP node, QObject *parent = 0); ~Node() override; bool operator==(const Node &other) const; bool operator!=(const Node &other) const; + public Q_SLOTS: /** * @brief clone clone the current node. The node is not associated with any image. */ Node *clone() const; /** * @brief alphaLocked checks whether the node is a paint layer and returns whether it is alpha locked * @return whether the paint layer is alpha locked, or false if the node is not a paint layer */ bool alphaLocked() const; /** * @brief setAlphaLocked set the layer to value if the the node is paint layer. */ void setAlphaLocked(bool value); /** * @return the blending mode of the layer. The values of the blending modes are defined in @see KoCompositeOpRegistry.h */ QString blendingMode() const; /** * @brief setBlendingMode set the blending mode of the node to the given value * @param value one of the string values from @see KoCompositeOpRegistry.h */ void setBlendingMode(QString value); /** * @brief channels creates a list of Channel objects that can be used individually to * show or hide certain channels, and to retrieve the contents of each channel in a * node separately. * * Only layers have channels, masks do not, and calling channels on a Node that is a mask * will return an empty list. * * @return the list of channels ordered in by position of the channels in pixel position */ QList channels() const; /** * Return a list of child nodes of the current node. The nodes are ordered from the bottommost up. * The function is not recursive. */ QList childNodes() const; /** * @brief addChildNode adds the given node in the list of children. * @param child the node to be added * @param above the node above which this node will be placed * @return false if adding the node failed */ bool addChildNode(Node *child, Node *above); /** * @brief removeChildNode removes the given node from the list of children. * @param child the node to be removed */ bool removeChildNode(Node *child); /** * @brief setChildNodes this replaces the existing set of child nodes with the new set. * @param nodes The list of nodes that will become children, bottom-up -- the first node, * is the bottom-most node in the stack. */ void setChildNodes(QList nodes); /** * colorDepth A string describing the color depth of the image: *
      *
    • U8: unsigned 8 bits integer, the most common type
    • *
    • U16: unsigned 16 bits integer
    • *
    • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
    • *
    • F32: 32 bits floating point
    • *
    * @return the color depth. */ QString colorDepth() const; /** * @brief colorModel retrieve the current color model of this document: *
      *
    • A: Alpha mask
    • *
    • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
    • *
    • XYZA: XYZ with alpha channel
    • *
    • LABA: LAB with alpha channel
    • *
    • CMYKA: CMYK with alpha channel
    • *
    • GRAYA: Gray with alpha channel
    • *
    • YCbCrA: YCbCr with alpha channel
    • *
    * @return the internal color model string. */ QString colorModel() const; /** * @return the name of the current color profile */ QString colorProfile() const; /** * @brief setColorProfile set the color profile of the image to the given profile. The profile has to * be registered with krita and be compatible with the current color model and depth; the image data * is not converted. * @param colorProfile * @return if assigining the colorprofiel worked */ bool setColorProfile(const QString &colorProfile); /** * @brief setColorSpace convert the node to the given colorspace * @param colorModel A string describing the color model of the node: *
      *
    • A: Alpha mask
    • *
    • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
    • *
    • XYZA: XYZ with alpha channel
    • *
    • LABA: LAB with alpha channel
    • *
    • CMYKA: CMYK with alpha channel
    • *
    • GRAYA: Gray with alpha channel
    • *
    • YCbCrA: YCbCr with alpha channel
    • *
    * @param colorDepth A string describing the color depth of the image: *
      *
    • U8: unsigned 8 bits integer, the most common type
    • *
    • U16: unsigned 16 bits integer
    • *
    • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
    • *
    • F32: 32 bits floating point
    • *
    * @param colorProfile a valid color profile for this color model and color depth combination. */ bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile); /** * @brief Krita layers can be animated, i.e., have frames. * @return return true if the layer has frames. Currently, the scripting framework * does not give access to the animation features. */ bool animated() const; /** * @brief enableAnimation make the current layer animated, so it can have frames. */ void enableAnimation() const; /** * Sets the state of the node to the value of @param collapsed */ void setCollapsed(bool collapsed); /** * returns the collapsed state of this node */ bool collapsed() const; /** * Sets a color label index associated to the layer. The actual * color of the label and the number of available colors is * defined by Krita GUI configuration. */ int colorLabel() const; /** * @brief setColorLabel sets a color label index associated to the layer. The actual * color of the label and the number of available colors is * defined by Krita GUI configuration. * @param index an integer corresponding to the set of available color labels. */ void setColorLabel(int index); /** * @brief inheritAlpha checks whether this node has the inherits alpha flag set * @return true if the Inherit Alpha is set */ bool inheritAlpha() const; /** * set the Inherit Alpha flag to the given value */ void setInheritAlpha(bool value); /** * @brief locked checks whether the Node is locked. A locked node cannot be changed. * @return true if the Node is locked, false if it hasn't been locked. */ bool locked() const; /** * set the Locked flag to the give value */ void setLocked(bool value); /** * @return the user-visible name of this node. */ QString name() const; /** * rename the Node to the given name */ void setName(QString name); /** * return the opacity of the Node. The opacity is a value between 0 and 255. */ int opacity() const; /** * set the opacity of the Node to the given value. The opacity is a value between 0 and 255. */ void setOpacity(int value); /** * return the Node that is the parent of the current Node, or 0 if this is the root Node. */ Node* parentNode() const; /** * @brief type Krita has several types of nodes, split in layers and masks. Group * layers can contain other layers, any layer can contain masks. * * @return The type of the node. Valid types are: *
      *
    • paintlayer *
    • grouplayer *
    • filelayer *
    • filterlayer *
    • filllayer *
    • clonelayer *
    • vectorlayer *
    • transparencymask *
    • filtermask *
    • transformmask *
    • selectionmask *
    • colorizemask *
    * * If the Node object isn't wrapping a valid Krita layer or mask object, and * empty string is returned. */ - QString type() const; + virtual QString type() const; /** * Check whether the current Node is visible in the layer stack */ bool visible() const; /** * Set the visibility of the current node to @param visible */ void setVisible(bool visible); /** * @brief pixelData reads the given rectangle from the Node's paintable pixels, if those * exist, and returns it as a byte array. The pixel data starts top-left, and is ordered row-first. * * The byte array can be interpreted as follows: 8 bits images have one byte per channel, * and as many bytes as there are channels. 16 bits integer images have two bytes per channel, * representing an unsigned short. 16 bits float images have two bytes per channel, representing * a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a * float. * * You can read outside the node boundaries; those pixels will be transparent black. * * The order of channels is: * *
      *
    • Integer RGBA: Blue, Green, Red, Alpha *
    • Float RGBA: Red, Green, Blue, Alpha *
    • GrayA: Gray, Alpha *
    • Selection: selectedness *
    • LabA: L, a, b, Alpha *
    • CMYKA: Cyan, Magenta, Yellow, Key, Alpha *
    • XYZA: X, Y, Z, A *
    • YCbCrA: Y, Cb, Cr, Alpha *
    * * The byte array is a copy of the original node data. In Python, you can use bytes, bytearray * and the struct module to interpret the data and construct, for instance, a Pillow Image object. * * If you read the pixeldata of a mask, a filter or generator layer, you get the selection bytes, * which is one channel with values in the range from 0..255. * * If you want to change the pixels of a node you can write the pixels back after manipulation * with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups * or file layers. * * @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 pixelDataAtTime a basic function to get pixeldata from an animated node at a given time. * @param x the position from the left to start reading. * @param y the position from the top to start reader * @param w the row length to read * @param h the number of rows to read * @param time the frame number * @return a QByteArray with the pixel data. The byte array may be empty. */ QByteArray pixelDataAtTime(int x, int y, int w, int h, int time) const; /** * @brief projectionPixelData reads the given rectangle from the Node's projection (that is, what the node * looks like after all sub-Nodes (like layers in a group or masks on a layer) have been applied, * and returns it as a byte array. The pixel data starts top-left, and is ordered row-first. * * The byte array can be interpreted as follows: 8 bits images have one byte per channel, * and as many bytes as there are channels. 16 bits integer images have two bytes per channel, * representing an unsigned short. 16 bits float images have two bytes per channel, representing * a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a * float. * * You can read outside the node boundaries; those pixels will be transparent black. * * The order of channels is: * *
      *
    • Integer RGBA: Blue, Green, Red, Alpha *
    • Float RGBA: Red, Green, Blue, Alpha *
    • GrayA: Gray, Alpha *
    • Selection: selectedness *
    • LabA: L, a, b, Alpha *
    • CMYKA: Cyan, Magenta, Yellow, Key, Alpha *
    • XYZA: X, Y, Z, A *
    • YCbCrA: Y, Cb, Cr, Alpha *
    * * The byte array is a copy of the original node data. In Python, you can use bytes, bytearray * and the struct module to interpret the data and construct, for instance, a Pillow Image object. * * If you read the projection of a mask, you get the selection bytes, which is one channel with * values in the range from 0..255. * * If you want to change the pixels of a node you can write the pixels back after manipulation * with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups * or file layers. * * @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 projectionPixelData(int x, int y, int w, int h) const; /** * @brief setPixelData writes the given bytes, of which there must be enough, into the * Node, if the Node has writable pixel data: * *
      *
    • paint layer: the layer's original pixels are overwritten *
    • filter layer, generator layer, any mask: the embedded selection's pixels are overwritten. * Note: for these *
    * * File layers, Group layers, Clone layers cannot be written to. Calling setPixelData on * those layer types will silently do nothing. * * @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 * (number of channels * size of channel * 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); /** * @brief bounds return the exact bounds of the node's paint device * @return the bounds, or an empty QRect if the node has no paint device or is empty. */ QRect bounds() const; /** * move the pixels to the given x, y location in the image coordinate space. */ void move(int x, int y); /** * @brief position returns the position of the paint device of this node * @return the top-left position of the node */ QPoint position() const; /** * @brief remove removes this node from its parent image. */ bool remove(); /** * @brief duplicate returns a full copy of the current node. The node is not inserted in the graphc * @return a valid Node object or 0 if the node couldn't be duplicated. */ Node* duplicate(); /** * @brief save exports the given node with this filename. The extension of the filename determines the filetype. * @param filename the filename including extension * @param xRes the horizontal resolution in pixels per pt (there are 72 pts in an inch) * @param yRes the horizontal resolution in pixels per pt (there are 72 pts in an inch) * @return true if saving succeeded, false if it failed. */ bool save(const QString &filename, double xRes, double yRes); /** * @brief mergeDown merges the given node with the first visible node underneath this node in the layerstack. * This will drop all per-layer metadata. * @param node the node to merge down; this node will be removed from the layer stack */ Node *mergeDown(); /** * @brief scaleNode * @param width * @param height * @param strategy the scaling strategy. There's several ones amongst these that aren't available in the regular UI. *
      *
    • Hermite
    • *
    • Bicubic - Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.
    • *
    • Box - Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.
    • *
    • Bilinear - Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.
    • *
    • Bell
    • *
    • BSpline
    • *
    • Lanczos3 - Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.
    • *
    • Mitchell
    • *
    */ void scaleNode(int width, int height, QString strategy); /** * @brief rotateNode rotate this layer by the given radians. * @param radians amount the layer should be rotated in, in radians. */ void rotateNode(double radians); /** * @brief cropNode crop this layer. * @param x the left edge of the cropping rectangle. * @param y the top edge of the cropping rectangle * @param w the right edge of the cropping rectangle * @param h the bottom edge of the cropping rectangle */ void cropNode(int x, int y, int w, int h); /** * @brief shearNode perform a shear operation on this node. * @param angleX the X-angle in degrees to shear by * @param angleY the Y-angle in degrees to shear by */ void shearNode(double angleX, double angleY); /** * @brief thumbnail create a thumbnail of the given dimensions. The thumbnail is sized according * to the layer dimensions, not the image dimensions. If the requested size is too big a null * QImage is created. If the current node cannot generate a thumbnail, a transparent QImage of the * requested size is generated. * @return a QImage representing the layer contents. */ QImage thumbnail(int w, int h); private: friend class Filter; friend class Document; friend class Selection; + friend class GroupLayer; + friend class FileLayer; + friend class FilterLayer; + friend class FillLayer; + friend class VectorLayer; + friend class FilterMask; + friend class SelectionMask; /** * @brief paintDevice gives access to the internal paint device of this Node * @return the paintdevice or 0 if the node does not have an editable paint device. */ KisPaintDeviceSP paintDevice() const; KisImageSP image() const; KisNodeSP node() const; struct Private; Private *const d; }; #endif // LIBKIS_NODE_H diff --git a/libs/libkis/Selection.h b/libs/libkis/Selection.h index a65dfe8caa..961463b6df 100644 --- a/libs/libkis/Selection.h +++ b/libs/libkis/Selection.h @@ -1,242 +1,245 @@ /* * 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 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); /** * @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/libs/libkis/SelectionMask.cpp b/libs/libkis/SelectionMask.cpp new file mode 100644 index 0000000000..4daee51c8f --- /dev/null +++ b/libs/libkis/SelectionMask.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "SelectionMask.h" +#include +#include +#include "Selection.h" +#include + +SelectionMask::SelectionMask(KisImageSP image, QString name, QObject *parent) : + Node(image, new KisSelectionMask(image), parent) +{ + this->node()->setName(name); +} + +SelectionMask::SelectionMask(KisImageSP image, KisSelectionMaskSP mask, QObject *parent): + Node(image, mask, parent) +{ + +} + +SelectionMask::~SelectionMask() +{ + +} + +Selection *SelectionMask::selection() const +{ + const KisSelectionMask *mask = qobject_cast(this->node()); + return new Selection(mask->selection()); +} + +void SelectionMask::setSelection(Selection *selection) +{ + KisSelectionMask *mask = dynamic_cast(this->node().data()); + mask->setSelection(selection->selection()); +} + +QString SelectionMask::type() const +{ + return "selectionmask"; +} diff --git a/libs/libkis/SelectionMask.h b/libs/libkis/SelectionMask.h new file mode 100644 index 0000000000..ec38a67ef2 --- /dev/null +++ b/libs/libkis/SelectionMask.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_SELECTIONMASK_H +#define LIBKIS_SELECTIONMASK_H + +#include +#include "Node.h" + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +/** + * @brief The SelectionMask class + * A selection mask is a mask type node that can be used + * to store selections. In the gui, these are refered to + * as local selections. + * + * A selection mask can hold both raster and vector selections, though + * the API only supports raster selections. + */ +class KRITALIBKIS_EXPORT SelectionMask : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(SelectionMask) + +public: + explicit SelectionMask(KisImageSP image, QString name, QObject *parent = 0); + explicit SelectionMask(KisImageSP image, KisSelectionMaskSP mask, QObject *parent = 0); + ~SelectionMask() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return selectionmask + * + * If the Node object isn't wrapping a valid Krita layer or mask object, and + * empty string is returned. + */ + virtual QString type() const override; + + Selection *selection() const; + + void setSelection(Selection *selection); +}; + +#endif // LIBKIS_SELECTIONMASK_H + diff --git a/libs/libkis/Shape.cpp b/libs/libkis/Shape.cpp new file mode 100644 index 0000000000..885c39c581 --- /dev/null +++ b/libs/libkis/Shape.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "Shape.h" +struct Shape::Private { + Private() {} + KoShape *shape; +}; + +Shape::Shape(KoShape *shape, QObject *parent) + : QObject(parent) + , d(new Private) +{ + d->shape = shape; +} + +Shape::~Shape() +{ + delete d; +} + +QString Shape::name() const +{ + return d->shape->name(); +} + +void Shape::setName(const QString &name) +{ + d->shape->setName(name); +} + +QString Shape::type() const +{ + return d->shape->shapeId(); +} + +bool Shape::visible() +{ + return d->shape->isVisible(); +} + +void Shape::setVisible(bool visible) +{ + d->shape->setVisible(visible); +} + +KoShape *Shape::shape() +{ + return d->shape; +} diff --git a/libs/libkis/Shape.h b/libs/libkis/Shape.h new file mode 100644 index 0000000000..e7fd1d3e69 --- /dev/null +++ b/libs/libkis/Shape.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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_SHAPE_H +#define LIBKIS_SHAPE_H + +#include +#include + +#include "kritalibkis_export.h" +#include "libkis.h" +#include + +/** + * @brief The Shape class + * The shape class is a wrapper around Krita's vector objects. + */ +class KRITALIBKIS_EXPORT Shape : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Shape) + +public: + explicit Shape(KoShape *shape, QObject *parent = 0); + ~Shape(); +public Q_SLOTS: + + /** + * @brief name + * @return the name of the shape + */ + QString name() const; + + /** + * @brief setName + * @param name which name the shape should have. + */ + void setName(const QString &name); + + /** + * @brief type + * @return the type of shape. + */ + virtual QString type() const; + + /** + * @brief visible + * @return whether the shape is visible. + */ + bool visible(); + + /** + * @brief setVisible + * @param visible whether the shape should be visible. + */ + void setVisible(bool visible); + +private: + friend class GroupShape; + friend class VectorLayer; + + struct Private; + Private *const d; + + KoShape *shape(); +}; + +#endif // LIBKIS_SHAPE_H diff --git a/libs/libkis/VectorLayer.cpp b/libs/libkis/VectorLayer.cpp new file mode 100644 index 0000000000..38067c6097 --- /dev/null +++ b/libs/libkis/VectorLayer.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "VectorLayer.h" +#include +#include + +VectorLayer::VectorLayer(KoShapeBasedDocumentBase* shapeController, KisImageSP image, QString name, QObject *parent) : + Node(image, new KisShapeLayer(shapeController, image, name, OPACITY_OPAQUE_U8), parent) +{ + +} + +VectorLayer::VectorLayer(KisShapeLayerSP layer, QObject *parent): + Node(layer->image(), layer, parent) +{ + +} + +VectorLayer::~VectorLayer() +{ + +} + +QString VectorLayer::type() const +{ + return "vectorlayer"; +} + +QList VectorLayer::shapes() const +{ + QList shapes; + KisShapeLayerSP vector = KisShapeLayerSP(dynamic_cast(this->node().data())); + if (vector) { + qDebug()<<"shape layer exists"; + qDebug()<shapes().size(); + for (int i=0; ishapeCount(); i++) { + shapes << new Shape(vector->shapes().at(i)); + } + qDebug() << "shapeslength"< + * + * 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_VECTORLAYER_H +#define LIBKIS_VECTORLAYER_H + +#include + +#include + +#include "kritalibkis_export.h" +#include "libkis.h" + +#include + +#include "Node.h" +#include "Shape.h" + +/** + * @brief The VectorLayer class + * A vector layer is a special layer that stores + * and shows vector shapes. + */ + +class KRITALIBKIS_EXPORT VectorLayer : public Node +{ + Q_OBJECT + Q_DISABLE_COPY(VectorLayer) + +public: + explicit VectorLayer(KoShapeBasedDocumentBase* shapeController, KisImageSP image, QString name, QObject *parent = 0); + explicit VectorLayer(KisShapeLayerSP layer, QObject *parent = 0); + ~VectorLayer() override; +public Q_SLOTS: + + /** + * @brief type Krita has several types of nodes, split in layers and masks. Group + * layers can contain other layers, any layer can contain masks. + * + * @return vectorlayer + */ + virtual QString type() const override; + + /** + * @brief shapes + * @return the list of top-level shapes in this vector layer. + */ + QList shapes() const; + +}; + +#endif // LIBKIS_VECTORLAYER_H + diff --git a/libs/libkis/libkis.h b/libs/libkis/libkis.h index 1f8106a627..62c67a6bd2 100644 --- a/libs/libkis/libkis.h +++ b/libs/libkis/libkis.h @@ -1,27 +1,37 @@ #include #include #include #include #include #include class Action; class Canvas; class Channel; class ColorDepth; class ColorManager; class ColorModel; class ColorProfile; class DockWidget; class DockWidgetFactoryBase; class Document; class Filter; class InfoObject; class Krita; class Node; class Notifier; class Resource; class Selection; class View; class Extension; class Window; + +class Shape; +class GroupShape; +class PaintLayer; +class CloneLayer; +class GroupLayer; +class FilterLayer; +class FillLayer; +class FileLayer; +class VectorLayer; diff --git a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt index 6c9edf4fff..bdb52b0771 100644 --- a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt @@ -1,110 +1,116 @@ # Copyright (C) 2012, 2013 Shaheed Haque # Copyright (C) 2013 Alex Turbov # Copyright (C) 2014-2016 Boudewijn Rempt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) # # Simple helper function to install plugin and related files # having only a name of the plugin... # (just to reduce syntactic noise when a lot of plugins get installed) # function(install_pykrita_plugin name) set(_options) set(_one_value_args) set(_multi_value_args PATTERNS FILE) cmake_parse_arguments(install_pykrita_plugin "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN}) if(NOT name) message(FATAL_ERROR "Plugin filename is not given") endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py) install(FILES kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) foreach(_f ${name}.py ${name}.ui ${install_pykrita_plugin_FILE}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_f}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${_f} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) endif() endforeach() elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name}) install(FILES ${name}/kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "*.ui" PATTERN "*.txt" PATTERN "*.csv" PATTERN "__pycache__*" EXCLUDE ) # TODO Is there any way to form a long PATTERN options string # and use it in a single install() call? # NOTE Install specified patterns one-by-one... foreach(_pattern ${install_pykrita_plugin_PATTERNS}) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "${_pattern}" PATTERN "__pycache__*" EXCLUDE ) endforeach() else() message(FATAL_ERROR "Do not know what to do with ${name}") endif() endfunction() install_pykrita_plugin(hello) install_pykrita_plugin(assignprofiledialog) install_pykrita_plugin(scripter) install_pykrita_plugin(colorspace) install_pykrita_plugin(documenttools) install_pykrita_plugin(filtermanager) install_pykrita_plugin(exportlayers) #install_pykrita_plugin(highpass) install_pykrita_plugin(tenbrushes) install_pykrita_plugin(tenscripts) -install( FILES - tenbrushes/tenbrushes.action - tenscripts/tenscripts.action -DESTINATION ${DATA_INSTALL_DIR}/krita/actions) - install_pykrita_plugin(palette_docker) install_pykrita_plugin(quick_settings_docker) install_pykrita_plugin(lastdocumentsdocker) install_pykrita_plugin(scriptdocker) install_pykrita_plugin(comics_project_management_tools) # if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3) # install_pykrita_plugin(cmake_utils) # install_pykrita_plugin(js_utils PATTERNS "*.json") # install_pykrita_plugin(expand PATTERNS "*.expand" "templates/*.tpl") # endif() +install( FILES + hello/hello.xmlgui +DESTINATION ${DATA_INSTALL_DIR}/kritaplugins/) + + +install( FILES + hello/hello.action + tenbrushes/tenbrushes.action + tenscripts/tenscripts.action +DESTINATION ${DATA_INSTALL_DIR}/krita/actions) + install( DIRECTORY libkritapykrita DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "__pycache__*" EXCLUDE ) diff --git a/plugins/extensions/pykrita/plugin/plugins/hello/hello.action b/plugins/extensions/pykrita/plugin/plugins/hello/hello.action new file mode 100644 index 0000000000..4eb87a8b39 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/hello/hello.action @@ -0,0 +1,18 @@ + + + + Hello + + + Say Hello World + Say Hello World + Say Hello World + + + + + false + + + + diff --git a/plugins/extensions/pykrita/plugin/plugins/hello/hello.py b/plugins/extensions/pykrita/plugin/plugins/hello/hello.py index da5aef849c..28501422b1 100644 --- a/plugins/extensions/pykrita/plugin/plugins/hello/hello.py +++ b/plugins/extensions/pykrita/plugin/plugins/hello/hello.py @@ -1,75 +1,75 @@ """ This is a simple example of a Python script for Krita. It demonstrates how to set up a custom extension and a custom docker! """ from PyQt5.QtCore import qDebug from PyQt5.QtWidgets import QWidget, QLabel, QMessageBox from krita import Krita, Extension, DockWidget, DockWidgetFactory, DockWidgetFactoryBase def hello(): """ Show a test message box. """ QMessageBox.information(QWidget(), i18n("Test"), i18n("Hello! This is Krita version %s") % Application.version()) class HelloExtension(Extension): """ HelloExtension is a small example extension demonstrating basic Python scripting support in Krita! """ def __init__(self, parent): """ Standard Krita Python extension constructor. Most of the initialization happens in :func:`setup` :param parent: Parent widget :type parent: :class:`QWidget` or None """ super().__init__(parent) def setup(self): """ This is where most of the setup takes place! """ #qDebug("Hello Setup") - action = Krita.instance().createAction("hello_python", "hello") + action = Krita.instance().createAction("hello_python", "hello", False) action.triggered.connect(hello) # Initialize and add the extension Scripter.addExtension(HelloExtension(Krita.instance())) class HelloDocker(DockWidget): """ The HelloDocker is an example of a simple Python-based docker. """ def __init__(self): """ Constructs an instance of HelloDocker and the widget it contains """ super().__init__() # The window title is also used in the Docker menu, # so it should be set to something sensible! self.setWindowTitle("HelloDocker") label = QLabel("Hello", self) self.setWidget(label) self._label = label def canvasChanged(self, canvas): """ Override canvasChanged from :class:`DockWidget`. This gets called when the canvas changes. You can also access the active canvas via :func:`DockWidget.canvas` Parameter `canvas` can be null if the last document is closed """ self._label.setText("HelloDocker: canvas changed") # Register the docker so Krita can use it! Application.addDockWidgetFactory(DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker)) diff --git a/plugins/extensions/pykrita/plugin/plugins/hello/hello.xmlgui b/plugins/extensions/pykrita/plugin/plugins/hello/hello.xmlgui new file mode 100644 index 0000000000..90bac2ae7a --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/hello/hello.xmlgui @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/plugins/extensions/pykrita/sip/krita/CloneLayer.sip b/plugins/extensions/pykrita/sip/krita/CloneLayer.sip new file mode 100644 index 0000000000..be9cde34c6 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/CloneLayer.sip @@ -0,0 +1,12 @@ +class CloneLayer : Node +{ +%TypeHeaderCode +#include "CloneLayer.h" +%End + CloneLayer(const CloneLayer & __0); +public: + virtual ~CloneLayer(); + virtual QString type() const; +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/Document.sip b/plugins/extensions/pykrita/sip/krita/Document.sip index f89f12d3c7..0b001c88a3 100644 --- a/plugins/extensions/pykrita/sip/krita/Document.sip +++ b/plugins/extensions/pykrita/sip/krita/Document.sip @@ -1,78 +1,85 @@ class Document : QObject /NoDefaultCtors/ { %TypeHeaderCode #include "Document.h" %End Document(const Document & __0); public: bool operator==(const Document &other) const; bool operator!=(const Document &other) const; QList horizontalGuides() const; QList verticalGuides() const; bool guidesVisible() const; bool guidesLocked() const; public Q_SLOTS: Document *clone() const /Factory/; Node * activeNode() const /Factory/; void setActiveNode(Node* value); QList topLevelNodes() const /Factory/; Node *nodeByName(const QString &node) const /Factory/; bool batchmode() const; void setBatchmode(bool value); QString colorDepth() const; QString colorModel() const; QString colorProfile() const; bool setColorProfile(const QString &colorProfile); bool setColorSpace(const QString &value, const QString &colorDepth, const QString &colorProfile); QString documentInfo() const; void setDocumentInfo(const QString &document); QString fileName() const; void setFileName(QString value); int height() const; void setHeight(int value); QString name() const; void setName(QString value); int resolution() const; void setResolution(int value); Node * rootNode() const /Factory/; Selection * selection() const /Factory/; void setSelection(Selection* value); int width() const; void setWidth(int value); int xOffset() const; void setXOffset(int x); int yOffset() const; void setYOffset(int y); double xRes() const; void setXRes(double xRes) const; double yRes() const; void setYRes(double yRes) const; QByteArray pixelData(int x, int y, int w, int h) const; bool close(); void crop(int x, int y, int w, int h); bool exportImage(const QString &filename, const InfoObject & exportConfiguration); void flatten(); void resizeImage(int x, int y, int w, int h); void scaleImage(int w, int h, int xres, int yres, QString strategy); void rotateImage(double radians); void shearImage(double angleX, double angleY); bool save(); bool saveAs(const QString & filename); Node *createNode(const QString & name, const QString & nodeType) /Factory/; + GroupLayer *createGroupLayer(const QString &name) /Factory/; + CloneLayer *createCloneLayer(const QString &name, const Node *source) /Factory/; + FilterLayer *createFilterLayer(const QString &name, Filter &filter, Selection &selection) /Factory/; + FillLayer *createFillLayer(const QString &name, const QString filterName, InfoObject &configuration, Selection &selection) /Factory/; + VectorLayer *createVectorLayer(const QString &name) /Factory/; + FilterMask *createFilterMask(const QString &name, Filter &filter) /Factory/; + SelectionMask *createSelectionMask(const QString &name) /Factory/; QImage projection(int x = 0, int y = 0, int w = 0, int h = 0) const; QImage thumbnail(int w, int h) const; void lock(); void unlock(); void waitForDone(); bool tryBarrierLock(); bool isIdle(); void refreshProjection(); void setHorizontalGuides(const QList &lines); void setVerticalGuides(const QList &lines); void setGuidesVisible(bool visible); void setGuidesLocked(bool locked); private: }; diff --git a/plugins/extensions/pykrita/sip/krita/FileLayer.sip b/plugins/extensions/pykrita/sip/krita/FileLayer.sip new file mode 100644 index 0000000000..edffcd1028 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/FileLayer.sip @@ -0,0 +1,15 @@ +class FileLayer : Node +{ +%TypeHeaderCode +#include "FileLayer.h" +%End + FileLayer(const FileLayer & __0); +public: + virtual ~FileLayer(); + virtual QString type() const; + void setProperties(QString FileName, QString ScalingMethod); + QString scalingMethod() const; + QString path() const; +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/FillLayer.sip b/plugins/extensions/pykrita/sip/krita/FillLayer.sip new file mode 100644 index 0000000000..1c9a22ab07 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/FillLayer.sip @@ -0,0 +1,15 @@ +class FillLayer : Node +{ +%TypeHeaderCode +#include "FillLayer.h" +%End + FillLayer(const FillLayer& __0); +public: + virtual ~FillLayer(); + virtual QString type() const; + void setFilter(QString &filterName, InfoObject *filterConfig); + QString filterName(); + InfoObject * filterConfig(); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/FilterLayer.sip b/plugins/extensions/pykrita/sip/krita/FilterLayer.sip new file mode 100644 index 0000000000..0e657ddd5c --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/FilterLayer.sip @@ -0,0 +1,14 @@ +class FilterLayer : Node +{ +%TypeHeaderCode +#include "FilterLayer.h" +%End + FilterLayer(const FilterLayer & __0); +public: + virtual ~FilterLayer(); + virtual QString type() const; + void setFilter(Filter &filter); + Filter * filter(); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/FilterMask.sip b/plugins/extensions/pykrita/sip/krita/FilterMask.sip new file mode 100644 index 0000000000..6208f2e996 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/FilterMask.sip @@ -0,0 +1,14 @@ +class FilterMask : Node +{ +%TypeHeaderCode +#include "FilterMask.h" +%End + FilterMask(const FilterMask & __0); +public: + virtual ~FilterMask(); + virtual QString type() const; + void setFilter(Filter &filter); + Filter * filter(); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/GroupLayer.sip b/plugins/extensions/pykrita/sip/krita/GroupLayer.sip new file mode 100644 index 0000000000..35490530c7 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/GroupLayer.sip @@ -0,0 +1,15 @@ +class GroupLayer : Node +{ +%TypeHeaderCode +#include "GroupLayer.h" +%End + GroupLayer(const GroupLayer & __0); +public: + virtual ~GroupLayer(); +public Q_SLOTS: + virtual QString type() const; + void setPassThroughMode(bool passthrough); + bool passThroughMode() const; +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/GroupShape.sip b/plugins/extensions/pykrita/sip/krita/GroupShape.sip new file mode 100644 index 0000000000..2f5f172f32 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/GroupShape.sip @@ -0,0 +1,12 @@ +class GroupShape : Shape +{ +%TypeHeaderCode +#include "GroupShape.h" +%End + GroupShape(const GroupShape & __0); +public: + virtual ~GroupShape(); + QList children(); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/Krita.sip b/plugins/extensions/pykrita/sip/krita/Krita.sip index a08c6bdb32..344987699e 100644 --- a/plugins/extensions/pykrita/sip/krita/Krita.sip +++ b/plugins/extensions/pykrita/sip/krita/Krita.sip @@ -1,62 +1,63 @@ class Krita : QObject { %TypeHeaderCode #include "Krita.h" %End public: -public Q_SLOTS: Krita(QObject* parent /TransferThis/ = 0); virtual ~Krita(); +public Q_SLOTS: + Document * activeDocument() const /Factory/; void setActiveDocument(Document* value); bool batchmode() const; void setBatchmode(bool value); QList actions() const /Factory/; Action * action(const QString & name) const; QList documents() const /Factory/; QStringList filters() const; Filter * filter(const QString &name) const /Factory/; QStringList colorModels() const; QStringList colorDepths(const QString &colorModel) const; QStringList filterStrategies() const; QStringList profiles(const QString &colorModel, const QString &ColorDepth) const; bool addProfile(const QString &profilePath); Notifier * notifier() const; QString version() const; QList views() const /Factory/; Window * activeWindow() const /Factory/; QList windows() const /Factory/; QMap resources(const QString &type) const /Factory/; QStringList recentDocuments() const; Document * createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile) /Factory/; QList extensions() /Factory/; Document * openDocument(const QString &filename) /Factory/; Window * openWindow(); - Action * createAction(const QString &id, const QString & text); + Action * createAction(const QString &id, const QString & text, bool addToScriptMenu = true); void addExtension(Extension* _extension /GetWrapper/); %MethodCode Py_BEGIN_ALLOW_THREADS sipCpp->addExtension(a0); Py_END_ALLOW_THREADS sipTransferTo(a0Wrapper, Py_None); %End void addDockWidgetFactory(DockWidgetFactoryBase* _factory /GetWrapper/); %MethodCode Py_BEGIN_ALLOW_THREADS sipCpp->addDockWidgetFactory(a0); Py_END_ALLOW_THREADS sipTransferTo(a0Wrapper, Py_None); %End void writeSetting(const QString &group, const QString &name, const QString &value); QString readSetting(const QString &group, const QString &name, const QString &defaultValue); static Krita * instance(); static QObject * fromVariant(const QVariant & v); private: Krita(const Krita &); // Generated }; diff --git a/plugins/extensions/pykrita/sip/krita/Node.sip b/plugins/extensions/pykrita/sip/krita/Node.sip index f5542bc878..341efb99df 100644 --- a/plugins/extensions/pykrita/sip/krita/Node.sip +++ b/plugins/extensions/pykrita/sip/krita/Node.sip @@ -1,63 +1,64 @@ class Node : QObject { %TypeHeaderCode #include "Node.h" %End Node(const Node & __0); public: virtual ~Node(); bool operator==(const Node &other) const; bool operator!=(const Node &other) const; public Q_SLOTS: + //QList shapes() const /Factory/; Node *clone() const /Factory/; bool alphaLocked() const; void setAlphaLocked(bool value); QString blendingMode() const; void setBlendingMode(QString value); - QList channels() const; - QList childNodes() const; + QList channels() const /Factory/; + QList childNodes() const /Factory/; bool addChildNode(Node *child, Node *above); bool removeChildNode(Node *child); void setChildNodes(QList nodes); QString colorDepth() const; bool animated() const; void enableAnimation() const; void setCollapsed(bool collapsed); bool collapsed() const; int colorLabel() const; void setColorLabel(int value); QString colorModel() const; QString colorProfile() const; bool setColorProfile(const QString &colorProfile); bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile); bool inheritAlpha() const; void setInheritAlpha(bool value); bool locked() const; void setLocked(bool value); QString name() const; void setName(QString value); int opacity() const; void setOpacity(int value); Node * parentNode() const /Factory/; QString type() const; bool visible() const; void setVisible(bool value); QByteArray pixelData(int x, int y, int w, int h) const; QByteArray pixelDataAtTime(int x, int y, int w, int h, int time) const; QByteArray projectionPixelData(int x, int y, int w, int h) const; void setPixelData(QByteArray value, int x, int y, int w, int h); QRect bounds() const; void move(int x, int y); QPoint position() const; bool remove(); Node *duplicate() /Factory/; void save(const QString &filename, double xRes, double yRes); Node *mergeDown() /Factory/; void scaleNode(int width, int height, QString strategy); void rotateNode(double radians); void cropNode(int x, int y, int w, int h); void shearNode(double angleX, double angleY); QImage thumbnail(int w, int h); Q_SIGNALS: private: }; diff --git a/plugins/extensions/pykrita/sip/krita/SelectionMask.sip b/plugins/extensions/pykrita/sip/krita/SelectionMask.sip new file mode 100644 index 0000000000..46426b3b06 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/SelectionMask.sip @@ -0,0 +1,14 @@ +class SelectionMask : Node +{ +%TypeHeaderCode +#include "SelectionMask.h" +%End + SelectionMask(const SelectionMask & __0); +public: + virtual ~SelectionMask(); + virtual QString type() const; + Selection* selection() const; + void setSelection(Selection *selection); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/Shape.sip b/plugins/extensions/pykrita/sip/krita/Shape.sip new file mode 100644 index 0000000000..a4bfad9d21 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/Shape.sip @@ -0,0 +1,16 @@ +class Shape : QObject +{ +%TypeHeaderCode +#include "Shape.h" +%End + Shape(const Shape & __0); + virtual ~Shape(); +public Q_SLOTS: + QString type() const; + QString name() const; + void setName(const QString &name); + bool visible(); + void setVisible(bool visible); +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/VectorLayer.sip b/plugins/extensions/pykrita/sip/krita/VectorLayer.sip new file mode 100644 index 0000000000..40e85f1868 --- /dev/null +++ b/plugins/extensions/pykrita/sip/krita/VectorLayer.sip @@ -0,0 +1,14 @@ +class VectorLayer : Node +{ +%TypeHeaderCode +#include "VectorLayer.h" +%End + VectorLayer(const VectorLayer & __0); +public: + virtual ~VectorLayer(); +public Q_SLOTS: + virtual QString type() const; + QList shapes() const /Factory/; +Q_SIGNALS: +private: +}; diff --git a/plugins/extensions/pykrita/sip/krita/kritamod.sip b/plugins/extensions/pykrita/sip/krita/kritamod.sip index 962ec2f4b5..1968925406 100644 --- a/plugins/extensions/pykrita/sip/krita/kritamod.sip +++ b/plugins/extensions/pykrita/sip/krita/kritamod.sip @@ -1,35 +1,50 @@ %Module PyKrita.krita %ModuleHeaderCode #pragma GCC visibility push(default) %End %Import QtCore/QtCoremod.sip %Import QtGui/QtGuimod.sip %Import QtXml/QtXmlmod.sip %Import QtWidgets/QtWidgetsmod.sip %Include Conversions.sip + + +%Include Shape.sip +%Include GroupShape.sip + %Include Action.sip %Include Canvas.sip %Include Channel.sip %Include DockWidgetFactoryBase.sip %Include DockWidget.sip %Include Document.sip %Include Filter.sip %Include InfoObject.sip %Include Extension.sip %Include View.sip %Include Window.sip %Include Krita.sip %Include Node.sip + +%Include GroupLayer.sip +%Include CloneLayer.sip +%Include FilterLayer.sip +%Include FileLayer.sip +%Include FillLayer.sip +%Include VectorLayer.sip +%Include FilterMask.sip +%Include SelectionMask.sip + %Include Notifier.sip %Include Resource.sip %Include Selection.sip %Include Extension.sip %Include PresetChooser.sip %Include Palette.sip %Include PaletteView.sip %Include ManagedColor.sip %Include KisCubicCurve.sip