diff --git a/libs/brush/kis_auto_brush_factory.cpp b/libs/brush/kis_auto_brush_factory.cpp index 266ae306e2..77c8aea521 100644 --- a/libs/brush/kis_auto_brush_factory.cpp +++ b/libs/brush/kis_auto_brush_factory.cpp @@ -1,43 +1,45 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2010-2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 //MSVC requires that Vc come first #include "kis_auto_brush_factory.h" #include #include "kis_auto_brush.h" #include "kis_mask_generator.h" #include -KisBrushSP KisAutoBrushFactory::createBrush(const QDomElement &brushDefinition) +KisBrushSP KisAutoBrushFactory::createBrush(const QDomElement &brushDefinition, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + KisMaskGenerator* mask = KisMaskGenerator::fromXML(brushDefinition.firstChildElement("MaskGenerator")); double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0")); double randomness = KisDomUtils::toDouble(brushDefinition.attribute("randomness", "0.0")); qreal density = KisDomUtils::toDouble(brushDefinition.attribute("density", "1.0")); double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0")); bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0")); qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0")); KisBrushSP brush = KisBrushSP(new KisAutoBrush(mask, angle, randomness, density)); brush->setSpacing(spacing); brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff); return brush; } diff --git a/libs/brush/kis_auto_brush_factory.h b/libs/brush/kis_auto_brush_factory.h index 1d00b102eb..aed0fa40f6 100644 --- a/libs/brush/kis_auto_brush_factory.h +++ b/libs/brush/kis_auto_brush_factory.h @@ -1,58 +1,58 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_AUTO_BRUSH_FACTORY #define KIS_AUTO_BRUSH_FACTORY #include #include #include #include #include "kis_brush.h" #include "kis_brush_factory.h" #include "kis_fixed_paint_device.h" /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisAutoBrushFactory : public KisBrushFactory { public: KisAutoBrushFactory() {} ~KisAutoBrushFactory() override {} QString id() const override { return "auto_brush"; } /** * Create a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - KisBrushSP createBrush(const QDomElement& brushDefinition) override; + KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override; }; #endif diff --git a/libs/brush/kis_brush.cpp b/libs/brush/kis_brush.cpp index fef0a7aab6..80ea2c06e9 100644 --- a/libs/brush/kis_brush.cpp +++ b/libs/brush/kis_brush.cpp @@ -1,649 +1,649 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush.h" #include #include #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_boundary.h" #include "kis_image.h" #include "kis_iterator_ng.h" #include "kis_brush_registry.h" #include #include #include #include #include KisBrush::ColoringInformation::~ColoringInformation() { } KisBrush::PlainColoringInformation::PlainColoringInformation(const quint8* color) : m_color(color) { } KisBrush::PlainColoringInformation::~PlainColoringInformation() { } const quint8* KisBrush::PlainColoringInformation::color() const { return m_color; } void KisBrush::PlainColoringInformation::nextColumn() { } void KisBrush::PlainColoringInformation::nextRow() { } KisBrush::PaintDeviceColoringInformation::PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width) : m_source(source) , m_iterator(m_source->createHLineConstIteratorNG(0, 0, width)) { } KisBrush::PaintDeviceColoringInformation::~PaintDeviceColoringInformation() { } const quint8* KisBrush::PaintDeviceColoringInformation::color() const { return m_iterator->oldRawData(); } void KisBrush::PaintDeviceColoringInformation::nextColumn() { m_iterator->nextPixel(); } void KisBrush::PaintDeviceColoringInformation::nextRow() { m_iterator->nextRow(); } struct KisBrush::Private { Private() : boundary(0) , angle(0) , scale(1.0) , hasColor(false) , brushType(INVALID) , autoSpacingActive(false) , autoSpacingCoeff(1.0) , threadingAllowed(true) {} ~Private() { delete boundary; } mutable KisBoundary* boundary; qreal angle; qreal scale; bool hasColor; enumBrushType brushType; qint32 width; qint32 height; double spacing; QPointF hotSpot; mutable QSharedPointer brushPyramid; QImage brushTipImage; bool autoSpacingActive; qreal autoSpacingCoeff; bool threadingAllowed; }; KisBrush::KisBrush() : KoResource(QString()) , d(new Private) { } KisBrush::KisBrush(const QString& filename) : KoResource(filename) , d(new Private) { } KisBrush::KisBrush(const KisBrush& rhs) : KoResource(rhs) , d(new Private) { *this = rhs; } KisBrush &KisBrush::operator=(const KisBrush &rhs) { if (*this != rhs) { d->brushType = rhs.d->brushType; d->width = rhs.d->width; d->height = rhs.d->height; d->spacing = rhs.d->spacing; d->hotSpot = rhs.d->hotSpot; d->hasColor = rhs.d->hasColor; d->angle = rhs.d->angle; d->scale = rhs.d->scale; d->autoSpacingActive = rhs.d->autoSpacingActive; d->autoSpacingCoeff = rhs.d->autoSpacingCoeff; d->threadingAllowed = rhs.d->threadingAllowed; d->brushTipImage = rhs.d->brushTipImage; /** * Be careful! The pyramid is shared between two brush objects, * therefore you cannot change it, only recreate! That is the * reason why it is defined as const! */ d->brushPyramid = rhs.d->brushPyramid; // don't copy the boundary, it will be regenerated -- see bug 291910 } return *this; } KisBrush::~KisBrush() { delete d; } QImage KisBrush::brushTipImage() const { if (d->brushTipImage.isNull()) { const_cast(this)->load(); } return d->brushTipImage; } qint32 KisBrush::width() const { return d->width; } void KisBrush::setWidth(qint32 width) { d->width = width; } qint32 KisBrush::height() const { return d->height; } void KisBrush::setHeight(qint32 height) { d->height = height; } void KisBrush::setHotSpot(QPointF pt) { double x = pt.x(); double y = pt.y(); if (x < 0) x = 0; else if (x >= width()) x = width() - 1; if (y < 0) y = 0; else if (y >= height()) y = height() - 1; d->hotSpot = QPointF(x, y); } QPointF KisBrush::hotSpot(KisDabShape const& shape, const KisPaintInformation& info) const { Q_UNUSED(info); QSizeF metric = characteristicSize(shape); qreal w = metric.width(); qreal h = metric.height(); // The smallest brush we can produce is a single pixel. if (w < 1) { w = 1; } if (h < 1) { h = 1; } // XXX: This should take d->hotSpot into account, though it // isn't specified by gimp brushes so it would default to the center // anyway. QPointF p(w / 2, h / 2); return p; } bool KisBrush::hasColor() const { return d->hasColor; } void KisBrush::setHasColor(bool hasColor) { d->hasColor = hasColor; } bool KisBrush::isPiercedApprox() const { QImage image = brushTipImage(); qreal w = image.width(); qreal h = image.height(); qreal xPortion = qMin(0.1, 5.0 / w); qreal yPortion = qMin(0.1, 5.0 / h); int x0 = std::floor((0.5 - xPortion) * w); int x1 = std::ceil((0.5 + xPortion) * w); int y0 = std::floor((0.5 - yPortion) * h); int y1 = std::ceil((0.5 + yPortion) * h); const int maxNumSamples = (x1 - x0 + 1) * (y1 - y0 + 1); const int failedPixelsThreshold = 0.1 * maxNumSamples; const int thresholdValue = 0.95 * 255; int failedPixels = 0; for (int y = y0; y <= y1; y++) { for (int x = x0; x <= x1; x++) { QRgb pixel = image.pixel(x,y); if (qRed(pixel) > thresholdValue) { failedPixels++; } } } return failedPixels > failedPixelsThreshold; } bool KisBrush::canPaintFor(const KisPaintInformation& /*info*/) { return true; } void KisBrush::setBrushTipImage(const QImage& image) { d->brushTipImage = image; if (!image.isNull()) { if (image.width() > 128 || image.height() > 128) { KoResource::setImage(image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { KoResource::setImage(image); } setWidth(image.width()); setHeight(image.height()); } clearBrushPyramid(); } void KisBrush::setBrushType(enumBrushType type) { d->brushType = type; } enumBrushType KisBrush::brushType() const { return d->brushType; } void KisBrush::predefinedBrushToXML(const QString &type, QDomElement& e) const { e.setAttribute("type", type); e.setAttribute("filename", filename()); e.setAttribute("spacing", QString::number(spacing())); e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive())); e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff())); e.setAttribute("angle", QString::number(angle())); e.setAttribute("scale", QString::number(scale())); } void KisBrush::toXML(QDomDocument& /*document*/ , QDomElement& element) const { element.setAttribute("BrushVersion", "2"); } -KisBrushSP KisBrush::fromXML(const QDomElement& element) +KisBrushSP KisBrush::fromXML(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface) { - KisBrushSP brush = KisBrushRegistry::instance()->createBrush(element); + KisBrushSP brush = KisBrushRegistry::instance()->createBrush(element, resourcesInterface); if (brush && element.attribute("BrushVersion", "1") == "1") { brush->setScale(brush->scale() * 2.0); } return brush; } QSizeF KisBrush::characteristicSize(KisDabShape const& shape) const { KisDabShape normalizedShape( shape.scale() * d->scale, shape.ratio(), normalizeAngle(shape.rotation() + d->angle)); return KisQImagePyramid::characteristicSize( QSize(width(), height()), normalizedShape); } qint32 KisBrush::maskWidth(KisDabShape const& shape, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const { Q_UNUSED(info); qreal angle = normalizeAngle(shape.rotation() + d->angle); qreal scale = shape.scale() * d->scale; return KisQImagePyramid::imageSize(QSize(width(), height()), KisDabShape(scale, shape.ratio(), angle), subPixelX, subPixelY).width(); } qint32 KisBrush::maskHeight(KisDabShape const& shape, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const { Q_UNUSED(info); qreal angle = normalizeAngle(shape.rotation() + d->angle); qreal scale = shape.scale() * d->scale; return KisQImagePyramid::imageSize(QSize(width(), height()), KisDabShape(scale, shape.ratio(), angle), subPixelX, subPixelY).height(); } double KisBrush::maskAngle(double angle) const { return normalizeAngle(angle + d->angle); } quint32 KisBrush::brushIndex(const KisPaintInformation& info) const { Q_UNUSED(info); return 0; } void KisBrush::setSpacing(double s) { if (s < 0.02) s = 0.02; d->spacing = s; } double KisBrush::spacing() const { return d->spacing; } void KisBrush::setAutoSpacing(bool active, qreal coeff) { d->autoSpacingCoeff = coeff; d->autoSpacingActive = active; } bool KisBrush::autoSpacingActive() const { return d->autoSpacingActive; } qreal KisBrush::autoSpacingCoeff() const { return d->autoSpacingCoeff; } void KisBrush::notifyStrokeStarted() { } void KisBrush::notifyCachedDabPainted(const KisPaintInformation& info) { Q_UNUSED(info); } void KisBrush::prepareForSeqNo(const KisPaintInformation &info, int seqNo) { Q_UNUSED(info); Q_UNUSED(seqNo); } void KisBrush::setThreadingAllowed(bool value) { d->threadingAllowed = value; } bool KisBrush::threadingAllowed() const { return d->threadingAllowed; } void KisBrush::clearBrushPyramid() { d->brushPyramid.reset(new KisSharedQImagePyramid()); } void KisBrush::mask(KisFixedPaintDeviceSP dst, const KoColor& color, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const { PlainColoringInformation pci(color.data()); generateMaskAndApplyMaskOrCreateDab(dst, &pci, shape, info, subPixelX, subPixelY, softnessFactor); } void KisBrush::mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const { PaintDeviceColoringInformation pdci(src, maskWidth(shape, subPixelX, subPixelY, info)); generateMaskAndApplyMaskOrCreateDab(dst, &pdci, shape, info, subPixelX, subPixelY, softnessFactor); } void KisBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInformation, KisDabShape const& shape, const KisPaintInformation& info_, double subPixelX, double subPixelY, qreal softnessFactor) const { KIS_SAFE_ASSERT_RECOVER_RETURN(valid()); Q_UNUSED(info_); Q_UNUSED(softnessFactor); QImage outputImage = d->brushPyramid->pyramid(this)->createImage(KisDabShape( shape.scale() * d->scale, shape.ratio(), -normalizeAngle(shape.rotation() + d->angle)), subPixelX, subPixelY); qint32 maskWidth = outputImage.width(); qint32 maskHeight = outputImage.height(); dst->setRect(QRect(0, 0, maskWidth, maskHeight)); dst->lazyGrowBufferWithoutInitialization(); quint8* color = 0; if (coloringInformation) { if (dynamic_cast(coloringInformation)) { color = const_cast(coloringInformation->color()); } } const KoColorSpace *cs = dst->colorSpace(); qint32 pixelSize = cs->pixelSize(); quint8 *dabPointer = dst->data(); quint8 *rowPointer = dabPointer; quint8 *alphaArray = new quint8[maskWidth]; bool hasColor = this->hasColor(); for (int y = 0; y < maskHeight; y++) { const quint8* maskPointer = outputImage.constScanLine(y); if (coloringInformation) { for (int x = 0; x < maskWidth; x++) { if (color) { memcpy(dabPointer, color, pixelSize); } else { memcpy(dabPointer, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); } dabPointer += pixelSize; } } if (hasColor) { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast(src); *dst = KoColorSpaceMaths::multiply(255 - qGray(*c), qAlpha(*c)); src += 4; dst++; } } else { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast(src); *dst = KoColorSpaceMaths::multiply(255 - *src, qAlpha(*c)); src += 4; dst++; } } cs->applyAlphaU8Mask(rowPointer, alphaArray, maskWidth); rowPointer += maskWidth * pixelSize; dabPointer = rowPointer; if (!color && coloringInformation) { coloringInformation->nextRow(); } } delete[] alphaArray; } KisFixedPaintDeviceSP KisBrush::paintDevice(const KoColorSpace * colorSpace, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY) const { Q_ASSERT(valid()); Q_UNUSED(info); double angle = normalizeAngle(shape.rotation() + d->angle); double scale = shape.scale() * d->scale; QImage outputImage = d->brushPyramid->pyramid(this)->createImage( KisDabShape(scale, shape.ratio(), -angle), subPixelX, subPixelY); KisFixedPaintDeviceSP dab = new KisFixedPaintDevice(colorSpace); Q_CHECK_PTR(dab); dab->convertFromQImage(outputImage, ""); return dab; } void KisBrush::resetBoundary() { delete d->boundary; d->boundary = 0; } void KisBrush::generateBoundary() const { KisFixedPaintDeviceSP dev; KisDabShape inverseTransform(1.0 / scale(), 1.0, -angle()); if (brushType() == IMAGE || brushType() == PIPE_IMAGE) { dev = paintDevice(KoColorSpaceRegistry::instance()->rgb8(), inverseTransform, KisPaintInformation()); } else { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); dev = new KisFixedPaintDevice(cs); mask(dev, KoColor(Qt::black, cs), inverseTransform, KisPaintInformation()); } d->boundary = new KisBoundary(dev); d->boundary->generateBoundary(); } const KisBoundary* KisBrush::boundary() const { if (!d->boundary) generateBoundary(); return d->boundary; } void KisBrush::setScale(qreal _scale) { d->scale = _scale; } qreal KisBrush::scale() const { return d->scale; } void KisBrush::setAngle(qreal _rotation) { d->angle = _rotation; } qreal KisBrush::angle() const { return d->angle; } QPainterPath KisBrush::outline() const { return boundary()->path(); } void KisBrush::lodLimitations(KisPaintopLodLimitations *l) const { if (spacing() > 0.5) { l->limitations << KoID("huge-spacing", i18nc("PaintOp instant preview limitation", "Spacing > 0.5, consider disabling Instant Preview")); } } diff --git a/libs/brush/kis_brush.h b/libs/brush/kis_brush.h index ca9ebe8a05..88e58df1d0 100644 --- a/libs/brush/kis_brush.h +++ b/libs/brush/kis_brush.h @@ -1,380 +1,380 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_BRUSH_ #define KIS_BRUSH_ #include #include #include #include #include #include class KisQImagemask; typedef KisSharedPtr KisQImagemaskSP; class QString; class KoColor; class KoColorSpace; class KisPaintInformation; class KisBoundary; class KisPaintopLodLimitations; enum enumBrushType { INVALID, MASK, IMAGE, PIPE_MASK, PIPE_IMAGE }; static const qreal DEFAULT_SOFTNESS_FACTOR = 1.0; class KisBrush; typedef QSharedPointer KisBrushSP; /** * KisBrush is the base class for brush resources. A brush resource * defines one or more images that are used to potato-stamp along * the drawn path. The brush type defines how this brush is used -- * the important difference is between masks (which take the current * painting color) and images (which do not). It is up to the paintop * to make use of this feature. * * Brushes must be serializable to an xml representation and provide * a factory class that can recreate or retrieve the brush based on * this representation. * * XXX: This api is still a big mess -- it needs a good refactoring. * And the whole KoResource architecture is way over-designed. */ class BRUSH_EXPORT KisBrush : public KoResource { public: class ColoringInformation { public: virtual ~ColoringInformation(); virtual const quint8* color() const = 0; virtual void nextColumn() = 0; virtual void nextRow() = 0; }; protected: class PlainColoringInformation : public ColoringInformation { public: PlainColoringInformation(const quint8* color); ~PlainColoringInformation() override; const quint8* color() const override ; void nextColumn() override; void nextRow() override; private: const quint8* m_color; }; class PaintDeviceColoringInformation : public ColoringInformation { public: PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width); ~PaintDeviceColoringInformation() override; const quint8* color() const override ; void nextColumn() override; void nextRow() override; private: const KisPaintDeviceSP m_source; KisHLineConstIteratorSP m_iterator; }; public: KisBrush(); KisBrush(const QString& filename); ~KisBrush() override; KisBrush(const KisBrush &rhs); KisBrush &operator=(const KisBrush &rhs); virtual qreal userEffectiveSize() const = 0; virtual void setUserEffectiveSize(qreal value) = 0; QPair resourceType() const override { return QPair(ResourceType::Brushes, ""); } /** * @brief brushImage the image the brush tip can paint with. Not all brush types have a single * image. * @return a valid QImage. */ virtual QImage brushTipImage() const; /** * Change the spacing of the brush. * @param spacing a spacing of 1.0 means that strokes will be separated from one time the size * of the brush. */ virtual void setSpacing(double spacing); /** * @return the spacing between two strokes for this brush */ double spacing() const; void setAutoSpacing(bool active, qreal coeff); bool autoSpacingActive() const; qreal autoSpacingCoeff() const; /** * @return the width (for scale == 1.0) */ qint32 width() const; /** * @return the height (for scale == 1.0) */ qint32 height() const; /** * @return the width of the mask for the given scale and angle */ virtual qint32 maskWidth(KisDabShape const&, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the height of the mask for the given scale and angle */ virtual qint32 maskHeight(KisDabShape const&, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the logical size of the brush, that is the size measured * in floating point value. * * This value should not be used for calculating future dab sizes * because it doesn't take any rounding into account. The only use * of this metric is calculation of brush-size derivatives like * hotspots and spacing. */ virtual QSizeF characteristicSize(KisDabShape const&) const; /** * @return the angle of the mask adding the given angle */ double maskAngle(double angle = 0) const; /** * @return the index of the brush * if the brush consists of multiple images */ virtual quint32 brushIndex(const KisPaintInformation& info) const; /** * The brush type defines how the brush is used. */ virtual enumBrushType brushType() const; QPointF hotSpot(KisDabShape const&, const KisPaintInformation& info) const; /** * Returns true if this brush can return something useful for the info. This is used * by Pipe Brushes that can't paint sometimes **/ virtual bool canPaintFor(const KisPaintInformation& /*info*/); /** * Is called by the paint op when a paintop starts a stroke. The * point is that we store brushes a server while the paint ops are * are recreated all the time. Is means that upon a stroke start * the brushes may need to clear its state. */ virtual void notifyStrokeStarted(); /** * Is called by the cache, when cache hit has happened. * Having got this notification the brush can update the counters * of dabs, generate some new random values if needed. * * * NOTE: one should use **either** notifyCachedDabPainted() or prepareForSeqNo() * * Currently, this is used by pipe'd brushes to implement * incremental and random parasites */ virtual void notifyCachedDabPainted(const KisPaintInformation& info); /** * Is called by the multithreaded queue to prepare a specific brush * tip for the particular seqNo. * * NOTE: one should use **either** notifyCachedDabPainted() or prepareForSeqNo() * * Currently, this is used by pipe'd brushes to implement * incremental and random parasites */ virtual void prepareForSeqNo(const KisPaintInformation& info, int seqNo); /** * Notify the brush if it can use QtConcurrent's threading capabilities in its * internal routines. By default it is allowed, but some paintops (who do their * own multithreading) may ask the brush to avoid internal threading. */ void setThreadingAllowed(bool value); /** * \see setThreadingAllowed() for details */ bool threadingAllowed() const; /** * Return a fixed paint device that contains a correctly scaled image dab. */ virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, KisDabShape const&, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0) const; /** * clear dst fill it with a mask colored with KoColor */ void mask(KisFixedPaintDeviceSP dst, const KoColor& color, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * clear dst and fill it with a mask colored with the corresponding colors of src */ void mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; virtual bool hasColor() const; /** * Create a mask and either mask dst (that is, change all alpha values of the * existing pixels to those of the mask) or, if coloringInfo is present, clear * dst and fill dst with pixels according to coloringInfo, masked according to the * generated mask. * * @param dst the destination that will be draw on the image, and this function * will edit its alpha channel * @param coloringInfo coloring information that will be copied on the dab, it can be null * @param shape a shape applied on the alpha mask * @param info the painting information (this is only and should only be used by * KisImagePipeBrush and only to be backward compatible with the Gimp, * KisImagePipeBrush is ignoring scale and angle information) * @param subPixelX sub position of the brush (contained between 0.0 and 1.0) * @param subPixelY sub position of the brush (contained between 0.0 and 1.0) * @param softnessFactor softness factor of the brush * * @return a mask computed from the grey-level values of the * pixels in the brush. */ virtual void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInfo, KisDabShape const&, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * Serialize this brush to XML. */ virtual void toXML(QDomDocument& , QDomElement&) const; - static KisBrushSP fromXML(const QDomElement& element); + static KisBrushSP fromXML(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface); virtual const KisBoundary* boundary() const; virtual QPainterPath outline() const; virtual void setScale(qreal _scale); qreal scale() const; virtual void setAngle(qreal _angle); qreal angle() const; void clearBrushPyramid(); virtual void lodLimitations(KisPaintopLodLimitations *l) const; protected: void setWidth(qint32 width); void setHeight(qint32 height); void setHotSpot(QPointF); /** * XXX */ virtual void setBrushType(enumBrushType type); virtual void setHasColor(bool hasColor); public: /** * The image is used to represent the brush in the gui, and may also, depending on the brush type * be used to define the actual brush instance. */ virtual void setBrushTipImage(const QImage& image); /** * Returns true if the brush has a bunch of pixels almost * fully transparent in the very center. If the brush is pierced, * then dulling mode may not work correctly due to empty samples. * * WARNING: this method is relatively expensive since it iterates * up to 100 pixels of the brush. */ bool isPiercedApprox() const; protected: void resetBoundary(); void predefinedBrushToXML(const QString &type, QDomElement& e) const; private: // Initialize our boundary void generateBoundary() const; struct Private; Private* const d; }; #endif // KIS_BRUSH_ diff --git a/libs/brush/kis_brush_factory.h b/libs/brush/kis_brush_factory.h index 1201e12674..d409d9e0b8 100644 --- a/libs/brush/kis_brush_factory.h +++ b/libs/brush/kis_brush_factory.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_BRUSH_FACTORY #define KIS_BRUSH_FACTORY #include "kis_brush.h" class QDomElement; /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisBrushFactory { public: KisBrushFactory() {} virtual ~KisBrushFactory() {} virtual QString id() const = 0; virtual QString name() const { return QString(); } /** * Create a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - virtual KisBrushSP createBrush(const QDomElement& element) = 0; + virtual KisBrushSP createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface) = 0; }; #endif diff --git a/libs/brush/kis_brush_registry.cpp b/libs/brush/kis_brush_registry.cpp index 249719f28e..9d5cf90ce3 100644 --- a/libs/brush/kis_brush_registry.cpp +++ b/libs/brush/kis_brush_registry.cpp @@ -1,74 +1,74 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush_registry.h" #include #include #include #include #include #include "KoResourceServer.h" #include "kis_auto_brush_factory.h" #include "kis_text_brush_factory.h" #include "kis_predefined_brush_factory.h" Q_GLOBAL_STATIC(KisBrushRegistry, s_instance) KisBrushRegistry::KisBrushRegistry() { } KisBrushRegistry::~KisBrushRegistry() { Q_FOREACH (const QString & id, keys()) { delete get(id); } dbgRegistry << "deleting KisBrushRegistry"; } KisBrushRegistry* KisBrushRegistry::instance() { if (!s_instance.exists()) { s_instance->add(new KisAutoBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("gbr_brush")); s_instance->add(new KisPredefinedBrushFactory("abr_brush")); s_instance->add(new KisTextBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("png_brush")); s_instance->add(new KisPredefinedBrushFactory("svg_brush")); } return s_instance; } -KisBrushSP KisBrushRegistry::createBrush(const QDomElement& element) +KisBrushSP KisBrushRegistry::createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface) { QString brushType = element.attribute("type"); if (brushType.isEmpty()) return 0; KisBrushFactory* factory = get(brushType); if (!factory) return 0; - return factory->createBrush(element); + return factory->createBrush(element, resourcesInterface); } diff --git a/libs/brush/kis_brush_registry.h b/libs/brush/kis_brush_registry.h index 1713cc067b..43226c04c0 100644 --- a/libs/brush/kis_brush_registry.h +++ b/libs/brush/kis_brush_registry.h @@ -1,52 +1,52 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_BRUSH_REGISTRY_H_ #define KIS_BRUSH_REGISTRY_H_ #include #include "kis_types.h" #include "KoGenericRegistry.h" #include #include "kis_brush.h" #include "kis_brush_factory.h" class QDomElement; class BRUSH_EXPORT KisBrushRegistry : public QObject, public KoGenericRegistry { Q_OBJECT public: KisBrushRegistry(); ~KisBrushRegistry() override; static KisBrushRegistry* instance(); - KisBrushSP createBrush(const QDomElement& element); + KisBrushSP createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface); private: KisBrushRegistry(const KisBrushRegistry&); KisBrushRegistry operator=(const KisBrushRegistry&); }; #endif // KIS_GENERATOR_REGISTRY_H_ diff --git a/libs/brush/kis_gbr_brush.cpp b/libs/brush/kis_gbr_brush.cpp index 9fd5117518..a5093e1e98 100644 --- a/libs/brush/kis_gbr_brush.cpp +++ b/libs/brush/kis_gbr_brush.cpp @@ -1,501 +1,502 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_gbr_brush.h" #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" struct GimpBrushV1Header { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ }; /// All fields are in MSB on disk! struct GimpBrushHeader { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ /* The following are only defined in version 2 */ quint32 magic_number; /* GIMP brush magic number */ quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ }; // Needed, or the GIMP won't open it! quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); struct KisGbrBrush::Private { QByteArray data; bool useColorAsMask; quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 bytes; /* depth of brush in bytes */ quint32 magic_number; /* GIMP brush magic number */ }; #define DEFAULT_SPACING 0.25 KisGbrBrush::KisGbrBrush(const QString& filename) : KisScalingSizeBrush(filename) , d(new Private) { d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); } KisGbrBrush::KisGbrBrush(const QString &filename, const QByteArray &data, qint32 &dataPos) : KisScalingSizeBrush(filename) , d(new Private) { d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos); init(); d->data.clear(); dataPos += d->header_size + (width() * height() * d->bytes); } KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h) : KisScalingSizeBrush() , d(new Private) { d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); initFromPaintDev(image, x, y, w, h); } KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name) : KisScalingSizeBrush() , d(new Private) { d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); setBrushTipImage(image); setName(name); } KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs) : KisScalingSizeBrush(rhs) , d(new Private(*rhs.d)) { d->data = QByteArray(); } KoResourceSP KisGbrBrush::clone() const { return KoResourceSP(new KisGbrBrush(*this)); } KisGbrBrush &KisGbrBrush::operator=(const KisGbrBrush &rhs) { if (*this != rhs) { d->useColorAsMask = rhs.d->useColorAsMask; } return *this; } KisGbrBrush::~KisGbrBrush() { delete d; } -bool KisGbrBrush::loadFromDevice(QIODevice *dev) +bool KisGbrBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); d->data = dev->readAll(); return init(); } bool KisGbrBrush::init() { GimpBrushHeader bh; if (sizeof(GimpBrushHeader) > (uint)d->data.size()) { qWarning() << filename() << "GBR could not be loaded: expected header size larger than bytearray size. Header Size:" << sizeof(GimpBrushHeader) << "Byte array size" << d->data.size(); return false; } memcpy(&bh, d->data, sizeof(GimpBrushHeader)); bh.header_size = qFromBigEndian(bh.header_size); d->header_size = bh.header_size; bh.version = qFromBigEndian(bh.version); d->version = bh.version; bh.width = qFromBigEndian(bh.width); bh.height = qFromBigEndian(bh.height); bh.bytes = qFromBigEndian(bh.bytes); d->bytes = bh.bytes; bh.magic_number = qFromBigEndian(bh.magic_number); d->magic_number = bh.magic_number; if (bh.version == 1) { // No spacing in version 1 files so use Gimp default bh.spacing = static_cast(DEFAULT_SPACING * 100); } else { bh.spacing = qFromBigEndian(bh.spacing); if (bh.spacing > 1000) { qWarning() << filename() << "GBR could not be loaded, spacing above 1000. Spacing:" << bh.spacing; return false; } } setSpacing(bh.spacing / 100.0); if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) { qWarning() << "GBR could not be loaded: header size larger than bytearray size. Header Size:" << bh.header_size << "Byte array size" << d->data.size(); return false; } QString name; if (bh.version == 1) { // Version 1 has no magic number or spacing, so the name // is at a different offset. Character encoding is undefined. const char *text = d->data.constData() + sizeof(GimpBrushV1Header); name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1); } else { // ### Version = 3->cinepaint; may be float16 data! // Version >=2: UTF-8 encoding is used name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader), bh.header_size - sizeof(GimpBrushHeader) - 1); } setName(name); if (bh.width == 0 || bh.height == 0) { qWarning() << filename() << "GBR loading failed: width or height is 0" << bh.width << bh.height; return false; } QImage::Format imageFormat; if (bh.bytes == 1) { imageFormat = QImage::Format_Indexed8; } else { imageFormat = QImage::Format_ARGB32; } QImage image(QImage(bh.width, bh.height, imageFormat)); if (image.isNull()) { qWarning() << filename() << "GBR loading failed; image could not be created from following dimensions" << bh.width << bh.height << "QImage::Format" << imageFormat; return false; } qint32 k = bh.header_size; if (bh.bytes == 1) { QVector table; for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i)); image.setColorTable(table); // Grayscale if (static_cast(k + bh.width * bh.height) > d->data.size()) { qWarning() << filename() << "GBR file dimensions bigger than bytearray size. Header:"<< k << "Width:" << bh.width << "height" << bh.height << "expected byte array size:" << (k + (bh.width * bh.height)) << "actual byte array size" << d->data.size(); return false; } setHasColor(false); for (quint32 y = 0; y < bh.height; y++) { uchar *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k++) { qint32 val = 255 - static_cast(d->data[k]); *pixel = val; ++pixel; } } } else if (bh.bytes == 4) { // RGBA if (static_cast(k + (bh.width * bh.height * 4)) > d->data.size()) { qWarning() << filename() << "GBR file dimensions bigger than bytearray size. Header:"<< k << "Width:" << bh.width << "height" << bh.height << "expected byte array size:" << (k + (bh.width * bh.height * 4)) << "actual byte array size" << d->data.size(); return false; } setHasColor(true); for (quint32 y = 0; y < bh.height; y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k += 4) { *pixel = qRgba(d->data[k], d->data[k + 1], d->data[k + 2], d->data[k + 3]); ++pixel; } } } else { warnKrita << filename() << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported"; return false; } setWidth(image.width()); setHeight(image.height()); if (!d->data.isEmpty()) { d->data.resize(0); // Save some memory, we're using enough of it as it is. } setValid(image.width() != 0 && image.height() != 0); setBrushTipImage(image); return true; } bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h) { // Forcefully convert to RGBA8 // XXX profile and exposure? setBrushTipImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags())); setName(image->objectName()); setHasColor(true); return true; } bool KisGbrBrush::saveToDevice(QIODevice* dev) const { if (!valid() || brushTipImage().isNull()) { qWarning() << "this brush is not valid, set a brush tip image" << filename(); return false; } GimpBrushHeader bh; QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int nameLength = qstrlen(name); int wrote; bh.header_size = qToBigEndian((quint32)sizeof(GimpBrushHeader) + nameLength + 1); bh.version = qToBigEndian((quint32)2); // Only RGBA8 data needed atm, no cinepaint stuff bh.width = qToBigEndian((quint32)width()); bh.height = qToBigEndian((quint32)height()); // Hardcoded, 4 bytes RGBA or 1 byte GREY if (!hasColor()) { bh.bytes = qToBigEndian((quint32)1); } else { bh.bytes = qToBigEndian((quint32)4); } bh.magic_number = qToBigEndian((quint32)GimpV2BrushMagic); bh.spacing = qToBigEndian(static_cast(spacing() * 100.0)); // Write header: first bh, then the name QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) { return false; } wrote = dev->write(name, nameLength + 1); if (wrote == -1) { return false; } int k = 0; QImage image = brushTipImage(); if (!hasColor()) { bytes.resize(width() * height()); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { QRgb c = image.pixel(x, y); bytes[k++] = static_cast(255 - qRed(c)); // red == blue == green } } } else { bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { // order for gimp brushes, v2 is: RGBA QRgb pixel = image.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } } wrote = dev->write(bytes); if (wrote == -1) { return false; } KoResource::saveToDevice(dev); return true; } QImage KisGbrBrush::brushTipImage() const { QImage image = KisBrush::brushTipImage(); if (hasColor() && useColorAsMask() && !image.isNull()) { for (int y = 0; y < image.height(); y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (int x = 0; x < image.width(); x++) { QRgb c = pixel[x]; float alpha = qAlpha(c) / 255.0f; int a = 255 + int(alpha * (qGray(c) - 255)); pixel[x] = qRgba(a, a, a, 255); } } } return image; } enumBrushType KisGbrBrush::brushType() const { return !hasColor() || useColorAsMask() ? MASK : IMAGE; } void KisGbrBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush"); } void KisGbrBrush::setBrushTipImage(const QImage& image) { KisBrush::setBrushTipImage(image); setValid(true); } void KisGbrBrush::makeMaskImage() { if (!hasColor()) { return; } QImage brushTip = brushTipImage(); if (brushTip.width() == width() && brushTip.height() == height()) { int imageWidth = brushTip.width(); int imageHeight = brushTip.height(); QImage image(imageWidth, imageHeight, QImage::Format_Indexed8); QVector table; for (int i = 0; i < 256; ++i) { table.append(qRgb(i, i, i)); } image.setColorTable(table); for (int y = 0; y < imageHeight; y++) { QRgb *pixel = reinterpret_cast(brushTip.scanLine(y)); uchar * dstPixel = image.scanLine(y); for (int x = 0; x < imageWidth; x++) { QRgb c = pixel[x]; float alpha = qAlpha(c) / 255.0f; // linear interpolation with maximum gray value which is transparent in the mask //int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255); // single multiplication version int a = 255 + int(alpha * (qGray(c) - 255)); dstPixel[x] = (uchar)a; } } setBrushTipImage(image); } setHasColor(false); setUseColorAsMask(false); resetBoundary(); clearBrushPyramid(); } void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("gbr_brush", e); e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask())); KisBrush::toXML(d, e); } void KisGbrBrush::setUseColorAsMask(bool useColorAsMask) { /** * WARNING: There is a problem in the brush server, since it * returns not copies of brushes, but direct pointers to them. It * means that the brushes are shared among all the currently * present paintops, which might be a problem for e.g. Multihand * Brush Tool. * * Right now, all the instances of Multihand Brush Tool share the * same brush, so there is no problem in this sharing, unless we * reset the internal state of the brush on our way. */ if (useColorAsMask != d->useColorAsMask) { d->useColorAsMask = useColorAsMask; resetBoundary(); clearBrushPyramid(); } } bool KisGbrBrush::useColorAsMask() const { return d->useColorAsMask; } QString KisGbrBrush::defaultFileExtension() const { return QString(".gbr"); } diff --git a/libs/brush/kis_gbr_brush.h b/libs/brush/kis_gbr_brush.h index b2b913aa46..e2bab1975a 100644 --- a/libs/brush/kis_gbr_brush.h +++ b/libs/brush/kis_gbr_brush.h @@ -1,126 +1,126 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_GBR_BRUSH_ #define KIS_GBR_BRUSH_ #include #include #include "kis_scaling_size_brush.h" #include #include #include #include "kritabrush_export.h" class KisQImagemask; typedef KisSharedPtr KisQImagemaskSP; class QString; class QIODevice; class BRUSH_EXPORT KisGbrBrush : public KisScalingSizeBrush { protected: public: /// Construct brush to load filename later as brush KisGbrBrush(const QString& filename); /// Load brush from the specified data, at position dataPos, and set the filename KisGbrBrush(const QString& filename, const QByteArray & data, qint32 & dataPos); /// Load brush from the specified paint device, in the specified region KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h); /// Load brush as a copy from the specified QImage (handy when you need to copy a brush!) KisGbrBrush(const QImage& image, const QString& name = QString()); ~KisGbrBrush() override; KisGbrBrush(const KisGbrBrush& rhs); KoResourceSP clone() const override; KisGbrBrush &operator=(const KisGbrBrush &rhs); - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::Brushes, ResourceSubType::GbrBrushes); } /** * @return a preview of the brush */ QImage brushTipImage() const override; /** * If the brush image data are colorful (e.g. you created the brush from the canvas with custom brush) * and you want to paint with it as with masks, set to true. */ virtual void setUseColorAsMask(bool useColorAsMask); virtual bool useColorAsMask() const; /** * Convert the mask to inverted gray scale, so it is alpha mask. * It can be used as MASK brush type. This operates on the date of the brush, * so it destruct the original brush data */ virtual void makeMaskImage(); enumBrushType brushType() const override; /** * @return default file extension for saving the brush */ QString defaultFileExtension() const override; protected: /** * save the content of this brush to an IO device */ friend class KisImageBrushesPipe; friend class KisBrushExport; void setBrushType(enumBrushType type) override; void setBrushTipImage(const QImage& image) override; void toXML(QDomDocument& d, QDomElement& e) const override; private: bool init(); bool initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h); struct Private; Private* const d; }; typedef QSharedPointer KisGbrBrushSP; #endif // KIS_GBR_BRUSH_ diff --git a/libs/brush/kis_imagepipe_brush.cpp b/libs/brush/kis_imagepipe_brush.cpp index e97ab79c9f..919a4dab6a 100644 --- a/libs/brush/kis_imagepipe_brush.cpp +++ b/libs/brush/kis_imagepipe_brush.cpp @@ -1,529 +1,530 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_imagepipe_brush.h" #include "kis_pipebrush_parasite.h" #include "kis_brushes_pipe.h" class KisImageBrushesPipe : public KisBrushesPipe { public: KisImageBrushesPipe() : m_currentBrushIndex(0) , m_isInitialized(false) { } /* pre and post are split because: 21:12:20 < dmitryK> boud: i guess it was somehow related to the fact that the maskWidth/maskHeight should correspond to the size of the mask returned by paintDevice() 21:13:33 < dmitryK> boud: the random stuff is called once per brush->paintDevice() call, after the device is returned to the paint op, that is "preparing the randomness for the next call" 21:14:16 < dmitryK> boud: and brushesPipe->currentBrush() always returning the same brush for any particular paintInfo. */ protected: static int selectPre(KisParasite::SelectionMode mode, int index, int rank, const KisPaintInformation& info) { qreal angle; qreal velocity; qreal capSpeed = 3; switch (mode) { case KisParasite::Constant: case KisParasite::Incremental: case KisParasite::Random: break; case KisParasite::Pressure: index = static_cast(info.pressure() * (rank - 1) + 0.5); break; case KisParasite::Angular: // + M_PI_2 + M_PI_4 to be compatible with the gimp angle = info.drawingAngle() + M_PI_2 + M_PI_4; angle = normalizeAngle(angle); index = static_cast(angle / (2.0 * M_PI) * rank); break; case KisParasite::TiltX: index = qRound(info.xTilt() / 2.0 * rank) + rank / 2; break; case KisParasite::TiltY: index = qRound(info.yTilt() / 2.0 * rank) + rank / 2; break; case KisParasite::Velocity: // log is slow, but allows for nicer dab transition velocity = log(info.drawingSpeed() + 1); if (velocity > capSpeed) { velocity = capSpeed; } velocity /= capSpeed; velocity *= (rank - 1) + 0.5; index = qRound(velocity); break; default: warnImage << "Parasite" << mode << "is not implemented"; index = 0; } return index; } static int selectPost(KisParasite::SelectionMode mode, int index, int rank, const KisPaintInformation& info, int seqNo) { switch (mode) { case KisParasite::Constant: break; case KisParasite::Incremental: index = (seqNo >= 0 ? seqNo : (index + 1)) % rank; break; case KisParasite::Random: index = info.randomSource()->generate(0, rank-1); break; case KisParasite::Pressure: case KisParasite::Angular: break; case KisParasite::TiltX: case KisParasite::TiltY: case KisParasite::Velocity: break; default: warnImage << "Parasite" << mode << "is not implemented"; index = 0; } return index; } int chooseNextBrush(const KisPaintInformation& info) override { quint32 brushIndex = 0; if (!m_isInitialized) { /** * Reset all the indexes to the initial values and do the * generation based on parameters. */ for (int i = 0; i < m_parasite.dim; i++) { m_parasite.index[i] = 0; } updateBrushIndexes(info, 0); m_isInitialized = true; } for (int i = 0; i < m_parasite.dim; i++) { int index = selectPre(m_parasite.selection[i], m_parasite.index[i], m_parasite.rank[i], info); brushIndex += m_parasite.brushesCount[i] * index; } brushIndex %= (quint32)m_brushes.size(); m_currentBrushIndex = brushIndex; return brushIndex; } int currentBrushIndex() override { return m_currentBrushIndex; } void updateBrushIndexes(const KisPaintInformation& info, int seqNo) override { for (int i = 0; i < m_parasite.dim; i++) { m_parasite.index[i] = selectPost(m_parasite.selection[i], m_parasite.index[i], m_parasite.rank[i], info, seqNo); } } public: using KisBrushesPipe::addBrush; using KisBrushesPipe::sizeBrush; void setParasite(const KisPipeBrushParasite& parasite) { m_parasite = parasite; } const KisPipeBrushParasite& parasite() const { return m_parasite; } void setUseColorAsMask(bool useColorAsMask) { Q_FOREACH (KisGbrBrushSP brush, m_brushes) { brush->setUseColorAsMask(useColorAsMask); } } void makeMaskImage() { Q_FOREACH (KisGbrBrushSP brush, m_brushes) { brush->makeMaskImage(); } } bool saveToDevice(QIODevice* dev) const { Q_FOREACH (KisGbrBrushSP brush, m_brushes) { if (!brush->saveToDevice(dev)) { return false; } } return true; } void notifyStrokeStarted() override { m_isInitialized = false; } private: KisPipeBrushParasite m_parasite; int m_currentBrushIndex; bool m_isInitialized; }; struct KisImagePipeBrush::Private { public: KisImageBrushesPipe brushesPipe; }; KisImagePipeBrush::KisImagePipeBrush(const QString& filename) : KisGbrBrush(filename) , d(new Private()) { } KisImagePipeBrush::KisImagePipeBrush(const QString& name, int w, int h, QVector< QVector > devices, QVector modes) : KisGbrBrush(QString()) , d(new Private()) { Q_ASSERT(devices.count() == modes.count()); Q_ASSERT(devices.count() > 0); Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim! setName(name); KisPipeBrushParasite parasite; parasite.dim = devices.count(); // XXX Change for multidim! : parasite.ncells = devices.at(0).count(); parasite.rank[0] = parasite.ncells; // ### This can masquerade some bugs, be careful here in the future parasite.selection[0] = modes.at(0); // XXX needsmovement! parasite.setBrushesCount(); setParasite(parasite); setDevices(devices, w, h); setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage()); } KisImagePipeBrush::KisImagePipeBrush(const KisImagePipeBrush& rhs) : KisGbrBrush(rhs), d(new Private(*rhs.d)) { } KisImagePipeBrush &KisImagePipeBrush::operator=(const KisImagePipeBrush &rhs) { if (*this != rhs) { d->brushesPipe = rhs.d->brushesPipe; } return *this; } KoResourceSP KisImagePipeBrush::clone() const { return KoResourceSP(new KisImagePipeBrush(*this)); } KisImagePipeBrush::~KisImagePipeBrush() { delete d; } - -bool KisImagePipeBrush::loadFromDevice(QIODevice *dev) +bool KisImagePipeBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QByteArray data = dev->readAll(); return initFromData(data); } bool KisImagePipeBrush::initFromData(const QByteArray &data) { if (data.size() == 0) return false; // XXX: this doesn't correctly load the image pipe brushes yet. // XXX: This stuff is in utf-8, too. // The first line contains the name -- this means we look until we arrive at the first newline QByteArray line1; qint32 i = 0; while (data[i] != '\n' && i < data.size()) { line1.append(data[i]); i++; } setName(QString::fromUtf8(line1, line1.size())); i++; // Skip past the first newline // The second line contains the number of brushes, separated by a space from the parasite // XXX: This stuff is in utf-8, too. QByteArray line2; while (data[i] != '\n' && i < data.size()) { line2.append(data[i]); i++; } QString paramline = QString::fromUtf8(line2, line2.size()); qint32 numOfBrushes = paramline.left(paramline.indexOf(' ')).toUInt(); QString parasiteString = paramline.mid(paramline.indexOf(' ') + 1); KisPipeBrushParasite parasite = KisPipeBrushParasite(parasiteString); parasite.sanitize(); parasiteSelectionString = parasite.selectionMode; // selection mode to return to UI d->brushesPipe.setParasite(parasite); i++; // Skip past the second newline for (int brushIndex = d->brushesPipe.sizeBrush(); brushIndex < numOfBrushes && i < data.size(); brushIndex++) { KisGbrBrushSP brush = KisGbrBrushSP(new KisGbrBrush(name() + '_' + QString().setNum(brushIndex), data, i)); d->brushesPipe.addBrush(brush); } if (numOfBrushes > 0) { setValid(true); setSpacing(d->brushesPipe.lastBrush()->spacing()); setWidth(d->brushesPipe.firstBrush()->width()); setHeight(d->brushesPipe.firstBrush()->height()); setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage()); } return true; } bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const { QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int len = qstrlen(name); if (d->brushesPipe.parasite().dim >= KisPipeBrushParasite::MaxDim) { warnImage << "Save to file for pipe brushes with dim != not yet supported!"; return false; } // Save this pipe brush: first the header, and then all individual brushes consecutively // XXX: this needs some care for when we have > 1 dimension) // Gimp Pipe Brush header format: Name\n \n // The name\n if (dev->write(name, len) == -1) return false; if (!dev->putChar('\n')) return false; // Write the parasite (also writes number of brushes) if (!d->brushesPipe.parasite().saveToDevice(dev)) return false; if (!dev->putChar('\n')) return false; KoResource::saveToDevice(dev); // return d->brushesPipe.saveToDevice(dev); } void KisImagePipeBrush::notifyStrokeStarted() { d->brushesPipe.notifyStrokeStarted(); } void KisImagePipeBrush::notifyCachedDabPainted(const KisPaintInformation& info) { d->brushesPipe.notifyCachedDabPainted(info); } void KisImagePipeBrush::prepareForSeqNo(const KisPaintInformation &info, int seqNo) { d->brushesPipe.prepareForSeqNo(info, seqNo); } void KisImagePipeBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX , double subPixelY, qreal softnessFactor) const { d->brushesPipe.generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor); } QVector KisImagePipeBrush::brushes() const { return d->brushesPipe.brushes(); } KisFixedPaintDeviceSP KisImagePipeBrush::paintDevice( const KoColorSpace * colorSpace, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY) const { return d->brushesPipe.paintDevice(colorSpace, shape, info, subPixelX, subPixelY); } enumBrushType KisImagePipeBrush::brushType() const { return !hasColor() || useColorAsMask() ? PIPE_MASK : PIPE_IMAGE; } QString KisImagePipeBrush::parasiteSelection() { return parasiteSelectionString; } bool KisImagePipeBrush::hasColor() const { return d->brushesPipe.hasColor(); } void KisImagePipeBrush::makeMaskImage() { d->brushesPipe.makeMaskImage(); setUseColorAsMask(false); } void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask) { KisGbrBrush::setUseColorAsMask(useColorAsMask); d->brushesPipe.setUseColorAsMask(useColorAsMask); } const KisBoundary* KisImagePipeBrush::boundary() const { KisGbrBrushSP brush = d->brushesPipe.firstBrush(); Q_ASSERT(brush); return brush->boundary(); } bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info) { return (!d->brushesPipe.parasite().needsMovement || info.drawingDistance() >= 0.5); } QString KisImagePipeBrush::defaultFileExtension() const { return QString(".gih"); } quint32 KisImagePipeBrush::brushIndex(const KisPaintInformation& info) const { return d->brushesPipe.brushIndex(info); } qint32 KisImagePipeBrush::maskWidth(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return d->brushesPipe.maskWidth(shape, subPixelX, subPixelY, info); } qint32 KisImagePipeBrush::maskHeight(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return d->brushesPipe.maskHeight(shape, subPixelX, subPixelY, info); } void KisImagePipeBrush::setAngle(qreal _angle) { KisGbrBrush::setAngle(_angle); d->brushesPipe.setAngle(_angle); } void KisImagePipeBrush::setScale(qreal _scale) { KisGbrBrush::setScale(_scale); d->brushesPipe.setScale(_scale); } void KisImagePipeBrush::setSpacing(double _spacing) { KisGbrBrush::setSpacing(_spacing); d->brushesPipe.setSpacing(_spacing); } void KisImagePipeBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisImagePipeBrush"); // brushType() is a function of hasColor() and useColorAsMask() } void KisImagePipeBrush::setHasColor(bool hasColor) { Q_UNUSED(hasColor); qFatal("FATAL: protected member setHasColor has no meaning for KisImagePipeBrush"); // hasColor() is a function of the underlying brushes } KisGbrBrushSP KisImagePipeBrush::testingGetCurrentBrush(const KisPaintInformation& info) const { return d->brushesPipe.currentBrush(info); } void KisImagePipeBrush::testingSelectNextBrush(const KisPaintInformation& info) const { return d->brushesPipe.testingSelectNextBrush(info); } const KisPipeBrushParasite& KisImagePipeBrush::parasite() const { return d->brushesPipe.parasite(); } void KisImagePipeBrush::setParasite(const KisPipeBrushParasite ¶site) { d->brushesPipe.setParasite(parasite); } void KisImagePipeBrush::setDevices(QVector > devices, int w, int h) { for (int i = 0; i < devices.at(0).count(); i++) { d->brushesPipe.addBrush(KisGbrBrushSP(new KisGbrBrush(devices.at(0).at(i), 0, 0, w, h))); } } diff --git a/libs/brush/kis_imagepipe_brush.h b/libs/brush/kis_imagepipe_brush.h index eed80e3ac9..93e326da5b 100644 --- a/libs/brush/kis_imagepipe_brush.h +++ b/libs/brush/kis_imagepipe_brush.h @@ -1,143 +1,143 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_IMAGEPIPE_BRUSH_ #define KIS_IMAGEPIPE_BRUSH_ #include #include #include #include #include "kis_gbr_brush.h" #include "kis_global.h" class KisPipeBrushParasite; /** * Velocity won't be supported, atm Tilt isn't either, * but have chances of implementation */ namespace KisParasite { enum SelectionMode { Constant, Incremental, Angular, Velocity, Random, Pressure, TiltX, TiltY }; } class BRUSH_EXPORT KisImagePipeBrush : public KisGbrBrush { public: KisImagePipeBrush(const QString& filename); /** * Specialized constructor that makes a new pipe brush from a sequence of samesize * devices. The fact that it's a vector of a vector, is to support multidimensional * brushes (not yet supported!) */ KisImagePipeBrush(const QString& name, int w, int h, QVector< QVector > devices, QVector modes); ~KisImagePipeBrush() override; /// Will call KisBrush's saveToDevice as well KisImagePipeBrush(const KisImagePipeBrush& rhs); KisImagePipeBrush &operator=(const KisImagePipeBrush &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; /** * @return the next image in the pipe. */ KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, KisDabShape const&, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0) const override; void setUseColorAsMask(bool useColorAsMask) override; bool hasColor() const override; enumBrushType brushType() const override; QString parasiteSelection(); // returns random, constant, etc const KisBoundary* boundary() const override; bool canPaintFor(const KisPaintInformation& info) override; void makeMaskImage() override; QString defaultFileExtension() const override; void setAngle(qreal _angle) override; void setScale(qreal _scale) override; void setSpacing(double _spacing) override; quint32 brushIndex(const KisPaintInformation& info) const override; qint32 maskWidth(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override; qint32 maskHeight(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override; void notifyStrokeStarted() override; void notifyCachedDabPainted(const KisPaintInformation& info) override; void prepareForSeqNo(const KisPaintInformation& info, int seqNo) override; void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, KisDabShape const&, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const override; QVector brushes() const; const KisPipeBrushParasite ¶site() const; void setParasite(const KisPipeBrushParasite& parasite); void setDevices(QVector< QVector > devices, int w, int h); protected: void setBrushType(enumBrushType type) override; void setHasColor(bool hasColor) override; private: friend class KisImagePipeBrushTest; KisGbrBrushSP testingGetCurrentBrush(const KisPaintInformation& info) const; void testingSelectNextBrush(const KisPaintInformation& info) const; bool initFromData(const QByteArray &data); QString parasiteSelectionString; // incremental, random, etc. private: struct Private; Private * const d; }; typedef QSharedPointer KisImagePipeBrushSP; #endif // KIS_IMAGEPIPE_BRUSH_ diff --git a/libs/brush/kis_png_brush.cpp b/libs/brush/kis_png_brush.cpp index 50d8e70815..8e484e1c47 100644 --- a/libs/brush/kis_png_brush.cpp +++ b/libs/brush/kis_png_brush.cpp @@ -1,142 +1,142 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_png_brush.h" #include #include #include #include #include #include #include KisPngBrush::KisPngBrush(const QString& filename) : KisScalingSizeBrush(filename) { setBrushType(INVALID); setSpacing(0.25); setHasColor(false); } KisPngBrush::KisPngBrush(const KisPngBrush &rhs) : KisScalingSizeBrush(rhs) { setSpacing(rhs.spacing()); if (brushTipImage().isGrayscale()) { setBrushType(MASK); setHasColor(false); } else { setBrushType(IMAGE); setHasColor(true); } } KoResourceSP KisPngBrush::clone() const { return KoResourceSP(new KisPngBrush(*this)); } - -bool KisPngBrush::loadFromDevice(QIODevice *dev) +bool KisPngBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = dev->readAll(); QBuffer buf(&data); buf.open(QIODevice::ReadOnly); QImageReader reader(&buf, "PNG"); if (!reader.canRead()) { dbgKrita << "Could not read brush" << filename() << ". Error:" << reader.errorString(); setValid(false); return false; } if (reader.textKeys().contains("brush_spacing")) { setSpacing(KisDomUtils::toDouble(reader.text("brush_spacing"))); } if (reader.textKeys().contains("brush_name")) { setName(reader.text("brush_name")); } else { QFileInfo info(filename()); setName(info.completeBaseName()); } QImage image = reader.read(); if (image.isNull()) { dbgKrita << "Could not create image for" << filename() << ". Error:" << reader.errorString(); setValid(false); return false; } setValid(true); if (image.allGray()) { // Make sure brush tips all have a white background QImage base(image.size(), image.format()); if ((int)base.format() < (int)QImage::Format_RGB32) { base = base.convertToFormat(QImage::Format_ARGB32); } QPainter gc(&base); gc.fillRect(base.rect(), Qt::white); gc.drawImage(0, 0, image); gc.end(); QImage converted = base.convertToFormat(QImage::Format_Grayscale8); setBrushTipImage(converted); setBrushType(MASK); setHasColor(false); } else { setBrushTipImage(image); setBrushType(IMAGE); setHasColor(true); } setWidth(brushTipImage().width()); setHeight(brushTipImage().height()); return valid(); } bool KisPngBrush::saveToDevice(QIODevice *dev) const { if(brushTipImage().save(dev, "PNG")) { KoResource::saveToDevice(dev); return true; } return false; } QString KisPngBrush::defaultFileExtension() const { return QString(".png"); } void KisPngBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("png_brush", e); KisBrush::toXML(d, e); } diff --git a/libs/brush/kis_png_brush.h b/libs/brush/kis_png_brush.h index 9a65ae31ee..d8aaa57331 100644 --- a/libs/brush/kis_png_brush.h +++ b/libs/brush/kis_png_brush.h @@ -1,44 +1,44 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_PNG_BRUSH_ #define KIS_PNG_BRUSH_ #include "kis_scaling_size_brush.h" class BRUSH_EXPORT KisPngBrush : public KisScalingSizeBrush { public: /// Construct brush to load filename later as brush KisPngBrush(const QString& filename); KisPngBrush(const KisPngBrush &rhs); KoResourceSP clone() const override; // No operator= needed, because there's no local data in KisPngBrush - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice *dev) const override; QString defaultFileExtension() const override; void toXML(QDomDocument& d, QDomElement& e) const override; QPair resourceType() const override { return QPair(ResourceType::Brushes, ResourceSubType::PngBrushes); } }; #endif diff --git a/libs/brush/kis_predefined_brush_factory.cpp b/libs/brush/kis_predefined_brush_factory.cpp index 2edeb8d86a..515e384eaa 100644 --- a/libs/brush/kis_predefined_brush_factory.cpp +++ b/libs/brush/kis_predefined_brush_factory.cpp @@ -1,81 +1,83 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_predefined_brush_factory.h" #include #include "KisBrushServerProvider.h" #include "kis_gbr_brush.h" #include +#include KisPredefinedBrushFactory::KisPredefinedBrushFactory(const QString &brushType) : m_id(brushType) { } QString KisPredefinedBrushFactory::id() const { return m_id; } -KisBrushSP KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefinition) +KisBrushSP KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) { - KoResourceServer *rServer = KisBrushServerProvider::instance()->brushServer(); - QString brushFileName = brushDefinition.attribute("filename", ""); - KisBrushSP brush = rServer->resourceByFilename(brushFileName); + auto resourceStorage = resourcesInterface->source(ResourceType::Brushes); + + const QString brushFileName = brushDefinition.attribute("filename", ""); + KisBrushSP brush = resourceStorage.resourceForFilename(brushFileName); //Fallback for files that still use the old format if (!brush) { QFileInfo info(brushFileName); - brush = rServer->resourceByFilename(info.fileName()); + brush = resourceStorage.resourceForFilename(info.fileName()); } if (!brush) { - brush = rServer->firstResource(); + brush = resourceStorage.fallbackResource(); } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(brush, 0); // we always return a copy of the brush! brush = brush->clone().dynamicCast(); double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "0.25")); brush->setSpacing(spacing); bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0")); qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0")); brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff); double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0")); brush->setAngle(angle); double scale = KisDomUtils::toDouble(brushDefinition.attribute("scale", "1.0")); brush->setScale(scale); if (m_id == "gbr_brush") { KisGbrBrush *gbrbrush = dynamic_cast(brush.data()); if (gbrbrush) { /** * WARNING: see comment in KisGbrBrush::setUseColorAsMask() */ gbrbrush->setUseColorAsMask((bool)brushDefinition.attribute("ColorAsMask").toInt()); } } return brush; } diff --git a/libs/brush/kis_predefined_brush_factory.h b/libs/brush/kis_predefined_brush_factory.h index 5c3cc18899..f23873bf1e 100644 --- a/libs/brush/kis_predefined_brush_factory.h +++ b/libs/brush/kis_predefined_brush_factory.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_PREDEFINED_BRUSH_FACTORY_H #define __KIS_PREDEFINED_BRUSH_FACTORY_H #include #include #include "kis_brush_factory.h" #include "kis_brush.h" class KisPredefinedBrushFactory : public KisBrushFactory { public: KisPredefinedBrushFactory(const QString &brushType); QString id() const override; - KisBrushSP createBrush(const QDomElement& brushDefinition) override; + KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override; private: const QString m_id; }; #endif /* __KIS_PREDEFINED_BRUSH_FACTORY_H */ diff --git a/libs/brush/kis_svg_brush.cpp b/libs/brush/kis_svg_brush.cpp index 2bc59b7d29..4732d334bc 100644 --- a/libs/brush/kis_svg_brush.cpp +++ b/libs/brush/kis_svg_brush.cpp @@ -1,116 +1,117 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_svg_brush.h" #include #include #include #include #include KisSvgBrush::KisSvgBrush(const QString& filename) : KisScalingSizeBrush(filename) { setBrushType(INVALID); setSpacing(0.25); setHasColor(false); } KisSvgBrush::KisSvgBrush(const KisSvgBrush& rhs) : KisScalingSizeBrush(rhs) , m_svg(rhs.m_svg) { } KisSvgBrush &KisSvgBrush::operator=(const KisSvgBrush &rhs) { if (*this != rhs) { m_svg = rhs.m_svg; } return *this; } KoResourceSP KisSvgBrush::clone() const { return KoResourceSP(new KisSvgBrush(*this)); } -bool KisSvgBrush::loadFromDevice(QIODevice *dev) +bool KisSvgBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); m_svg = dev->readAll(); QSvgRenderer renderer(m_svg); QRect box = renderer.viewBox(); if (box.isEmpty()) return false; QImage image_(1000, (1000 * box.height()) / box.width(), QImage::Format_ARGB32); { QPainter p(&image_); p.fillRect(0, 0, image_.width(), image_.height(), Qt::white); renderer.render(&p); } QVector table; for (int i = 0; i < 256; ++i) table.push_back(qRgb(i, i, i)); image_ = image_.convertToFormat(QImage::Format_Indexed8, table); setBrushTipImage(image_); setValid(true); // Well for now, always true if (brushTipImage().isGrayscale()) { setBrushType(MASK); setHasColor(false); } else { setBrushType(IMAGE); setHasColor(true); } setWidth(brushTipImage().width()); setHeight(brushTipImage().height()); QFileInfo fi(filename()); setName(fi.completeBaseName()); return !brushTipImage().isNull() && valid(); } bool KisSvgBrush::saveToDevice(QIODevice *dev) const { if((dev->write(m_svg.constData(), m_svg.size()) == m_svg.size())) { KoResource::saveToDevice(dev); return true; } return false; } QString KisSvgBrush::defaultFileExtension() const { return QString(".svg"); } void KisSvgBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("svg_brush", e); KisBrush::toXML(d, e); } diff --git a/libs/brush/kis_svg_brush.h b/libs/brush/kis_svg_brush.h index 77444c2849..39e1086af2 100644 --- a/libs/brush/kis_svg_brush.h +++ b/libs/brush/kis_svg_brush.h @@ -1,46 +1,46 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SVG_BRUSH_ #define KIS_SVG_BRUSH_ #include "kis_scaling_size_brush.h" class BRUSH_EXPORT KisSvgBrush : public KisScalingSizeBrush { public: /// Construct brush to load filename later as brush KisSvgBrush(const QString &filename); KisSvgBrush(const KisSvgBrush &rhs); KisSvgBrush &operator=(const KisSvgBrush &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice *dev) const override; QPair resourceType() const override { return QPair(ResourceType::Brushes, ResourceSubType::SvgBrushes); } QString defaultFileExtension() const override; void toXML(QDomDocument& d, QDomElement& e) const override; private: QByteArray m_svg; }; #endif diff --git a/libs/brush/kis_text_brush_factory.cpp b/libs/brush/kis_text_brush_factory.cpp index f1d8984253..76559ee047 100644 --- a/libs/brush/kis_text_brush_factory.cpp +++ b/libs/brush/kis_text_brush_factory.cpp @@ -1,43 +1,45 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_text_brush_factory.h" #include #include #include #include "kis_text_brush.h" -KisBrushSP KisTextBrushFactory::createBrush(const QDomElement& brushDefinition) +KisBrushSP KisTextBrushFactory::createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QString text = brushDefinition.attribute("text", "The quick brown fox ate your text"); QFont font; font.fromString(brushDefinition.attribute("font")); double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0")); QString pipeMode = brushDefinition.attribute("pipe", "false"); bool pipe = (pipeMode == "true") ? true : false; KisTextBrushSP brush = KisTextBrushSP(new KisTextBrush()); brush->setText(text); brush->setFont(font); brush->setPipeMode(pipe); brush->setSpacing(spacing); brush->updateBrush(); return brush; } diff --git a/libs/brush/kis_text_brush_factory.h b/libs/brush/kis_text_brush_factory.h index c7f8af0c83..e3c435872f 100644 --- a/libs/brush/kis_text_brush_factory.h +++ b/libs/brush/kis_text_brush_factory.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_TEXT_BRUSH_FACTORY #define KIS_TEXT_BRUSH_FACTORY #include #include #include "kis_brush_factory.h" #include "kis_brush.h" /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisTextBrushFactory : public KisBrushFactory { public: KisTextBrushFactory() {} ~KisTextBrushFactory() override {} QString id() const override { return "kis_text_brush"; } /** * Create a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - KisBrushSP createBrush(const QDomElement& brushDefinition) override; + KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override; }; #endif diff --git a/libs/flake/resources/KoGamutMask.cpp b/libs/flake/resources/KoGamutMask.cpp index 005d97d633..bd2f8f4121 100644 --- a/libs/flake/resources/KoGamutMask.cpp +++ b/libs/flake/resources/KoGamutMask.cpp @@ -1,436 +1,437 @@ /* * Copyright (c) 2018 Anna Medonosova * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KoGamutMask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include KoGamutMaskShape::KoGamutMaskShape(KoShape* shape) : m_maskShape(shape) , m_shapePaintingContext(KoShapePaintingContext()) { } KoGamutMaskShape::KoGamutMaskShape() {}; KoGamutMaskShape::~KoGamutMaskShape() {}; KoShape* KoGamutMaskShape::koShape() { return m_maskShape; } bool KoGamutMaskShape::coordIsClear(const QPointF& coord) const { bool isClear = m_maskShape->hitTest(coord); return isClear; } void KoGamutMaskShape::paint(QPainter &painter) { painter.save(); painter.setTransform(m_maskShape->absoluteTransformation(), true); m_maskShape->paint(painter, m_shapePaintingContext); painter.restore(); } void KoGamutMaskShape::paintStroke(QPainter &painter) { painter.save(); painter.setTransform(m_maskShape->absoluteTransformation(), true); m_maskShape->paintStroke(painter, m_shapePaintingContext); painter.restore(); } struct KoGamutMask::Private { QString name; QString title; QString description; QByteArray data; QVector maskShapes; QVector previewShapes; QSizeF maskSize; int rotation {0}; }; KoGamutMask::KoGamutMask(const QString& filename) : KoResource(filename) , d(new Private) { d->maskSize = QSizeF(144.0,144.0); setRotation(0); } KoGamutMask::KoGamutMask() : KoResource(QString()) , d(new Private) { d->maskSize = QSizeF(144.0,144.0); setRotation(0); } KoGamutMask::KoGamutMask(KoGamutMask* rhs) : QObject(0) , KoResource(QString()) , d(new Private) { *this = *rhs; } KoGamutMask::KoGamutMask(const KoGamutMask &rhs) : QObject(0) , KoResource(rhs) , d(new Private) { *this = rhs; } KoGamutMask &KoGamutMask::operator=(const KoGamutMask &rhs) { if (*this != rhs) { setTitle(rhs.title()); setDescription(rhs.description()); d->maskSize = rhs.d->maskSize; QList newShapes; for(KoShape* sh: rhs.koShapes()) { newShapes.append(sh->cloneShape()); } setMaskShapes(newShapes); } return *this; } KoResourceSP KoGamutMask::clone() const { return KoResourceSP(new KoGamutMask(*this)); } KoGamutMask::~KoGamutMask() { delete d; } bool KoGamutMask::coordIsClear(const QPointF& coord, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { if (shape->coordIsClear(coord) == true) { return true; } } return false; } void KoGamutMask::paint(QPainter &painter, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { shape->paint(painter); } } void KoGamutMask::paintStroke(QPainter &painter, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { shape->paintStroke(painter); } } QTransform KoGamutMask::maskToViewTransform(quint8 viewSize) { // apply mask rotation before drawing QPointF centerPoint(viewSize*0.5, viewSize*0.5); QTransform transform; transform.translate(centerPoint.x(), centerPoint.y()); transform.rotate(rotation()); transform.translate(-centerPoint.x(), -centerPoint.y()); qreal scale = viewSize/(maskSize().width()); transform.scale(scale, scale); return transform; } QTransform KoGamutMask::viewToMaskTransform(quint8 viewSize) { QPointF centerPoint(viewSize*0.5, viewSize*0.5); QTransform transform; qreal scale = viewSize/(maskSize().width()); transform.scale(1/scale, 1/scale); transform.translate(centerPoint.x(), centerPoint.y()); transform.rotate(-rotation()); transform.translate(-centerPoint.x(), -centerPoint.y()); return transform; } - -bool KoGamutMask::loadFromDevice(QIODevice *dev) +bool KoGamutMask::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); // TODO: test KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false); if (filename().isNull()) { warnFlake << "Cannot load gamut mask" << name() << "there is no filename set"; return false; } if (d->data.isNull()) { QFile file(filename()); if (file.size() == 0) { warnFlake << "Cannot load gamut mask" << name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); d->data = file.readAll(); file.close(); } QBuffer buf(&d->data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-gamutmask", KoStore::Zip)); if (!store || store->bad()) return false; bool storeOpened = store->open("gamutmask.svg"); if (!storeOpened) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QString errorMsg; int errorLine = 0; int errorColumn = 0; KoXmlDocument xmlDocument = SvgParser::createDocumentFromSvg(ba, &errorMsg, &errorLine, &errorColumn); if (xmlDocument.isNull()) { errorFlake << "Parsing error in " << filename() << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; errorFlake << "Parsing error in the main document at line" << errorLine << ", column" << errorColumn << endl << "Error message: " << errorMsg; return false; } KoDocumentResourceManager manager; SvgParser parser(&manager); parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values QSizeF fragmentSize; QList shapes = parser.parseSvg(xmlDocument.documentElement(), &fragmentSize); d->maskSize = fragmentSize; d->title = parser.documentTitle(); setName(d->title); d->description = parser.documentDescription(); setMaskShapes(shapes); if (store->open("preview.png")) { KoStoreDevice previewDev(store.data()); previewDev.open(QIODevice::ReadOnly); QImage preview = QImage(); preview.load(&previewDev, "PNG"); setImage(preview); (void)store->close(); } buf.close(); setValid(true); return true; } void KoGamutMask::setMaskShapes(QList shapes) { setMaskShapesToVector(shapes, d->maskShapes); } QList KoGamutMask::koShapes() const { QList shapes; for(KoGamutMaskShape* maskShape: d->maskShapes) { shapes.append(maskShape->koShape()); } return shapes; } bool KoGamutMask::saveToDevice(QIODevice *dev) const { KoStore* store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-gamutmask", KoStore::Zip)); if (!store || store->bad()) return false; QList shapes = koShapes(); std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); if (!store->open("gamutmask.svg")) { return false; } KoStoreDevice storeDev(store); storeDev.open(QIODevice::WriteOnly); SvgWriter writer(shapes); writer.setDocumentTitle(d->title); writer.setDocumentDescription(d->description); writer.save(storeDev, d->maskSize); if (!store->close()) { return false; } if (!store->open("preview.png")) { return false; } KoStoreDevice previewDev(store); previewDev.open(QIODevice::WriteOnly); image().save(&previewDev, "PNG"); if (!store->close()) { return false; } - return store->finalize(); + return store->finalize() && KoResource::saveToDevice(dev); } QString KoGamutMask::title() const { return d->title; } void KoGamutMask::setTitle(QString title) { d->title = title; setName(title); } QString KoGamutMask::description() const { return d->description; } void KoGamutMask::setDescription(QString description) { d->description = description; } QString KoGamutMask::defaultFileExtension() const { return ".kgm"; } int KoGamutMask::rotation() { return d->rotation; } void KoGamutMask::setRotation(int rotation) { d->rotation = rotation; } QSizeF KoGamutMask::maskSize() { return d->maskSize; } void KoGamutMask::setPreviewMaskShapes(QList shapes) { setMaskShapesToVector(shapes, d->previewShapes); } void KoGamutMask::setMaskShapesToVector(QList shapes, QVector &targetVector) { targetVector.clear(); for(KoShape* sh: shapes) { KoGamutMaskShape* maskShape = new KoGamutMaskShape(sh); targetVector.append(maskShape); } } // clean up when ending mask preview void KoGamutMask::clearPreview() { d->previewShapes.clear(); } diff --git a/libs/flake/resources/KoGamutMask.h b/libs/flake/resources/KoGamutMask.h index 2d0ffd7ddb..241461fbb1 100644 --- a/libs/flake/resources/KoGamutMask.h +++ b/libs/flake/resources/KoGamutMask.h @@ -1,114 +1,114 @@ /* * Copyright (c) 2018 Anna Medonosova * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KOGAMUTMASK_H #define KOGAMUTMASK_H #include #include #include #include #include #include #include #include //class KoViewConverter; class QTransform; class KoGamutMaskShape { public: KoGamutMaskShape(KoShape* shape); KoGamutMaskShape(); ~KoGamutMaskShape(); bool coordIsClear(const QPointF& coord) const; QPainterPath outline(); void paint(QPainter &painter); void paintStroke(QPainter &painter); KoShape* koShape(); private: KoShape* m_maskShape; KoShapePaintingContext m_shapePaintingContext; }; /** * @brief The resource type for gamut masks used by the artistic color selector */ class KRITAFLAKE_EXPORT KoGamutMask : public QObject, public KoResource { Q_OBJECT public: KoGamutMask(const QString &filename); KoGamutMask(); KoGamutMask(KoGamutMask *rhs); KoGamutMask(const KoGamutMask &rhs); KoGamutMask &operator=(const KoGamutMask &rhs); KoResourceSP clone() const override; ~KoGamutMask() override; bool coordIsClear(const QPointF& coord, bool preview); - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::GamutMasks, ""); } void paint(QPainter &painter, bool preview); void paintStroke(QPainter &painter, bool preview); QTransform maskToViewTransform(quint8 viewSize); QTransform viewToMaskTransform(quint8 viewSize); QString title() const; void setTitle(QString title); QString description() const; void setDescription(QString description); QString defaultFileExtension() const override; int rotation(); void setRotation(int rotation); QSizeF maskSize(); void setMaskShapes(QList shapes); void setPreviewMaskShapes(QList shapes); QList koShapes() const; void clearPreview(); private: void setMaskShapesToVector(QList shapes, QVector& targetVector); struct Private; Private* const d; }; typedef QSharedPointer KoGamutMaskSP; #endif // KOGAMUTMASK_H diff --git a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp index 19a2ea8adc..73497ae9d5 100644 --- a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp +++ b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp @@ -1,232 +1,232 @@ /* This file is part of the KDE project Copyright (c) 2017 L. E. Segovia This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 "kis_debug.h" #include #include #include #include #include #include #include #include QImage KoSvgSymbol::icon() { KoShapeGroup *group = dynamic_cast(shape); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(group, QImage()); QRectF rc = group->boundingRect().normalized(); QImage image(rc.width(), rc.height(), QImage::Format_ARGB32_Premultiplied); QPainter gc(&image); image.fill(Qt::gray); KoShapePaintingContext ctx; // debugFlake << "Going to render. Original bounding rect:" << group->boundingRect() // << "Normalized: " << rc // << "Scale W" << 256 / rc.width() << "Scale H" << 256 / rc.height(); gc.translate(-rc.x(), -rc.y()); KoShapeManager::renderSingleShape(group, gc, ctx); gc.end(); image = image.scaled(128, 128, Qt::KeepAspectRatio); return image; } struct KoSvgSymbolCollectionResource::Private { QVector symbols; QString title; QString description; }; KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource(const QString& filename) : KoResource(filename) , d(new Private()) { } KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource() : KoResource(QString()) , d(new Private()) { } KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource(const KoSvgSymbolCollectionResource& rhs) : KoResource(QString()) , d(new Private()) { *this = rhs; } KoSvgSymbolCollectionResource &KoSvgSymbolCollectionResource::operator=(const KoSvgSymbolCollectionResource &rhs) { if (*this != rhs) { d->symbols = rhs.d->symbols; d->title = rhs.d->title; d->description = rhs.d->description; } return *this; } KoResourceSP KoSvgSymbolCollectionResource::clone() const { return KoResourceSP(new KoSvgSymbolCollectionResource(*this)); } KoSvgSymbolCollectionResource::~KoSvgSymbolCollectionResource() { } - - -bool KoSvgSymbolCollectionResource::loadFromDevice(QIODevice *dev) +bool KoSvgSymbolCollectionResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + if (!dev->isOpen()) { dev->open(QIODevice::ReadOnly); } QByteArray ba = dev->readAll(); KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); setMD5(hashGenerator->generateHash(ba)); dev->seek(0); QString errorMsg; int errorLine = 0; int errorColumn; KoXmlDocument doc = SvgParser::createDocumentFromSvg(dev, &errorMsg, &errorLine, &errorColumn); if (doc.isNull()) { errKrita << "Parsing error in " << filename() << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; errKrita << i18n("Parsing error in the main document at line %1, column %2\nError message: %3" , errorLine , errorColumn , errorMsg); return false; } KoDocumentResourceManager manager; SvgParser parser(&manager); parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values QSizeF fragmentSize; // We're not interested in the shapes themselves qDeleteAll(parser.parseSvg(doc.documentElement(), &fragmentSize)); d->symbols = parser.takeSymbols(); // debugFlake << "Loaded" << filename() << "\n\t" // << "Title" << parser.documentTitle() << "\n\t" // << "Description" << parser.documentDescription() // << "\n\tgot" << d->symbols.size() << ResourceType::Symbols // << d->symbols[0]->shape->outlineRect() // << d->symbols[0]->shape->size(); d->title = parser.documentTitle(); setName(d->title); d->description = parser.documentDescription(); if (d->symbols.size() < 1) { setValid(false); return false; } setValid(true); setImage(d->symbols[0]->icon()); return true; } bool KoSvgSymbolCollectionResource::saveToDevice(QIODevice *dev) const { bool res = false; // XXX if (res) { KoResource::saveToDevice(dev); } return res; } QString KoSvgSymbolCollectionResource::defaultFileExtension() const { return QString(".svg"); } QString KoSvgSymbolCollectionResource::title() const { return d->title; } QString KoSvgSymbolCollectionResource::description() const { return d->description; } QString KoSvgSymbolCollectionResource::creator() const { return ""; } QString KoSvgSymbolCollectionResource::rights() const { return ""; } QString KoSvgSymbolCollectionResource::language() const { return ""; } QStringList KoSvgSymbolCollectionResource::subjects() const { return QStringList(); } QString KoSvgSymbolCollectionResource::license() const { return ""; } QStringList KoSvgSymbolCollectionResource::permits() const { return QStringList(); } QVector KoSvgSymbolCollectionResource::symbols() const { return d->symbols; } diff --git a/libs/flake/resources/KoSvgSymbolCollectionResource.h b/libs/flake/resources/KoSvgSymbolCollectionResource.h index 93afb91b5d..55f0953be4 100644 --- a/libs/flake/resources/KoSvgSymbolCollectionResource.h +++ b/libs/flake/resources/KoSvgSymbolCollectionResource.h @@ -1,106 +1,106 @@ /* This file is part of the KDE project Copyright (c) 2017 Boudewijn Rempt This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOSVGSYMBOLCOLLECTIONRESOURCE #define KOSVGSYMBOLCOLLECTIONRESOURCE #include #include #include #include #include #include #include #include #include #include #include #include "kritaflake_export.h" struct KRITAFLAKE_EXPORT KoSvgSymbol { KoSvgSymbol() {} KoSvgSymbol(const QString &_title) : title(_title) {} ~KoSvgSymbol() { delete shape; } QString id; QString title; KoShape *shape; QImage icon(); bool operator==(const KoSvgSymbol& rhs) const { return title == rhs.title; } }; /** * Loads an svg file that contains "symbol" objects and creates a collection of those objects. */ class KRITAFLAKE_EXPORT KoSvgSymbolCollectionResource : public KoResource { public: /** */ explicit KoSvgSymbolCollectionResource(const QString &filename); /// Create an empty color set KoSvgSymbolCollectionResource(); ~KoSvgSymbolCollectionResource() override; KoSvgSymbolCollectionResource(const KoSvgSymbolCollectionResource &rhs); KoSvgSymbolCollectionResource &operator=(const KoSvgSymbolCollectionResource &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; QPair resourceType() const override { return QPair(ResourceType::Symbols, ""); } QString title() const; QString description() const; QString creator() const; QString rights() const; QString language() const; QStringList subjects() const; QString license() const; QStringList permits() const; QVector symbols() const; private: struct Private; const QScopedPointer d; }; #endif // KoSvgSymbolCollectionResource diff --git a/libs/image/brushengine/kis_no_size_paintop_settings.cpp b/libs/image/brushengine/kis_no_size_paintop_settings.cpp index 1efdf16d88..66a68e8212 100644 --- a/libs/image/brushengine/kis_no_size_paintop_settings.cpp +++ b/libs/image/brushengine/kis_no_size_paintop_settings.cpp @@ -1,34 +1,35 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_no_size_paintop_settings.h" -KisNoSizePaintOpSettings::KisNoSizePaintOpSettings() +KisNoSizePaintOpSettings::KisNoSizePaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisPaintOpSettings(resourcesInterface) { } void KisNoSizePaintOpSettings::setPaintOpSize(qreal value) { Q_UNUSED(value); } qreal KisNoSizePaintOpSettings::paintOpSize() const { return 1.0; } diff --git a/libs/image/brushengine/kis_no_size_paintop_settings.h b/libs/image/brushengine/kis_no_size_paintop_settings.h index 8cc29ca7a8..0cff0b7d2a 100644 --- a/libs/image/brushengine/kis_no_size_paintop_settings.h +++ b/libs/image/brushengine/kis_no_size_paintop_settings.h @@ -1,35 +1,35 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISNOSIZEPAINTOPSETTINGS_H #define KISNOSIZEPAINTOPSETTINGS_H #include "kis_paintop_settings.h" #include "kritaimage_export.h" class KRITAIMAGE_EXPORT KisNoSizePaintOpSettings : public KisPaintOpSettings { public: - KisNoSizePaintOpSettings(); + KisNoSizePaintOpSettings(KisResourcesInterfaceSP resourcesInterface); void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; }; #endif // KISNOSIZEPAINTOPSETTINGS_H diff --git a/libs/image/brushengine/kis_paintop_factory.h b/libs/image/brushengine/kis_paintop_factory.h index c6129638a8..1088177ff9 100644 --- a/libs/image/brushengine/kis_paintop_factory.h +++ b/libs/image/brushengine/kis_paintop_factory.h @@ -1,118 +1,124 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_PAINTOP_FACTORY_H_ #define KIS_PAINTOP_FACTORY_H_ #include "kis_types.h" #include "kritaimage_export.h" #include #include #include #include #include class KisPainter; class KisPaintOp; class QWidget; class KisPaintOpConfigWidget; +class KoResource; +using KoResourceSP = QSharedPointer; + +class KisResourcesInterface; +using KisResourcesInterfaceSP = QSharedPointer; + /** * The paintop factory is responsible for creating paintops of the specified class. * If there is an optionWidget, the derived paintop itself must support settings, * and it's up to the factory to do that. */ class KRITAIMAGE_EXPORT KisPaintOpFactory : public QObject { Q_OBJECT public: enum PaintopVisibility { AUTO, ALWAYS, NEVER }; /** * @param whiteListedCompositeOps list of compositeops that don't work with this paintop */ KisPaintOpFactory(const QStringList & whiteListedCompositeOps = QStringList()); ~KisPaintOpFactory() override {} static QString categoryStable(); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND virtual void preinitializePaintOpIfNeeded(const KisPaintOpSettingsSP settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ /** * Create a KisPaintOp with the given settings and painter. * @param settings the settings associated with the input device * @param painter the painter used to draw * @param node the node used to draw * @param image the image used to draw */ virtual KisPaintOp * createOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) = 0; virtual QString id() const = 0; virtual QString name() const = 0; virtual QString category() const = 0; + /** + * @return all the resources linked to \p settings. The resources are first tried to be loaded + * from \p resourcesInterface, and, if it fails, loaded from the embedded data. + */ + virtual QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) = 0; + /** * List of usually hidden compositeops that are useful for this paintop. */ QStringList whiteListedCompositeOps() const; /** * @brief icon * @return the icon to represent this paintop. */ virtual QIcon icon(); /** * Create and return an settings object for this paintop. */ - virtual KisPaintOpSettingsSP createSettings() = 0; + virtual KisPaintOpSettingsSP createSettings(KisResourcesInterfaceSP resourcesInterface) = 0; /** * create a widget that can display paintop settings */ virtual KisPaintOpConfigWidget* createConfigWidget(QWidget* parent) = 0; /** * Set the priority of this paintop, as it is shown in the UI; lower number means * it will be show more to the front of the list. * @param newPriority the priority */ void setPriority(int newPriority); int priority() const; - /** - * This method will be called by the registry after all paintops are loaded - * Overwrite to let the factory do something. - */ - virtual void processAfterLoading() {} - private: QStringList m_whiteListedCompositeOps; int m_priority; PaintopVisibility m_visibility; }; #endif diff --git a/libs/image/brushengine/kis_paintop_preset.cpp b/libs/image/brushengine/kis_paintop_preset.cpp index 04e6401cb5..b43631813b 100644 --- a/libs/image/brushengine/kis_paintop_preset.cpp +++ b/libs/image/brushengine/kis_paintop_preset.cpp @@ -1,427 +1,452 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2009 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. 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 "kis_paintop_registry.h" #include "kis_painter.h" #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_paintop_settings_update_proxy.h" #include +#include #include struct Q_DECL_HIDDEN KisPaintOpPreset::Private { Private(KisPaintOpPreset *q) { proxyParent = new ProxyParent(q); } QPointer proxyParent{0}; KisPaintOpSettingsSP settings {0}; QPointer updateProxy {0}; }; KisPaintOpPreset::KisPaintOpPreset() : KoResource(QString()) , d(new Private(this)) { } KisPaintOpPreset::KisPaintOpPreset(const QString & fileName) : KoResource(fileName) , d(new Private(this)) { } KisPaintOpPreset::~KisPaintOpPreset() { delete d; } KisPaintOpPreset::KisPaintOpPreset(const KisPaintOpPreset &rhs) : KoResource(rhs) , d(new Private(this)) { *this = rhs; } KisPaintOpPreset &KisPaintOpPreset::operator=(const KisPaintOpPreset &rhs) { if (*this != rhs) { if (rhs.settings()) { setSettings(rhs.settings()); // the settings are cloned inside! } setDirty(isDirty()); // only valid if we could clone the settings setValid(rhs.settings()); setPaintOp(rhs.paintOp()); setName(rhs.name()); setImage(rhs.image()); settings()->setUpdateProxy(rhs.updateProxy()); } return *this; } KoResourceSP KisPaintOpPreset::clone() const { return KoResourceSP(new KisPaintOpPreset(*this)); } void KisPaintOpPreset::setPaintOp(const KoID & paintOp) { Q_ASSERT(d->settings); d->settings->setProperty("paintop", paintOp.id()); } KoID KisPaintOpPreset::paintOp() const { Q_ASSERT(d->settings); return KoID(d->settings->getString("paintop")); } void KisPaintOpPreset::setOptionsWidget(KisPaintOpConfigWidget* widget) { if (d->settings) { d->settings->setOptionsWidget(widget); if (widget) { widget->setConfigurationSafe(d->settings); } } } void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings) { Q_ASSERT(settings); Q_ASSERT(!settings->getString("paintop", QString()).isEmpty()); KisResourceDirtyStateSaver dirtyStateSaver(this); Q_UNUSED(dirtyStateSaver); KisPaintOpConfigWidget *oldOptionsWidget = 0; if (d->settings) { oldOptionsWidget = d->settings->optionsWidget(); d->settings->setOptionsWidget(0); d->settings->setUpdateProxy(updateProxy()); d->settings = 0; } if (settings) { d->settings = settings->clone(); d->settings->setUpdateProxy(updateProxy()); if (oldOptionsWidget) { oldOptionsWidget->setConfigurationSafe(d->settings); d->settings->setOptionsWidget(oldOptionsWidget); } } setValid(d->settings); if (d->updateProxy) { d->updateProxy->notifyUniformPropertiesChanged(); d->updateProxy->notifySettingsChanged(); } } KisPaintOpSettingsSP KisPaintOpPreset::settings() const { Q_ASSERT(d->settings); Q_ASSERT(!d->settings->getString("paintop", QString()).isEmpty()); return d->settings; } -bool KisPaintOpPreset::load() +bool KisPaintOpPreset::load(KisResourcesInterfaceSP resourcesInterface) { qDebug() << "Load preset " << filename(); setValid(false); if (filename().isEmpty()) { return false; } QIODevice *dev = 0; QByteArray ba; if (filename().startsWith("bundle://")) { QString bn = filename().mid(9); int pos = bn.lastIndexOf(":"); QString fn = bn.right(bn.size() - pos - 1); bn = bn.left(pos); QScopedPointer resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << bn; return false; } if (resourceStore->isOpen()) resourceStore->close(); if (!resourceStore->open(fn)) { warnKrita << "Could not open preset" << fn << "in bundle" << bn; return false; } ba = resourceStore->device()->readAll(); dev = new QBuffer(&ba); resourceStore->close(); } else { dev = new QFile(filename()); if (dev->size() == 0) { delete dev; return false; } if (!dev->open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); delete dev; return false; } } - bool res = loadFromDevice(dev); + bool res = loadFromDevice(dev, resourcesInterface); delete dev; setValid(res); setDirty(false); return res; } -bool KisPaintOpPreset::loadFromDevice(QIODevice *dev) +bool KisPaintOpPreset::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { QImageReader reader(dev, "PNG"); QString version = reader.text("version"); QString preset = reader.text("preset"); dbgImage << version; if (version != "2.2") { return false; } QImage img; if (!reader.read(&img)) { dbgImage << "Fail to decode PNG"; return false; } //Workaround for broken presets //Presets was saved with nested cdata section preset.replace(""); preset.replace("]]>", ""); QDomDocument doc; if (!doc.setContent(preset)) { return false; } - fromXML(doc.documentElement()); + fromXML(doc.documentElement(), resourcesInterface); if (!d->settings) { return false; } setValid(true); setImage(img); return true; } bool KisPaintOpPreset::save() { - - if (filename().isEmpty()) - return false; - - QString paintopid = d->settings->getString("paintop", QString()); - + const QString paintopid = d->settings->getString("paintop", QString()); if (paintopid.isEmpty()) return false; - QFile f(filename()); - f.open(QFile::WriteOnly); - - return saveToDevice(&f); + return KoResource::save(); } void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const { QString paintopid = d->settings->getString("paintop", QString()); elt.setAttribute("paintopid", paintopid); elt.setAttribute("name", name()); // sanitize the settings bool hasTexture = d->settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, d->settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { d->settings->removeProperty(key); } } } d->settings->toXML(doc, elt); } -void KisPaintOpPreset::fromXML(const QDomElement& presetElt) +void KisPaintOpPreset::fromXML(const QDomElement& presetElt, KisResourcesInterfaceSP resourcesInterface) { setName(presetElt.attribute("name")); QString paintopid = presetElt.attribute("paintopid"); if (paintopid.isEmpty()) { dbgImage << "No paintopid attribute"; setValid(false); return; } if (KisPaintOpRegistry::instance()->get(paintopid) == 0) { dbgImage << "No paintop " << paintopid; setValid(false); return; } KoID id(paintopid, QString()); - KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(id); + KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(id, resourcesInterface); if (!settings) { setValid(false); warnKrita << "Could not load settings for preset" << paintopid; return; } settings->fromXML(presetElt); // sanitize the settings bool hasTexture = settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { settings->removeProperty(key); } } } setSettings(settings); } bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const { QImageWriter writer(dev, "PNG"); QDomDocument doc; QDomElement root = doc.createElement("Preset"); toXML(doc, root); doc.appendChild(root); writer.setText("version", "2.2"); writer.setText("preset", doc.toString()); QImage img; if (image().isNull()) { img = QImage(1, 1, QImage::Format_RGB32); } else { img = image(); } const_cast(this)->setDirty(false); KoResource::saveToDevice(dev); return writer.write(img); } QPointer KisPaintOpPreset::updateProxy() const { if (!d->updateProxy) { d->updateProxy = new KisPaintopSettingsUpdateProxy(d->proxyParent); } return d->updateProxy; } QPointer KisPaintOpPreset::updateProxyNoCreate() const { return d->updateProxy; } QList KisPaintOpPreset::uniformProperties() { return d->settings->uniformProperties(d->settings); } bool KisPaintOpPreset::hasMaskingPreset() const { return d->settings && d->settings->hasMaskingSettings(); } KisPaintOpPresetSP KisPaintOpPreset::createMaskingPreset() const { KisPaintOpPresetSP result; if (d->settings && d->settings->hasMaskingSettings()) { result.reset(new KisPaintOpPreset()); result->setSettings(d->settings->createMaskingSettings()); if (!result->valid()) { result.clear(); } } return result; } +KisResourcesInterfaceSP KisPaintOpPreset::resourcesInterface() const +{ + return d->settings ? d->settings->resourcesInterface() : nullptr; +} + +void KisPaintOpPreset::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(d->settings); + d->settings->setResourcesInterface(resourcesInterface); +} + +void KisPaintOpPreset::createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(d->settings); + + QList resources; + + KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(paintOp().id()); + KIS_SAFE_ASSERT_RECOVER_RETURN(f); + resources << f->prepareResources(d->settings, globalResourcesInterface); + + if (hasMaskingPreset()) { + KisPaintOpPresetSP maskingPreset = createMaskingPreset(); + + KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(maskingPreset->paintOp().id()); + KIS_SAFE_ASSERT_RECOVER_RETURN(f); + resources << f->prepareResources(maskingPreset->settings(), globalResourcesInterface); + } + + d->settings->setResourcesInterface(QSharedPointer::create(resources)); +} + KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPresetSP preset) : m_updateProxy(preset->updateProxyNoCreate()) { if (m_updateProxy) { m_updateProxy->postponeSettingsChanges(); } } KisPaintOpPreset::UpdatedPostponer::~UpdatedPostponer() { if (m_updateProxy) { m_updateProxy->unpostponeSettingsChanges(); } } diff --git a/libs/image/brushengine/kis_paintop_preset.h b/libs/image/brushengine/kis_paintop_preset.h index bec45b41dd..514480156a 100644 --- a/libs/image/brushengine/kis_paintop_preset.h +++ b/libs/image/brushengine/kis_paintop_preset.h @@ -1,151 +1,171 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_PRESET_H #define KIS_PAINTOP_PRESET_H #include #include #include "KoID.h" #include "kis_types.h" #include "kis_shared.h" #include "kritaimage_export.h" #include #include class KisPaintOpConfigWidget; class ProxyParent : public QObject { Q_OBJECT public: ProxyParent(KisPaintOpPreset *preset) { m_preset = preset; } KisPaintOpPreset *m_preset {0}; }; /** * A KisPaintOpPreset contains a particular set of settings * associated with a paintop, like brush, paintopsettings. * A new property in this class is to make it dirty. That means the * user can now temporarily save any tweaks in the Preset throughout * the session. The Dirty Preset setting/unsetting is handled by KisPaintOpPresetSettings */ class KRITAIMAGE_EXPORT KisPaintOpPreset : public KoResource { public: /** * @brief The UpdatedPostponer class * @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges() */ class KRITAIMAGE_EXPORT UpdatedPostponer{ public: UpdatedPostponer(KisPaintOpPresetSP preset); ~UpdatedPostponer(); private: QPointer m_updateProxy; }; public: KisPaintOpPreset(); KisPaintOpPreset(const QString& filename); ~KisPaintOpPreset() override; KisPaintOpPreset(const KisPaintOpPreset &rhs); KisPaintOpPreset &operator=(const KisPaintOpPreset &rhs); KoResourceSP clone() const override; /// set the id of the paintop plugin void setPaintOp(const KoID & paintOp); /// return the id of the paintop plugin KoID paintOp() const; /// replace the current settings object with the specified settings void setSettings(KisPaintOpSettingsSP settings); void setOriginalSettings(KisPaintOpSettingsSP originalSettings); /// return the settings that define this paintop preset KisPaintOpSettingsSP settings() const; KisPaintOpSettingsSP originalSettings() const; - bool load() override; - bool loadFromDevice(QIODevice *dev) override; + bool load(KisResourcesInterfaceSP resourcesInterface) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::PaintOpPresets, ""); } void toXML(QDomDocument& doc, QDomElement& elt) const; - void fromXML(const QDomElement& elt); + void fromXML(const QDomElement& elt, KisResourcesInterfaceSP resourcesInterface); bool removable() const { return true; } QString defaultFileExtension() const override { return ".kpp"; } void setOptionsWidget(KisPaintOpConfigWidget *widget); QPointer updateProxy() const; QPointer updateProxyNoCreate() const; QList uniformProperties(); /** * @return true if this preset demands a secondary masked brush running * alongside it */ bool hasMaskingPreset() const; /** * @return a newly created preset of the masked brush that should be run * alongside the current brush */ KisPaintOpPresetSP createMaskingPreset() const; + /** + * @return resource interface that is used by KisPaintOpSettings object for + * loading linked resources + */ + KisResourcesInterfaceSP resourcesInterface() const; + + /** + * Set resource interface that will be used by KisPaintOpSettings object for + * loading linked resources + */ + void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface); + + /** + * Loads all the linked resources either from \p globalResourcesInterface or + * from embedded data. The preset first tried to fetch the linked resource + * from the global source, and only if it fails, tries to load it from the + * embedded data. + */ + void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface); + private: struct Private; Private * const d; }; Q_DECLARE_METATYPE(KisPaintOpPresetSP) #endif diff --git a/libs/image/brushengine/kis_paintop_registry.cc b/libs/image/brushengine/kis_paintop_registry.cc index 9c461f4401..c1a74c45fa 100644 --- a/libs/image/brushengine/kis_paintop_registry.cc +++ b/libs/image/brushengine/kis_paintop_registry.cc @@ -1,175 +1,161 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_paintop_registry.h" #include #include #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include "kis_debug.h" #include "kis_layer.h" #include "kis_image.h" #include "kis_paintop_config_widget.h" Q_GLOBAL_STATIC(KisPaintOpRegistry, s_registryInstance) KisPaintOpRegistry::KisPaintOpRegistry() { } KisPaintOpRegistry::~KisPaintOpRegistry() { Q_FOREACH (const QString & id, keys()) { delete get(id); } dbgRegistry << "Deleting KisPaintOpRegistry"; } void KisPaintOpRegistry::initRegistry() { KoPluginLoader::instance()->load("Krita/Paintop", "(Type == 'Service') and ([X-Krita-Version] == 28)"); - - QStringList toBeRemoved; - - Q_FOREACH (const QString & id, keys()) { - KisPaintOpFactory *factory = get(id); - if (!factory->createSettings()) { - toBeRemoved << id; - } else { - factory->processAfterLoading(); - } - } - Q_FOREACH (const QString & id, toBeRemoved) { - remove(id); - } } KisPaintOpRegistry* KisPaintOpRegistry::instance() { if (!s_registryInstance.exists()) { dbgRegistry << "initializing KisPaintOpRegistry"; s_registryInstance->initRegistry(); } return s_registryInstance; } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND void KisPaintOpRegistry::preinitializePaintOpIfNeeded(const KisPaintOpPresetSP preset) { if (!preset) return; KisPaintOpFactory *f = value(preset->paintOp().id()); f->preinitializePaintOpIfNeeded(preset->settings()); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisPaintOp * KisPaintOpRegistry::paintOp(const QString & id, const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) const { if (painter == 0) { warnKrita << " KisPaintOpRegistry::paintOp painter is null"; return 0; } Q_ASSERT(settings); KisPaintOpFactory* f = value(id); if (f) { KisPaintOp * op = f->createOp(settings, painter, node, image); if (op) { return op; } } warnKrita << "Could not create paintop for factory" << id << "with settings" << settings; return 0; } KisPaintOp * KisPaintOpRegistry::paintOp(const KisPaintOpPresetSP preset, KisPainter * painter, KisNodeSP node, KisImageSP image) const { Q_ASSERT(preset); Q_ASSERT(painter); if (!preset) return 0; return paintOp(preset->paintOp().id(), preset->settings(), painter, node, image); } -KisPaintOpSettingsSP KisPaintOpRegistry::createSettings(const KoID& id) const +KisPaintOpSettingsSP KisPaintOpRegistry::createSettings(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const { KisPaintOpFactory *f = value(id.id()); Q_ASSERT(f); if (f) { - KisPaintOpSettingsSP settings = f->createSettings(); + KisPaintOpSettingsSP settings = f->createSettings(resourcesInterface); settings->setProperty("paintop", id.id()); return settings; } return 0; } -KisPaintOpPresetSP KisPaintOpRegistry::defaultPreset(const KoID& id) const +KisPaintOpPresetSP KisPaintOpRegistry::defaultPreset(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const { - KisPaintOpSettingsSP s = createSettings(id); + KisPaintOpSettingsSP s = createSettings(id, resourcesInterface); if (s.isNull()) { return KisPaintOpPresetSP(); } KisPaintOpPresetSP preset(new KisPaintOpPreset()); preset->setName(i18n("default")); preset->setSettings(s); preset->setPaintOp(id); Q_ASSERT(!preset->paintOp().id().isEmpty()); preset->setValid(true); return preset; } QIcon KisPaintOpRegistry::icon(const KoID &id) const { KisPaintOpFactory* f = value(id.id()); if (!f) { dbgRegistry << "No paintop" << id.id() << ""; QPixmap p = QPixmap(22, 22); p.fill(Qt::transparent); return QIcon(p); } return f->icon(); } QList KisPaintOpRegistry::listKeys() const { QList answer; Q_FOREACH (const QString & key, keys()) { answer.append(KoID(key, get(key)->name())); } return answer; } diff --git a/libs/image/brushengine/kis_paintop_registry.h b/libs/image/brushengine/kis_paintop_registry.h index 52feaf7e91..e4591d0570 100644 --- a/libs/image/brushengine/kis_paintop_registry.h +++ b/libs/image/brushengine/kis_paintop_registry.h @@ -1,105 +1,105 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_PAINTOP_REGISTRY_H_ #define KIS_PAINTOP_REGISTRY_H_ #include #include "KoGenericRegistry.h" #include "kis_paintop.h" #include #include "kis_types.h" #include #include #include #include class KisPaintOp; class KisPainter; /** * Manages the loading and creating of all paintop plugins. */ class KRITAIMAGE_EXPORT KisPaintOpRegistry : public QObject, public KoGenericRegistry { Q_OBJECT public: KisPaintOpRegistry(); ~KisPaintOpRegistry() override; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND void preinitializePaintOpIfNeeded(const KisPaintOpPresetSP preset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ /** * Create and return a paintop based on the given preset. A preset defines * a paintop, a settings object and possible a brush tip. */ KisPaintOp* paintOp(const KisPaintOpPresetSP preset, KisPainter * painter, KisNodeSP node, KisImageSP image) const; /** * Create and return an (abstracted) configuration widget * for using the specified paintop with the specified input device, * with the specified parent as widget parent. Returns 0 if there * are no settings available for the given device. */ - KisPaintOpSettingsSP createSettings(const KoID& id) const; + KisPaintOpSettingsSP createSettings(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const; /** * @return a default preset for the given paintop. */ - KisPaintOpPresetSP defaultPreset(const KoID& id) const; + KisPaintOpPresetSP defaultPreset(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const; // Get the icon to show in the user interface QIcon icon(const KoID & id) const; /** * This function return a list of all the keys in KoID format by using the name() method * on the objects stored in the registry. */ QList listKeys() const; public: static KisPaintOpRegistry* instance(); private: KisPaintOpRegistry(const KisPaintOpRegistry&); KisPaintOpRegistry operator=(const KisPaintOpRegistry&); void initRegistry(); // So the settings can get a paintop to render their sample image friend class KisPaintOpSettings; /** * Return a newly created paintop. You are responsible for deleting */ KisPaintOp * paintOp(const QString& id, const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) const; }; #endif // KIS_PAINTOP_REGISTRY_H_ diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp index 69d2639a57..7a91f82d9b 100644 --- a/libs/image/brushengine/kis_paintop_settings.cpp +++ b/libs/image/brushengine/kis_paintop_settings.cpp @@ -1,502 +1,515 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Lukáš Tvrdý * Copyright (c) 2014 Mohit Goyal * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_paintop_preset.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_paintop_registry.h" #include "kis_timing_information.h" #include #include "kis_paintop_config_widget.h" #include #include "kis_paintop_settings_update_proxy.h" #include #include #include #include #include #include "KisPaintopSettingsIds.h" #include "kis_algebra_2d.h" struct Q_DECL_HIDDEN KisPaintOpSettings::Private { Private() : disableDirtyNotifications(false) {} QPointer settingsWidget; QString modelName; QPointer updateProxy; QList uniformProperties; + KisResourcesInterfaceSP resourcesInterface = 0; bool disableDirtyNotifications; class DirtyNotificationsLocker { public: DirtyNotificationsLocker(KisPaintOpSettings::Private *d) : m_d(d), m_oldNotificationsState(d->disableDirtyNotifications) { m_d->disableDirtyNotifications = true; } ~DirtyNotificationsLocker() { m_d->disableDirtyNotifications = m_oldNotificationsState; } private: KisPaintOpSettings::Private *m_d; bool m_oldNotificationsState; Q_DISABLE_COPY(DirtyNotificationsLocker) }; }; -KisPaintOpSettings::KisPaintOpSettings() +KisPaintOpSettings::KisPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) : d(new Private) { d->updateProxy = 0; + d->resourcesInterface = resourcesInterface; } KisPaintOpSettings::~KisPaintOpSettings() { } KisPaintOpSettings::KisPaintOpSettings(const KisPaintOpSettings &rhs) : KisPropertiesConfiguration(rhs) , d(new Private) { d->settingsWidget = 0; d->updateProxy = rhs.updateProxy(); d->modelName = rhs.modelName(); + d->resourcesInterface = rhs.d->resourcesInterface; } void KisPaintOpSettings::setOptionsWidget(KisPaintOpConfigWidget* widget) { d->settingsWidget = widget; } void KisPaintOpSettings::setUpdateProxy(const QPointer proxy) { d->updateProxy = proxy; } QPointer KisPaintOpSettings::updateProxy() const { return d->updateProxy; } bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) { Q_UNUSED(modifiers); Q_UNUSED(currentNode); setRandomOffset(paintInformation); return true; // ignore the event by default } bool KisPaintOpSettings::mouseReleaseEvent() { return true; // ignore the event by default } void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation) { bool disableDirtyBefore = d->disableDirtyNotifications; d->disableDirtyNotifications = true; if (getBool("Texture/Pattern/Enabled")) { if (getBool("Texture/Pattern/isRandomOffsetX")) { setProperty("Texture/Pattern/OffsetX", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX"))); } if (getBool("Texture/Pattern/isRandomOffsetY")) { setProperty("Texture/Pattern/OffsetY", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY"))); } } d->disableDirtyNotifications = disableDirtyBefore; } bool KisPaintOpSettings::hasMaskingSettings() const { return getBool(KisPaintOpUtils::MaskingBrushEnabledTag, false); } KisPaintOpSettingsSP KisPaintOpSettings::createMaskingSettings() const { if (!hasMaskingSettings()) return KisPaintOpSettingsSP(); const KoID pixelBrushId(KisPaintOpUtils::MaskingBrushPaintOpId, QString()); - KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->createSettings(pixelBrushId); + KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->createSettings(pixelBrushId, resourcesInterface()); this->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSettings); const bool useMasterSize = this->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true); if (useMasterSize) { const qreal masterSizeCoeff = getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0); maskingSettings->setPaintOpSize(masterSizeCoeff * paintOpSize()); } return maskingSettings; } QString KisPaintOpSettings::maskingBrushCompositeOp() const { return getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT); } +KisResourcesInterfaceSP KisPaintOpSettings::resourcesInterface() const +{ + return d->resourcesInterface; +} + +void KisPaintOpSettings::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface) +{ + d->resourcesInterface = resourcesInterface; +} + KisPaintOpSettingsSP KisPaintOpSettings::clone() const { QString paintopID = getString("paintop"); if (paintopID.isEmpty()) return 0; - KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(KoID(paintopID)); + KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(KoID(paintopID), resourcesInterface()); QMapIterator i(getProperties()); while (i.hasNext()) { i.next(); settings->setProperty(i.key(), QVariant(i.value())); } settings->setUpdateProxy(this->updateProxy()); return settings; } void KisPaintOpSettings::resetSettings(const QStringList &preserveProperties) { QStringList allKeys = preserveProperties; allKeys << "paintop"; QHash preserved; Q_FOREACH (const QString &key, allKeys) { if (hasProperty(key)) { preserved[key] = getProperty(key); } } clearProperties(); for (auto it = preserved.constBegin(); it != preserved.constEnd(); ++it) { setProperty(it.key(), it.value()); } } void KisPaintOpSettings::activate() { } void KisPaintOpSettings::setPaintOpOpacity(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("OpacityValue", value); } void KisPaintOpSettings::setPaintOpFlow(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("FlowValue", value); } void KisPaintOpSettings::setPaintOpCompositeOp(const QString &value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("CompositeOp", value); } qreal KisPaintOpSettings::paintOpOpacity() { KisLockedPropertiesProxySP proxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this); return proxy->getDouble("OpacityValue", 1.0); } qreal KisPaintOpSettings::paintOpFlow() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getDouble("FlowValue", 1.0); } QString KisPaintOpSettings::paintOpCompositeOp() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getString("CompositeOp", COMPOSITE_OVER); } void KisPaintOpSettings::setEraserMode(bool value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("EraserMode", value); } bool KisPaintOpSettings::eraserMode() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getBool("EraserMode", false); } QString KisPaintOpSettings::effectivePaintOpCompositeOp() { return !eraserMode() ? paintOpCompositeOp() : COMPOSITE_ERASE; } qreal KisPaintOpSettings::savedEraserSize() const { return getDouble("SavedEraserSize", 0.0); } void KisPaintOpSettings::setSavedEraserSize(qreal value) { setProperty("SavedEraserSize", value); setPropertyNotSaved("SavedEraserSize"); } qreal KisPaintOpSettings::savedBrushSize() const { return getDouble("SavedBrushSize", 0.0); } void KisPaintOpSettings::setSavedBrushSize(qreal value) { setProperty("SavedBrushSize", value); setPropertyNotSaved("SavedBrushSize"); } qreal KisPaintOpSettings::savedEraserOpacity() const { return getDouble("SavedEraserOpacity", 0.0); } void KisPaintOpSettings::setSavedEraserOpacity(qreal value) { setProperty("SavedEraserOpacity", value); setPropertyNotSaved("SavedEraserOpacity"); } qreal KisPaintOpSettings::savedBrushOpacity() const { return getDouble("SavedBrushOpacity", 0.0); } void KisPaintOpSettings::setSavedBrushOpacity(qreal value) { setProperty("SavedBrushOpacity", value); setPropertyNotSaved("SavedBrushOpacity"); } QString KisPaintOpSettings::modelName() const { return d->modelName; } void KisPaintOpSettings::setModelName(const QString & modelName) { d->modelName = modelName; } KisPaintOpConfigWidget* KisPaintOpSettings::optionsWidget() const { if (d->settingsWidget.isNull()) return 0; return d->settingsWidget.data(); } bool KisPaintOpSettings::isValid() const { return true; } bool KisPaintOpSettings::isLoadable() { return isValid(); } QString KisPaintOpSettings::indirectPaintingCompositeOp() const { return COMPOSITE_ALPHA_DARKEN; } bool KisPaintOpSettings::isAirbrushing() const { return getBool(AIRBRUSH_ENABLED, false); } qreal KisPaintOpSettings::airbrushInterval() const { qreal rate = getDouble(AIRBRUSH_RATE, 1.0); if (rate == 0.0) { return LONG_TIME; } else { return 1000.0 / rate; } } bool KisPaintOpSettings::useSpacingUpdates() const { return getBool(SPACING_USE_UPDATES, false); } bool KisPaintOpSettings::needsAsynchronousUpdates() const { return false; } QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { path = ellipseOutline(10, 10, 1.0, 0); if (mode.showTiltDecoration) { path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0)); } path.translate(KisAlgebra2D::alignForZoom(info.pos(), alignForZoom)); } return path; } QPainterPath KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation) { QPainterPath path; QRectF ellipse(0, 0, width * scale, height * scale); ellipse.translate(-ellipse.center()); path.addEllipse(ellipse); QTransform m; m.reset(); m.rotate(rotation); path = m.map(path); return path; } QPainterPath KisPaintOpSettings::makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal maxLength, qreal angle) { if (maxLength == 0.0) maxLength = 50.0; maxLength = qMax(maxLength, 50.0); qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true)); qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0); QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle); guideLine.translate(start); QPainterPath ret; ret.moveTo(guideLine.p1()); ret.lineTo(guideLine.p2()); guideLine.setAngle(baseAngle - angle); ret.lineTo(guideLine.p2()); ret.lineTo(guideLine.p1()); return ret; } void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value) { if (value != KisPropertiesConfiguration::getProperty(name) && !d->disableDirtyNotifications) { if (d->updateProxy) { d->updateProxy->setDirty(true); } } KisPropertiesConfiguration::setProperty(name, value); onPropertyChanged(); } void KisPaintOpSettings::onPropertyChanged() { if (d->updateProxy) { d->updateProxy->notifySettingsChanged(); } } bool KisPaintOpSettings::isLodUserAllowed(const KisPropertiesConfigurationSP config) { return config->getBool("lodUserAllowed", true); } void KisPaintOpSettings::setLodUserAllowed(KisPropertiesConfigurationSP config, bool value) { config->setProperty("lodUserAllowed", value); } bool KisPaintOpSettings::lodSizeThresholdSupported() const { return true; } qreal KisPaintOpSettings::lodSizeThreshold() const { return getDouble("lodSizeThreshold", 100.0); } void KisPaintOpSettings::setLodSizeThreshold(qreal value) { setProperty("lodSizeThreshold", value); } #include "kis_standard_uniform_properties_factory.h" QList KisPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(d->uniformProperties); if (props.isEmpty()) { using namespace KisStandardUniformPropertiesFactory; props.append(createProperty(opacity, settings, d->updateProxy)); props.append(createProperty(size, settings, d->updateProxy)); props.append(createProperty(flow, settings, d->updateProxy)); d->uniformProperties = listStrongToWeak(props); } return props; } diff --git a/libs/image/brushengine/kis_paintop_settings.h b/libs/image/brushengine/kis_paintop_settings.h index 5e3298bd77..ac5637acb4 100644 --- a/libs/image/brushengine/kis_paintop_settings.h +++ b/libs/image/brushengine/kis_paintop_settings.h @@ -1,349 +1,361 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_PAINTOP_SETTINGS_H_ #define KIS_PAINTOP_SETTINGS_H_ #include "kis_types.h" #include "kritaimage_export.h" #include #include #include "kis_properties_configuration.h" #include #include - - class KisPaintOpConfigWidget; class KisPaintopSettingsUpdateProxy; +class KisResourcesInterface; +using KisResourcesInterfaceSP = QSharedPointer; + + /** * Configuration property used to control whether airbrushing is enabled. */ const QString AIRBRUSH_ENABLED = "PaintOpSettings/isAirbrushing"; /** * Configuration property used to control airbrushing rate. The value should be in dabs per second. */ const QString AIRBRUSH_RATE = "PaintOpSettings/rate"; /** * Configuration property used to control whether airbrushing is configured to ignore distance-based * spacing. */ const QString AIRBRUSH_IGNORE_SPACING = "PaintOpSettings/ignoreSpacing"; /** * Configuration property used to control whether the spacing settings can be updated between * painted dabs. */ const QString SPACING_USE_UPDATES = "PaintOpSettings/updateSpacingBetweenDabs"; /** * This class is used to cache the settings for a paintop * between two creations. There is one KisPaintOpSettings per input device (mouse, tablet, * etc...). * * The settings may be stored in a preset. Note that if your * paintop's settings subclass has data that is not stored as a property, that data is not * saved and restored. * * The object also contains a pointer to its parent KisPaintOpPreset object.This is to control the DirtyPreset * property of KisPaintOpPreset. Whenever the settings are changed/modified from the original -- the preset is * set to dirty. */ class KRITAIMAGE_EXPORT KisPaintOpSettings : public KisPropertiesConfiguration { public: - KisPaintOpSettings(); + KisPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisPaintOpSettings() override; KisPaintOpSettings(const KisPaintOpSettings &rhs); /** * */ void setOptionsWidget(KisPaintOpConfigWidget* widget); /** * This function is called by a tool when the mouse is pressed. It's useful if * the paintop needs mouse interaction for instance in the case of the clone op. * If the tool is supposed to ignore the event, the paint op should return true * and if the tool is supposed to use the event, return false. * See kis_tool_freehand:tryPickByPaintOp() */ virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode); /** * This function is called by a tool when the mouse is released. It's useful if * the paintop needs mouse interaction for instance in the case of the clone op. * If the tool is supposed to ignore the event, the paint op should return true * and if the tool is supposed to use the event, return false. */ virtual bool mouseReleaseEvent(); /** * Clone the current settings object. Override this if your settings instance doesn't * store everything as properties. */ virtual KisPaintOpSettingsSP clone() const; /** * Removes all the settings from the object while keeping the paintop id, * which is loaded to the object by the factory */ void resetSettings(const QStringList &preserveProperties = QStringList()); /** * @return the node the paintop is working on. */ KisNodeSP node() const; /** * Call this function when the paint op is selected or the tool is activated */ virtual void activate(); /** * XXX: Remove this after 2.0, when the paint operation (incremental/non incremental) will * be completely handled in the paintop, not in the tool. This is a filthy hack to move * the option to the right place, at least. * @return true if we paint incrementally, false if we paint like Photoshop. By default, paintops * do not support non-incremental. */ virtual bool paintIncremental() { return true; } /** * @return the composite op it to which the indirect painting device * should be initialized to. This is used by clone op to reset * the composite op to COMPOSITE_COPY */ virtual QString indirectPaintingCompositeOp() const; /** * Whether this paintop wants to deposit paint even when not moving, i.e. the tool needs to * activate its timer. If this is true, painting updates need to be generated at regular * intervals even in the absence of input device events, e.g. when the cursor is not moving. * * The default implementation checks the property AIRBRUSH_ENABLED, defaulting to false if the * property is not found. This should be suitable for most paintops. */ virtual bool isAirbrushing() const; /** * Indicates the minimum time interval that might be needed between airbrush dabs, in * milliseconds. A lower value means painting updates need to happen more frequently. This value * should be ignored if isAirbrushing() is false. * * The default implementation uses the property AIRBRUSH_RATE, defaulting to an interval of * one second if the property is not found. This should be suitable for most paintops. */ virtual qreal airbrushInterval() const; /** * Indicates whether this configuration allows spacing information to be updated between painted * dabs during a stroke. */ virtual bool useSpacingUpdates() const; /** * Indicates if the tool should call paintOp->doAsynchronousUpdate() inbetween * paintAt() calls to do the asynchronous rendering */ virtual bool needsAsynchronousUpdates() const; /** * This structure defines the current mode for painting an outline. */ struct OutlineMode { bool isVisible = false; bool forceCircle = false; bool showTiltDecoration = false; bool forceFullSize = false; }; /** * Returns the brush outline in pixel coordinates. Tool is responsible for conversion into view coordinates. * Outline mode has to be passed to the paintop which builds the outline as some paintops have to paint outline * always like clone paintop indicating the duplicate position */ virtual QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom); /** * Helpers for drawing the brush outline */ static QPainterPath ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation); /** * Helper for drawing a triangle representing the tilt of the stylus. * * @param start is the offset from the brush's outline's bounding box * @param lengthScale is used for deciding the size of the triangle. * Brush diameter or width are common choices for this. * @param angle is the angle between the two sides of the triangle. */ static QPainterPath makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal lengthScale, qreal angle); /** * Set paintop opacity directly in the properties */ void setPaintOpOpacity(qreal value); /** * Set paintop flow directly in the properties */ void setPaintOpFlow(qreal value); /** * Set paintop composite mode directly in the properties */ void setPaintOpCompositeOp(const QString &value); /** * @return opacity saved in the properties */ qreal paintOpOpacity(); /** * @return flow saved in the properties */ qreal paintOpFlow(); /** * @return composite mode saved in the properties */ QString paintOpCompositeOp(); /** * Set paintop size directly in the properties */ virtual void setPaintOpSize(qreal value) = 0; /** * @return size saved in the properties */ virtual qreal paintOpSize() const = 0; void setEraserMode(bool value); bool eraserMode(); qreal savedEraserSize() const; void setSavedEraserSize(qreal value); qreal savedBrushSize() const; void setSavedBrushSize(qreal value); qreal savedEraserOpacity() const; void setSavedEraserOpacity(qreal value); qreal savedBrushOpacity() const; void setSavedBrushOpacity(qreal value); QString effectivePaintOpCompositeOp(); void setUpdateProxy(const QPointer proxy); QPointer updateProxy() const; /** * @return filename of the 3D brush model, empty if no brush is set */ virtual QString modelName() const; /** * Set filename of 3D brush model. By default no brush is set */ void setModelName(const QString & modelName); /// Check if the settings are valid, setting might be invalid through missing brushes etc /// Overwrite if the settings of a paintop can be invalid /// @return state of the settings, default implementation is true virtual bool isValid() const; /// Check if the settings are loadable, that might the case if we can fallback to something /// Overwrite if the settings can do some kind of fallback /// @return loadable state of the settings, by default implementation return the same as isValid() virtual bool isLoadable(); /** * Overrides the method in KisPropertiesCofiguration to allow * onPropertyChanged() callback */ void setProperty(const QString & name, const QVariant & value) override; virtual QList uniformProperties(KisPaintOpSettingsSP settings); static bool isLodUserAllowed(const KisPropertiesConfigurationSP config); static void setLodUserAllowed(KisPropertiesConfigurationSP config, bool value); virtual bool lodSizeThresholdSupported() const; qreal lodSizeThreshold() const; void setLodSizeThreshold(qreal value); /** * @return the option widget of the paintop (can be 0 is no option widgets is set) */ KisPaintOpConfigWidget* optionsWidget() const; /** * This function is called to set random offsets to the brush whenever the mouse is clicked. It is * specific to when the pattern option is set. * */ virtual void setRandomOffset(const KisPaintInformation &paintInformation); /** * @return true if this preset demands a secondary masked brush running * alongside it */ bool hasMaskingSettings() const; /** * @return a newly created settings object representing a preset of the masking * brush that should be run alongside the current brush */ KisPaintOpSettingsSP createMaskingSettings() const; /** * @return a composite op id of the masked brush rendering algorithm. * * Please take into account that the brush itself always paints in alpha- * darken mode, but the final result is combined with this composite op. */ QString maskingBrushCompositeOp() const; + /** + * @return resource interface that is used for loading linked resources + */ + KisResourcesInterfaceSP resourcesInterface() const; + + /** + * Set resource interface that will be used for loading linked resources + */ + void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface); + protected: /** * The callback is called every time when a property changes */ virtual void onPropertyChanged(); private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index ac4f14c502..afa75a4738 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1652 +1,1645 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 // qFromLittleEndian #include #include #include #include #include #include #include #include #include "KisSwatch.h" #include "KoColorSet.h" #include "KoColorSet_p.h" namespace { /** * readAllLinesSafe() reads all the lines in the byte array * using the automated UTF8 and CR/LF transformations. That * might be necessary for opening GPL palettes created on Linux * in Windows environment. */ QStringList readAllLinesSafe(QByteArray *data) { QStringList lines; QBuffer buffer(data); buffer.open(QBuffer::ReadOnly); QTextStream stream(&buffer); QString line; while (stream.readLineInto(&line)) { lines << line; } return lines; } } const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); const QString KoColorSet::KPL_VERSION_ATTR = "version"; const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; const QString KoColorSet::KPL_GROUP_TAG = "Group"; const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; const int MAXIMUM_ALLOWED_COLUMNS = 4096; KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { QFileInfo f(filename); setIsEditable(f.isWritable()); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : KoResource(rhs) , d(new Private(this)) { *this = rhs; } KoColorSet::~KoColorSet() { } KoColorSet &KoColorSet::operator=(const KoColorSet &rhs) { if (*this != rhs) { d->paletteType = rhs.d->paletteType; d->data = rhs.d->data; d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; d->isGlobal = rhs.d->isGlobal; d->isEditable = rhs.d->isEditable; } return *this; } KoResourceSP KoColorSet::clone() const { return KoResourceSP(new KoColorSet(*this)); } -bool KoColorSet::load() +bool KoColorSet::load(KisResourcesInterfaceSP resourcesInterface) { - QFile file(filename()); - if (file.size() == 0) return false; - if (!file.open(QIODevice::ReadOnly)) { - warnPigment << "Can't open file " << filename(); - return false; - } - bool res = loadFromDevice(&file); - file.close(); - if (!QFileInfo(filename()).isWritable()) { - setIsEditable(false); - } - return res; + const bool result = KoResource::load(resourcesInterface); + setIsEditable(result && QFileInfo(filename()).isWritable()); + return result; } -bool KoColorSet::loadFromDevice(QIODevice *dev) +bool KoColorSet::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); Q_ASSERT(d->data.size() != 0); return d->init(); } bool KoColorSet::save() { if (d->isGlobal) { return KoResource::save(); } else { return true; // palette is not global, but still indicate that it's saved } } bool KoColorSet::saveToDevice(QIODevice *dev) const { bool res; switch(d->paletteType) { case GPL: res = d->saveGpl(dev); break; default: res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); } return res; } QByteArray KoColorSet::toByteArray() const { QBuffer s; s.open(QIODevice::WriteOnly); if (!saveToDevice(&s)) { warnPigment << "saving palette failed:" << name(); return QByteArray(); } s.close(); s.open(QIODevice::ReadOnly); QByteArray res = s.readAll(); s.close(); return res; } -bool KoColorSet::fromByteArray(QByteArray &data) +bool KoColorSet::fromByteArray(QByteArray &data, KisResourcesInterfaceSP resourcesInterface) { QBuffer buf(&data); buf.open(QIODevice::ReadOnly); - return loadFromDevice(&buf); + return loadFromDevice(&buf, resourcesInterface); } KoColorSet::PaletteType KoColorSet::paletteType() const { return d->paletteType; } void KoColorSet::setPaletteType(PaletteType paletteType) { d->paletteType = paletteType; QString suffix; switch(d->paletteType) { case GPL: suffix = ".gpl"; break; case ACT: suffix = ".act"; break; case RIFF_PAL: case PSP_PAL: suffix = ".pal"; break; case ACO: suffix = ".aco"; break; case XML: suffix = ".xml"; break; case KPL: suffix = ".kpl"; break; case SBZ: suffix = ".sbz"; break; default: suffix = defaultFileExtension(); } QStringList fileName = filename().split("."); fileName.last() = suffix.replace(".", ""); setFilename(fileName.join(".")); } quint32 KoColorSet::colorCount() const { int colorCount = 0; for (KisSwatchGroup &g : d->groups.values()) { colorCount += g.colorCount(); } return colorCount; } void KoColorSet::add(const KisSwatch &c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); d->groupNames.append(GLOBAL_GROUP_NAME); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { for (const QString &groupName : getGroupNames()) { if (d->groups.contains(groupName)) { if ((int)y < d->groups[groupName].rowCount()) { return d->groups[groupName].getEntry(x, y); } else { y -= d->groups[groupName].rowCount(); } } } return KisSwatch(); } KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { KisSwatch e; const KisSwatchGroup &sourceGroup = groupName == QString() ? d->global() : d->groups[groupName]; if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); } return e; } QStringList KoColorSet::getGroupNames() const { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) { if (!d->groups.contains(oldGroupName)) { return false; } if (oldGroupName == newGroupName) { return true; } d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } void KoColorSet::setColumnCount(int columns) { d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() { return d->comment; } void KoColorSet::setComment(QString comment) { d->comment = comment; } bool KoColorSet::addGroup(const QString &groupName) { if (d->groups.contains(groupName) || getGroupNames().contains(groupName)) { return false; } d->groupNames.append(groupName); d->groups[groupName] = KisSwatchGroup(); d->groups[groupName].setName(groupName); return true; } bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore) { if (!d->groupNames.contains(groupName) || d->groupNames.contains(groupNameInsertBefore)==false) { return false; } if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { d->groupNames.removeAt(d->groupNames.indexOf(groupName)); int index = d->groupNames.indexOf(groupNameInsertBefore); d->groupNames.insert(index, groupName); } return true; } bool KoColorSet::removeGroup(const QString &groupName, bool keepColors) { if (!d->groups.contains(groupName)) { return false; } if (groupName == GLOBAL_GROUP_NAME) { return false; } if (keepColors) { // put all colors directly below global int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, info.column, info.row + startingRow); } } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } QString KoColorSet::defaultFileExtension() const { return QString(".kpl"); } int KoColorSet::rowCount() const { int res = 0; for (const QString &name : getGroupNames()) { res += d->groups[name].rowCount(); } return res; } KisSwatchGroup *KoColorSet::getGroup(const QString &name) { if (!d->groups.contains(name)) { return 0; } return &(d->groups[name]); } KisSwatchGroup *KoColorSet::getGlobalGroup() { return getGroup(GLOBAL_GROUP_NAME); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } bool KoColorSet::isEditable() const { return d->isEditable; } void KoColorSet::setIsEditable(bool isEditable) { d->isEditable = isEditable; } KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { KisSwatchGroup::SwatchInfo res; quint8 highestPercentage = 0; quint8 testPercentage = 0; for (const QString &groupName : getGroupNames()) { KisSwatchGroup *group = getGroup(groupName); for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { KoColor color = currInfo.swatch.color(); if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { color.convertTo(compare.colorSpace()); } else if (compare.colorSpace() != color.colorSpace()) { compare.convertTo(color.colorSpace()); } testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); if (testPercentage > highestPercentage) { highestPercentage = testPercentage; res = currInfo; } } } return res; } /********************************KoColorSet::Private**************************/ KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) { groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); } KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) { QFileInfo fi(fileName); // .pal if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { return KoColorSet::RIFF_PAL; } // .gpl else if (ba.startsWith("GIMP Palette")) { return KoColorSet::GPL; } // .pal else if (ba.startsWith("JASC-PAL")) { return KoColorSet::PSP_PAL; } else if (fi.suffix().toLower() == "aco") { return KoColorSet::ACO; } else if (fi.suffix().toLower() == "act") { return KoColorSet::ACT; } else if (fi.suffix().toLower() == "xml") { return KoColorSet::XML; } else if (fi.suffix().toLower() == "kpl") { return KoColorSet::KPL; } else if (fi.suffix().toLower() == "sbz") { return KoColorSet::SBZ; } return KoColorSet::UNKNOWN; } void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); QStringRef colorName = colorProperties.value("NAME"); colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString()); // RGB or CMYK? if (colorProperties.hasAttribute("RGB")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB"); KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8()); QStringRef colorValue = colorProperties.value("RGB"); if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid rgb8 color (malformed): " + colorValue); return; } else { bool rgbOk; quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16); if (!rgbOk) { xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue); return; } quint8 r = rgb >> 16 & 0xff; quint8 g = rgb >> 8 & 0xff; quint8 b = rgb & 0xff; dbgPigment << "Color parsed: "<< r << g << b; currentColor.data()[0] = r; currentColor.data()[1] = g; currentColor.data()[2] = b; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else if (colorProperties.hasAttribute("CMYK")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK"); KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString())); QStringRef colorValue = colorProperties.value("CMYK"); if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid cmyk color (malformed): " % colorValue); return; } else { bool cmykOk; quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits if (!cmykOk) { xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue); return; } quint8 c = cmyk >> 24 & 0xff; quint8 m = cmyk >> 16 & 0xff; quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; dbgPigment << "Color parsed: "<< c << m << y << k; currentColor.data()[0] = c; currentColor.data()[1] = m; currentColor.data()[2] = y; currentColor.data()[3] = k; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else { xml->raiseError("Unknown color space for color " + colorEntry.name()); } } bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) { //1. Get name QXmlStreamAttributes paletteProperties = xml->attributes(); QStringRef paletteName = paletteProperties.value("Name"); dbgPigment << "Processed name of palette:" << paletteName; set->setName(paletteName.toString()); //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them while(xml->readNextStartElement()) { QStringRef currentElement = xml->name(); if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { scribusParseColor(set, xml); } else { xml->skipCurrentElement(); } } if(xml->hasError()) { return false; } return true; } quint16 KoColorSet::Private::readShort(QIODevice *io) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; return qFromBigEndian(val); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); if (colorSet->filename().isNull()) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; return false; } if (data.isNull()) { QFile file(colorSet->filename()); if (file.size() == 0) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); data = file.readAll(); file.close(); } bool res = false; paletteType = detectFormat(colorSet->filename(), data); switch(paletteType) { case GPL: res = loadGpl(); break; case ACT: res = loadAct(); break; case RIFF_PAL: res = loadRiff(); break; case PSP_PAL: res = loadPsp(); break; case ACO: res = loadAco(); break; case XML: res = loadXml(); break; case KPL: res = loadKpl(); break; case SBZ: res = loadSbz(); break; default: res = false; } colorSet->setValid(res); QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { QColor c = info.swatch.color().toQColor(); gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); } colorSet->setImage(img); colorSet->setValid(res); data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { Q_ASSERT(dev->isOpen()); Q_ASSERT(dev->isWritable()); QTextStream stream(dev); stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; /* * Qt doesn't provide an interface to get a const reference to a QHash, that is * the underlying data structure of groups. Therefore, directly use * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); QColor c = entry.color().toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name().isEmpty()) stream << "Untitled\n"; else stream << entry.name() << "\n"; } } return true; } bool KoColorSet::Private::loadGpl() { if (data.isEmpty() || data.isNull() || data.length() < 50) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } quint32 index = 0; QStringList lines = readAllLinesSafe(&data); if (lines.size() < 3) { warnPigment << "Not enough lines in palette file: " << colorSet->filename(); return false; } QString columnsText; qint32 r, g, b; KisSwatch e; // Read name if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); index = 2; // Read columns int columns = 0; if (lines[index].toLower().contains("columns")) { columnsText = lines[index].split(":")[1].trimmed(); columns = columnsText.toInt(); if (columns > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << columns << ") in GIMP Palette file " << colorSet->filename() << " - using maximum number of allowed columns instead"; global().setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { global().setColumnCount(columns); } index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() < 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); for (int i = 0; i != 3; i++) { a.pop_front(); } QString name = a.join(" "); e.setName(name.isEmpty() || name == "Untitled" ? i18n("Untitled") : name); global().addEntry(e); } } int rowCount = global().colorCount()/ global().columnCount(); if (global().colorCount() % global().columnCount()>0) { rowCount ++; } global().setRowCount(rowCount); return true; } bool KoColorSet::Private::loadAct() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; for (int i = 0; i < data.size(); i += 3) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); global().addEntry(e); } return true; } bool KoColorSet::Private::loadRiff() { // https://worms2d.info/Palette_file QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; RiffHeader header; memcpy(&header, data.constData(), sizeof(RiffHeader)); header.colorcount = qFromBigEndian(header.colorcount); for (int i = sizeof(RiffHeader); (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); i += 4) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadPsp() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; qint32 r, g, b; QStringList l = readAllLinesSafe(&data); if (l.size() < 4) return false; if (l[0] != "JASC-PAL") return false; if (l[1] != "0100") return false; int entries = l[2].toInt(); for (int i = 0; i < entries; ++i) { QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() != 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadKpl() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) { return false; } if (store->hasFile("profiles.xml")) { if (!store->open("profiles.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); while (!c.isNull()) { QString name = c.attribute(KPL_PALETTE_NAME_ATTR); QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); } } c = c.nextSiblingElement(); } } { if (!store->open("colorset.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); int desiredColumnCount; QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); desiredColumnCount = e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt(); if (desiredColumnCount > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << desiredColumnCount << ") in KPL palette file " << colorSet->filename() << " - setting maximum allowed column count instead."; colorSet->setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { colorSet->setColumnCount(desiredColumnCount); } loadKplGroup(doc, e, colorSet->getGlobalGroup()); QDomElement g = e.firstChildElement(KPL_GROUP_TAG); while (!g.isNull()) { QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); colorSet->addGroup(groupName); loadKplGroup(doc, g, colorSet->getGroup(groupName)); g = g.nextSiblingElement(KPL_GROUP_TAG); } } buf.close(); return true; } bool KoColorSet::Private::loadAco() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); version = readShort(&buf); numColors = readShort(&buf); } const quint16 quint16_MAX = 65535; for (int i = 0; i < numColors && !buf.atEnd(); ++i) { quint16 colorSpace = readShort(&buf); quint16 ch1 = readShort(&buf); quint16 ch2 = readShort(&buf); quint16 ch3 = readShort(&buf); quint16 ch4 = readShort(&buf); bool skip = false; if (colorSpace == 0) { // RGB const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb)); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 1) { // HSB QColor qc; qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0); KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16()); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 2) { // CMYK KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = quint16_MAX - ch1; reinterpret_cast(c.data())[1] = quint16_MAX - ch2; reinterpret_cast(c.data())[2] = quint16_MAX - ch3; reinterpret_cast(c.data())[3] = quint16_MAX - ch4; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 7) { // LAB KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16()); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 8) { // GRAY KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = ch1 * (quint16_MAX / 10000); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else { warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped. Q_UNUSED(v2); quint16 size = readShort(&buf) -1; //then comes the length if (size>0) { QByteArray ba = buf.read(size*2); if (ba.size() == size*2) { QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); } } v2 = readShort(&buf); //end marker also needs to be skipped. Q_UNUSED(v2); } if (!skip) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } bool KoColorSet::Private::loadSbz() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip)); if (!store || store->bad()) return false; if (store->hasFile("swatchbook.xml")) { // Try opening... if (!store->open("swatchbook.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; QDomDocument doc; int errorLine, errorColumn; QString errorMessage; bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); if (!status) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } QDomElement e = doc.documentElement(); // SwatchBook // Start reading properties... QDomElement metadata = e.firstChildElement("metadata"); if (e.isNull()) { warnPigment << "Palette metadata not found"; return false; } QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; colorSet->setName(colorName); dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... QDomElement materials = e.firstChildElement("materials"); if (materials.isNull()) { warnPigment << "Materials (color definitions) not found"; return false; } // This one has lots of "color" elements QDomElement colorElement = materials.firstChildElement("color"); if (colorElement.isNull()) { warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")"; return false; } // Also read the swatch book... QDomElement book = e.firstChildElement("book"); if (book.isNull()) { warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")"; return false; } // Which has lots of "swatch"es (todo: support groups) QDomElement swatch = book.firstChildElement(); if (swatch.isNull()) { warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")"; return false; } // We'll store colors here, and as we process swatches // we'll add them to the palette QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); // inside contains id and name // one or more define the color QDomElement currentColorMetadata = colorElement.firstChildElement("metadata"); QDomNodeList currentColorValues = colorElement.elementsByTagName("values"); // Get color name QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title"); QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier"); // Is there an id? (we need that at the very least for identifying a color) if (colorId.text().isEmpty()) { warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } if (materialsBook.contains(colorId.text())) { warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } // Get a valid color name currentEntry.setId(colorId.text()); currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text()); // Get a valid color definition if (currentColorValues.isEmpty()) { warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")"; return false; } bool firstDefinition = false; const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); // Priority: Lab, otherwise the first definition found for(int j = 0; j < currentColorValues.size(); j++) { QDomNode colorValue = currentColorValues.at(j); QDomElement colorValueE = colorValue.toElement(); QString model = colorValueE.attribute("model", QString()); // sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1 // YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5 // Lab: L 0 -> 100 : ab -128 -> 127 // XYZ: 0 -> ~100 if (model == "Lab") { QStringList lab = colorValueE.text().split(" "); if (lab.length() != 3) { warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float l = lab.at(0).toFloat(&status); float a = lab.at(1).toFloat(&status); float b = lab.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = l; reinterpret_cast(c.data())[1] = a; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); firstDefinition = true; currentEntry.setColor(c); break; // Immediately add this one } else if (model == "sRGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb)); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "XYZ" && !firstDefinition) { QStringList xyz = colorValueE.text().split(" "); if (xyz.length() != 3) { warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float x = xyz.at(0).toFloat(&status); float y = xyz.at(1).toFloat(&status); float z = xyz.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = x; reinterpret_cast(c.data())[1] = y; reinterpret_cast(c.data())[2] = z; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } // The following color spaces admit an ICC profile (in SwatchBooker) else if (model == "CMYK" && !firstDefinition) { QStringList cmyk = colorValueE.text().split(" "); if (cmyk.length() != 4) { warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float c = cmyk.at(0).toFloat(&status); float m = cmyk.at(1).toFloat(&status); float y = cmyk.at(2).toFloat(&status); float k = cmyk.at(3).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor color(colorSpace); reinterpret_cast(color.data())[0] = c; reinterpret_cast(color.data())[1] = m; reinterpret_cast(color.data())[2] = y; reinterpret_cast(color.data())[3] = k; color.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(color); firstDefinition = true; } else if (model == "GRAY" && !firstDefinition) { QString gray = colorValueE.text(); float g = gray.toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = g; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "RGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else { warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")"; } } if (firstDefinition) { materialsBook.insert(currentEntry.id(), currentEntry); } else { warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")"; return false; } } // End colors // Now decide which ones will go into the palette for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) { QString type = swatch.tagName(); if (type.isEmpty() || type.isNull()) { warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } else if (type == "swatch") { QString id = swatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } } else if (type == "group") { QDomElement groupMetadata = swatch.firstChildElement("metadata"); if (groupMetadata.isNull()) { warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")"; return false; } QDomElement groupTitle = metadata.firstChildElement("dc:title"); if (groupTitle.isNull()) { warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")"; return false; } QString currentGroupName = groupTitle.text(); QDomElement groupSwatch = swatch.firstChildElement("swatch"); while(!groupSwatch.isNull()) { QString id = groupSwatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } groupSwatch = groupSwatch.nextSiblingElement("swatch"); } } } // End palette } buf.close(); return true; } bool KoColorSet::Private::loadXml() { bool res = false; QXmlStreamReader *xml = new QXmlStreamReader(data); if (xml->readNextStartElement()) { QStringRef paletteId = xml->name(); if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; res = loadScribusXmlPalette(colorSet, xml); } else { // Unknown XML format xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); } } // If there is any error (it should be returned through the stream) if (xml->hasError() || !res) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); return false; } else { dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); return true; } } bool KoColorSet::Private::saveKpl(QIODevice *dev) const { QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) return false; QSet colorSpaces; { QDomDocument doc; QDomElement root = doc.createElement(KPL_PALETTE_TAG); root.setAttribute(KPL_VERSION_ATTR, "1.0"); root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); root.setAttribute(KPL_PALETTE_READONLY_ATTR, (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } QDomElement gl = doc.createElement(KPL_GROUP_TAG); gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); root.appendChild(gl); saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); } doc.appendChild(root); if (!store->open("colorset.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } } QDomDocument doc; QDomElement profileElement = doc.createElement("Profiles"); for (const KoColorSpace *colorSpace : colorSpaces) { QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = colorSpace->profile()->rawData(); if (!store->write(profileRawData)) { return false; } if (!store->close()) { return false; } QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); profileElement.appendChild(el); } doc.appendChild(profileElement); if (!store->open("profiles.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } return store->finalize(); } void KoColorSet::Private::saveKplGroup(QDomDocument &doc, QDomElement &groupEle, const KisSwatchGroup *group, QSet &colorSetSet) const { groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); for (const SwatchInfoType &info : group->infoList()) { const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); // Only save non-builtin profiles.= if (!profile->fileName().isEmpty()) { colorSetSet.insert(info.swatch.color().colorSpace()); } QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); info.swatch.color().toXML(doc, swatchEle); QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); swatchEle.appendChild(positionEle); groupEle.appendChild(swatchEle); } } void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) { Q_UNUSED(doc); if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); } group->setColumnCount(colorSet->columnCount()); for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); !swatchEle.isNull(); swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); if (!positionEle.isNull()) { int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); if (columnNumber < 0 || columnNumber >= colorSet->columnCount() || rowNumber < 0 ) { warnPigment << "Swatch" << entry.name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } if (parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull() && group->colorCount() > 0 && group->columnCount() > 0 && (group->colorCount() / (group->columnCount()) + 1) < 20) { group->setRowCount((group->colorCount() / group->columnCount()) + 1); } } diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h index cec799a647..abff9f934a 100644 --- a/libs/pigment/resources/KoColorSet.h +++ b/libs/pigment/resources/KoColorSet.h @@ -1,226 +1,226 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOCOLORSET #define KOCOLORSET #include #include #include #include #include #include #include "KoColor.h" #include "KisSwatch.h" #include "KisSwatchGroup.h" /** * Also called palette. * Open Gimp, Photoshop or RIFF palette files. This is a straight port * from the Gimp. */ class KRITAPIGMENT_EXPORT KoColorSet :public KoResource { public: static const QString GLOBAL_GROUP_NAME; static const QString KPL_VERSION_ATTR; static const QString KPL_GROUP_ROW_COUNT_ATTR; static const QString KPL_PALETTE_COLUMN_COUNT_ATTR; static const QString KPL_PALETTE_NAME_ATTR; static const QString KPL_PALETTE_COMMENT_ATTR; static const QString KPL_PALETTE_FILENAME_ATTR; static const QString KPL_PALETTE_READONLY_ATTR; static const QString KPL_COLOR_MODEL_ID_ATTR; static const QString KPL_COLOR_DEPTH_ID_ATTR; static const QString KPL_GROUP_NAME_ATTR; static const QString KPL_SWATCH_ROW_ATTR; static const QString KPL_SWATCH_COL_ATTR; static const QString KPL_SWATCH_NAME_ATTR; static const QString KPL_SWATCH_SPOT_ATTR; static const QString KPL_SWATCH_ID_ATTR; static const QString KPL_SWATCH_BITDEPTH_ATTR; static const QString KPL_PALETTE_PROFILE_TAG; static const QString KPL_SWATCH_POS_TAG; static const QString KPL_SWATCH_TAG; static const QString KPL_GROUP_TAG; static const QString KPL_PALETTE_TAG; public: enum PaletteType { UNKNOWN = 0, GPL, // GIMP RIFF_PAL, // RIFF ACT, // Photoshop binary PSP_PAL, // PaintShop Pro ACO, // Photoshop Swatches XML, // XML palette (Scribus) KPL, // KoColor-based XML palette SBZ // SwatchBooker }; /** * Load a color set from a file. This can be a Gimp * palette, a RIFF palette, a Photoshop palette, * a Krita palette, * a Scribus palette or a SwatchBooker palette. */ explicit KoColorSet(const QString &filename = QString()); KoColorSet(const KoColorSet& rhs); ~KoColorSet() override; KoColorSet &operator=(const KoColorSet &rhs); KoResourceSP clone() const override; - bool load() override; - bool loadFromDevice(QIODevice *dev) override; + bool load(KisResourcesInterfaceSP resourcesInterface) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; QPair resourceType() const override { return QPair(ResourceType::Palettes, ""); } void setColumnCount(int columns); int columnCount() const; void setComment(QString comment); QString comment(); int rowCount() const; quint32 colorCount() const; PaletteType paletteType() const; void setPaletteType(PaletteType paletteType); /** * @brief isGlobal * A global color set is a set stored in the config directory * Such a color set would be opened every time Krita is launched. * * A non-global color set, on contrary, would be stored in a kra file, * and would only be opened when that file is opened by Krita. * @return @c true if the set is global */ bool isGlobal() const; void setIsGlobal(bool); bool isEditable() const; void setIsEditable(bool isEditable); QByteArray toByteArray() const; - bool fromByteArray(QByteArray &data); + bool fromByteArray(QByteArray &data, KisResourcesInterfaceSP resourcesInterface); /** * @brief Add a color to the palette. * @param c the swatch * @param groupName color to add the group to. If empty, it will be added to the unsorted. */ void add(const KisSwatch &, const QString &groupName = GLOBAL_GROUP_NAME); void setEntry(const KisSwatch &e, int x, int y, const QString &groupName = GLOBAL_GROUP_NAME); /** * @brief getColorGlobal * A function for getting a color based on a global index. Useful for iterating through all color entries. * @param x the global x index over the whole palette. * @param y the global y index over the whole palette. * @return the entry. */ KisSwatch getColorGlobal(quint32 x, quint32 y) const; /** * @brief getColorGroup * A function for getting the color from a specific group. * @param x the x index over the group. * @param y the y index over the group. * @param groupName the name of the group, will give unsorted when not defined. * @return the entry */ KisSwatch getColorGroup(quint32 x, quint32 y, QString groupName); /** * @brief getGroupNames * @return returns a list of group names, excluding the unsorted group. */ QStringList getGroupNames() const; /** * @brief getGroup * @param name * @return the group with the name given; global group if no parameter is given * null pointer if not found. */ KisSwatchGroup *getGroup(const QString &name); KisSwatchGroup *getGlobalGroup(); bool changeGroupName(const QString &oldGroupName, const QString &newGroupName); /** * @brief addGroup * Adds a new group. * @param groupName the name of the new group. When not specified, this will fail. * @return whether thegroup was made. */ bool addGroup(const QString &groupName); /** * @brief moveGroup * Move a group in the internal stringlist. * @param groupName the groupname to move. * @param groupNameInsertBefore the groupname to insert before. Empty means it will be added to the end. * @return */ bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore = GLOBAL_GROUP_NAME); /** * @brief removeGroup * Remove a group from the KoColorSet * @param groupName the name of the group you want to remove. * @param keepColors Whether you wish to keep the colorsetentries. These will be added to the unsorted. * @return whether it could find the group to remove. */ bool removeGroup(const QString &groupName, bool keepColors = true); void clear(); /** * @brief getIndexClosestColor * function that matches the color to all colors in the colorset, and returns the index * of the closest match. * @param compare the color you wish to compare. * @param useGivenColorSpace whether to use the color space of the color given * when the two colors' colorspaces don't match. Else it'll use the entry's colorspace. * @return returns the int of the closest match. */ KisSwatchGroup::SwatchInfo getClosestColorInfo(KoColor compare, bool useGivenColorSpace = true); private: class Private; const QScopedPointer d; }; typedef QSharedPointer KoColorSetSP; #endif // KOCOLORSET diff --git a/libs/pigment/resources/KoPattern.cpp b/libs/pigment/resources/KoPattern.cpp index adf13f8da3..239727a3d7 100644 --- a/libs/pigment/resources/KoPattern.cpp +++ b/libs/pigment/resources/KoPattern.cpp @@ -1,382 +1,384 @@ /* This file is part of the KDE project Copyright (c) 2000 Matthias Elter Copyright (c) 2004 Boudewijn Rempt This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 namespace { struct GimpPatternHeader { quint32 header_size; /* header_size = sizeof (PatternHeader) + brush name */ quint32 version; /* pattern file version # */ quint32 width; /* width of pattern */ quint32 height; /* height of pattern */ quint32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/ quint32 magic_number; /* GIMP brush magic number */ }; // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT' quint32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0)); } KoPattern::KoPattern(const QString& file) : KoResource(file) { } KoPattern::KoPattern(const QImage &image, const QString &name, const QString &folderName) : KoResource(QString()) { setPatternImage(image); setName(name); QFileInfo fileInfo(folderName + QDir::separator() + name + defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(folderName + QDir::separator() + name + QString::number(i) + defaultFileExtension()); i++; } setFilename(fileInfo.filePath()); } KoPattern::~KoPattern() { } KoPattern::KoPattern(const KoPattern &rhs) : KoResource(rhs) { *this = rhs; } KoPattern &KoPattern::operator=(const KoPattern &rhs) { if (*this != rhs) { m_pattern = rhs.m_pattern; m_md5 = rhs.m_md5; } return *this; } KoResourceSP KoPattern::clone() const { return KoResourceSP(new KoPattern(*this)); } bool KoPattern::loadPatFromDevice(QIODevice *dev) { QByteArray data = dev->readAll(); return init(data); } bool KoPattern::savePatToDevice(QIODevice* dev) const { // Header: header_size (24+name length),version,width,height,colordepth of brush,magic,name // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA // magic = "GPAT", as a single uint32, the docs are wrong here! // name is UTF-8 (\0-terminated! The docs say nothing about this!) // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!) // We only save RGBA at the moment // Version is 1 for now... GimpPatternHeader ph; QByteArray utf8Name = name().toUtf8(); char const* name = utf8Name.data(); int nameLength = qstrlen(name); ph.header_size = qToBigEndian((quint32)sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0 ph.version = qToBigEndian((quint32)1); ph.width = qToBigEndian((quint32)width()); ph.height = qToBigEndian((quint32)height()); ph.bytes = qToBigEndian((quint32)4); ph.magic_number = qToBigEndian((quint32)GimpPatternMagic); QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); int wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) return false; wrote = dev->write(name, nameLength + 1); // Trailing 0 apparently! if (wrote == -1) return false; int k = 0; bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); ++y) { for (qint32 x = 0; x < width(); ++x) { // RGBA only QRgb pixel = m_pattern.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } wrote = dev->write(bytes); if (wrote == -1) return false; KoResource::saveToDevice(dev); return true; } -bool KoPattern::loadFromDevice(QIODevice *dev) +bool KoPattern::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QString fileExtension; int index = filename().lastIndexOf('.'); if (index != -1) fileExtension = filename().mid(index + 1).toLower(); bool result; if (fileExtension == "pat") { result = loadPatFromDevice(dev); } else { QImage image; // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = dev->readAll(); QBuffer buffer(&data); result = image.load(&buffer, fileExtension.toUpper().toLatin1()); setPatternImage(image); } return result; } bool KoPattern::saveToDevice(QIODevice *dev) const { QString fileExtension; int index = filename().lastIndexOf('.'); if (index != -1) fileExtension = filename().mid(index + 1).toLower(); bool result = false; if (fileExtension == "pat") { result = savePatToDevice(dev); } else { result = m_pattern.save(dev, fileExtension.toUpper().toLatin1()); } return result && KoResource::saveToDevice(dev); } bool KoPattern::init(QByteArray& bytes) { int dataSize = bytes.size(); const char* data = bytes.constData(); // load Gimp patterns GimpPatternHeader bh; qint32 k; char* name; if ((int)sizeof(GimpPatternHeader) > dataSize) { return false; } memcpy(&bh, data, sizeof(GimpPatternHeader)); bh.header_size = qFromBigEndian(bh.header_size); bh.version = qFromBigEndian(bh.version); bh.width = qFromBigEndian(bh.width); bh.height = qFromBigEndian(bh.height); bh.bytes = qFromBigEndian(bh.bytes); bh.magic_number = qFromBigEndian(bh.magic_number); if ((int)bh.header_size > dataSize || bh.header_size == 0) { return false; } int size = bh.header_size - sizeof(GimpPatternHeader); name = new char[size]; memcpy(name, data + sizeof(GimpPatternHeader), size); if (name[size - 1]) { delete[] name; return false; } // size -1 so we don't add the end 0 to the QString... setName(QString::fromLatin1(name, size -1)); delete[] name; if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1 || bh.bytes == 3) { imageFormat = QImage::Format_RGB32; } else { imageFormat = QImage::Format_ARGB32; } QImage pattern = QImage(bh.width, bh.height, imageFormat); if (pattern.isNull()) { return false; } k = bh.header_size; if (bh.bytes == 1) { // Grayscale qint32 val; for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x, ++k) { if (k > dataSize) { qWarning() << "failed to load grayscale pattern" << filename(); return false; } val = data[k]; pixels[x] = qRgb(val, val, val); } } // It was grayscale, so make the pattern as small as possible // by converting it to Indexed8 pattern = pattern.convertToFormat(QImage::Format_Indexed8); } else if (bh.bytes == 2) { // Grayscale + A qint32 val; qint32 alpha; for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x, ++k) { if (k + 2 > dataSize) { qWarning() << "failed to load grayscale +_ alpha pattern" << filename(); return false; } val = data[k]; alpha = data[k++]; pixels[x] = qRgba(val, val, val, alpha); } } } else if (bh.bytes == 3) { // RGB without alpha for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x) { if (k + 3 > dataSize) { qWarning() << "failed to load RGB pattern" << filename(); return false; } pixels[x] = qRgb(data[k], data[k + 1], data[k + 2]); k += 3; } } } else if (bh.bytes == 4) { // Has alpha for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x) { if (k + 4 > dataSize) { qWarning() << "failed to load RGB + Alpha pattern" << filename(); return false; } pixels[x] = qRgba(data[k], data[k + 1], data[k + 2], data[k + 3]); k += 4; } } } else { return false; } if (pattern.isNull()) { return false; } setPatternImage(pattern); setValid(true); return true; } qint32 KoPattern::width() const { return m_pattern.width(); } qint32 KoPattern::height() const { return m_pattern.height(); } void KoPattern::setPatternImage(const QImage& image) { m_pattern = image; setImage(image); setValid(true); } QString KoPattern::defaultFileExtension() const { return QString(".pat"); } QImage KoPattern::pattern() const { return m_pattern; } diff --git a/libs/pigment/resources/KoPattern.h b/libs/pigment/resources/KoPattern.h index d68e14f10f..8add410954 100644 --- a/libs/pigment/resources/KoPattern.h +++ b/libs/pigment/resources/KoPattern.h @@ -1,89 +1,89 @@ /* Copyright (c) 2000 Matthias Elter This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOPATTERN_H #define KOPATTERN_H #include #include #include #include class KoPattern; typedef QSharedPointer KoPatternSP; /// Write API docs here class KRITAPIGMENT_EXPORT KoPattern : public KoResource { public: /** * Creates a new KoPattern object using @p filename. No file is opened * in the constructor, you have to call load. * * @param filename the file name to save and load from. */ explicit KoPattern(const QString &filename); KoPattern(const QImage &image, const QString &name, const QString &folderName); ~KoPattern() override; KoPattern(const KoPattern &rhs); KoPattern& operator=(const KoPattern& rhs); KoResourceSP clone() const; public: - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; bool loadPatFromDevice(QIODevice *dev); bool savePatToDevice(QIODevice* dev) const; qint32 width() const; qint32 height() const; QString defaultFileExtension() const override; QPair resourceType() const override { return QPair(ResourceType::Patterns, ""); } /** * @brief pattern the actual pattern image * @return a valid QImage. There are no guarantees to the image format. */ QImage pattern() const; private: bool init(QByteArray& data); void setPatternImage(const QImage& image); private: QImage m_pattern; mutable QByteArray m_md5; }; Q_DECLARE_METATYPE(KoPattern*) Q_DECLARE_METATYPE(QSharedPointer) #endif // KOPATTERN_H diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp index d74e3677a2..44546ec8bd 100644 --- a/libs/pigment/resources/KoSegmentGradient.cpp +++ b/libs/pigment/resources/KoSegmentGradient.cpp @@ -1,967 +1,968 @@ /* Copyright (c) 2000 Matthias Elter 2001 John Califf 2004 Boudewijn Rempt 2004 Adrian Page 2004, 2007 Sven Langkamp This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 "KoColorSpaceRegistry.h" #include "KoColorSpace.h" #include "KoMixColorsOp.h" #include #include #include KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::m_instance = 0; KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::m_instance = 0; KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0; KoSegmentGradient::KoSegmentGradient(const QString& file) : KoAbstractGradient(file) { } KoSegmentGradient::~KoSegmentGradient() { for (int i = 0; i < m_segments.count(); i++) { delete m_segments[i]; m_segments[i] = 0; } } KoSegmentGradient::KoSegmentGradient(const KoSegmentGradient &rhs) : KoAbstractGradient(rhs) { *this = rhs; } KoSegmentGradient &KoSegmentGradient::operator=(const KoSegmentGradient &rhs) { if (*this != rhs) { Q_FOREACH (KoGradientSegment *segment, rhs.m_segments) { pushSegment(new KoGradientSegment(*segment)); } } return *this; } KoResourceSP KoSegmentGradient::clone() const { return KoResourceSP(new KoSegmentGradient(*this)); } - -bool KoSegmentGradient::loadFromDevice(QIODevice *dev) +bool KoSegmentGradient::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QByteArray data = dev->readAll(); QTextStream fileContent(data, QIODevice::ReadOnly); fileContent.setAutoDetectUnicode(true); QString header = fileContent.readLine(); if (header != "GIMP Gradient") { return false; } QString nameDefinition = fileContent.readLine(); QString numSegmentsText; if (nameDefinition.startsWith("Name: ")) { QString nameText = nameDefinition.right(nameDefinition.length() - 6); setName(nameText); numSegmentsText = fileContent.readLine(); } else { // Older format without name. numSegmentsText = nameDefinition; } dbgPigment << "Loading gradient: " << name(); int numSegments; bool ok; numSegments = numSegmentsText.toInt(&ok); if (!ok || numSegments < 1) { return false; } dbgPigment << "Number of segments = " << numSegments; const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); for (int i = 0; i < numSegments; i++) { QString segmentText = fileContent.readLine(); QTextStream segmentFields(&segmentText); QStringList values = segmentText.split(' '); qreal leftOffset = values[0].toDouble(); qreal middleOffset = values[1].toDouble(); qreal rightOffset = values[2].toDouble(); qreal leftRed = values[3].toDouble(); qreal leftGreen = values[4].toDouble(); qreal leftBlue = values[5].toDouble(); qreal leftAlpha = values[6].toDouble(); qreal rightRed = values[7].toDouble(); qreal rightGreen = values[8].toDouble(); qreal rightBlue = values[9].toDouble(); qreal rightAlpha = values[10].toDouble(); int interpolationType = values[11].toInt(); int colorInterpolationType = values[12].toInt(); quint8 data[4]; data[2] = static_cast(leftRed * 255 + 0.5); data[1] = static_cast(leftGreen * 255 + 0.5); data[0] = static_cast(leftBlue * 255 + 0.5); data[3] = static_cast(leftAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor leftColor(data, rgbColorSpace); data[2] = static_cast(rightRed * 255 + 0.5); data[1] = static_cast(rightGreen * 255 + 0.5); data[0] = static_cast(rightBlue * 255 + 0.5); data[3] = static_cast(rightAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor rightColor(data, rgbColorSpace); KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, leftOffset, middleOffset, rightOffset, leftColor, rightColor); Q_CHECK_PTR(segment); if (!segment -> isValid()) { delete segment; return false; } m_segments.push_back(segment); } if (!m_segments.isEmpty()) { updatePreview(); setValid(true); return true; } else { return false; } } bool KoSegmentGradient::saveToDevice(QIODevice *dev) const { QTextStream fileContent(dev); fileContent << "GIMP Gradient\n"; fileContent << "Name: " << name() << "\n"; fileContent << m_segments.count() << "\n"; Q_FOREACH (KoGradientSegment* segment, m_segments) { fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " " << QString::number(segment->endOffset(), 'f') << " "; QColor startColor = segment->startColor().toQColor(); QColor endColor = segment->endColor().toQColor(); fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " " << QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " "; fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " " << QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " "; fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n"; } KoResource::saveToDevice(dev); return true; } KoGradientSegment *KoSegmentGradient::segmentAt(qreal t) const { if (t < 0.0) return 0; if (t > 1.0) return 0; if (m_segments.isEmpty()) return 0; for (QList::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) { if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) { return *it; } } return 0; } void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const { const KoGradientSegment *segment = segmentAt(t); Q_ASSERT(segment != 0); if (segment) { segment->colorAt(dst, t); } } QGradient* KoSegmentGradient::toQGradient() const { QGradient* gradient = new QLinearGradient(); QColor color; Q_FOREACH (KoGradientSegment* segment, m_segments) { segment->startColor().toQColor(&color); gradient->setColorAt(segment->startOffset() , color); segment->endColor().toQColor(&color); gradient->setColorAt(segment->endOffset() , color); } return gradient; } QString KoSegmentGradient::defaultFileExtension() const { return QString(".ggr"); } void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const { gradientElt.setAttribute("type", "segment"); Q_FOREACH(KoGradientSegment *segment, this->segments()) { QDomElement segmentElt = doc.createElement("segment"); QDomElement start = doc.createElement("start"); QDomElement end = doc.createElement("end"); segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset())); const KoColor startColor = segment->startColor(); segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF())); startColor.toXML(doc, start); segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset())); segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset())); const KoColor endColor = segment->endColor(); segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF())); endColor.toXML(doc, end); segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation())); segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation())); segmentElt.appendChild(start); segmentElt.appendChild(end); gradientElt.appendChild(segmentElt); } } KoSegmentGradient KoSegmentGradient::fromXML(const QDomElement &elt) { KoSegmentGradient gradient; QDomElement segmentElt = elt.firstChildElement("segment"); while (!segmentElt.isNull()) { int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0")); int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0")); double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0")); qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0")); qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0")); QDomElement start = segmentElt.firstChildElement("start"); QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id()); QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor(); left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0"))); QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id()); QDomElement end = segmentElt.firstChildElement("end"); QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor(); right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0"))); gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right); segmentElt = segmentElt.nextSiblingElement("segment"); } return gradient; } KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor) { m_interpolator = 0; switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } m_colorInterpolator = 0; switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } if (startOffset < DBL_EPSILON) { m_startOffset = 0; } else if (startOffset > 1 - DBL_EPSILON) { m_startOffset = 1; } else { m_startOffset = startOffset; } if (middleOffset < m_startOffset + DBL_EPSILON) { m_middleOffset = m_startOffset; } else if (middleOffset > 1 - DBL_EPSILON) { m_middleOffset = 1; } else { m_middleOffset = middleOffset; } if (endOffset < m_middleOffset + DBL_EPSILON) { m_endOffset = m_middleOffset; } else if (endOffset > 1 - DBL_EPSILON) { m_endOffset = 1; } else { m_endOffset = endOffset; } m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } m_startColor = startColor; m_endColor = endColor; } const KoColor& KoGradientSegment::startColor() const { return m_startColor; } const KoColor& KoGradientSegment::endColor() const { return m_endColor; } qreal KoGradientSegment::startOffset() const { return m_startOffset; } qreal KoGradientSegment::middleOffset() const { return m_middleOffset; } qreal KoGradientSegment::endOffset() const { return m_endOffset; } void KoGradientSegment::setStartOffset(qreal t) { m_startOffset = t; m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } void KoGradientSegment::setMiddleOffset(qreal t) { m_middleOffset = t; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } void KoGradientSegment::setEndOffset(qreal t) { m_endOffset = t; m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } int KoGradientSegment::interpolation() const { return m_interpolator->type(); } void KoGradientSegment::setInterpolation(int interpolationType) { switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } } int KoGradientSegment::colorInterpolation() const { return m_colorInterpolator->type(); } void KoGradientSegment::setColorInterpolation(int colorInterpolationType) { switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } } void KoGradientSegment::colorAt(KoColor& dst, qreal t) const { Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON); qreal segmentT; if (m_length < DBL_EPSILON) { segmentT = 0.5; } else { segmentT = (t - m_startOffset) / m_length; } qreal colorT = m_interpolator->valueAt(segmentT, m_middleT); m_colorInterpolator->colorAt(dst, colorT, m_startColor, m_endColor); } bool KoGradientSegment::isValid() const { if (m_interpolator == 0 || m_colorInterpolator == 0) return false; return true; } KoGradientSegment::RGBColorInterpolationStrategy::RGBColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new RGBColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& _start, const KoColor& _end) const { KoColor buffer(m_colorSpace); KoColor start(m_colorSpace); KoColor end(m_colorSpace); KoColor startDummy, endDummy; //hack to get a color space with the bitdepth of the gradients(8bit), but with the colour profile of the image// const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile()); //convert to the right colorspace for the start and end if we have our mixSpace. if (mixSpace){ startDummy = KoColor(_start, mixSpace); endDummy = KoColor(_end, mixSpace); } else { startDummy = _start; endDummy = _end; } start.fromKoColor(_start); end.fromKoColor(_end); const quint8 *colors[2]; colors[0] = startDummy.data(); colors[1] = endDummy.data(); qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - t) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; //check if our mixspace exists, it doesn't at startup. if (mixSpace){ if (*buffer.colorSpace() != *mixSpace) { buffer = KoColor(mixSpace); } mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } else { buffer = KoColor(m_colorSpace); m_colorSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } dst.fromKoColor(buffer); } KoGradientSegment::HSVCWColorInterpolationStrategy::HSVCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor ec; start.toQColor(&sc); end.toQColor(&ec); int s = static_cast(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (ec.value() - sc.value()) + 0.5); int h; if (ec.hue() < sc.hue()) { h = static_cast(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5); } else { h = static_cast(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: added an explicit cast. Is this correct? quint8 opacity = static_cast(sc.alpha() + t * (ec.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::HSVCCWColorInterpolationStrategy::HSVCCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor se; start.toQColor(&sc); end.toQColor(&se); int s = static_cast(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (se.value() - sc.value()) + 0.5); int h; if (sc.hue() < se.hue()) { h = static_cast(sc.hue() + t * (se.hue() - sc.hue()) + 0.5); } else { h = static_cast(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: Added an explicit static cast quint8 opacity = static_cast(sc.alpha() + t * (se.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new LinearInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::LinearInterpolationStrategy::calcValueAt(qreal t, qreal middle) { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (t <= middle) { if (middle < DBL_EPSILON) { value = 0; } else { value = (t / middle) * 0.5; } } else { if (middle > 1 - DBL_EPSILON) { value = 1; } else { value = ((t - middle) / (1 - middle)) * 0.5 + 0.5; } } return value; } qreal KoGradientSegment::LinearInterpolationStrategy::valueAt(qreal t, qreal middle) const { return calcValueAt(t, middle); } KoGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy() { m_logHalf = log(0.5); } KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new CurvedInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::CurvedInterpolationStrategy::valueAt(qreal t, qreal middle) const { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (middle < DBL_EPSILON) { middle = DBL_EPSILON; } value = pow(t, m_logHalf / log(middle)); return value; } KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SineInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SineInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0; return value; } KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereIncreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1; qreal value = sqrt(1 - lt * lt); return value; } KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereDecreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = 1 - sqrt(1 - lt * lt); return value; } void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right) { pushSegment(new KoGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, KoColor(left, colorSpace()), KoColor(right, colorSpace()))); } const QList KoSegmentGradient::getHandlePositions() const { QList handlePositions; handlePositions.push_back(m_segments[0]->startOffset()); for (int i = 0; i < m_segments.count(); i++) { handlePositions.push_back(m_segments[i]->endOffset()); } return handlePositions; } const QList KoSegmentGradient::getMiddleHandlePositions() const { QList middleHandlePositions; for (int i = 0; i < m_segments.count(); i++) { middleHandlePositions.push_back(m_segments[i]->middleOffset()); } return middleHandlePositions; } void KoSegmentGradient::moveSegmentStartOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it == m_segments.begin()) { segment->setStartOffset(0.0); return; } KoGradientSegment* previousSegment = (*(it - 1)); if (t > segment->startOffset()) { if (t > segment->middleOffset()) t = segment->middleOffset(); } else { if (t < previousSegment->middleOffset()) t = previousSegment->middleOffset(); } previousSegment->setEndOffset(t); segment->setStartOffset(t); } } void KoSegmentGradient::moveSegmentEndOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it + 1 == m_segments.end()) { segment->setEndOffset(1.0); return; } KoGradientSegment* followingSegment = (*(it + 1)); if (t < segment->endOffset()) { if (t < segment->middleOffset()) t = segment->middleOffset(); } else { if (t > followingSegment->middleOffset()) t = followingSegment->middleOffset(); } followingSegment->setStartOffset(t); segment->setEndOffset(t); } } void KoSegmentGradient::moveSegmentMiddleOffset(KoGradientSegment* segment, double t) { if (segment) { if (t > segment->endOffset()) segment->setMiddleOffset(segment->endOffset()); else if (t < segment->startOffset()) segment->setMiddleOffset(segment->startOffset()); else segment->setMiddleOffset(t); } } void KoSegmentGradient::splitSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { KoColor midleoffsetColor(segment->endColor().colorSpace()); segment->colorAt(midleoffsetColor, segment->middleOffset()); KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), segment ->startOffset(), (segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset(), segment->middleOffset(), segment->startColor(), midleoffsetColor); m_segments.insert(it, newSegment); segment->setStartColor(midleoffsetColor); segment->setStartOffset(segment->middleOffset()); segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset()); } } void KoSegmentGradient::duplicateSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length(); double center = segment->startOffset() + segment->length() / 2; KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), segment ->startOffset(), segment->length() / 2 * middlePostionPercentage + segment->startOffset(), center, segment->startColor(), segment->endColor()); m_segments.insert(it, newSegment); segment->setStartOffset(center); segment->setMiddleOffset(segment->length() * middlePostionPercentage + segment->startOffset()); } } void KoSegmentGradient::mirrorSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); KoColor tmpColor = segment->startColor(); segment->setStartColor(segment->endColor()); segment->setEndColor(tmpColor); segment->setMiddleOffset(segment->endOffset() - (segment->middleOffset() - segment->startOffset())); if (segment->interpolation() == INTERP_SPHERE_INCREASING) segment->setInterpolation(INTERP_SPHERE_DECREASING); else if (segment->interpolation() == INTERP_SPHERE_DECREASING) segment->setInterpolation(INTERP_SPHERE_INCREASING); if (segment->colorInterpolation() == COLOR_INTERP_HSV_CW) segment->setColorInterpolation(COLOR_INTERP_HSV_CCW); else if (segment->colorInterpolation() == COLOR_INTERP_HSV_CCW) segment->setColorInterpolation(COLOR_INTERP_HSV_CW); } KoGradientSegment* KoSegmentGradient::removeSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); if (m_segments.count() < 2) return 0; QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage; KoGradientSegment* nextSegment; if (it == m_segments.begin()) { nextSegment = (*(it + 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setStartOffset(segment->startOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } else { nextSegment = (*(it - 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setEndOffset(segment->endOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } delete segment; m_segments.erase(it); return nextSegment; } return 0; } bool KoSegmentGradient::removeSegmentPossible() const { if (m_segments.count() < 2) return false; return true; } const QList& KoSegmentGradient::segments() const { return m_segments; } diff --git a/libs/pigment/resources/KoSegmentGradient.h b/libs/pigment/resources/KoSegmentGradient.h index 299da35145..6d6d0657d2 100644 --- a/libs/pigment/resources/KoSegmentGradient.h +++ b/libs/pigment/resources/KoSegmentGradient.h @@ -1,436 +1,433 @@ /* Copyright (c) 2000 Matthias Elter 2004 Boudewijn Rempt 2004 Adrian Page 2004, 2007 Sven Langkamp 2017 Wolthera van Hövell tot Westerflier This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOSEGMENTGRADIENT_H #define KOSEGMENTGRADIENT_H #include #include #include #include #include "KoColor.h" #include enum { INTERP_LINEAR = 0, INTERP_CURVED, INTERP_SINE, INTERP_SPHERE_INCREASING, INTERP_SPHERE_DECREASING }; enum { COLOR_INTERP_RGB, COLOR_INTERP_HSV_CCW, COLOR_INTERP_HSV_CW }; /// Write API docs here class KRITAPIGMENT_EXPORT KoGradientSegment { public: KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor); // startOffset <= t <= endOffset void colorAt(KoColor&, qreal t) const; const KoColor& startColor() const; const KoColor& endColor() const; void setStartColor(const KoColor& color) { m_startColor = color; } void setEndColor(const KoColor& color) { m_endColor = color; } qreal startOffset() const; qreal middleOffset() const; qreal endOffset() const; void setStartOffset(qreal t); void setMiddleOffset(qreal t); void setEndOffset(qreal t); qreal length() { return m_length; } int interpolation() const; int colorInterpolation() const; void setInterpolation(int interpolationType); void setColorInterpolation(int colorInterpolationType); bool isValid() const; protected: class ColorInterpolationStrategy { public: ColorInterpolationStrategy() {} virtual ~ColorInterpolationStrategy() {} virtual void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const = 0; virtual int type() const = 0; }; class RGBColorInterpolationStrategy : public ColorInterpolationStrategy { public: static RGBColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_RGB; } private: RGBColorInterpolationStrategy(); static RGBColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class HSVCWColorInterpolationStrategy : public ColorInterpolationStrategy { public: static HSVCWColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_HSV_CW; } private: HSVCWColorInterpolationStrategy(); static HSVCWColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class HSVCCWColorInterpolationStrategy : public ColorInterpolationStrategy { public: static HSVCCWColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_HSV_CCW; } private: HSVCCWColorInterpolationStrategy(); static HSVCCWColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class InterpolationStrategy { public: InterpolationStrategy() {} virtual ~InterpolationStrategy() {} virtual qreal valueAt(qreal t, qreal middle) const = 0; virtual int type() const = 0; }; class LinearInterpolationStrategy : public InterpolationStrategy { public: static LinearInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_LINEAR; } // This does the actual calculation and is made // static as an optimization for the other // strategies that need this for their own calculation. static qreal calcValueAt(qreal t, qreal middle); private: LinearInterpolationStrategy() {} static LinearInterpolationStrategy *m_instance; }; class CurvedInterpolationStrategy : public InterpolationStrategy { public: static CurvedInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_CURVED; } private: CurvedInterpolationStrategy(); static CurvedInterpolationStrategy *m_instance; qreal m_logHalf; }; class SphereIncreasingInterpolationStrategy : public InterpolationStrategy { public: static SphereIncreasingInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SPHERE_INCREASING; } private: SphereIncreasingInterpolationStrategy() {} static SphereIncreasingInterpolationStrategy *m_instance; }; class SphereDecreasingInterpolationStrategy : public InterpolationStrategy { public: static SphereDecreasingInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SPHERE_DECREASING; } private: SphereDecreasingInterpolationStrategy() {} static SphereDecreasingInterpolationStrategy *m_instance; }; class SineInterpolationStrategy : public InterpolationStrategy { public: static SineInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SINE; } private: SineInterpolationStrategy() {} static SineInterpolationStrategy *m_instance; }; private: InterpolationStrategy *m_interpolator; ColorInterpolationStrategy *m_colorInterpolator; qreal m_startOffset; qreal m_middleOffset; qreal m_endOffset; qreal m_length; qreal m_middleT; KoColor m_startColor; KoColor m_endColor; }; /** * KoSegmentGradient stores a segment based gradients like Gimp gradients */ class KRITAPIGMENT_EXPORT KoSegmentGradient : public KoAbstractGradient { public: explicit KoSegmentGradient(const QString &file = QString()); ~KoSegmentGradient() override; KoSegmentGradient(const KoSegmentGradient &rhs); KoSegmentGradient &operator=(const KoSegmentGradient &rhs); KoResourceSP clone() const override; - /// reimplemented - bool loadFromDevice(QIODevice *dev) override; - - /// not implemented + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::Gradients, ResourceSubType::SegmentedGradients); } /// reimplemented void colorAt(KoColor& dst, qreal t) const override; /** * Returns the segment at a given position * @param t position inside the gradient, with 0 <= t <= 1 * @return the segment the position, 0 if no segment is found */ KoGradientSegment *segmentAt(qreal t) const; /// reimplemented QGradient* toQGradient() const override; /// reimplemented QString defaultFileExtension() const override; /** * @brief toXML * convert the gradient to xml. */ void toXML(QDomDocument& doc, QDomElement& gradientElt) const; /** * @brief fromXML * get a segment gradient from xml. * @return gradient */ static KoSegmentGradient fromXML(const QDomElement& elt); /** * a gradient colour picker can consist of one or more segments. * A segment has two end points - each colour in the gradient * colour picker represents a segment end point. * @param interpolation * @param colorInterpolation * @param startOffset * @param endOffset * @param middleOffset * @param left * @param right * @return void */ void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right); /** * gets a list of end points of the segments in the gradient * colour picker. If two colours, one segment then two end * points, and if three colours, then two segments with four * endpoints. * @return a list of double values */ const QList getHandlePositions() const; /** * gets a list of middle points of the segments in the gradient * colour picker. * @return a list of double values */ const QList getMiddleHandlePositions() const; /** * Moves the StartOffset of the specified segment to the * specified value and corrects the endoffset of the previous * segment. If the segment is the first Segment the startoffset * will be set to 0.0 . The offset will maximally be moved till * the middle of the current or the previous segment. This is * useful if someone clicks to move the handler for a segment, * to set the half the segment to the right and half the segment * to the left of the handler. * @param segment the segment for which to move the relative * offset within the gradient colour picker. * @param t the new startoff position for the segment * @return void */ void moveSegmentStartOffset(KoGradientSegment* segment, double t); /** * Moves the endoffset of the specified segment to the specified * value and corrects the startoffset of the following segment. * If the segment is the last segment the endoffset will be set * to 1.0 . The offset will maximally be moved till the middle * of the current or the following segment. This is useful if * someone moves the segment handler in the gradient colour * picker, and needs the segment to move with it. Sets the end * position of the segment to the correct new position. * @param segment the segment for which to move the relative * end position within the gradient colour picker. * @param t the new end position for the segment * @return void */ void moveSegmentEndOffset(KoGradientSegment* segment, double t); /** * moves the Middle of the specified segment to the specified * value. The offset will maximally be moved till the endoffset * or startoffset of the segment. This sets the middle of the * segment to the same position as the handler of the gradient * colour picker. * @param segment the segment for which to move the relative * middle position within the gradient colour picker. * @param t the new middle position for the segment * @return void */ void moveSegmentMiddleOffset(KoGradientSegment* segment, double t); /** * splits the specified segment into two equal parts * @param segment the segment to split * @return void */ void splitSegment(KoGradientSegment* segment); /** * duplicate the specified segment * @param segment the segment to duplicate * @return void */ void duplicateSegment(KoGradientSegment* segment); /** * create a segment horizontally reversed to the specified one. * @param segment the segment to reverse * @return void */ void mirrorSegment(KoGradientSegment* segment); /** * removes the specific segment from the gradient colour picker. * @param segment the segment to remove * @return the segment which will be at the place of the old * segment. 0 if the segment is not in the gradient or it is * not possible to remove the segment. */ KoGradientSegment* removeSegment(KoGradientSegment* segment); /** * checks if it's possible to remove a segment (at least two * segments in the gradient) * @return true if it's possible to remove an segment */ bool removeSegmentPossible() const; const QList& segments() const; protected: inline void pushSegment(KoGradientSegment* segment) { m_segments.push_back(segment); } QList m_segments; private: bool init(); }; typedef QSharedPointer KoSegmentGradientSP; #endif // KOSEGMENTGRADIENT_H diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp index 1fb071c065..2eb0ce26e7 100644 --- a/libs/pigment/resources/KoStopGradient.cpp +++ b/libs/pigment/resources/KoStopGradient.cpp @@ -1,597 +1,598 @@ /* Copyright (C) 2005 Tim Beaulen Copyright (C) 2007 Jan Hambrecht Copyright (c) 2007 Sven Langkamp This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 "KoColorSpaceRegistry.h" #include "KoMixColorsOp.h" #include "kis_dom_utils.h" #include #include KoStopGradient::KoStopGradient(const QString& filename) : KoAbstractGradient(filename) { } KoStopGradient::~KoStopGradient() { } KoStopGradient::KoStopGradient(const KoStopGradient &rhs) : KoAbstractGradient(rhs) { *this = rhs; } bool KoStopGradient::operator==(const KoStopGradient &rhs) const { return *colorSpace() == *rhs.colorSpace() && spread() == rhs.spread() && type() == rhs.type() && m_start == rhs.m_start && m_stop == rhs.m_stop && m_focalPoint == rhs.m_focalPoint && m_stops == rhs.m_stops; } KoStopGradient &KoStopGradient::operator=(const KoStopGradient &rhs) { if (*this != rhs) { m_stops = rhs.m_stops; m_start = rhs.m_start; m_stop = rhs.m_stop; m_focalPoint = rhs.m_focalPoint; } return *this; } KoResourceSP KoStopGradient::clone() const { return KoResourceSP(new KoStopGradient(*this)); } - -bool KoStopGradient::loadFromDevice(QIODevice *dev) +bool KoStopGradient::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QString strExt; const int result = filename().lastIndexOf('.'); if (result >= 0) { strExt = filename().mid(result).toLower(); } QByteArray ba = dev->readAll(); QBuffer buf(&ba); loadSvgGradient(&buf); if (m_stops.count() >= 2) { setValid(true); } updatePreview(); return true; } QGradient* KoStopGradient::toQGradient() const { QGradient* gradient; switch (type()) { case QGradient::LinearGradient: { gradient = new QLinearGradient(m_start, m_stop); break; } case QGradient::RadialGradient: { QPointF diff = m_stop - m_start; qreal radius = sqrt(diff.x() * diff.x() + diff.y() * diff.y()); gradient = new QRadialGradient(m_start, radius, m_focalPoint); break; } case QGradient::ConicalGradient: { qreal angle = atan2(m_start.y(), m_start.x()) * 180.0 / M_PI; if (angle < 0.0) angle += 360.0; gradient = new QConicalGradient(m_start, angle); break; } default: return 0; } QColor color; for (QList::const_iterator i = m_stops.begin(); i != m_stops.end(); ++i) { i->second.toQColor(&color); gradient->setColorAt(i->first , color); } gradient->setCoordinateMode(QGradient::ObjectBoundingMode); gradient->setSpread(this->spread()); return gradient; } bool KoStopGradient::stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop, qreal t) const { if (! m_stops.count()) return false; if (t <= m_stops.first().first || m_stops.count() == 1) { // we have only one stop or t is before the first stop leftStop = m_stops.first(); rightStop = KoGradientStop(-std::numeric_limits::infinity(), leftStop.second); return true; } else if (t >= m_stops.last().first) { // t is after the last stop rightStop = m_stops.last(); leftStop = KoGradientStop(std::numeric_limits::infinity(), rightStop.second); return true; } else { // we have at least two color stops // -> find the two stops which frame our t auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor()), [](const KoGradientStop &a, const KoGradientStop &b){ return a.first < b.first; }); leftStop = *(it - 1); rightStop = *(it); return true; } } void KoStopGradient::colorAt(KoColor& dst, qreal t) const { KoColor buffer; KoGradientStop leftStop, rightStop; if (!stopsAt(leftStop, rightStop, t)) return; const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile()); KoColor startDummy, endDummy; if (mixSpace){ startDummy = KoColor(leftStop.second, mixSpace); endDummy = KoColor(rightStop.second, mixSpace); } else { startDummy = leftStop.second; endDummy = rightStop.second; } const quint8 *colors[2]; colors[0] = startDummy.data(); colors[1] = endDummy.data(); qreal localT; qreal stopDistance = rightStop.first - leftStop.first; if (stopDistance < DBL_EPSILON) { localT = 0.5; } else { localT = (t - leftStop.first) / stopDistance; } qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - localT) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; //check if our mixspace exists, it doesn't at startup. if (mixSpace){ if (*buffer.colorSpace() != *mixSpace) { buffer = KoColor(mixSpace); } mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } else { buffer = KoColor(colorSpace()); colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } dst.fromKoColor(buffer); } QSharedPointer KoStopGradient::fromQGradient(const QGradient *gradient) { if (! gradient) return QSharedPointer(0); QSharedPointer newGradient(new KoStopGradient(QString())); newGradient->setType(gradient->type()); newGradient->setSpread(gradient->spread()); switch (gradient->type()) { case QGradient::LinearGradient: { const QLinearGradient * g = static_cast(gradient); newGradient->m_start = g->start(); newGradient->m_stop = g->finalStop(); newGradient->m_focalPoint = g->start(); break; } case QGradient::RadialGradient: { const QRadialGradient * g = static_cast(gradient); newGradient->m_start = g->center(); newGradient->m_stop = g->center() + QPointF(g->radius(), 0); newGradient->m_focalPoint = g->focalPoint(); break; } case QGradient::ConicalGradient: { const QConicalGradient * g = static_cast(gradient); qreal radian = g->angle() * M_PI / 180.0; newGradient->m_start = g->center(); newGradient->m_stop = QPointF(100.0 * cos(radian), 100.0 * sin(radian)); newGradient->m_focalPoint = g->center(); break; } default: return QSharedPointer(0);; } Q_FOREACH (const QGradientStop & stop, gradient->stops()) { KoColor color(newGradient->colorSpace()); color.fromQColor(stop.second); newGradient->m_stops.append(KoGradientStop(stop.first, color)); } newGradient->setValid(true); return newGradient; } void KoStopGradient::setStops(QList< KoGradientStop > stops) { m_stops.clear(); KoColor color; Q_FOREACH (const KoGradientStop & stop, stops) { color = stop.second; color.convertTo(colorSpace()); m_stops.append(KoGradientStop(stop.first, color)); } updatePreview(); } QList KoStopGradient::stops() const { return m_stops; } void KoStopGradient::loadSvgGradient(QIODevice *file) { QDomDocument doc; if (!(doc.setContent(file))) file->close(); else { for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.isNull()) continue; if (e.tagName() == "linearGradient" || e.tagName() == "radialGradient") { parseSvgGradient(e); return; } // Inkscape gradients are in another defs if (e.tagName() == "defs") { for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) { QDomElement defelement = defnode.toElement(); if (defelement.isNull()) continue; if (defelement.tagName() == "linearGradient" || defelement.tagName() == "radialGradient") { parseSvgGradient(defelement); return; } } } } } } void KoStopGradient::parseSvgGradient(const QDomElement& element) { m_stops.clear(); setSpread(QGradient::PadSpread); /*QString href = e.attribute( "xlink:href" ).mid( 1 ); if( !href.isEmpty() ) { }*/ setName(element.attribute("id", i18n("SVG Gradient"))); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); bool bbox = element.attribute("gradientUnits") != "userSpaceOnUse"; if (element.tagName() == "linearGradient") { if (bbox) { QString s; s = element.attribute("x1", "0%"); qreal xOrigin; if (s.endsWith('%')) xOrigin = s.remove('%').toDouble(); else xOrigin = s.toDouble() * 100.0; s = element.attribute("y1", "0%"); qreal yOrigin; if (s.endsWith('%')) yOrigin = s.remove('%').toDouble(); else yOrigin = s.toDouble() * 100.0; s = element.attribute("x2", "100%"); qreal xVector; if (s.endsWith('%')) xVector = s.remove('%').toDouble(); else xVector = s.toDouble() * 100.0; s = element.attribute("y2", "0%"); qreal yVector; if (s.endsWith('%')) yVector = s.remove('%').toDouble(); else yVector = s.toDouble() * 100.0; m_start = QPointF(xOrigin, yOrigin); m_stop = QPointF(xVector, yVector); } else { m_start = QPointF(element.attribute("x1").toDouble(), element.attribute("y1").toDouble()); m_stop = QPointF(element.attribute("x2").toDouble(), element.attribute("y2").toDouble()); } setType(QGradient::LinearGradient); } else { if (bbox) { QString s; s = element.attribute("cx", "50%"); qreal xOrigin; if (s.endsWith('%')) xOrigin = s.remove('%').toDouble(); else xOrigin = s.toDouble() * 100.0; s = element.attribute("cy", "50%"); qreal yOrigin; if (s.endsWith('%')) yOrigin = s.remove('%').toDouble(); else yOrigin = s.toDouble() * 100.0; s = element.attribute("cx", "50%"); qreal xVector; if (s.endsWith('%')) xVector = s.remove('%').toDouble(); else xVector = s.toDouble() * 100.0; s = element.attribute("r", "50%"); if (s.endsWith('%')) xVector += s.remove('%').toDouble(); else xVector += s.toDouble() * 100.0; s = element.attribute("cy", "50%"); qreal yVector; if (s.endsWith('%')) yVector = s.remove('%').toDouble(); else yVector = s.toDouble() * 100.0; s = element.attribute("fx", "50%"); qreal xFocal; if (s.endsWith('%')) xFocal = s.remove('%').toDouble(); else xFocal = s.toDouble() * 100.0; s = element.attribute("fy", "50%"); qreal yFocal; if (s.endsWith('%')) yFocal = s.remove('%').toDouble(); else yFocal = s.toDouble() * 100.0; m_start = QPointF(xOrigin, yOrigin); m_stop = QPointF(xVector, yVector); m_focalPoint = QPointF(xFocal, yFocal); } else { m_start = QPointF(element.attribute("cx").toDouble(), element.attribute("cy").toDouble()); m_stop = QPointF(element.attribute("cx").toDouble() + element.attribute("r").toDouble(), element.attribute("cy").toDouble()); m_focalPoint = QPointF(element.attribute("fx").toDouble(), element.attribute("fy").toDouble()); } setType(QGradient::RadialGradient); } // handle spread method QString spreadMethod = element.attribute("spreadMethod"); if (!spreadMethod.isEmpty()) { if (spreadMethod == "reflect") setSpread(QGradient::ReflectSpread); else if (spreadMethod == "repeat") setSpread(QGradient::RepeatSpread); } for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement colorstop = n.toElement(); if (colorstop.tagName() == "stop") { qreal opacity = 0.0; QColor c; float off; QString temp = colorstop.attribute("offset"); if (temp.contains('%')) { temp = temp.left(temp.length() - 1); off = temp.toFloat() / 100.0; } else off = temp.toFloat(); if (!colorstop.attribute("stop-color").isEmpty()) parseSvgColor(c, colorstop.attribute("stop-color")); else { // try style attr QString style = colorstop.attribute("style").simplified(); QStringList substyles = style.split(';', QString::SkipEmptyParts); Q_FOREACH (const QString & s, substyles) { QStringList substyle = s.split(':'); QString command = substyle[0].trimmed(); QString params = substyle[1].trimmed(); if (command == "stop-color") parseSvgColor(c, params); if (command == "stop-opacity") opacity = params.toDouble(); } } if (!colorstop.attribute("stop-opacity").isEmpty()) opacity = colorstop.attribute("stop-opacity").toDouble(); KoColor color(rgbColorSpace); color.fromQColor(c); color.setOpacity(static_cast(opacity * OPACITY_OPAQUE_U8 + 0.5)); //According to the SVG spec each gradient offset has to be equal to or greater than the previous one //if not it needs to be adjusted to be equal if (m_stops.count() > 0 && m_stops.last().first >= off) { off = m_stops.last().first; } m_stops.append(KoGradientStop(off, color)); } } } void KoStopGradient::parseSvgColor(QColor &color, const QString &s) { if (s.startsWith("rgb(")) { QString parse = s.trimmed(); QStringList colors = parse.split(','); QString r = colors[0].right((colors[0].length() - 4)); QString g = colors[1]; QString b = colors[2].left((colors[2].length() - 1)); if (r.contains('%')) { r = r.left(r.length() - 1); r = QString::number(int((qreal(255 * r.toDouble()) / 100.0))); } if (g.contains('%')) { g = g.left(g.length() - 1); g = QString::number(int((qreal(255 * g.toDouble()) / 100.0))); } if (b.contains('%')) { b = b.left(b.length() - 1); b = QString::number(int((qreal(255 * b.toDouble()) / 100.0))); } color = QColor(r.toInt(), g.toInt(), b.toInt()); } else { QString rgbColor = s.trimmed(); QColor c; if (rgbColor.startsWith('#')) c.setNamedColor(rgbColor); else { c = QColor(rgbColor); } color = c; } } QString KoStopGradient::defaultFileExtension() const { return QString(".svg"); } void KoStopGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const { gradientElt.setAttribute("type", "stop"); for (int s = 0; s < m_stops.size(); s++) { KoGradientStop stop = m_stops.at(s); QDomElement stopElt = doc.createElement("stop"); stopElt.setAttribute("offset", KisDomUtils::toString(stop.first)); stopElt.setAttribute("bitdepth", stop.second.colorSpace()->colorDepthId().id()); stopElt.setAttribute("alpha", KisDomUtils::toString(stop.second.opacityF())); stop.second.toXML(doc, stopElt); gradientElt.appendChild(stopElt); } } KoStopGradient KoStopGradient::fromXML(const QDomElement &elt) { KoStopGradient gradient; QList stops; QDomElement stopElt = elt.firstChildElement("stop"); while (!stopElt.isNull()) { qreal offset = KisDomUtils::toDouble(stopElt.attribute("offset", "0.0")); QString bitDepth = stopElt.attribute("bitdepth", Integer8BitsColorDepthID.id()); KoColor color = KoColor::fromXML(stopElt.firstChildElement(), bitDepth); color.setOpacity(KisDomUtils::toDouble(stopElt.attribute("alpha", "1.0"))); stops.append(KoGradientStop(offset, color)); stopElt = stopElt.nextSiblingElement("stop"); } gradient.setStops(stops); return gradient; } bool KoStopGradient::saveToDevice(QIODevice *dev) const { QTextStream stream(dev); const QString spreadMethod[3] = { QString("spreadMethod=\"pad\" "), QString("spreadMethod=\"reflect\" "), QString("spreadMethod=\"repeat\" ") }; const QString indent = " "; stream << "" << endl; stream << indent; stream << "" << endl; QColor color; // color stops Q_FOREACH (const KoGradientStop & stop, m_stops) { stop.second.toQColor(&color); stream << indent << indent; stream << "(color.alpha()) / 255.0f << "\"" << " />" << endl; } stream << indent; stream << "" << endl; stream << "" << endl; KoResource::saveToDevice(dev); return true; } diff --git a/libs/pigment/resources/KoStopGradient.h b/libs/pigment/resources/KoStopGradient.h index 6c6978963e..20ac6a9f98 100644 --- a/libs/pigment/resources/KoStopGradient.h +++ b/libs/pigment/resources/KoStopGradient.h @@ -1,108 +1,108 @@ /* Copyright (c) 2007 Sven Langkamp This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOSTOPGRADIENT_H #define KOSTOPGRADIENT_H #include #include #include "KoColor.h" #include #include #include #include typedef QPair KoGradientStop; struct KoGradientStopValueSort { inline bool operator() (const KoGradientStop& a, const KoGradientStop& b) { return (a.second.toQColor().valueF() < b.second.toQColor().valueF()); } }; /** * Resource for colorstop based gradients like SVG gradients */ class KRITAPIGMENT_EXPORT KoStopGradient : public KoAbstractGradient, public boost::equality_comparable { public: explicit KoStopGradient(const QString &filename = QString()); ~KoStopGradient() override; KoStopGradient(const KoStopGradient &rhs); bool operator==(const KoStopGradient &rhs) const; KoStopGradient &operator=(const KoStopGradient &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::Gradients, ResourceSubType::StopGradients); } /// reimplemented QGradient* toQGradient() const override; /// Find stops surrounding position, returns false if position outside gradient bool stopsAt(KoGradientStop& leftStop, KoGradientStop& rightStop, qreal t) const; /// reimplemented void colorAt(KoColor&, qreal t) const override; /// Creates KoStopGradient from a QGradient static QSharedPointer fromQGradient(const QGradient *gradient); /// Sets the gradient stops void setStops(QList stops); QList stops() const; /// reimplemented QString defaultFileExtension() const override; /** * @brief toXML * Convert the gradient to an XML string. */ void toXML(QDomDocument& doc, QDomElement& gradientElt) const; /** * @brief fromXML * convert a gradient from xml. * @return a gradient. */ static KoStopGradient fromXML(const QDomElement& elt); protected: QList m_stops; QPointF m_start; QPointF m_stop; QPointF m_focalPoint; private: void loadSvgGradient(QIODevice *file); void parseSvgGradient(const QDomElement& element); void parseSvgColor(QColor &color, const QString &s); }; typedef QSharedPointer KoStopGradientSP; #endif // KOSTOPGRADIENT_H diff --git a/libs/resources/KisFolderStorage.cpp b/libs/resources/KisFolderStorage.cpp index 507cdde5a0..9cae96cbdc 100644 --- a/libs/resources/KisFolderStorage.cpp +++ b/libs/resources/KisFolderStorage.cpp @@ -1,265 +1,266 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisFolderStorage.h" #include #include #include #include #include #include +#include class FolderTagIterator : public KisResourceStorage::TagIterator { public: FolderTagIterator(const QString &location, const QString &resourceType) : m_location(location) , m_resourceType(resourceType) { m_dirIterator.reset(new QDirIterator(location + '/' + resourceType, QStringList() << "*.tag", QDir::Files | QDir::Readable, QDirIterator::Subdirectories)); } bool hasNext() const override { return m_dirIterator->hasNext(); } void next() override { m_dirIterator->next(); const_cast(this)->m_tag.reset(new KisTag); load(m_tag); } QString url() const override { return m_tag ? m_tag->url() : QString(); } QString name() const override { return m_tag ? m_tag->name() : QString(); } QString comment() const override {return m_tag ? m_tag->comment() : QString(); } KisTagSP tag() const override { return m_tag; } private: bool load(KisTagSP tag) const { QFile f(m_dirIterator->filePath()); if (f.exists()) { f.open(QFile::ReadOnly); if (!tag->load(f)) { qWarning() << m_dirIterator << "is not a valid tag desktop file"; return false; } } return true; } QScopedPointer m_dirIterator; QString m_location; QString m_resourceType; KisTagSP m_tag; }; class FolderItem : public KisResourceStorage::ResourceItem { public: ~FolderItem() override {} }; class FolderIterator : public KisResourceStorage::ResourceIterator { public: FolderIterator(const QString &location, const QString &resourceType) : m_location(location) , m_resourceType(resourceType) { m_dirIterator.reset(new QDirIterator(location + '/' + resourceType, KisResourceLoaderRegistry::instance()->filters(resourceType), QDir::Files | QDir::Readable, QDirIterator::Subdirectories)); } ~FolderIterator() override {} bool hasNext() const override { return m_dirIterator->hasNext(); } void next() override { m_dirIterator->next(); } QString url() const override { return m_dirIterator->filePath(); } QString type() const override { return m_resourceType; } QDateTime lastModified() const override { return m_dirIterator->fileInfo().lastModified(); } KoResourceSP resource() const override { if (!loadResourceInternal()) { qWarning() << "Could not load resource" << m_dirIterator->filePath(); } return m_resource; } protected: bool loadResourceInternal() const { if (!m_resource || (m_resource && m_resource->filename() != m_dirIterator->filePath())) { QFile f(m_dirIterator->filePath()); f.open(QFile::ReadOnly); if (!m_resourceLoader) { const_cast(this)->m_resourceLoader = KisResourceLoaderRegistry::instance()->loader(m_resourceType, KisMimeDatabase::mimeTypeForFile(m_dirIterator->filePath())); } if (m_resourceLoader) { - const_cast(this)->m_resource = m_resourceLoader->load(m_dirIterator->filePath(), f); + const_cast(this)->m_resource = m_resourceLoader->load(m_dirIterator->filePath(), f, KisGlobalResourcesInterface::instance()); } else { qWarning() << "Could not get resource loader for type" << m_resourceType; } f.close(); } return !m_resource.isNull(); } KisResourceLoaderBase *m_resourceLoader {0}; KoResourceSP m_resource; QScopedPointer m_dirIterator; const QString m_location; const QString m_resourceType; }; KisFolderStorage::KisFolderStorage(const QString &location) : KisStoragePlugin(location) { } KisFolderStorage::~KisFolderStorage() { } bool KisFolderStorage::addTag(const QString &/*resourceType*/, KisTagSP /*tag*/) { return false; } bool KisFolderStorage::addResource(const QString &resourceType, KoResourceSP _resource) { // Find a new filename for the resource if it already exists: we do not rename old resources, but rename updated resources QString fn = location() + "/" + resourceType + "/" + _resource->filename(); if (!QFileInfo(fn).exists()) { // Simply save it QFile f(fn); if (!f.open(QFile::WriteOnly)) { qWarning() << "Could not open resource file for writing" << fn; return false; } if (!_resource->saveToDevice(&f)) { qWarning() << "Could not save resource file" << fn; return false; } f.close(); } else { QFileInfo fi(_resource->filename()); // Rename the old resource KBackup::backupFile(fn); // Save the new resource QString newFileName = fi.baseName() + "_" + QString("%1").arg(_resource->version() + 1, 4, 10, QChar('0')) + "." + fi.completeSuffix(); QFile f(location() + "/" + resourceType + "/" + newFileName); if (!f.open(QFile::WriteOnly)) { qWarning() << "Could not open resource file for writing" << fn; return false; } if (!_resource->saveToDevice(&f)) { qWarning() << "Could not save resource file" << fn; return false; } _resource->setVersion(_resource->version() + 1); _resource->setFilename(newFileName); f.close(); } return true; } KisResourceStorage::ResourceItem KisFolderStorage::resourceItem(const QString &url) { QFileInfo fi(url); FolderItem item; item.url = url; item.folder = fi.path().split("/").last(); item.lastModified = fi.lastModified(); return item; } KoResourceSP KisFolderStorage::resource(const QString &url) { QFileInfo fi(location() + '/' + url); const QString resourceType = fi.path().split("/").last(); KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, KisMimeDatabase::mimeTypeForFile(fi.absoluteFilePath(), false)); Q_ASSERT(loader); QFile f(fi.absoluteFilePath()); if (!f.open(QFile::ReadOnly)) { qWarning() << "Could not open" << fi.absoluteFilePath() << "for reading"; return 0; } - KoResourceSP res = loader->load(url, f); + KoResourceSP res = loader->load(url, f, KisGlobalResourcesInterface::instance()); f.close(); return res; } QSharedPointer KisFolderStorage::resources(const QString &resourceType) { return QSharedPointer(new FolderIterator(location(), resourceType)); } QSharedPointer KisFolderStorage::tags(const QString &resourceType) { return QSharedPointer(new FolderTagIterator(location(), resourceType)); } diff --git a/libs/resources/KisResourceLoader.h b/libs/resources/KisResourceLoader.h index bf921e61f5..1301d51beb 100644 --- a/libs/resources/KisResourceLoader.h +++ b/libs/resources/KisResourceLoader.h @@ -1,131 +1,131 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISRESOURCELOADER_H #define KISRESOURCELOADER_H #include #include #include #include #include #include #include #include /** * @brief The KisResourceLoader class is an abstract interface * class that must be implemented by actual resource classes and * registered with the KisResourceLoaderRegistry. */ class KRITARESOURCES_EXPORT KisResourceLoaderBase { public: KisResourceLoaderBase(const QString &resourceSubType, const QString &resourceType, const QString &name, const QStringList &mimetypes) { m_resourceSubType = resourceSubType; m_resourceType = resourceType; m_mimetypes = mimetypes; m_name = name; } virtual ~KisResourceLoaderBase() { } /** * @return a set of filters ("*.bla,*.foo") that is suitable for filtering * the contents of a directory. */ QStringList filters() const; /** * @return the mimetypes this resource can load */ QStringList mimetypes() const { return m_mimetypes; } /** * @return the folder in the resource storage where resources * of this type are located */ QString resourceType() const { return m_resourceType; } QString resourceSubType() const { return id(); } /// For registration in KisResourceLoaderRegistry QString id() const { return m_resourceSubType; } /// The user-friendly name of the category QString name() const { return m_name; } /** * Load this resource. * @return a resource if loading the resource succeeded, 0 otherwise */ - virtual KoResourceSP load(const QString &name, QIODevice &dev) { Q_UNUSED(name); Q_UNUSED(dev); return 0; } + virtual KoResourceSP load(const QString &name, QIODevice &dev, KisResourcesInterfaceSP resourcesInterface) { Q_UNUSED(name); Q_UNUSED(dev); Q_UNUSED(resourcesInterface); return 0; } private: QString m_resourceSubType; QString m_resourceType; QStringList m_mimetypes; QString m_name; }; template class KisResourceLoader : public KisResourceLoaderBase { public: KisResourceLoader(const QString &id, const QString &folder, const QString &name, const QStringList &mimetypes) : KisResourceLoaderBase(id, folder, name, mimetypes) { } - KoResourceSP load(const QString &name, QIODevice &dev) override + KoResourceSP load(const QString &name, QIODevice &dev, KisResourcesInterfaceSP resourcesInterface) override { QSharedPointer resource = QSharedPointer::create(name); Q_ASSERT(dev.isOpen() && dev.isReadable()); - if (resource->loadFromDevice(&dev)) { + if (resource->loadFromDevice(&dev, resourcesInterface)) { return resource; } return 0; } }; #endif // KISRESOURCELOADER_H diff --git a/libs/resources/KisResourceLocator.cpp b/libs/resources/KisResourceLocator.cpp index e5d3eac34a..b106ae86c6 100644 --- a/libs/resources/KisResourceLocator.cpp +++ b/libs/resources/KisResourceLocator.cpp @@ -1,555 +1,556 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisResourceLocator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResourcePaths.h" #include "KisResourceStorage.h" #include "KisResourceCacheDb.h" #include "KisResourceLoaderRegistry.h" #include "KisMemoryStorage.h" #include "KisResourceModelProvider.h" +#include const QString KisResourceLocator::resourceLocationKey {"ResourceDirectory"}; class KisResourceLocator::Private { public: QString resourceLocation; QMap storages; QHash, KoResourceSP> resourceCache; QStringList errorMessages; }; KisResourceLocator::KisResourceLocator(QObject *parent) : QObject(parent) , d(new Private()) { } KisResourceLocator *KisResourceLocator::instance() { // Not a regular Q_GLOBAL_STATIC, because we want this deleted as // part of the app destructor. KisResourceLocator *locator = qApp->findChild(QString()); if (!locator) { locator = new KisResourceLocator(qApp); } return locator; } KisResourceLocator::~KisResourceLocator() { } KisResourceLocator::LocatorError KisResourceLocator::initialize(const QString &installationResourcesLocation) { InitalizationStatus initalizationStatus = InitalizationStatus::Unknown; KConfigGroup cfg(KSharedConfig::openConfig(), ""); d->resourceLocation = cfg.readEntry(resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); if (!d->resourceLocation.endsWith('/')) d->resourceLocation += '/'; QFileInfo fi(d->resourceLocation); if (!fi.exists()) { if (!QDir().mkpath(d->resourceLocation)) { d->errorMessages << i18n("1. Could not create the resource location at %1.", d->resourceLocation); return LocatorError::CannotCreateLocation; } initalizationStatus = InitalizationStatus::FirstRun; } if (!fi.isWritable()) { d->errorMessages << i18n("2. The resource location at %1 is not writable.", d->resourceLocation); return LocatorError::LocationReadOnly; } // Check whether we're updating from an older version if (initalizationStatus != InitalizationStatus::FirstRun) { QFile fi(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION"); if (!fi.exists()) { initalizationStatus = InitalizationStatus::FirstUpdate; } else { fi.open(QFile::ReadOnly); QVersionNumber resource_version = QVersionNumber::fromString(QString::fromUtf8(fi.readAll())); QVersionNumber krita_version = QVersionNumber::fromString(KritaVersionWrapper::versionString()); if (krita_version > resource_version) { initalizationStatus = InitalizationStatus::Updating; } else { initalizationStatus = InitalizationStatus::Initialized; } } } if (initalizationStatus != InitalizationStatus::Initialized) { KisResourceLocator::LocatorError res = firstTimeInstallation(initalizationStatus, installationResourcesLocation); if (res != LocatorError::Ok) { return res; } initalizationStatus = InitalizationStatus::Initialized; } else { if (!synchronizeDb()) { return LocatorError::CannotSynchronizeDb; } } return LocatorError::Ok; } QStringList KisResourceLocator::errorMessages() const { return d->errorMessages; } QString KisResourceLocator::resourceLocationBase() const { return d->resourceLocation; } bool KisResourceLocator::resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const { storageLocation = makeStorageLocationAbsolute(storageLocation); QPair key = QPair (storageLocation, resourceType + "/" + filename); return d->resourceCache.contains(key); } KoResourceSP KisResourceLocator::resource(QString storageLocation, const QString &resourceType, const QString &filename) { storageLocation = makeStorageLocationAbsolute(storageLocation); QPair key = QPair (storageLocation, resourceType + "/" + filename); KoResourceSP resource; if (d->resourceCache.contains(key)) { resource = d->resourceCache[key]; } else { KisResourceStorageSP storage = d->storages[storageLocation]; if (!storage) { qWarning() << "Could not find storage" << storageLocation; return 0; } resource = storage->resource(resourceType + "/" + filename); if (!resource) { qWarning() << "Could not find resource" << resourceType + "/" + filename; return 0; } d->resourceCache[key] = resource; } Q_ASSERT(resource); resource->setStorageLocation(storageLocation); Q_ASSERT(!resource->storageLocation().isEmpty()); return resource; } KoResourceSP KisResourceLocator::resourceForId(int resourceId) { ResourceStorage rs = getResourceStorage(resourceId); KoResourceSP r = resource(rs.storageLocation, rs.resourceType, rs.resourceFileName); if (r) { r->setResourceId(resourceId); } return r; } bool KisResourceLocator::removeResource(int resourceId, const QString &/*storageLocation*/) { // First remove the resource from the cache ResourceStorage rs = getResourceStorage(resourceId); QPair key = QPair (rs.storageLocation, rs.resourceType + "/" + rs.resourceFileName); d->resourceCache.remove(key); return KisResourceCacheDb::removeResource(resourceId); } bool KisResourceLocator::importResourceFromFile(const QString &resourceType, const QString &fileName, const QString &storageLocation) { KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, KisMimeDatabase::mimeTypeForFile(fileName)); QFile f(fileName); if (!f.open(QFile::ReadOnly)) { qWarning() << "Could not open" << fileName << "for loading"; return false; } - KoResourceSP resource = loader->load(QFileInfo(fileName).fileName(), f); + KoResourceSP resource = loader->load(QFileInfo(fileName).fileName(), f, KisGlobalResourcesInterface::instance()); if (!resource) { qWarning() << "Could not import" << fileName << ": resource doesn't load."; return false; } KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)]; Q_ASSERT(storage); if (!storage->addResource(resource)) { qWarning() << "Could not add resource" << resource->filename() << "to the folder storage"; return false; } return KisResourceCacheDb::addResource(folderStorage(), QFileInfo(resource->filename()).lastModified(), resource, resourceType); } bool KisResourceLocator::addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation) { if (!resource || !resource->valid()) return false; KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)]; Q_ASSERT(storage); //If we have gotten this far and the resource still doesn't have a filename to save to, we should generate one. if (resource->filename().isEmpty()) { if (storageLocation == "memory") { resource->setFilename("memory/" + resourceType + "/" + resource->name()); } else { resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension()); } } // Save the resource to the storage storage if (!storage->addResource(resource)) { qWarning() << "Could not add resource" << resource->filename() << "to the folder storage"; return false; } // And the database return KisResourceCacheDb::addResource(storage, QFileInfo(resource->filename()).lastModified(), resource, resourceType); } bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource) { QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation()); Q_ASSERT(d->storages.contains(storageLocation)); Q_ASSERT(resource->resourceId() > -1); KisResourceStorageSP storage = d->storages[storageLocation]; int version = resource->version(); // This increments the version in the resource if (!storage->addResource(resource)) { qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation; return false; } // Memory storages don't store versioned resources if (storage->type() == KisResourceStorage::StorageType::Memory) { return true; } // It's the storages that keep track of the version Q_ASSERT(resource->version() == version + 1); // The version needs already to have been incremented if (!KisResourceCacheDb::addResourceVersion(resource->resourceId(), QDateTime::currentDateTime(), storage, resource)) { qWarning() << "Failed to add a new version of the resource to the database" << resource->name(); return false; } // Update the resource in the cache QPair key = QPair (storageLocation, resourceType + "/" + QFileInfo(resource->filename()).fileName()); d->resourceCache[key] = resource; return true; } QMap KisResourceLocator::metaDataForResource(int id) const { return KisResourceCacheDb::metaDataForId(id, "resources"); } bool KisResourceLocator::setMetaDataForResource(int id, QMap map) const { return KisResourceCacheDb::updateMetaDataForId(map, id, "resources"); } void KisResourceLocator::purge() { d->resourceCache.clear(); } bool KisResourceLocator::addStorage(const QString &document, KisResourceStorageSP storage) { Q_ASSERT(!d->storages.contains(document)); d->storages[document] = storage; if (!KisResourceCacheDb::addStorage(storage, false)) { d->errorMessages.append(i18n("Could not add %1 to the database", storage->location())); return false; } KisResourceModelProvider::resetAllModels(); return true; } bool KisResourceLocator::removeStorage(const QString &document) { // Cloned documents have a document storage, but that isn't in the locator. if (!d->storages.contains(document)) return true; purge(); KisResourceStorageSP storage = d->storages.take(document); if (!KisResourceCacheDb::deleteStorage(storage)) { d->errorMessages.append(i18n("Could not remove storage %1 from the database", storage->location())); return false; } KisResourceModelProvider::resetAllModels(); return true; } bool KisResourceLocator::hasStorage(const QString &document) { return d->storages.contains(document); } KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitalizationStatus initalizationStatus, const QString &installationResourcesLocation) { emit progressMessage(i18n("Krita is running for the first time. Intialization will take some time.")); Q_UNUSED(initalizationStatus); Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) { QDir dir(d->resourceLocation + '/' + folder + '/'); if (!dir.exists()) { if (!QDir().mkpath(d->resourceLocation + '/' + folder + '/')) { d->errorMessages << i18n("3. Could not create the resource location at %1.", dir.path()); return LocatorError::CannotCreateLocation; } } } Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) { QDir dir(installationResourcesLocation + '/' + folder + '/'); if (dir.exists()) { Q_FOREACH(const QString &entry, dir.entryList(QDir::Files | QDir::Readable)) { QFile f(dir.canonicalPath() + '/'+ entry); if (!QFileInfo(d->resourceLocation + '/' + folder + '/' + entry).exists()) { if (!f.copy(d->resourceLocation + '/' + folder + '/' + entry)) { d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation + '/' + folder + '/' + entry); } } } } } // And add bundles and adobe libraries QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl"; QDirIterator iter(installationResourcesLocation, filters, QDir::Files, QDirIterator::Subdirectories); while (iter.hasNext()) { iter.next(); emit progressMessage(i18n("Installing the resources from bundle %1.", iter.filePath())); QFile f(iter.filePath()); Q_ASSERT(f.exists()); if (!f.copy(d->resourceLocation + '/' + iter.fileName())) { d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation); } } QFile f(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION"); f.open(QFile::WriteOnly); f.write(KritaVersionWrapper::versionString().toUtf8()); f.close(); if (!initializeDb()) { return LocatorError::CannotInitializeDb; } return LocatorError::Ok; } bool KisResourceLocator::initializeDb() { emit progressMessage(i18n("Initalizing the resources.")); d->errorMessages.clear(); findStorages(); Q_FOREACH(KisResourceStorageSP storage, d->storages) { QTime t; t.start(); if (!KisResourceCacheDb::addStorage(storage, (storage->type() == KisResourceStorage::StorageType::Folder ? false : true))) { d->errorMessages.append(i18n("Could not add storage %1 to the cache database", storage->location())); } qDebug() << "Adding storage" << storage->location() << "to the database took" << t.elapsed() << "ms"; } return (d->errorMessages.isEmpty()); } void KisResourceLocator::findStorages() { d->storages.clear(); // Add the folder KisResourceStorageSP storage = QSharedPointer::create(d->resourceLocation); Q_ASSERT(storage->location() == d->resourceLocation); d->storages[d->resourceLocation] = storage; // Add the memory storage d->storages["memory"] = QSharedPointer::create("memory"); // And add bundles and adobe libraries QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl"; QDirIterator iter(d->resourceLocation, filters, QDir::Files, QDirIterator::Subdirectories); while (iter.hasNext()) { iter.next(); KisResourceStorageSP storage = QSharedPointer::create(iter.filePath()); d->storages[storage->location()] = storage; } } QList KisResourceLocator::storages() const { return d->storages.values(); } KisResourceStorageSP KisResourceLocator::storageByLocation(const QString &location) const { if (!d->storages.contains(location)) { qWarning() << "No" << location << "storage defined"; return 0; } KisResourceStorageSP storage = d->storages[location]; if (!storage || !storage->valid()) { qWarning() << "Could not retrieve the" << location << "storage object or the object is not valid"; return 0; } return storage; } KisResourceStorageSP KisResourceLocator::folderStorage() const { return storageByLocation(d->resourceLocation); } KisResourceStorageSP KisResourceLocator::memoryStorage() const { return storageByLocation("memory"); } KisResourceLocator::ResourceStorage KisResourceLocator::getResourceStorage(int resourceId) const { ResourceStorage rs; QSqlQuery q; bool r = q.prepare("SELECT storages.location\n" ", resource_types.name as resource_type\n" ", resources.filename\n" "FROM resources\n" ", storages\n" ", resource_types\n" "WHERE resources.id = :resource_id\n" "AND resources.storage_id = storages.id\n" "AND resource_types.id = resources.resource_type_id"); if (!r) { qWarning() << "KisResourceLocator::removeResource: could not prepare query." << q.lastError(); return rs; } q.bindValue(":resource_id", resourceId); r = q.exec(); if (!r) { qWarning() << "KisResourceLocator::removeResource: could not execute query." << q.lastError(); return rs; } q.first(); QString storageLocation = q.value("location").toString(); QString resourceType= q.value("resource_type").toString(); QString resourceFilename = q.value("filename").toString(); rs.storageLocation = makeStorageLocationAbsolute(storageLocation); rs.resourceType = resourceType; rs.resourceFileName = resourceFilename; return rs; } QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const { if (storageLocation.isEmpty()) { storageLocation = resourceLocationBase(); } // XXX: This breaks with document storages! if (!storageLocation.startsWith('/') && storageLocation != "memory") { if (resourceLocationBase().endsWith('/')) { storageLocation = resourceLocationBase() + storageLocation; } else { storageLocation = resourceLocationBase() + '/' + storageLocation; } } return storageLocation; } bool KisResourceLocator::synchronizeDb() { d->errorMessages.clear(); findStorages(); Q_FOREACH(const KisResourceStorageSP storage, d->storages) { if (!KisResourceCacheDb::synchronizeStorage(storage)) { d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location())); } } return d->errorMessages.isEmpty(); } QString KisResourceLocator::makeStorageLocationRelative(QString location) const { return location.remove(resourceLocationBase()); } diff --git a/libs/resources/KoEphemeralResource.h b/libs/resources/KoEphemeralResource.h index 8096be2e5d..b934ce572d 100644 --- a/libs/resources/KoEphemeralResource.h +++ b/libs/resources/KoEphemeralResource.h @@ -1,73 +1,75 @@ /* * Copyright (c) 2020 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KOEPHEMERALRESOURCE_H #define KOEPHEMERALRESOURCE_H #include /** * KoEphemeralResource is a type of resource that has no physical * representation on disk. Therefore, its load()/save() calls do * nothing. * * This type of resources is created directly by the factory. */ template class KoEphemeralResource : public ParentClass { public: KoEphemeralResource() : ParentClass() { } KoEphemeralResource(const QString &arg) : ParentClass(arg) { } KoEphemeralResource(const KoEphemeralResource &rhs) : ParentClass(rhs) { } - bool load() override + bool load(KisResourcesInterfaceSP resourcesInterface) override { + Q_UNUSED(resourcesInterface); return false; } - bool loadFromDevice(QIODevice *dev) override + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override { Q_UNUSED(dev); + Q_UNUSED(resourcesInterface); return false; } bool save() override { return false; } bool saveToDevice(QIODevice *dev) const override { Q_UNUSED(dev); return false; } }; #endif // KOEPHEMERALRESOURCE_H diff --git a/libs/resources/KoResource.cpp b/libs/resources/KoResource.cpp index f179e8a387..6e3d275bbe 100644 --- a/libs/resources/KoResource.cpp +++ b/libs/resources/KoResource.cpp @@ -1,266 +1,266 @@ /* This file is part of the KDE project Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2007 Jan Hambrecht This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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 "KoHashGenerator.h" #include "KoHashGeneratorProvider.h" #include struct Q_DECL_HIDDEN KoResource::Private { int version {0}; int resourceId {-1}; bool valid {false}; bool permanent {false}; bool dirty {false}; QString name; QString filename; QString storageLocation; QByteArray md5; QImage image; QMap metadata; }; KoResource::KoResource(const QString& filename) : d(new Private) { d->filename = filename; } KoResource::~KoResource() { delete d; } KoResource::KoResource(const KoResource &rhs) : d(new Private) { *this = rhs; } KoResource &KoResource::operator=(const KoResource &rhs) { if (this != &rhs) { d->name = rhs.d->name; d->filename= rhs.d->filename; d->valid = rhs.d->valid; d->md5 = rhs.d->md5; d->image = rhs.d->image; d->permanent = rhs.d->permanent; d->resourceId = rhs.d->resourceId; d->storageLocation = rhs.d->storageLocation; d->dirty = rhs.d->dirty; d->metadata = rhs.d->metadata; d->version = rhs.d->version; } return *this; } -bool KoResource::load() +bool KoResource::load(KisResourcesInterfaceSP resourcesInterface) { QFile file(filename()); if (!file.exists()) { warnKrita << "File doesn't exist: " << filename(); return false; } if (file.size() == 0) { warnKrita << "File is empty: " << filename(); return false; } if (!file.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file for reading" << filename(); return false; } - const bool res = loadFromDevice(&file); + const bool res = loadFromDevice(&file, resourcesInterface); file.close(); return res; } bool KoResource::save() { if (filename().isEmpty()) return false; QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { warnKrita << "Can't open file for writing" << filename(); return false; } saveToDevice(&file); file.close(); return true; } bool KoResource::saveToDevice(QIODevice *dev) const { Q_UNUSED(dev) d->md5 = QByteArray(); return true; } QImage KoResource::image() const { return d->image; } void KoResource::setImage(const QImage &image) { d->image = image; } QByteArray KoResource::md5() const { if (d->md5.isEmpty()) { const_cast(this)->setMD5(generateMD5()); } return d->md5; } void KoResource::setMD5(const QByteArray &md5) { d->md5 = md5; } QByteArray KoResource::generateMD5() const { KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); QByteArray hash; QByteArray ba; QBuffer buf(&ba); buf.open(QBuffer::WriteOnly); if (saveToDevice(&buf)) { buf.close(); hash = hashGenerator->generateHash(ba); } else { qWarning() << "Could not create md5sum for resource" << d->filename; } return hash; } QString KoResource::filename() const { return d->filename; } void KoResource::setFilename(const QString& filename) { d->filename = filename; } QString KoResource::name() const { return (!d->name.isEmpty() ? d->name : QFileInfo(filename()).fileName()); } void KoResource::setName(const QString& name) { d->name = name; } bool KoResource::valid() const { return d->valid; } void KoResource::setValid(bool valid) { d->valid = valid; } QString KoResource::defaultFileExtension() const { return QString(); } bool KoResource::permanent() const { return d->permanent; } void KoResource::setPermanent(bool permanent) { d->permanent = permanent; } int KoResource::resourceId() const { return d->resourceId; } QString KoResource::storageLocation() const { return d->storageLocation; } void KoResource::setDirty(bool value) { d->dirty = value; } bool KoResource::isDirty() const { return d->dirty; } void KoResource::addMetaData(QString key, QVariant value) { d->metadata.insert(key, value); } QMap KoResource::metadata() const { return d->metadata; } int KoResource::version() const { return d->version; } void KoResource::setVersion(int version) { d->version = version; } void KoResource::setResourceId(int id) { d->resourceId = id; } void KoResource::setStorageLocation(const QString &location) { d->storageLocation = location; } diff --git a/libs/resources/KoResource.h b/libs/resources/KoResource.h index 685bb4f492..cafcc5fe1c 100644 --- a/libs/resources/KoResource.h +++ b/libs/resources/KoResource.h @@ -1,199 +1,202 @@ /* This file is part of the KDE project Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Boudewijn Rempt This library 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.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCE_H #define KORESOURCE_H #include #include #include #include #include #include "KisResourceTypes.h" #include class QDomDocument; class QDomElement; class KoResource; typedef QSharedPointer KoResourceSP; +class KisResourcesInterface; +typedef QSharedPointer KisResourcesInterfaceSP; + /** * The KoResource class provides a representation of resources. This * includes, but not limited to, brushes and patterns. * * A resource knows its filename, but not the location where it's stored. * A new version of a resource is stored with an updated filename, the old * version isn't renamed. * */ class KRITARESOURCES_EXPORT KoResource { public: /** * Creates a new KoResource object using @p filename. No file is opened * in the constructor, you have to call load. * * @param filename the file name to save and load from. */ explicit KoResource(const QString &filename); virtual ~KoResource(); KoResource(const KoResource &rhs); KoResource &operator=(const KoResource &rhs); virtual KoResourceSP clone() const = 0; bool operator !=(const KoResource &other) const { return &other != this; } bool operator ==(const KoResource &other) const { return other.md5() == md5(); } public: /** * Load this resource. * @return true if loading the resource succeeded. */ - virtual bool load(); - virtual bool loadFromDevice(QIODevice *dev) = 0; + virtual bool load(KisResourcesInterfaceSP resourcesInterface); + virtual bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) = 0; /** * Save this resource. *@return true if saving the resource succeeded. */ virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @returns a QImage thumbnail image representing this resource. * * This image could be null. The image can be in any valid format. */ QImage image() const; void setImage(const QImage &image); /// @return the md5sum calculated over the contents of the resource. QByteArray md5() const; /// @return the unique identifier of this resource within the container (folder, bundle, ...) QString filename() const; void setFilename(const QString& filename); /// @return the user-visible name of the resource QString name() const; void setName(const QString& name); /// @return true if the resource is ready for use bool valid() const; void setValid(bool valid); /// @return the default file extension which should be used when saving the resource virtual QString defaultFileExtension() const; /// @return true if the resource is permanent and can't be removed by the user bool permanent() const; void setPermanent(bool permanent); /// @return the name of the storage location of the resource QString storageLocation() const; /// Mark the preset as modified but not saved void setDirty(bool value); /// @return true if the preset has been modified, but not saved bool isDirty() const; /// store the given key, value pair in the resource void addMetaData(QString key, QVariant value); /// get a map with all the metadata QMap metadata() const; /// Get the version of the resource int version() const; /// @return the unique id of the resource in the resource database int resourceId() const; /// @return the resource type virtual QPair resourceType() const = 0; private: friend class KisResourceModel; friend class KisResourceLocator; friend class TestResourceModel; friend class TestResourceLocator; friend class TestFolderStorage; friend class KisFolderStorage; friend class KisMemoryStorage; void setVersion(int version); void setResourceId(int id); void setStorageLocation(const QString &location); protected: /// override generateMD5 and in your resource subclass virtual QByteArray generateMD5() const; /// call this when the contents of the resource change so the md5 needs to be recalculated void setMD5(const QByteArray &md5); private: struct Private; Private* const d; }; static inline bool operator==(const KoResource &resource1, const KoResource &resource2) { return (resource1.md5() == resource2.md5()); } static inline uint qHash(const KoResource &resource) { return qHash(resource.md5()); } Q_DECLARE_METATYPE(QSharedPointer) inline QDebug operator<<(QDebug dbg, const KoResourceSP res) { if (!res) { dbg.noquote() << "NULL Resource"; } else { dbg.nospace() << "[RESOURCE] Name: " << res->name() << " Version: " << res->version() << " Filename: " << res->filename() << " Valid: " << res->valid() << " Storage: " << res->storageLocation(); } return dbg.space(); } #endif // KORESOURCE_H_ diff --git a/libs/resources/KoResourceBundle.cpp b/libs/resources/KoResourceBundle.cpp index 080d9771b2..ec83b7cc0d 100644 --- a/libs/resources/KoResourceBundle.cpp +++ b/libs/resources/KoResourceBundle.cpp @@ -1,537 +1,539 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoResourceBundle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResourceBundleManifest.h" #include #include #include #include #include #include #include "KisStoragePlugin.h" #include "KisResourceLoaderRegistry.h" #include #include +#include + KoResourceBundle::KoResourceBundle(QString const& fileName) : m_filename(fileName), m_bundleVersion("1") { m_metadata[KisResourceStorage::s_meta_generator] = "Krita (" + KritaVersionWrapper::versionString(true) + ")"; } KoResourceBundle::~KoResourceBundle() { } QString KoResourceBundle::defaultFileExtension() const { return QString(".bundle"); } bool KoResourceBundle::load() { if (m_filename.isEmpty()) return false; QScopedPointer resourceStore(KoStore::createStore(m_filename, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { qWarning() << "Could not open store on bundle" << m_filename; return false; } else { m_metadata.clear(); if (resourceStore->open("META-INF/manifest.xml")) { if (!m_manifest.load(resourceStore->device())) { qWarning() << "Could not open manifest for bundle" << m_filename; return false; } resourceStore->close(); Q_FOREACH (KoResourceBundleManifest::ResourceReference ref, m_manifest.files()) { if (!resourceStore->open(ref.resourcePath)) { qWarning() << "Bundle is broken. File" << ref.resourcePath << "is missing"; } else { resourceStore->close(); } } } else { qWarning() << "Could not load META-INF/manifest.xml"; return false; } bool versionFound = false; if (!readMetaData(resourceStore.data())) { qWarning() << "Could not load meta.xml"; return false; } if (resourceStore->open("preview.png")) { // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = resourceStore->device()->readAll(); QBuffer buffer(&data); m_thumbnail.load(&buffer, "PNG"); resourceStore->close(); } else { qWarning() << "Could not open preview.png"; } /* * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle. */ if (!versionFound) { m_metadata.insert(KisResourceStorage::s_meta_version, "1"); } } return true; } bool KoResourceBundle::loadFromDevice(QIODevice *) { return false; } bool saveResourceToStore(KoResourceSP resource, KoStore *store, const QString &resType) { if (!resource) { qWarning() << "No Resource"; return false; } if (!resource->valid()) { qWarning() << "Resource is not valid"; return false; } if (!store || store->bad()) { qWarning() << "No Store or Store is Bad"; return false; } QByteArray ba; QBuffer buf; QFileInfo fi(resource->filename()); if (fi.exists() && fi.isReadable()) { QFile f(resource->filename()); if (!f.open(QFile::ReadOnly)) { qWarning() << "Could not open resource" << resource->filename(); return false; } ba = f.readAll(); if (ba.size() == 0) { qWarning() << "Resource is empty" << resource->filename(); return false; } f.close(); buf.setBuffer(&ba); } else { qWarning() << "Could not find the resource " << resource->filename() << " or it isn't readable"; return false; } if (!buf.open(QBuffer::ReadOnly)) { qWarning() << "Could not open buffer"; return false; } Q_ASSERT(!store->hasFile(resType + "/" + resource->filename())); if (!store->open(resType + "/" + resource->filename())) { qWarning() << "Could not open file in store for resource"; return false; } bool res = (store->write(buf.data()) == buf.size()); store->close(); return res; } bool KoResourceBundle::save() { if (m_filename.isEmpty()) return false; setMetaData(KisResourceStorage::s_meta_dc_date, QDate::currentDate().toString("dd/MM/yyyy")); QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles"); bundleDir.cdUp(); QScopedPointer store(KoStore::createStore(m_filename, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); if (!store || store->bad()) return false; // Q_FOREACH (const QString &resType, m_manifest.types()) { // if (resType == ResourceType::Gradients) { // KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KoResourceSP res = gradientServer->resourceByMD5(ref.md5sum); // if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).m_filename); // if (!saveResourceToStore(res, store.data(), ResourceType::Gradients)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).m_filename; // } // } // } // } // else if (resType == ResourceType::Patterns) { // KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KoResourceSP res = patternServer->resourceByMD5(ref.md5sum); // if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).m_filename); // if (!saveResourceToStore(res, store.data(), ResourceType::Patterns)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); // } // } // } // } // else if (resType == ResourceType::Brushes) { // KoResourceServer* brushServer = KisBrushServerProvider::instance()->brushServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KisBrushSP brush = brushServer->resourceByMD5(ref.md5sum); // if (!brush) brush = brushServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); // KoResourceSP res = brush.data(); // if (!saveResourceToStore(res, store.data(), ResourceType::Brushes)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); // } // } // } // } // else if (resType == ResourceType::Palettes) { // KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KoResourceSP res = paletteServer->resourceByMD5(ref.md5sum); // if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); // if (!saveResourceToStore(res, store.data(), ResourceType::Palettes)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); // } // } // } // } // else if (resType == ResourceType::Workspaces) { // KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KoResourceSP res = workspaceServer->resourceByMD5(ref.md5sum); // if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); // if (!saveResourceToStore(res, store.data(), ResourceType::Workspaces)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); // } // } // } // } // else if (resType == ResourceType::PaintOpPresets) { // KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); // Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { // KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); // if (!res) res = paintoppresetServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); // if (!saveResourceToStore(res.data(), store.data(), ResourceType::PaintOpPresets)) { // if (res) { // qWarning() << "Could not save resource" << resType << res->name(); // } // else { // qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); // } // } // } // } // } if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) qWarning() << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) qWarning() << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); return true; } bool KoResourceBundle::saveToDevice(QIODevice */*dev*/) const { return false; } void KoResourceBundle::setMetaData(const QString &key, const QString &value) { m_metadata.insert(key, value); } const QString KoResourceBundle::metaData(const QString &key, const QString &defaultValue) const { if (m_metadata.contains(key)) { return m_metadata[key]; } else { return defaultValue; } } void KoResourceBundle::addResource(QString fileType, QString filePath, QVector fileTagList, const QByteArray md5sum) { QStringList tags; Q_FOREACH(KisTagSP tag, fileTagList) { tags << tag->url(); } m_manifest.addResource(fileType, filePath, tags, md5sum); } QList KoResourceBundle::getTagsList() { return QList::fromSet(m_bundletags); } QStringList KoResourceBundle::resourceTypes() const { return m_manifest.types(); } void KoResourceBundle::setThumbnail(QString filename) { if (QFileInfo(filename).exists()) { m_thumbnail = QImage(filename); m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { m_thumbnail = QImage(256, 256, QImage::Format_ARGB32); QPainter gc(&m_thumbnail); gc.fillRect(0, 0, 256, 256, Qt::red); gc.end(); } } void KoResourceBundle::writeMeta(const QString &metaTag, KoXmlWriter *writer) { if (m_metadata.contains(metaTag)) { writer->startElement(metaTag.toUtf8()); writer->addTextNode(m_metadata[metaTag].toUtf8()); writer->endElement(); } } void KoResourceBundle::writeUserDefinedMeta(const QString &metaTag, KoXmlWriter *writer) { if (m_metadata.contains(metaTag)) { writer->startElement("meta:meta-userdefined"); writer->addAttribute("meta:name", metaTag); writer->addAttribute("meta:value", m_metadata[metaTag]); writer->endElement(); } } bool KoResourceBundle::readMetaData(KoStore *resourceStore) { if (resourceStore->open("meta.xml")) { KoXmlDocument doc; if (!doc.setContent(resourceStore->device())) { qWarning() << "Could not parse meta.xml for" << m_filename; return false; } // First find the manifest:manifest node. KoXmlNode n = doc.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (!n.isElement()) { continue; } if (n.toElement().tagName() == "meta:meta") { break; } } if (n.isNull()) { qWarning() << "Could not find manifest node for bundle" << m_filename; return false; } const KoXmlElement metaElement = n.toElement(); for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { KoXmlElement e = n.toElement(); if (e.tagName() == "meta:meta-userdefined") { if (e.attribute("meta:name") == "tag") { m_bundletags << e.attribute("meta:value"); } else { m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value")); } } else { m_metadata.insert(e.tagName(), e.firstChild().toText().data()); } } } resourceStore->close(); return true; } return false; } void KoResourceBundle::saveMetadata(QScopedPointer &store) { QBuffer buf; store->open("meta.xml"); buf.open(QBuffer::WriteOnly); KoXmlWriter metaWriter(&buf); metaWriter.startDocument("office:document-meta"); metaWriter.startElement("meta:meta"); writeMeta(KisResourceStorage::s_meta_generator, &metaWriter); metaWriter.startElement(KisResourceStorage::s_meta_version.toUtf8()); metaWriter.addTextNode(m_bundleVersion.toUtf8()); metaWriter.endElement(); writeMeta(KisResourceStorage::s_meta_author, &metaWriter); writeMeta(KisResourceStorage::s_meta_title, &metaWriter); writeMeta(KisResourceStorage::s_meta_description, &metaWriter); writeMeta(KisResourceStorage::s_meta_initial_creator, &metaWriter); writeMeta(KisResourceStorage::s_meta_creator, &metaWriter); writeMeta(KisResourceStorage::s_meta_creation_date, &metaWriter); writeMeta(KisResourceStorage::s_meta_dc_date, &metaWriter); writeUserDefinedMeta("email", &metaWriter); writeUserDefinedMeta("license", &metaWriter); writeUserDefinedMeta("website", &metaWriter); Q_FOREACH (const QString &tag, m_bundletags) { metaWriter.startElement(KisResourceStorage::s_meta_user_defined.toUtf8()); metaWriter.addAttribute(KisResourceStorage::s_meta_name.toUtf8(), "tag"); metaWriter.addAttribute(KisResourceStorage::s_meta_value.toUtf8(), tag); metaWriter.endElement(); } metaWriter.endElement(); // meta:meta metaWriter.endDocument(); buf.close(); store->write(buf.data()); store->close(); } void KoResourceBundle::saveManifest(QScopedPointer &store) { store->open("META-INF/manifest.xml"); QBuffer buf; buf.open(QBuffer::WriteOnly); m_manifest.save(&buf); buf.close(); store->write(buf.data()); store->close(); } int KoResourceBundle::resourceCount() const { return m_manifest.files().count(); } KoResourceBundleManifest &KoResourceBundle::manifest() { return m_manifest; } KoResourceSP KoResourceBundle::resource(const QString &resourceType, const QString &filepath) { if (m_filename.isEmpty()) return 0; QScopedPointer resourceStore(KoStore::createStore(m_filename, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { qWarning() << "Could not open store on bundle" << m_filename; return 0; } if (!resourceStore->open(filepath)) { qWarning() << "Could not open file in bundle" << filepath; } QString mime = KisMimeDatabase::mimeTypeForSuffix(filepath); KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, mime); if (!loader) { qWarning() << "Could not create loader for" << resourceType << filepath << mime; return 0; } - KoResourceSP res = loader->load(filepath, *resourceStore->device()); + KoResourceSP res = loader->load(filepath, *resourceStore->device(), KisGlobalResourcesInterface::instance()); resourceStore->close(); return res; } QImage KoResourceBundle::image() const { return m_thumbnail; } QString KoResourceBundle::filename() const { return m_filename; } diff --git a/libs/ui/KisPaintopPropertiesBase.cpp b/libs/ui/KisPaintopPropertiesBase.cpp index b31df4386c..fd94da5c20 100644 --- a/libs/ui/KisPaintopPropertiesBase.cpp +++ b/libs/ui/KisPaintopPropertiesBase.cpp @@ -1,45 +1,80 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisPaintopPropertiesBase.h" #include "kis_properties_configuration.h" +#include -KisPaintopPropertiesBase::~KisPaintopPropertiesBase() + +KisPaintopPropertiesResourcesBase::~KisPaintopPropertiesResourcesBase() { + } -void KisPaintopPropertiesBase::readOptionSetting(KisPropertiesConfigurationSP settings) +void KisPaintopPropertiesResourcesBase::readOptionSetting(KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) { - readOptionSettingImpl(settings.data()); + readOptionSettingResourceImpl(settings.data(), resourcesInterface); } -void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfigurationSP settings) const +void KisPaintopPropertiesResourcesBase::writeOptionSetting(KisPropertiesConfigurationSP settings) const { writeOptionSettingImpl(settings.data()); } +void KisPaintopPropertiesResourcesBase::readOptionSetting(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) +{ + readOptionSettingResourceImpl(settings, resourcesInterface); +} + +void KisPaintopPropertiesResourcesBase::writeOptionSetting(KisPropertiesConfiguration *settings) const +{ + writeOptionSettingImpl(settings); +} + +QList KisPaintopPropertiesResourcesBase::prepareResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const +{ + return prepareResourcesImpl(settings.data(), resourcesInterface); +} + +QList KisPaintopPropertiesResourcesBase::prepareResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const +{ + return prepareResourcesImpl(settings, resourcesInterface); +} + +void KisPaintopPropertiesBase::readOptionSetting(KisPropertiesConfigurationSP settings) +{ + readOptionSettingImpl(settings.data()); +} + void KisPaintopPropertiesBase::readOptionSetting(const KisPropertiesConfiguration *settings) { readOptionSettingImpl(settings); } -void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfiguration *settings) const +void KisPaintopPropertiesBase::readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) { - writeOptionSettingImpl(settings); + Q_UNUSED(resourcesInterface); + readOptionSettingImpl(settings); } +QList KisPaintopPropertiesBase::prepareResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const +{ + Q_UNUSED(settings); + Q_UNUSED(resourcesInterface); + return {}; +} diff --git a/libs/ui/KisPaintopPropertiesBase.h b/libs/ui/KisPaintopPropertiesBase.h index 93a305a557..5904c9da3d 100644 --- a/libs/ui/KisPaintopPropertiesBase.h +++ b/libs/ui/KisPaintopPropertiesBase.h @@ -1,59 +1,83 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISBASEOPTION_H #define KISBASEOPTION_H #include #include +class KoResource; +using KoResourceSP = QSharedPointer; + +class KisResourcesInterface; +using KisResourcesInterfaceSP = QSharedPointer; /** * This is a special base class for all the options that load/save * settings into a properties objects and do *not* store the properties * themselves. A KisPaintOpOption derived class generates a QWidget for * its configuration page. This cannot be created from a KisPaintO[ * * This class adapts your option to allow its easy use with * both raw pointers and shared pointers. * * Motivation: * In quite a lot of places we call some options from the KisPaintOpSettings * class itself with patting 'this' as a parameter into * read/writeOptionSetting(). Conversion of 'this' into a shared pointer is * extremely dangerous, and, ideally, should be prohibited. We cannot prohibit * it atm, but we still can create a special interface for accepting raw pointers, * which will be used automatically, when 'this' is passed. */ -class KRITAUI_EXPORT KisPaintopPropertiesBase + +class KRITAUI_EXPORT KisPaintopPropertiesResourcesBase { public: - virtual ~KisPaintopPropertiesBase(); + virtual ~KisPaintopPropertiesResourcesBase(); - void readOptionSetting(KisPropertiesConfigurationSP settings); + void readOptionSetting(KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface); void writeOptionSetting(KisPropertiesConfigurationSP setting) const; - void readOptionSetting(const KisPropertiesConfiguration *settings); + void readOptionSetting(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface); void writeOptionSetting(KisPropertiesConfiguration *settings) const; + QList prepareResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const; + QList prepareResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const; + protected: - virtual void readOptionSettingImpl(const KisPropertiesConfiguration *settings) = 0; + virtual void readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) = 0; virtual void writeOptionSettingImpl(KisPropertiesConfiguration *settings) const = 0; + virtual QList prepareResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const = 0; +}; + +class KRITAUI_EXPORT KisPaintopPropertiesBase : public KisPaintopPropertiesResourcesBase +{ +public: + void readOptionSetting(KisPropertiesConfigurationSP settings); + void readOptionSetting(const KisPropertiesConfiguration *settings); + +private: + void readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) override final; + QList prepareResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override final; + +protected: + virtual void readOptionSettingImpl(const KisPropertiesConfiguration *settings) = 0; }; #endif // KISBASEOPTION_H diff --git a/libs/ui/KisPaletteEditor.cpp b/libs/ui/KisPaletteEditor.cpp index 5c8dbef1b1..88c452ffa7 100644 --- a/libs/ui/KisPaletteEditor.cpp +++ b/libs/ui/KisPaletteEditor.cpp @@ -1,685 +1,686 @@ /* * Copyright (c) 2018 Michael Zhou * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisPaletteEditor.h" +#include struct KisPaletteEditor::PaletteInfo { QString name; QString filename; int columnCount; bool isGlobal; bool isReadOnly; QHash groups; }; struct KisPaletteEditor::Private { bool isGlobalModified {false}; bool isNameModified {false}; bool isFilenameModified {false}; bool isColumnCountModified {false}; QSet modifiedGroupNames; // key is original group name QSet newGroupNames; QSet keepColorGroups; QSet pathsToRemove; QString groupBeingRenamed; QPointer model; QPointer view; PaletteInfo modified; QPointer query; KoResourceServer *rServer {0}; QPalette normalPalette; QPalette warnPalette; }; KisPaletteEditor::KisPaletteEditor(QObject *parent) : QObject(parent) , m_d(new Private) { m_d->rServer = KoResourceServerProvider::instance()->paletteServer(); m_d->warnPalette.setColor(QPalette::Text, Qt::red); } KisPaletteEditor::~KisPaletteEditor() { } void KisPaletteEditor::setPaletteModel(KisPaletteModel *model) { if (!model) { return; } m_d->model = model; slotPaletteChanged(); connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged())); connect(model, SIGNAL(sigPaletteModified()), SLOT(slotSetDocumentModified())); } void KisPaletteEditor::setView(KisViewManager *view) { m_d->view = view; } void KisPaletteEditor::addPalette() { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; QFormLayout layout; dlg.mainWidget()->setLayout(&layout); QLabel lbl(i18nc("Label for line edit to set a palette name.","Name")); QLineEdit le(i18nc("Default name for a new palette","New Palette")); layout.addRow(&lbl, &le); QLabel lbl2(i18nc("Label for line edit to set a palette filename.","File Name")); QLineEdit le2(i18nc("Default file name for a new palette", "New Palette")); layout.addRow(&lbl2, &le2); QCheckBox chkSaveInDocument(i18n("Save Palette in the Current Document")); chkSaveInDocument.setChecked(false); layout.addRow(&chkSaveInDocument); if (dlg.exec() != QDialog::Accepted) { return; } KoColorSetSP newColorSet(new KoColorSet(newPaletteFileName(!chkSaveInDocument.isChecked(), le2.text()))); newColorSet->setPaletteType(KoColorSet::KPL); newColorSet->setIsGlobal(!chkSaveInDocument.isChecked()); newColorSet->setIsEditable(true); newColorSet->setValid(true); newColorSet->setName(le.text()); m_d->rServer->addResource(newColorSet, !chkSaveInDocument.isChecked()); //m_d->rServer->removeFromBlacklist(newColorSet); uploadPaletteList(); } void KisPaletteEditor::importPalette() { KoFileDialog dialog(0, KoFileDialog::OpenFile, "Open Palette"); dialog.setDefaultDir(QDir::homePath()); dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset" << "application/x-gimp-color-palette"); QString filename = dialog.filename(); if (filename.isEmpty()) { return; } if (duplicateExistsFilename(filename, false)) { QMessageBox message; message.setWindowTitle(i18n("Can't Import Palette")); message.setText(i18n("Can't import palette: there's already imported with the same filename")); message.exec(); return; } QMessageBox messageBox; messageBox.setText(i18n("Do you want to store this palette in your current image?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); bool global = (messageBox.exec() == QMessageBox::Yes); KoColorSetSP colorSet(new KoColorSet(filename)); - colorSet->load(); + colorSet->load(KisGlobalResourcesInterface::instance()); QString name = filenameFromPath(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(global)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(global); m_d->rServer->addResource(colorSet, global); //m_d->rServer->removeFromBlacklist(colorSet); uploadPaletteList(); } void KisPaletteEditor::removePalette(KoColorSetSP cs) { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!cs || !cs->isEditable()) { return; } m_d->rServer->removeResourceFromServer(cs); uploadPaletteList(); } int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const { if (!m_d->modified.groups.contains(oriName)) { return 0; } return m_d->modified.groups[oriName].rowCount(); } bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const { if (name == m_d->groupBeingRenamed) { return false; } Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) { if (name == g.name()) { return true; } } return false; } bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const { return m_d->modified.groups.contains(name); } QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const { Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) { if (m_d->modified.groups[oldGroupName].name() == newName) { return oldGroupName; } } return QString(); } void KisPaletteEditor::rename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isNameModified = true; m_d->modified.name = newName; } void KisPaletteEditor::changeFilename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isFilenameModified = true; m_d->pathsToRemove.insert(m_d->modified.filename); if (m_d->modified.isGlobal) { m_d->modified.filename = m_d->rServer->saveLocation() + newName; } else { m_d->modified.filename = newName; } } void KisPaletteEditor::changeColCount(int newCount) { m_d->isColumnCountModified = true; m_d->modified.columnCount = newCount; } QString KisPaletteEditor::addGroup() { KoDialog dlg; m_d->query = &dlg; QVBoxLayout layout(&dlg); dlg.mainWidget()->setLayout(&layout); QLabel lblName(i18n("Name"), &dlg); layout.addWidget(&lblName); QLineEdit leName(&dlg); leName.setText(newGroupName()); connect(&leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); layout.addWidget(&leName); QLabel lblRowCount(i18n("Row count"), &dlg); layout.addWidget(&lblRowCount); QSpinBox spxRow(&dlg); spxRow.setValue(20); layout.addWidget(&spxRow); if (dlg.exec() != QDialog::Accepted) { return QString(); } if (duplicateExistsGroupName(leName.text())) { return QString(); } QString realName = leName.text(); QString name = realName; if (duplicateExistsOriginalGroupName(name)) { name = newGroupName(); } m_d->modified.groups[name] = KisSwatchGroup(); KisSwatchGroup &newGroup = m_d->modified.groups[name]; newGroup.setName(realName); m_d->newGroupNames.insert(name); newGroup.setRowCount(spxRow.value()); return realName; } bool KisPaletteEditor::removeGroup(const QString &name) { KoDialog window; window.setWindowTitle(i18nc("@title:window", "Removing Group")); QFormLayout editableItems(&window); QCheckBox chkKeep(&window); window.mainWidget()->setLayout(&editableItems); editableItems.addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), &chkKeep); if (window.exec() != KoDialog::Accepted) { return false; } m_d->modified.groups.remove(name); m_d->newGroupNames.remove(name); if (chkKeep.isChecked()) { m_d->keepColorGroups.insert(name); } return true; } QString KisPaletteEditor::renameGroup(const QString &oldName) { if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); } KoDialog dlg; m_d->query = &dlg; m_d->groupBeingRenamed = m_d->modified.groups[oldName].name(); QFormLayout form(&dlg); dlg.mainWidget()->setLayout(&form); QLineEdit leNewName; connect(&leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); leNewName.setText(m_d->modified.groups[oldName].name()); form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName); if (dlg.exec() != KoDialog::Accepted) { return QString(); } if (leNewName.text().isEmpty()) { return QString(); } if (duplicateExistsGroupName(leNewName.text())) { return QString(); } m_d->modified.groups[oldName].setName(leNewName.text()); m_d->modifiedGroupNames.insert(oldName); return leNewName.text(); } void KisPaletteEditor::slotGroupNameChanged(const QString &newName) { QLineEdit *leGroupName = qobject_cast(sender()); if (duplicateExistsGroupName(newName) || newName == QString()) { leGroupName->setPalette(m_d->warnPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(false); } return; } leGroupName->setPalette(m_d->normalPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(true); } } void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount) { if (!m_d->modified.groups.contains(name)) { return; } m_d->modified.groups[name].setRowCount(newRowCount); m_d->modifiedGroupNames.insert(name); } void KisPaletteEditor::setGlobal(bool isGlobal) { m_d->isGlobalModified = true; m_d->modified.isGlobal = isGlobal; } void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KisSwatch c = KisSwatch(color); c.setId(QString::number(m_d->model->colorSet()->colorCount() + 1)); c.setName(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); m_d->model->setEntry(c, index); } void KisPaletteEditor::slotSetDocumentModified() { m_d->view->document()->setModified(true); } void KisPaletteEditor::removeEntry(const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { removeGroup(qvariant_cast(index.data(KisPaletteModel::GroupNameRole))); updatePalette(); } else { m_d->model->removeEntry(index, false); } if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } } void KisPaletteEditor::modifyEntry(const QModelIndex &index) { if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; dlg.setCaption(i18nc("@title:window", "Add a Color")); QFormLayout *editableItems = new QFormLayout(&dlg); dlg.mainWidget()->setLayout(editableItems); QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { renameGroup(groupName); updatePalette(); } else { QLineEdit *lnIDName = new QLineEdit(&dlg); QLineEdit *lnGroupName = new QLineEdit(&dlg); KisColorButton *bnColor = new KisColorButton(&dlg); QCheckBox *chkSpot = new QCheckBox(&dlg); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); KisSwatch entry = m_d->model->getEntry(index); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18nc("Name for a swatch group", "Swatch group name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot color"), chkSpot); lnGroupName->setText(entry.name()); lnIDName->setText(entry.id()); bnColor->setColor(entry.color()); chkSpot->setChecked(entry.spotColor()); if (dlg.exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->setEntry(entry, index); } } } void KisPaletteEditor::addEntry(const KoColor &color) { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()->isEditable()) { return; } KoDialog window; window.setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout editableItems(&window); window.mainWidget()->setLayout(&editableItems); QComboBox cmbGroups(&window); cmbGroups.addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit lnIDName(&window); QLineEdit lnName(&window); KisColorButton bnColor(&window); QCheckBox chkSpot(&window); chkSpot.setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems.addRow(i18n("Group"), &cmbGroups); editableItems.addRow(i18n("ID"), &lnIDName); editableItems.addRow(i18n("Name"), &lnName); editableItems.addRow(i18n("Color"), &bnColor); editableItems.addRow(i18nc("Spot color", "Spot"), &chkSpot); cmbGroups.setCurrentIndex(0); lnName.setText(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); lnIDName.setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); bnColor.setColor(color); chkSpot.setChecked(false); if (window.exec() != KoDialog::Accepted) { return; } QString groupName = cmbGroups.currentText(); KisSwatch newEntry; newEntry.setColor(bnColor.color()); newEntry.setName(lnName.text()); newEntry.setId(lnIDName.text()); newEntry.setSpotColor(chkSpot.isChecked()); m_d->model->addEntry(newEntry, groupName); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } m_d->modifiedGroupNames.insert(groupName); m_d->modified.groups[groupName].addEntry(newEntry); } void KisPaletteEditor::updatePalette() { Q_ASSERT(m_d->model); Q_ASSERT(m_d->model->colorSet()); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoColorSetSP palette = m_d->model->colorSet(); PaletteInfo &modified = m_d->modified; if (m_d->isColumnCountModified) { palette->setColumnCount(modified.columnCount); } if (m_d->isNameModified) { palette->setName(modified.name); } if (m_d->isFilenameModified) { QString originalPath = palette->filename(); palette->setFilename(modified.filename); if (palette->isGlobal()) { if (!palette->save()) { palette->setFilename(newPaletteFileName(true)); palette->save(); } QFile::remove(originalPath); } } if (m_d->isGlobalModified) { palette->setIsGlobal(modified.isGlobal); if (modified.isGlobal) { setGlobal(); } else { setNonGlobal(); } } Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (!modified.groups.contains(groupName)) { m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName)); } } m_d->keepColorGroups.clear(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (m_d->modifiedGroupNames.contains(groupName)) { m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount()); if (groupName != modified.groups[groupName].name()) { m_d->model->renameGroup(groupName, modified.groups[groupName].name()); modified.groups[modified.groups[groupName].name()] = modified.groups[groupName]; modified.groups.remove(groupName); } } } m_d->modifiedGroupNames.clear(); Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) { m_d->model->addGroup(modified.groups[newGroupName]); } m_d->newGroupNames.clear(); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); } } void KisPaletteEditor::slotPaletteChanged() { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()) { return; } KoColorSetSP palette = m_d->model->colorSet(); m_d->modified.groups.clear(); m_d->keepColorGroups.clear(); m_d->newGroupNames.clear(); m_d->modifiedGroupNames.clear(); m_d->modified.name = palette->name(); m_d->modified.filename = palette->filename(); m_d->modified.columnCount = palette->columnCount(); m_d->modified.isGlobal = palette->isGlobal(); m_d->modified.isReadOnly = !palette->isEditable(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { KisSwatchGroup *cs = palette->getGroup(groupName); m_d->modified.groups[groupName] = KisSwatchGroup(*cs); } } void KisPaletteEditor::setGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSetSP colorSet = m_d->model->colorSet(); QString saveLocation = m_d->rServer->saveLocation(); QString name = filenameFromPath(colorSet->filename()); QFileInfo fileInfo(saveLocation + name); colorSet->setFilename(fileInfo.filePath()); colorSet->setIsGlobal(true); //m_d->rServer->removeFromBlacklist(colorSet); if (!colorSet->save()) { QMessageBox message; message.setWindowTitle(i18n("Saving palette failed")); message.setText(i18n("Failed to save global palette file. Please set it to non-global, or you will lose the file when you close Krita")); message.exec(); } uploadPaletteList(); } bool KisPaletteEditor::duplicateExistsFilename(const QString &filename, bool global) const { QString prefix; if (global) { prefix = m_d->rServer->saveLocation(); } Q_FOREACH (const KoResourceSP r, KoResourceServerProvider::instance()->paletteServer()->resources()) { if (r->filename() == prefix + filename && r != m_d->model->colorSet()) { return true; } } return false; } QString KisPaletteEditor::relativePathFromSaveLocation() const { return filenameFromPath(m_d->modified.filename); } void KisPaletteEditor::setNonGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSetSP colorSet = m_d->model->colorSet(); QString name = filenameFromPath(colorSet->filename()); QFile::remove(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(false)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(false); uploadPaletteList(); } QString KisPaletteEditor::newPaletteFileName(bool isGlobal, const QString &filename) { QSet nameSet; Q_FOREACH (const KoResourceSP r, m_d->rServer->resources()) { nameSet.insert(r->filename()); } KoColorSet tmpColorSet; QString result = (filename.isEmpty() ? "new_palette" : filename); if (isGlobal) { result = m_d->rServer->saveLocation() + result; } int i = 0; while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) { i++; } result = result + (i > 0 ? QString::number(i) : "") + tmpColorSet.defaultFileExtension(); return result; } QString KisPaletteEditor::newGroupName() const { int i = 1; QString groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); while (m_d->modified.groups.contains(groupname)) { i++; groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); } return groupname; } void KisPaletteEditor::uploadPaletteList() const { QList list; Q_FOREACH (KoResourceSP paletteResource, m_d->rServer->resources()) { KoColorSetSP palette = paletteResource.staticCast(); Q_ASSERT(palette); if (!palette->isGlobal()) { list.append(palette); } } m_d->view->document()->setPaletteList(list); } QString KisPaletteEditor::filenameFromPath(const QString &path) const { return QDir::fromNativeSeparators(path).section('/', -1, -1); } diff --git a/libs/ui/KisWindowLayoutResource.cpp b/libs/ui/KisWindowLayoutResource.cpp index 6405381327..97631d39c4 100644 --- a/libs/ui/KisWindowLayoutResource.cpp +++ b/libs/ui/KisWindowLayoutResource.cpp @@ -1,444 +1,446 @@ /* * Copyright (c) 2018 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisWindowLayoutResource.h" #include "KisWindowLayoutManager.h" #include #include #include #include #include #include #include #include #include #include #include static const int WINDOW_LAYOUT_VERSION = 1; struct KisWindowLayoutResource::Private { struct WindowGeometry{ int screen = -1; Qt::WindowStates stateFlags = Qt::WindowNoState; QByteArray data; static WindowGeometry fromWindow(const QWidget *window, QList screens) { WindowGeometry geometry; QWindow *windowHandle = window->windowHandle(); geometry.data = window->saveGeometry(); geometry.stateFlags = windowHandle->windowState(); int index = screens.indexOf(windowHandle->screen()); if (index >= 0) { geometry.screen = index; } return geometry; } void forceOntoCorrectScreen(QWidget *window, QList screens) { QWindow *windowHandle = window->windowHandle(); if (screens.indexOf(windowHandle->screen()) != screen) { QScreen *qScreen = screens[screen]; windowHandle->setScreen(qScreen); windowHandle->setPosition(qScreen->availableGeometry().topLeft()); } if (stateFlags) { window->setWindowState(stateFlags); } } void save(QDomDocument &doc, QDomElement &elem) const { if (screen >= 0) { elem.setAttribute("screen", screen); } if (stateFlags & Qt::WindowMaximized) { elem.setAttribute("maximized", "1"); } QDomElement geometry = doc.createElement("geometry"); geometry.appendChild(doc.createCDATASection(data.toBase64())); elem.appendChild(geometry); } static WindowGeometry load(const QDomElement &element) { WindowGeometry geometry; geometry.screen = element.attribute("screen", "-1").toInt(); if (element.attribute("maximized", "0") != "0") { geometry.stateFlags |= Qt::WindowMaximized; } QDomElement dataElement = element.firstChildElement("geometry"); geometry.data = QByteArray::fromBase64(dataElement.text().toLatin1()); return geometry; } }; struct Window { QUuid windowId; QByteArray windowState; WindowGeometry geometry; bool canvasDetached = false; WindowGeometry canvasWindowGeometry; }; QVector windows; bool showImageInAllWindows {false}; bool primaryWorkspaceFollowsFocus {false}; QUuid primaryWindow; Private() = default; explicit Private(QVector windows) : windows(std::move(windows)) {} void openNecessaryWindows(QList> ¤tWindows) { auto *kisPart = KisPart::instance(); Q_FOREACH(const Window &window, windows) { QPointer mainWindow = kisPart->windowById(window.windowId); if (mainWindow.isNull()) { mainWindow = kisPart->createMainWindow(window.windowId); currentWindows.append(mainWindow); mainWindow->show(); } } } void closeUnneededWindows(QList> ¤tWindows) { QVector> windowsToClose; Q_FOREACH(KisMainWindow *mainWindow, currentWindows) { bool keep = false; Q_FOREACH(const Window &window, windows) { if (window.windowId == mainWindow->id()) { keep = true; break; } } if (!keep) { windowsToClose.append(mainWindow); // Set the window hidden to prevent "show image in all windows" feature from opening new views on it // while we migrate views onto the remaining windows mainWindow->hide(); } } migrateViewsFromClosingWindows(windowsToClose); Q_FOREACH(QPointer mainWindow, windowsToClose) { mainWindow->close(); } } void migrateViewsFromClosingWindows(QVector> &closingWindows) const { auto *kisPart = KisPart::instance(); KisMainWindow *migrationTarget = nullptr; Q_FOREACH(KisMainWindow *mainWindow, kisPart->mainWindows()) { if (!closingWindows.contains(mainWindow)) { migrationTarget = mainWindow; break; } } if (!migrationTarget) { qWarning() << "Problem: window layout with no windows would leave user with zero main windows."; migrationTarget = closingWindows.takeLast(); migrationTarget->show(); } QVector visibleDocuments; Q_FOREACH(KisView *view, kisPart->views()) { KisMainWindow *window = view->mainWindow(); if (!closingWindows.contains(window)) { visibleDocuments.append(view->document()); } } Q_FOREACH(KisDocument *document, kisPart->documents()) { if (!visibleDocuments.contains(document)) { visibleDocuments.append(document); migrationTarget->newView(document); } } } QList getScreensInConsistentOrder() { QList screens = QGuiApplication::screens(); std::sort(screens.begin(), screens.end(), [](const QScreen *a, const QScreen *b) { QRect aRect = a->geometry(); QRect bRect = b->geometry(); if (aRect.y() == bRect.y()) return aRect.x() < bRect.x(); return (aRect.y() < aRect.y()); }); return screens; } }; KisWindowLayoutResource::KisWindowLayoutResource(const QString &filename) : KoResource(filename) , d(new Private) {} KisWindowLayoutResource::~KisWindowLayoutResource() {} KisWindowLayoutResource::KisWindowLayoutResource(const KisWindowLayoutResource &rhs) : KoResource(rhs) , d(new Private) { *this = rhs; } KisWindowLayoutResource &KisWindowLayoutResource::operator=(const KisWindowLayoutResource &rhs) { if (*this != rhs) { d->windows = rhs.d->windows; d->primaryWindow = rhs.d->primaryWindow; d->showImageInAllWindows = rhs.d->showImageInAllWindows; d->primaryWorkspaceFollowsFocus = rhs.d->primaryWorkspaceFollowsFocus; } return *this; } KoResourceSP KisWindowLayoutResource::clone() const { return KoResourceSP(new KisWindowLayoutResource(*this)); } KisWindowLayoutResourceSP KisWindowLayoutResource::fromCurrentWindows( const QString &filename, const QList> &mainWindows, bool showImageInAllWindows, bool primaryWorkspaceFollowsFocus, KisMainWindow *primaryWindow ) { KisWindowLayoutResourceSP resource(new KisWindowLayoutResource(filename)); resource->setWindows(mainWindows); resource->d->showImageInAllWindows = showImageInAllWindows; resource->d->primaryWorkspaceFollowsFocus = primaryWorkspaceFollowsFocus; resource->d->primaryWindow = primaryWindow->id(); return resource; } void KisWindowLayoutResource::applyLayout() { auto *kisPart = KisPart::instance(); auto *layoutManager= KisWindowLayoutManager::instance(); layoutManager->setLastUsedLayout(this); QList> currentWindows = kisPart->mainWindows(); if (d->windows.isEmpty()) { // No windows defined (e.g. fresh new session). Leave things as they are, but make sure there's at least one visible main window if (kisPart->mainwindowCount() == 0) { kisPart->createMainWindow(); } else { kisPart->mainWindows().first()->show(); } } else { d->openNecessaryWindows(currentWindows); d->closeUnneededWindows(currentWindows); } // Wait for the windows to finish opening / closing before applying saved geometry. // If we don't, the geometry may get reset after we apply it. QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); Q_FOREACH(const auto &window, d->windows) { QPointer mainWindow = kisPart->windowById(window.windowId); KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow); mainWindow->restoreGeometry(window.geometry.data); mainWindow->restoreWorkspaceState(window.windowState); mainWindow->setCanvasDetached(window.canvasDetached); if (window.canvasDetached) { QWidget *canvasWindow = mainWindow->canvasWindow(); canvasWindow->restoreGeometry(window.canvasWindowGeometry.data); } } QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QList screens = d->getScreensInConsistentOrder(); Q_FOREACH(const auto &window, d->windows) { Private::WindowGeometry geometry = window.geometry; QPointer mainWindow = kisPart->windowById(window.windowId); KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow); if (geometry.screen >= 0 && geometry.screen < screens.size()) { geometry.forceOntoCorrectScreen(mainWindow, screens); } if (window.canvasDetached) { Private::WindowGeometry canvasWindowGeometry = window.canvasWindowGeometry; if (canvasWindowGeometry.screen >= 0 && canvasWindowGeometry.screen < screens.size()) { canvasWindowGeometry.forceOntoCorrectScreen(mainWindow->canvasWindow(), screens); } } } layoutManager->setShowImageInAllWindowsEnabled(d->showImageInAllWindows); layoutManager->setPrimaryWorkspaceFollowsFocus(d->primaryWorkspaceFollowsFocus, d->primaryWindow); } bool KisWindowLayoutResource::saveToDevice(QIODevice *dev) const { QDomDocument doc; QDomElement root = doc.createElement("WindowLayout"); root.setAttribute("name", name()); root.setAttribute("version", WINDOW_LAYOUT_VERSION); saveXml(doc, root); doc.appendChild(root); QTextStream textStream(dev); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(dev); return true; } -bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev) +bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); d->windows.clear(); loadXml(element); setValid(true); return true; } void KisWindowLayoutResource::saveXml(QDomDocument &doc, QDomElement &root) const { root.setAttribute("showImageInAllWindows", (int)d->showImageInAllWindows); root.setAttribute("primaryWorkspaceFollowsFocus", (int)d->primaryWorkspaceFollowsFocus); root.setAttribute("primaryWindow", d->primaryWindow.toString()); Q_FOREACH(const auto &window, d->windows) { QDomElement elem = doc.createElement("window"); elem.setAttribute("id", window.windowId.toString()); window.geometry.save(doc, elem); if (window.canvasDetached) { QDomElement canvasWindowElement = doc.createElement("canvasWindow"); window.canvasWindowGeometry.save(doc, canvasWindowElement); elem.appendChild(canvasWindowElement); } QDomElement state = doc.createElement("windowState"); state.appendChild(doc.createCDATASection(window.windowState.toBase64())); elem.appendChild(state); root.appendChild(elem); } } void KisWindowLayoutResource::loadXml(const QDomElement &element) const { d->showImageInAllWindows = KisDomUtils::toInt(element.attribute("showImageInAllWindows", "0")); d->primaryWorkspaceFollowsFocus = KisDomUtils::toInt(element.attribute("primaryWorkspaceFollowsFocus", "0")); d->primaryWindow = element.attribute("primaryWindow"); for (auto windowElement = element.firstChildElement("window"); !windowElement.isNull(); windowElement = windowElement.nextSiblingElement("window")) { Private::Window window; window.windowId = QUuid(windowElement.attribute("id", QUuid().toString())); if (window.windowId.isNull()) { window.windowId = QUuid::createUuid(); } window.geometry = Private::WindowGeometry::load(windowElement); QDomElement canvasWindowElement = windowElement.firstChildElement("canvasWindow"); if (!canvasWindowElement.isNull()) { window.canvasDetached = true; window.canvasWindowGeometry = Private::WindowGeometry::load(canvasWindowElement); } QDomElement state = windowElement.firstChildElement("windowState"); window.windowState = QByteArray::fromBase64(state.text().toLatin1()); d->windows.append(window); } } QString KisWindowLayoutResource::defaultFileExtension() const { return QString(".kwl"); } void KisWindowLayoutResource::setWindows(const QList> &mainWindows) { d->windows.clear(); QList screens = d->getScreensInConsistentOrder(); Q_FOREACH(auto window, mainWindows) { if (!window->isVisible()) continue; Private::Window state; state.windowId = window->id(); state.windowState = window->saveState(); state.geometry = Private::WindowGeometry::fromWindow(window, screens); state.canvasDetached = window->canvasDetached(); if (state.canvasDetached) { state.canvasWindowGeometry = Private::WindowGeometry::fromWindow(window->canvasWindow(), screens); } d->windows.append(state); } } diff --git a/libs/ui/KisWindowLayoutResource.h b/libs/ui/KisWindowLayoutResource.h index 23ce4e8528..5672efa565 100644 --- a/libs/ui/KisWindowLayoutResource.h +++ b/libs/ui/KisWindowLayoutResource.h @@ -1,71 +1,71 @@ /* * Copyright (c) 2018 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISWINDOWLAYOUTRESOURCE_H #define KISWINDOWLAYOUTRESOURCE_H #include #include class KisWindowLayoutResource; typedef QSharedPointer KisWindowLayoutResourceSP; class KisWindowLayoutResource : public KoResource { public: explicit KisWindowLayoutResource(const QString &filename); ~KisWindowLayoutResource() override; KisWindowLayoutResource(const KisWindowLayoutResource &rhs); KisWindowLayoutResource &operator=(const KisWindowLayoutResource &rhs); KoResourceSP clone() const override; static KisWindowLayoutResourceSP fromCurrentWindows ( const QString &filename, const QList> &mainWindows, bool showImageInAllWindows, bool primaryWorkspaceFollowsFocus, KisMainWindow *primaryWindow ); void applyLayout(); bool saveToDevice(QIODevice *dev) const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; QPair resourceType() const override { return QPair(ResourceType::WindowLayouts, ""); } QString defaultFileExtension() const override; protected: void setWindows(const QList> &mainWindows); virtual void saveXml(QDomDocument &doc, QDomElement &root) const; virtual void loadXml(const QDomElement &root) const; private: struct Private; QScopedPointer d; }; #endif diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc index 2b6a9d8520..b2b4e4525a 100644 --- a/libs/ui/kis_paintop_box.cc +++ b/libs/ui/kis_paintop_box.cc @@ -1,1390 +1,1391 @@ /* * kis_paintop_box.cc - part of KImageShop/Krayon/Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com) * Copyright (c) 2010 Lukáš Tvrdý * Copyright (C) 2011 Silvio Heinrich * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (c) 2014 Mohit Goyal * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_paintop_box.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 "kis_canvas2.h" #include "kis_node_manager.h" #include "KisViewManager.h" #include "kis_canvas_resource_provider.h" #include "KisResourceServerProvider.h" #include "kis_favorite_resource_manager.h" #include "kis_config.h" #include "KisPopupButton.h" #include "widgets/kis_iconwidget.h" #include "widgets/kis_tool_options_popup.h" #include "widgets/kis_paintop_presets_popup.h" #include "widgets/kis_paintop_presets_chooser_popup.h" #include "widgets/kis_workspace_chooser.h" #include "widgets/kis_paintop_list_widget.h" #include "widgets/kis_slider_spin_box.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_widget_chooser.h" #include "tool/kis_tool.h" #include "kis_signals_blocker.h" #include "kis_action_manager.h" #include "KisHighlightedToolButton.h" +#include KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name) : QWidget(parent) , m_resourceProvider(view->canvasResourceProvider()) , m_optionWidget(0) , m_toolOptionsPopupButton(0) , m_brushEditorPopupButton(0) , m_presetSelectorPopupButton(0) , m_toolOptionsPopup(0) , m_viewManager(view) , m_previousNode(0) , m_currTabletToolID(KoInputDevice::invalid()) , m_presetsEnabled(true) , m_blockUpdate(false) , m_dirtyPresetsEnabled(false) , m_eraserBrushSizeEnabled(false) , m_eraserBrushOpacityEnabled(false) { Q_ASSERT(view != 0); setObjectName(name); KisConfig cfg(true); m_dirtyPresetsEnabled = cfg.useDirtyPresets(); m_eraserBrushSizeEnabled = cfg.useEraserBrushSize(); m_eraserBrushOpacityEnabled = cfg.useEraserBrushOpacity(); KAcceleratorManager::setNoAccel(this); setWindowTitle(i18n("Painter's Toolchest")); m_favoriteResourceManager = new KisFavoriteResourceManager(this); KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff"); int iconsize = grp.readEntry("IconSize", 32); if (!cfg.toolOptionsInDocker()) { m_toolOptionsPopupButton = new KisPopupButton(this); m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure")); m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings")); m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize); } m_brushEditorPopupButton = new KisIconWidget(this); m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02")); m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings")); m_brushEditorPopupButton->setFixedSize(iconsize, iconsize); m_presetSelectorPopupButton = new KisPopupButton(this); m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01")); m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset")); m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize); m_eraseModeButton = new KisHighlightedToolButton(this); m_eraseModeButton->setFixedSize(iconsize, iconsize); m_eraseModeButton->setCheckable(true); m_eraseAction = m_viewManager->actionManager()->createAction("erase_action"); m_eraseModeButton->setDefaultAction(m_eraseAction); m_reloadButton = new QToolButton(this); m_reloadButton->setFixedSize(iconsize, iconsize); m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action"); m_reloadButton->setDefaultAction(m_reloadAction); m_alphaLockButton = new KisHighlightedToolButton(this); m_alphaLockButton->setFixedSize(iconsize, iconsize); m_alphaLockButton->setCheckable(true); KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha"); m_alphaLockButton->setDefaultAction(alphaLockAction); // horizontal and vertical mirror toolbar buttons // mirror tool options for the X Mirror QMenu *toolbarMenuXMirror = new QMenu(); hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations"); toolbarMenuXMirror->addAction(hideCanvasDecorationsX); lockActionX = m_viewManager->actionManager()->createAction("mirrorX-lock"); toolbarMenuXMirror->addAction(lockActionX); moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter"); toolbarMenuXMirror->addAction(moveToCenterActionX); // mirror tool options for the Y Mirror QMenu *toolbarMenuYMirror = new QMenu(); hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations"); toolbarMenuYMirror->addAction(hideCanvasDecorationsY); lockActionY = m_viewManager->actionManager()->createAction("mirrorY-lock"); toolbarMenuYMirror->addAction(lockActionY); moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter"); toolbarMenuYMirror->addAction(moveToCenterActionY); // create horizontal and vertical mirror buttons m_hMirrorButton = new KisHighlightedToolButton(this); int menuPadding = 10; m_hMirrorButton->setFixedSize(iconsize + menuPadding, iconsize); m_hMirrorButton->setCheckable(true); m_hMirrorAction = m_viewManager->actionManager()->createAction("hmirror_action"); m_hMirrorButton->setDefaultAction(m_hMirrorAction); m_hMirrorButton->setMenu(toolbarMenuXMirror); m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup); m_vMirrorButton = new KisHighlightedToolButton(this); m_vMirrorButton->setFixedSize(iconsize + menuPadding, iconsize); m_vMirrorButton->setCheckable(true); m_vMirrorAction = m_viewManager->actionManager()->createAction("vmirror_action"); m_vMirrorButton->setDefaultAction(m_vMirrorAction); m_vMirrorButton->setMenu(toolbarMenuYMirror); m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup); // add connections for horizontal and mirrror buttons connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool))); connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool))); connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX())); connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY())); connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool))); connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool))); const bool sliderLabels = cfg.sliderLabels(); int sliderWidth; if (sliderLabels) { sliderWidth = 150 * logicalDpiX() / 96; } else { sliderWidth = 120 * logicalDpiX() / 96; } for (int i = 0; i < 3; ++i) { m_sliderChooser[i] = new KisWidgetChooser(i + 1); KisDoubleSliderSpinBox* slOpacity; KisDoubleSliderSpinBox* slFlow; KisDoubleSliderSpinBox* slSize; if (sliderLabels) { slOpacity = m_sliderChooser[i]->addWidget("opacity"); slFlow = m_sliderChooser[i]->addWidget("flow"); slSize = m_sliderChooser[i]->addWidget("size"); slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:"))); slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:"))); slSize->setPrefix(QString("%1 ").arg(i18n("Size:"))); } else { slOpacity = m_sliderChooser[i]->addWidget("opacity", i18n("Opacity:")); slFlow = m_sliderChooser[i]->addWidget("flow", i18n("Flow:")); slSize = m_sliderChooser[i]->addWidget("size", i18n("Size:")); } slOpacity->setRange(0, 100, 0); slOpacity->setValue(100); slOpacity->setSingleStep(5); slOpacity->setSuffix(i18n("%")); slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width())); slOpacity->setFixedHeight(iconsize); slOpacity->setBlockUpdateSignalOnDrag(true); slFlow->setRange(0, 100, 0); slFlow->setValue(100); slFlow->setSingleStep(5); slFlow->setSuffix(i18n("%")); slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width())); slFlow->setFixedHeight(iconsize); slFlow->setBlockUpdateSignalOnDrag(true); slSize->setRange(0.01, cfg.readEntry("maximumBrushSize", 1000), 2); slSize->setValue(100); slSize->setSingleStep(1); slSize->setExponentRatio(3.0); slSize->setSuffix(i18n(" px")); slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width())); slSize->setFixedHeight(iconsize); slSize->setBlockUpdateSignalOnDrag(true); m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1)); } m_cmbCompositeOp = new KisCompositeOpComboBox(); m_cmbCompositeOp->setFixedHeight(iconsize); Q_FOREACH (KisAction * a, m_cmbCompositeOp->createBlendmodeActions()) { m_viewManager->actionManager()->addAction(a->text(), a); } m_workspaceWidget = new KisPopupButton(this); m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose")); m_workspaceWidget->setToolTip(i18n("Choose workspace")); m_workspaceWidget->setFixedSize(iconsize, iconsize); m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view)); QHBoxLayout* baseLayout = new QHBoxLayout(this); m_paintopWidget = new QWidget(this); baseLayout->addWidget(m_paintopWidget); baseLayout->setSpacing(4); baseLayout->setContentsMargins(0, 0, 0, 0); m_layout = new QHBoxLayout(m_paintopWidget); if (!cfg.toolOptionsInDocker()) { m_layout->addWidget(m_toolOptionsPopupButton); } m_layout->addWidget(m_brushEditorPopupButton); m_layout->addWidget(m_presetSelectorPopupButton); m_layout->setSpacing(4); m_layout->setContentsMargins(0, 0, 0, 0); QWidget* compositeActions = new QWidget(this); QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions); compositeLayout->addWidget(m_cmbCompositeOp); compositeLayout->addWidget(m_eraseModeButton); compositeLayout->addWidget(m_alphaLockButton); compositeLayout->setSpacing(4); compositeLayout->setContentsMargins(0, 0, 0, 0); compositeLayout->addWidget(m_reloadButton); QWidgetAction * action; action = new QWidgetAction(this); view->actionCollection()->addAction("composite_actions", action); action->setText(i18n("Brush composite")); action->setDefaultWidget(compositeActions); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider1", action); view->actionCollection()->addAction("brushslider1", action); action->setDefaultWidget(m_sliderChooser[0]); connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider2", action); view->actionCollection()->addAction("brushslider2", action); action->setDefaultWidget(m_sliderChooser[1]); connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider3", action); view->actionCollection()->addAction("brushslider3", action); action->setDefaultWidget(m_sliderChooser[2]); connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action); view->actionCollection()->addAction("next_favorite_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action); view->actionCollection()->addAction("previous_favorite_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("previous_preset", action); view->actionCollection()->addAction("previous_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset())); if (!cfg.toolOptionsInDocker()) { action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_tool_options", action); view->actionCollection()->addAction("show_tool_options", action); connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget())); } action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_brush_editor", action); view->actionCollection()->addAction("show_brush_editor", action); connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_brush_presets", action); view->actionCollection()->addAction("show_brush_presets", action); connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget())); QWidget* mirrorActions = new QWidget(this); QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions); mirrorLayout->addWidget(m_hMirrorButton); mirrorLayout->addWidget(m_vMirrorButton); mirrorLayout->setSpacing(4); mirrorLayout->setContentsMargins(0, 0, 0, 0); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("mirror_actions", action); action->setDefaultWidget(mirrorActions); view->actionCollection()->addAction("mirror_actions", action); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction(ResourceType::Workspaces, action); view->actionCollection()->addAction(ResourceType::Workspaces, action); action->setDefaultWidget(m_workspaceWidget); if (!cfg.toolOptionsInDocker()) { m_toolOptionsPopup = new KisToolOptionsPopup(); m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup); m_toolOptionsPopup->switchDetached(false); } m_savePresetWidget = new KisPresetSaveWidget(this); m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider, m_favoriteResourceManager, m_savePresetWidget); m_brushEditorPopupButton->setPopupWidget(m_presetsPopup); m_presetsPopup->parentWidget()->setWindowTitle(i18n("Brush Editor")); connect(m_presetsPopup, SIGNAL(brushEditorShown()), SLOT(slotUpdateOptionsWidgetPopup())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(updateThemedIcons())); m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup(); m_presetsChooserPopup->setMinimumHeight(550); m_presetsChooserPopup->setMinimumWidth(450); m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup); m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); slotNodeChanged(view->activeNode()); // Get all the paintops QList keys = KisPaintOpRegistry::instance()->keys(); QList factoryList; Q_FOREACH (const QString & paintopId, keys) { factoryList.append(KisPaintOpRegistry::instance()->get(paintopId)); } m_presetsPopup->setPaintOpList(factoryList); connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString))); connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset())); connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResourceSP )), SLOT(resourceSelected(KoResourceSP ))); connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset())); connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool))); connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool))); connect(m_presetsPopup , SIGNAL(eraserBrushOpacityToggled(bool)) , SLOT(slotEraserBrushOpacityToggled(bool))); connect(m_presetsPopup, SIGNAL(createPresetFromScratch(QString)), this, SLOT(slotCreatePresetFromScratch(QString))); connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResourceSP )) , SLOT(resourceSelected(KoResourceSP ))); connect(m_presetsChooserPopup, SIGNAL(resourceClicked(KoResourceSP )) , SLOT(resourceSelected(KoResourceSP ))); connect(m_resourceProvider , SIGNAL(sigNodeChanged(KisNodeSP)) , SLOT(slotNodeChanged(KisNodeSP))); connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int))); connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool))); connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool))); m_disablePressureAction = m_viewManager->actionManager()->createAction("disable_pressure"); connect(m_disablePressureAction , SIGNAL(toggled(bool)) , SLOT(slotDisablePressureMode(bool))); m_disablePressureAction->setChecked(true); connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool))); connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool))); connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset())); connect(m_sliderChooser[0]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[0]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[0]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[1]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[1]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[1]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[2]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); connect(m_sliderChooser[2]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); connect(m_sliderChooser[2]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); //Needed to connect canvas to favorite resource manager connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode())); connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor))); connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor))); connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor))); // cold initialization m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor()); m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor()); connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor))); connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor))); connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool))); connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon())); connect(m_resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(slotCanvasResourceChanged(int,QVariant))); slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice()); findDefaultPresets(); } KisPaintopBox::~KisPaintopBox() { KisConfig cfg(false); QMapIterator iter(m_tabletToolMap); while (iter.hasNext()) { iter.next(); if ((iter.key().pointer) == QTabletEvent::Eraser) { cfg.writeEntry(QString("LastEraser") , iter.value().preset->name()); } else { cfg.writeEntry(QString("LastPreset"), iter.value().preset->name()); } } // Do not delete the widget, since it is global to the application, not owned by the view m_presetsPopup->setPaintOpSettingsWidget(0); qDeleteAll(m_paintopOptionWidgets); delete m_favoriteResourceManager; for (int i = 0; i < 3; ++i) { delete m_sliderChooser[i]; } } void KisPaintopBox::restoreResource(KoResourceSP resource) { KisPaintOpPresetSP preset = resource.dynamicCast(); if (preset) { setCurrentPaintop(preset); m_presetsPopup->setPresetImage(preset->image()); m_presetsPopup->resourceSelected(resource); } } void KisPaintopBox::newOptionWidgets(const QList > &optionWidgetList) { if (m_toolOptionsPopup) { m_toolOptionsPopup->newOptionWidgets(optionWidgetList); } } void KisPaintopBox::resourceSelected(KoResourceSP resource) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget); m_presetsPopup->setCreatingBrushFromScratch(false); // show normal UI elements when we are not creating // qDebug() << ">>>>>>>>>>>>>>>" << resource // << (resource ? resource->name() : "") // << (resource ? QString("%1").arg(resource->valid()) : "") // << (resource ? QString("%1").arg(resource->filename()) : ""); KisPaintOpPresetSP preset = resource.dynamicCast(); if (preset && preset->valid() && preset != m_resourceProvider->currentPreset()) { qWarning() << "Preset reloading if presets are dirty is broken"; // if (!preset->settings()->isLoadable()) { // return; // } // if (!m_dirtyPresetsEnabled) { // KisSignalsBlocker blocker(m_optionWidget); // Q_UNUSED(blocker) // if (!preset->load()) { // qWarning() << "failed to load the preset."; // } // } dbgResources << "resourceSelected: preset" << preset << (preset ? QString("%1").arg(preset->valid()) : ""); setCurrentPaintop(preset); m_presetsPopup->setPresetImage(preset->image()); m_presetsPopup->resourceSelected(resource); } } void KisPaintopBox::setCurrentPaintop(const KoID& paintop) { KisPaintOpPresetSP preset = activePreset(paintop); Q_ASSERT(preset && preset->settings()); //qDebug() << "setCurrentPaintop();" << paintop << preset; setCurrentPaintop(preset); } void KisPaintopBox::setCurrentPaintop(KisPaintOpPresetSP preset) { //qDebug() << "setCurrentPaintop(); " << preset->name(); if (preset == m_resourceProvider->currentPreset()) { if (preset == m_tabletToolMap[m_currTabletToolID].preset) { return; } } Q_ASSERT(preset); const KoID& paintop = preset->paintOp(); m_presetConnections.clear(); if (m_resourceProvider->currentPreset()) { m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset()); if (m_optionWidget) { m_optionWidget->hide(); } } if (!m_paintopOptionWidgets.contains(paintop)) m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this); m_optionWidget = m_paintopOptionWidgets[paintop]; KisSignalsBlocker b(m_optionWidget); preset->setOptionsWidget(m_optionWidget); m_optionWidget->setImage(m_viewManager->image()); m_optionWidget->setNode(m_viewManager->activeNode()); m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget); m_resourceProvider->setPaintOpPreset(preset); Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton); m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset())); m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP))); m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotDropLockedOption(KisPropertiesConfigurationSP))); // load the current brush engine icon for the brush editor toolbar button m_brushEditorPopupButton->setThumbnail(preset->image()); m_presetsPopup->setCurrentPaintOpId(paintop.id()); ////qDebug() << "\tsetting the new preset for" << m_currTabletToolID.uniqueID << "to" << preset->name(); m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = preset; m_tabletToolMap[m_currTabletToolID].preset = preset; m_tabletToolMap[m_currTabletToolID].paintOpID = preset->paintOp(); if (m_presetsPopup->currentPaintOpId() != paintop.id()) { // Must change the paintop as the current one is not supported // by the new colorspace. dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace"; } m_currCompositeOpID = preset->settings()->paintOpCompositeOp(); updateCompositeOp(m_currCompositeOpID); } void KisPaintopBox::slotUpdateOptionsWidgetPopup() { KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); // This happens when we have a new brush engine for which no default preset exists yet. if (!preset) return; KIS_SAFE_ASSERT_RECOVER_RETURN(preset); KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget); m_optionWidget->setConfigurationSafe(preset->settings()); m_presetsPopup->resourceSelected(preset); m_presetsPopup->updateViewSettings(); // the m_viewManager->image() is set earlier, but the reference will be missing when the stamp button is pressed // need to later do some research on how and when we should be using weak shared pointers (WSP) that creates this situation m_optionWidget->setImage(m_viewManager->image()); } KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp) { QString defaultName = paintOp.id() + ".kpp"; QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName); KisPaintOpPresetSP preset(new KisPaintOpPreset(path)); - if (!preset->load()) { - preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp); + if (!preset->load(KisGlobalResourcesInterface::instance())) { + preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp, KisGlobalResourcesInterface::instance()); } Q_ASSERT(preset); Q_ASSERT(preset->valid()); return preset; } KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp) { if (m_paintOpPresetMap[paintOp] == 0) { m_paintOpPresetMap[paintOp] = defaultPreset(paintOp); } return m_paintOpPresetMap[paintOp]; } void KisPaintopBox::updateCompositeOp(QString compositeOpID) { if (!m_optionWidget) return; KisSignalsBlocker blocker(m_optionWidget); KisNodeSP node = m_resourceProvider->currentNode(); if (node && node->paintDevice()) { if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID)) compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); { KisSignalsBlocker b1(m_cmbCompositeOp); m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID)); } if (compositeOpID != m_currCompositeOpID) { m_currCompositeOpID = compositeOpID; } if (compositeOpID == COMPOSITE_ERASE || m_resourceProvider->eraserMode()) { m_eraseModeButton->setChecked(true); } else { m_eraseModeButton->setChecked(false); } } else if (!node) { KisSignalsBlocker b1(m_cmbCompositeOp); m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID)); m_currCompositeOpID = compositeOpID; } } void KisPaintopBox::setWidgetState(int flags) { if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) { m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP); m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP); } if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) { m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS); m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS); } for (int i = 0; i < 3; ++i) { if (flags & (ENABLE_OPACITY | DISABLE_OPACITY)) m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY); if (flags & (ENABLE_FLOW | DISABLE_FLOW)) m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW); if (flags & (ENABLE_SIZE | DISABLE_SIZE)) m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE); } } void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value) { for (int i = 0; i < 3; ++i) { KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget(sliderID); KisSignalsBlocker b(slider); if (sliderID == "opacity" || sliderID == "flow") { // opacity and flows UI stored at 0-100% slider->setValue(value*100); } else { slider->setValue(value); // brush size } } } void KisPaintopBox::slotSetPaintop(const QString& paintOpId) { if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) { KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name()); //qDebug() << "slotsetpaintop" << id; setCurrentPaintop(id); } } void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice) { TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice); //qDebug() << "slotInputDeviceChanged()" << inputDevice.device() << inputDevice.uniqueTabletId(); m_currTabletToolID = TabletToolID(inputDevice); if (toolData == m_tabletToolMap.end()) { KisConfig cfg(true); KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPresetSP preset; if (inputDevice.pointer() == QTabletEvent::Eraser) { preset = rserver->resourceByName(cfg.readEntry(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), m_eraserName)); } else { preset = rserver->resourceByName(cfg.readEntry(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), m_defaultPresetName)); //if (preset) //qDebug() << "found stored preset " << preset->name() << "for" << inputDevice.uniqueTabletId(); //else //qDebug() << "no preset found for" << inputDevice.uniqueTabletId(); } if (!preset) { preset = rserver->resourceByName(m_defaultPresetName); } if (preset) { //qDebug() << "inputdevicechanged 1" << preset; setCurrentPaintop(preset); } } else { if (toolData->preset) { //qDebug() << "inputdevicechanged 2" << toolData->preset; setCurrentPaintop(toolData->preset); } else { //qDebug() << "inputdevicechanged 3" << toolData->paintOpID; setCurrentPaintop(toolData->paintOpID); } } } void KisPaintopBox::slotCreatePresetFromScratch(QString paintop) { //First try to select an available default preset for that engine. If it doesn't exist, then //manually set the engine to use a new preset. KoID id(paintop, KisPaintOpRegistry::instance()->get(paintop)->name()); KisPaintOpPresetSP preset = defaultPreset(id); slotSetPaintop(paintop); // change the paintop settings area and update the UI if (!preset) { m_presetsPopup->setCreatingBrushFromScratch(true); // disable UI elements while creating from scratch preset = m_resourceProvider->currentPreset(); } else { m_resourceProvider->setPaintOpPreset(preset); preset->setOptionsWidget(m_optionWidget); } m_presetsPopup->resourceSelected(preset); // this helps update the UI on the brush editor } void KisPaintopBox::slotCanvasResourceChanged(int key, const QVariant &value) { if (m_viewManager) { sender()->blockSignals(true); KisPaintOpPresetSP preset = m_viewManager->canvasResourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) { QString compositeOp = preset->settings()->getString("CompositeOp"); updateCompositeOp(compositeOp); resourceSelected(preset); } /** * Update currently selected preset in both the popup widgets */ m_presetsChooserPopup->canvasResourceChanged(preset); m_presetsPopup->currentPresetChanged(preset); if (key == KisCanvasResourceProvider::CurrentCompositeOp) { if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) { updateCompositeOp(m_resourceProvider->currentCompositeOp()); } } if (key == KisCanvasResourceProvider::Size) { setSliderValue("size", m_resourceProvider->size()); } if (key == KisCanvasResourceProvider::Opacity) { setSliderValue("opacity", m_resourceProvider->opacity()); } if (key == KisCanvasResourceProvider::Flow) { setSliderValue("flow", m_resourceProvider->flow()); } if (key == KisCanvasResourceProvider::EraserMode) { m_eraseAction->setChecked(value.toBool()); } if (key == KisCanvasResourceProvider::DisablePressure) { m_disablePressureAction->setChecked(value.toBool()); } if (key == KisCanvasResourceProvider::MirrorHorizontal) { m_hMirrorAction->setChecked(value.toBool()); } if (key == KisCanvasResourceProvider::MirrorVertical) { m_vMirrorAction->setChecked(value.toBool()); } sender()->blockSignals(false); } } void KisPaintopBox::slotSetupDefaultPreset() { KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp()); preset->setOptionsWidget(m_optionWidget); m_resourceProvider->setPaintOpPreset(preset); // tell the brush editor that the resource has changed // so it can update everything m_presetsPopup->resourceSelected(preset); } void KisPaintopBox::slotNodeChanged(const KisNodeSP node) { if (m_previousNode.isValid() && m_previousNode->paintDevice()) disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*))); // Reconnect colorspace change of node if (node && node->paintDevice()) { connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*))); m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID); m_previousNode = node; slotColorSpaceChanged(node->colorSpace()); } if (m_optionWidget) { m_optionWidget->setNode(node); } } void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace) { m_cmbCompositeOp->validate(colorSpace); } void KisPaintopBox::slotToggleEraseMode(bool checked) { const bool oldEraserMode = m_resourceProvider->eraserMode(); m_resourceProvider->setEraserMode(checked); if (oldEraserMode != checked && m_eraserBrushSizeEnabled) { const qreal currentSize = m_resourceProvider->size(); KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings(); // remember brush size. set the eraser size to the normal brush size if not set if (checked) { settings->setSavedBrushSize(currentSize); if (qFuzzyIsNull(settings->savedEraserSize())) { settings->setSavedEraserSize(currentSize); } } else { settings->setSavedEraserSize(currentSize); if (qFuzzyIsNull(settings->savedBrushSize())) { settings->setSavedBrushSize(currentSize); } } //update value in UI (this is the main place the value is 'stored' in memory) qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize(); m_resourceProvider->setSize(newSize); } if (oldEraserMode != checked && m_eraserBrushOpacityEnabled) { const qreal currentOpacity = m_resourceProvider->opacity(); KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings(); // remember brush opacity. set the eraser opacity to the normal brush opacity if not set if (checked) { settings->setSavedBrushOpacity(currentOpacity); if (qFuzzyIsNull(settings->savedEraserOpacity())) { settings->setSavedEraserOpacity(currentOpacity); } } else { settings->setSavedEraserOpacity(currentOpacity); if (qFuzzyIsNull(settings->savedBrushOpacity())) { settings->setSavedBrushOpacity(currentOpacity); } } //update value in UI (this is the main place the value is 'stored' in memory) qreal newOpacity = checked ? settings->savedEraserOpacity() : settings->savedBrushOpacity(); m_resourceProvider->setOpacity(newOpacity); } } void KisPaintopBox::slotSetCompositeMode(int index) { Q_UNUSED(index); QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id(); m_resourceProvider->setCurrentCompositeOp(compositeOp); } void KisPaintopBox::slotHorizontalMirrorChanged(bool value) { m_resourceProvider->setMirrorHorizontal(value); } void KisPaintopBox::slotVerticalMirrorChanged(bool value) { m_resourceProvider->setMirrorVertical(value); } void KisPaintopBox::sliderChanged(int n) { if (!m_optionWidget) // widget will not exist if the are no documents open return; KisSignalsBlocker blocker(m_optionWidget); // flow and opacity are shown as 0-100% on the UI, but their data is actually 0-1. Convert those two values // back for further work qreal opacity = m_sliderChooser[n]->getWidget("opacity")->value()/100; qreal flow = m_sliderChooser[n]->getWidget("flow")->value()/100; qreal size = m_sliderChooser[n]->getWidget("size")->value(); setSliderValue("opacity", opacity); setSliderValue("flow" , flow); setSliderValue("size" , size); if (m_presetsEnabled) { // IMPORTANT: set the PaintOp size before setting the other properties // it won't work the other way // TODO: why?! m_resourceProvider->setSize(size); m_resourceProvider->setOpacity(opacity); m_resourceProvider->setFlow(flow); KisLockedPropertiesProxySP propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings()); propertiesProxy->setProperty("OpacityValue", opacity); propertiesProxy->setProperty("FlowValue", flow); m_optionWidget->setConfigurationSafe(m_resourceProvider->currentPreset()->settings().data()); } else { m_resourceProvider->setOpacity(opacity); } m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset()); } void KisPaintopBox::slotSlider1Changed() { sliderChanged(0); } void KisPaintopBox::slotSlider2Changed() { sliderChanged(1); } void KisPaintopBox::slotSlider3Changed() { sliderChanged(2); } void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId) { Q_UNUSED(canvas); Q_UNUSED(toolId); if (!m_viewManager->canvasBase()) return; QString id = KoToolManager::instance()->activeToolId(); KisTool* tool = dynamic_cast(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id)); if (tool) { int flags = tool->flags(); if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) { setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY); } else { setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY); } if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) { setWidgetState(ENABLE_PRESETS); if (!m_resourceProvider->currentPreset()) return; // block updates of avoid some over updating of the option widget m_blockUpdate = true; setSliderValue("size", m_resourceProvider->size()); { qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity(); m_resourceProvider->setOpacity(opacity); setSliderValue("opacity", opacity); setWidgetState(ENABLE_OPACITY); } { setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow()); setWidgetState(ENABLE_FLOW); } { updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp()); setWidgetState(ENABLE_COMPOSITEOP); } m_blockUpdate = false; m_presetsEnabled = true; } else { setWidgetState(DISABLE_PRESETS); m_presetsEnabled = false; } if (flags & KisTool::FLAG_USES_CUSTOM_SIZE) { setWidgetState(ENABLE_SIZE | ENABLE_FLOW); } else { setWidgetState(DISABLE_SIZE | DISABLE_FLOW); } } else setWidgetState(DISABLE_ALL); } void KisPaintopBox::slotPreviousFavoritePreset() { if (!m_favoriteResourceManager) return; QVector presets = m_favoriteResourceManager->favoritePresetNamesList(); for (int i=0; i < presets.size(); ++i) { if (m_resourceProvider->currentPreset() && m_resourceProvider->currentPreset()->name() == presets[i]) { if (i > 0) { m_favoriteResourceManager->slotChangeActivePaintop(i - 1); } else { m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1); } //floating message should have least 2 lines, otherwise //preset thumbnail will be too small to distinguish //(because size of image on floating message depends on amount of lines in msg) m_viewManager->showFloatingMessage( i18n("%1\nselected", m_resourceProvider->currentPreset()->name()), QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image()))); return; } } } void KisPaintopBox::slotNextFavoritePreset() { if (!m_favoriteResourceManager) return; QVector presets = m_favoriteResourceManager->favoritePresetNamesList(); for(int i = 0; i < presets.size(); ++i) { if (m_resourceProvider->currentPreset()->name() == presets[i]) { if (i < m_favoriteResourceManager->numFavoritePresets() - 1) { m_favoriteResourceManager->slotChangeActivePaintop(i + 1); } else { m_favoriteResourceManager->slotChangeActivePaintop(0); } m_viewManager->showFloatingMessage( i18n("%1\nselected", m_resourceProvider->currentPreset()->name()), QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image()))); return; } } } void KisPaintopBox::slotSwitchToPreviousPreset() { if (m_resourceProvider->previousPreset()) { //qDebug() << "slotSwitchToPreviousPreset();" << m_resourceProvider->previousPreset(); setCurrentPaintop(m_resourceProvider->previousPreset()); m_viewManager->showFloatingMessage( i18n("%1\nselected", m_resourceProvider->currentPreset()->name()), QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image()))); } } void KisPaintopBox::slotUnsetEraseMode() { m_eraseAction->setChecked(false); } void KisPaintopBox::slotToggleAlphaLockMode(bool checked) { if (checked) { m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked")); } else { m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked")); } m_resourceProvider->setGlobalAlphaLock(checked); } void KisPaintopBox::slotDisablePressureMode(bool checked) { if (checked) { m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure")); } else { m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked")); } m_resourceProvider->setDisablePressure(checked); } void KisPaintopBox::slotReloadPreset() { KisSignalsBlocker blocker(m_optionWidget); // Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading. KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QSharedPointer preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name()); if (preset) { - preset->load(); + preset->load(KisGlobalResourcesInterface::instance()); } if (m_resourceProvider->currentPreset() != preset) { m_resourceProvider->setPaintOpPreset(preset); } else { /** * HACK ALERT: here we emit a private signal from the resource manager to * ensure that all the subscribers of resource-changed signal got the * notification. That is especially important for * KisPaintopTransformationConnector. See bug 392622. */ emit m_resourceProvider->resourceManager()->canvasResourceChanged( KisCanvasResourceProvider::CurrentPaintOpPreset, QVariant::fromValue(preset)); } } void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed { KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); { /** * Here we postpone all the settings updates events until the entire writing * operation will be finished. As soon as it is finished, the updates will be * emitted happily (if there were any). */ KisPaintOpPreset::UpdatedPostponer postponer(preset); QStringList preserveProperties; preserveProperties << "lodUserAllowed"; preserveProperties << "lodSizeThreshold"; // clear all the properties before dumping the stuff into the preset, // some of the options add the values incrementally // (e.g. KisPaintOpUtils::RequiredBrushFilesListTag), therefore they // may add up if we pass the same preset multiple times preset->settings()->resetSettings(preserveProperties); m_optionWidget->writeConfigurationSafe(const_cast(preset->settings().data())); } // we should also update the preset strip to update the status of the "dirty" mark m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset()); // TODO!!!!!!!! //m_presetsPopup->updateViewSettings(); } void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p) { QMapIterator i(p->getProperties()); while (i.hasNext()) { i.next(); m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value())); if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) { m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous"); } } slotGuiChangedCurrentPreset(); } void KisPaintopBox::slotDropLockedOption(KisPropertiesConfigurationSP p) { KisSignalsBlocker blocker(m_optionWidget); KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); { KisResourceDirtyStateSaver dirtySaver(preset); QMapIterator i(p->getProperties()); while (i.hasNext()) { i.next(); if (preset->settings()->hasProperty(i.key() + "_previous")) { preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous")); preset->settings()->removeProperty(i.key() + "_previous"); } } } } void KisPaintopBox::slotDirtyPresetToggled(bool value) { if (!value) { slotReloadPreset(); m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset()); m_presetsPopup->updateViewSettings(); } m_dirtyPresetsEnabled = value; KisConfig cfg(false); cfg.setUseDirtyPresets(m_dirtyPresetsEnabled); } void KisPaintopBox::slotEraserBrushSizeToggled(bool value) { m_eraserBrushSizeEnabled = value; KisConfig cfg(false); cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled); } void KisPaintopBox::slotEraserBrushOpacityToggled(bool value) { m_eraserBrushOpacityEnabled = value; KisConfig cfg(false); cfg.setUseEraserBrushOpacity(m_eraserBrushOpacityEnabled); } void KisPaintopBox::slotUpdateSelectionIcon() { m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal")); m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical")); KisConfig cfg(true); if (!cfg.toolOptionsInDocker() && m_toolOptionsPopupButton) { m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure")); } m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01")); m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02")); m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose")); m_eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser")); m_reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh")); if (m_disablePressureAction->isChecked()) { m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure")); } else { m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked")); } } void KisPaintopBox::slotLockXMirrorToggle(bool toggleLock) { m_resourceProvider->setMirrorHorizontalLock(toggleLock); } void KisPaintopBox::slotLockYMirrorToggle(bool toggleLock) { m_resourceProvider->setMirrorVerticalLock(toggleLock); } void KisPaintopBox::slotHideDecorationMirrorX(bool toggled) { m_resourceProvider->setMirrorHorizontalHideDecorations(toggled); } void KisPaintopBox::slotHideDecorationMirrorY(bool toggled) { m_resourceProvider->setMirrorVerticalHideDecorations(toggled); } void KisPaintopBox::slotMoveToCenterMirrorX() { m_resourceProvider->mirrorHorizontalMoveCanvasToCenter(); } void KisPaintopBox::slotMoveToCenterMirrorY() { m_resourceProvider->mirrorVerticalMoveCanvasToCenter(); } void KisPaintopBox::findDefaultPresets() { KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); m_eraserName = "eraser_circle"; m_defaultPresetName = "basic_tip_default"; KisResourceModel *resourceModel = rserver->resourceModel(); for (int i = 0; i < resourceModel->rowCount(); i++) { QModelIndex idx = resourceModel->index(i, 0); QString resourceName = idx.data(Qt::UserRole + KisResourceModel::Name).toString().toLower(); QString fileName = idx.data(Qt::UserRole + KisResourceModel::Filename).toString().toLower(); if (resourceName.contains("eraser_circle")) { m_eraserName = resourceName; } else if (resourceName.contains("eraser") || fileName.contains("eraser")) { m_eraserName = resourceName; } if (resourceName.contains("basic_tip_default")) { m_defaultPresetName = resourceName; } else if (resourceName.contains("default") || fileName.contains("default")) { m_defaultPresetName = resourceName; } } } diff --git a/libs/ui/kis_workspace_resource.cpp b/libs/ui/kis_workspace_resource.cpp index 69e42ff856..58ee199105 100644 --- a/libs/ui/kis_workspace_resource.cpp +++ b/libs/ui/kis_workspace_resource.cpp @@ -1,144 +1,144 @@ /* This file is part of the KDE project * Copyright (C) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_workspace_resource.h" #include #include #include #include #define WORKSPACE_VERSION 1 KisWorkspaceResource::KisWorkspaceResource(const QString& filename): KoResource(filename) { } KisWorkspaceResource::~KisWorkspaceResource() { } KisWorkspaceResource::KisWorkspaceResource(const KisWorkspaceResource &rhs) : KoResource(rhs) , KisPropertiesConfiguration(rhs) { *this = rhs; } KisWorkspaceResource &KisWorkspaceResource::operator=(const KisWorkspaceResource &rhs) { if (*this != rhs) { m_dockerState = rhs.m_dockerState; } return *this; } KoResourceSP KisWorkspaceResource::clone() const { return KoResourceSP(new KisWorkspaceResource(*this)); } bool KisWorkspaceResource::saveToDevice(QIODevice *dev) const { QDomDocument doc; QDomElement root = doc.createElement("Workspace"); root.setAttribute("name", name() ); root.setAttribute("version", WORKSPACE_VERSION); QDomElement state = doc.createElement("state"); state.appendChild(doc.createCDATASection(m_dockerState.toBase64())); root.appendChild(state); // Save KisPropertiesConfiguration settings QDomElement settings = doc.createElement("settings"); KisPropertiesConfiguration::toXML(doc, settings); root.appendChild(settings); if (!image().isNull()) { QDomElement thumb = doc.createElement("image"); QByteArray arr; QBuffer buffer(&arr); buffer.open(QIODevice::WriteOnly); image().save(&buffer, "PNG"); buffer.close(); thumb.appendChild(doc.createCDATASection(arr.toBase64())); root.appendChild(thumb); } doc.appendChild(root); QTextStream textStream(dev); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(dev); return true; } - - -bool KisWorkspaceResource::loadFromDevice(QIODevice *dev) +bool KisWorkspaceResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); QDomElement state = element.firstChildElement("state"); if (!state.isNull()) { m_dockerState = QByteArray::fromBase64(state.text().toLatin1()); } QDomElement settings = element.firstChildElement("settings"); if (!settings.isNull()) { KisPropertiesConfiguration::fromXML(settings); } QDomElement thumb = element.firstChildElement("image"); if (!thumb.isNull()) { QImage img; img.loadFromData(QByteArray::fromBase64(thumb.text().toLatin1())); this->setImage(img); } setValid(true); return true; } QString KisWorkspaceResource::defaultFileExtension() const { return QString(".kws"); } void KisWorkspaceResource::setDockerState(const QByteArray& state) { m_dockerState = state; } QByteArray KisWorkspaceResource::dockerState() { return m_dockerState; } diff --git a/libs/ui/kis_workspace_resource.h b/libs/ui/kis_workspace_resource.h index 1ab9f729cb..ddeb1b346a 100644 --- a/libs/ui/kis_workspace_resource.h +++ b/libs/ui/kis_workspace_resource.h @@ -1,55 +1,55 @@ /* This file is part of the KDE project * Copyright (C) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_WORKSPACE_RESOURCE_H #define KIS_WORKSPACE_RESOURCE_H #include #include #include "kritaui_export.h" /// Resource for storing of workspaces class KRITAUI_EXPORT KisWorkspaceResource : public KoResource , public KisPropertiesConfiguration { public: KisWorkspaceResource(const QString& filename); ~KisWorkspaceResource() override; KisWorkspaceResource(const KisWorkspaceResource &rhs); KisWorkspaceResource &operator=(const KisWorkspaceResource &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; QPair resourceType() const override { return QPair(ResourceType::Workspaces, ""); } void setDockerState(const QByteArray& state); QByteArray dockerState(); private: QByteArray m_dockerState; }; typedef QSharedPointer KisWorkspaceResourceSP; #endif // KIS_WORKSPACE_RESOURCE_H diff --git a/libs/ui/tool/kis_figure_painting_tool_helper.h b/libs/ui/tool/kis_figure_painting_tool_helper.h index 5c5348a64e..8edd3d1377 100644 --- a/libs/ui/tool/kis_figure_painting_tool_helper.h +++ b/libs/ui/tool/kis_figure_painting_tool_helper.h @@ -1,67 +1,69 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_FIGURE_PAINTING_TOOL_HELPER_H #define __KIS_FIGURE_PAINTING_TOOL_HELPER_H #include "kis_types.h" #include "kritaui_export.h" #include #include "strokes/freehand_stroke.h" #include "KisToolShapeUtils.h" class KoCanvasResourceProvider; class KisStrokesFacade; class KRITAUI_EXPORT KisFigurePaintingToolHelper { public: KisFigurePaintingToolHelper(const KUndo2MagicString &name, KisImageWSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisToolShapeUtils::StrokeStyle strokeStyle, KisToolShapeUtils::FillStyle fillStyle); ~KisFigurePaintingToolHelper(); void paintLine(const KisPaintInformation &pi0, const KisPaintInformation &pi1); void paintPolyline(const vQPointF &points); void paintPolygon(const vQPointF &points); void paintRect(const QRectF &rect); void paintEllipse(const QRectF &rect); void paintPainterPath(const QPainterPath &path); void setFGColorOverride(const KoColor &color); void setBGColorOverride(const KoColor &color); void setSelectionOverride(KisSelectionSP m_selection); + // WARNING: setBrush() should be called with a cloned copy of the brush! The + // helper (reosurce snapshot) will own and modify it! void setBrush(const KisPaintOpPresetSP &brush); void paintPainterPathQPen(const QPainterPath, const QPen &pen, const KoColor &color); void paintPainterPathQPenFill(const QPainterPath, const QPen &pen, const KoColor &color); private: void setupPaintStyles(KisResourcesSnapshotSP resources, KisToolShapeUtils::StrokeStyle strokeStyle, KisToolShapeUtils::FillStyle fillStyle); private: KisStrokeId m_strokeId; KisResourcesSnapshotSP m_resources; KisStrokesFacade *m_strokesFacade; }; #endif /* __KIS_FIGURE_PAINTING_TOOL_HELPER_H */ diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp index 911fea338f..9c210bc740 100644 --- a/libs/ui/tool/kis_resources_snapshot.cpp +++ b/libs/ui/tool/kis_resources_snapshot.cpp @@ -1,419 +1,429 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_resources_snapshot.h" #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "filter/kis_filter_configuration.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_algebra_2d.h" +#include struct KisResourcesSnapshot::Private { Private() : currentPattern(0) , currentGradient(0) , currentGenerator(0) , compositeOp(0) { } KisImageSP image; KisDefaultBoundsBaseSP bounds; KoColor currentFgColor; KoColor currentBgColor; KoPatternSP currentPattern; KoAbstractGradientSP currentGradient; KisPaintOpPresetSP currentPaintOpPreset; KisNodeSP currentNode; qreal currentExposure; KisFilterConfigurationSP currentGenerator; QPointF axesCenter; bool mirrorMaskHorizontal = false; bool mirrorMaskVertical = false; quint8 opacity = OPACITY_OPAQUE_U8; QString compositeOpId = COMPOSITE_OVER; const KoCompositeOp *compositeOp; KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush; KisPainter::FillStyle fillStyle = KisPainter::FillStyleForegroundColor; bool globalAlphaLock = false; qreal effectiveZoom = 1.0; bool presetAllowsLod = false; KisSelectionSP selectionOverride; bool hasOverrideSelection = false; }; KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; m_d->currentFgColor = resourceManager->resource(KoCanvasResourceProvider::ForegroundColor).value(); m_d->currentBgColor = resourceManager->resource(KoCanvasResourceProvider::BackgroundColor).value(); m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value(); m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value(); /** * We should deep-copy the preset, so that long-running actions * will have correct brush parameters. Theoretically this cloning * can be expensive, but according to measurements, it takes * something like 0.1 ms for an average preset. */ KisPaintOpPresetSP p = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (p) { m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value()->clone().dynamicCast(); + m_d->currentPaintOpPreset->createLocalResourcesSnapshot(KisGlobalResourcesInterface::instance()); } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble(); m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool(); m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool(); qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble(); m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8); m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString(); setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool(); m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble(); m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::EffectiveLodAvailablility).toBool(); } KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->opacity = OPACITY_OPAQUE_U8; setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; } KisResourcesSnapshot::~KisResourcesSnapshot() { delete m_d; } void KisResourcesSnapshot::setupPainter(KisPainter* painter) { painter->setPaintColor(m_d->currentFgColor); painter->setBackgroundColor(m_d->currentBgColor); painter->setGenerator(m_d->currentGenerator); painter->setPattern(m_d->currentPattern); painter->setGradient(m_d->currentGradient); QBitArray lockflags = channelLockFlags(); if (lockflags.size() > 0) { painter->setChannelFlags(lockflags); } painter->setOpacity(m_d->opacity); painter->setCompositeOp(m_d->compositeOp); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); painter->setFillStyle(m_d->fillStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image); } void KisResourcesSnapshot::setupMaskingBrushPainter(KisPainter *painter) { KIS_SAFE_ASSERT_RECOVER_RETURN(painter->device()); KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->currentPaintOpPreset->hasMaskingPreset()); painter->setPaintColor(KoColor(Qt::white, painter->device()->colorSpace())); painter->setBackgroundColor(KoColor(Qt::black, painter->device()->colorSpace())); painter->setOpacity(OPACITY_OPAQUE_U8); painter->setChannelFlags(QBitArray()); // masked brush always paints in indirect mode painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset->createMaskingPreset(), m_d->currentNode, m_d->image); } KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const { return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0; } void KisResourcesSnapshot::setCurrentNode(KisNodeSP node) { m_d->currentNode = node; KisPaintDeviceSP device; if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) { m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId); if(!m_d->compositeOp) { m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER); } } } void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { m_d->strokeStyle = strokeStyle; } void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle) { m_d->fillStyle = fillStyle; } KisNodeSP KisResourcesSnapshot::currentNode() const { return m_d->currentNode; } KisImageSP KisResourcesSnapshot::image() const { return m_d->image; } bool KisResourcesSnapshot::needsIndirectPainting() const { return !m_d->currentPaintOpPreset->settings()->paintIncremental(); } QString KisResourcesSnapshot::indirectPaintingCompositeOp() const { return m_d->currentPaintOpPreset ? m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp() : COMPOSITE_ALPHA_DARKEN; } bool KisResourcesSnapshot::needsMaskingBrushRendering() const { return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->hasMaskingPreset(); } KisSelectionSP KisResourcesSnapshot::activeSelection() const { /** * It is possible to have/use the snapshot without the image. Such * usecase is present for example in the scratchpad. */ if (m_d->hasOverrideSelection) { return m_d->selectionOverride; } KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0; KisLayerSP layer = qobject_cast(m_d->currentNode.data()); KisSelectionMaskSP mask; if((layer = qobject_cast(m_d->currentNode.data()))) { selection = layer->selection(); } else if ((mask = dynamic_cast(m_d->currentNode.data())) && mask->selection() == selection) { selection = 0; } return selection; } bool KisResourcesSnapshot::needsAirbrushing() const { return m_d->currentPaintOpPreset->settings()->isAirbrushing(); } qreal KisResourcesSnapshot::airbrushingInterval() const { return m_d->currentPaintOpPreset->settings()->airbrushInterval(); } bool KisResourcesSnapshot::needsSpacingUpdates() const { return m_d->currentPaintOpPreset->settings()->useSpacingUpdates(); } void KisResourcesSnapshot::setOpacity(qreal opacity) { m_d->opacity = opacity * OPACITY_OPAQUE_U8; } quint8 KisResourcesSnapshot::opacity() const { return m_d->opacity; } const KoCompositeOp* KisResourcesSnapshot::compositeOp() const { return m_d->compositeOp; } QString KisResourcesSnapshot::compositeOpId() const { return m_d->compositeOpId; } KoPatternSP KisResourcesSnapshot::currentPattern() const { return m_d->currentPattern; } KoColor KisResourcesSnapshot::currentFgColor() const { return m_d->currentFgColor; } KoColor KisResourcesSnapshot::currentBgColor() const { return m_d->currentBgColor; } KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const { return m_d->currentPaintOpPreset; } QBitArray KisResourcesSnapshot::channelLockFlags() const { QBitArray channelFlags; KisPaintLayer *paintLayer; if ((paintLayer = dynamic_cast(m_d->currentNode.data()))) { channelFlags = paintLayer->channelLockFlags(); if (m_d->globalAlphaLock) { if (channelFlags.isEmpty()) { channelFlags = paintLayer->colorSpace()->channelFlags(true, true); } channelFlags &= paintLayer->colorSpace()->channelFlags(true, false); } } return channelFlags; } qreal KisResourcesSnapshot::effectiveZoom() const { return m_d->effectiveZoom; } bool KisResourcesSnapshot::presetAllowsLod() const { return m_d->presetAllowsLod; } bool KisResourcesSnapshot::presetNeedsAsynchronousUpdates() const { return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->settings()->needsAsynchronousUpdates(); } void KisResourcesSnapshot::setFGColorOverride(const KoColor &color) { m_d->currentFgColor = color; } void KisResourcesSnapshot::setBGColorOverride(const KoColor &color) { m_d->currentBgColor = color; } void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection) { m_d->selectionOverride = selection; m_d->hasOverrideSelection = true; // needed if selection passed is null to ignore selection } void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush) { m_d->currentPaintOpPreset = brush; + +#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND + KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); +#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ + + if (m_d->currentPaintOpPreset) { + m_d->currentPaintOpPreset->createLocalResourcesSnapshot(KisGlobalResourcesInterface::instance()); + } } diff --git a/libs/ui/tool/strokes/freehand_stroke.cpp b/libs/ui/tool/strokes/freehand_stroke.cpp index 15162bf7fa..1688bbb9ad 100644 --- a/libs/ui/tool/strokes/freehand_stroke.cpp +++ b/libs/ui/tool/strokes/freehand_stroke.cpp @@ -1,354 +1,352 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "freehand_stroke.h" #include #include #include #include "kis_canvas_resource_provider.h" #include #include #include "kis_painter.h" #include "kis_paintop.h" #include "kis_update_time_monitor.h" #include #include #include "FreehandStrokeRunnableJobDataWithUpdate.h" #include #include "KisStrokeEfficiencyMeasurer.h" #include #include #include #include "brushengine/kis_paintop_utils.h" #include "KisAsyncronousStrokeUpdateHelper.h" struct FreehandStrokeStrategy::Private { Private(KisResourcesSnapshotSP _resources) : resources(_resources), needsAsynchronousUpdates(_resources->presetNeedsAsynchronousUpdates()) { if (needsAsynchronousUpdates) { timeSinceLastUpdate.start(); } } Private(const Private &rhs) : randomSource(rhs.randomSource), resources(rhs.resources), needsAsynchronousUpdates(rhs.needsAsynchronousUpdates) { if (needsAsynchronousUpdates) { timeSinceLastUpdate.start(); } } KisStrokeRandomSource randomSource; KisResourcesSnapshotSP resources; KisStrokeEfficiencyMeasurer efficiencyMeasurer; QElapsedTimer timeSinceLastUpdate; int currentUpdatePeriod = 40; const bool needsAsynchronousUpdates = false; std::mutex updateEntryMutex; }; FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources, KisFreehandStrokeInfo *strokeInfo, const KUndo2MagicString &name) : KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name, resources, strokeInfo), m_d(new Private(resources)) { init(); } FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources, QVector strokeInfos, const KUndo2MagicString &name) : KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name, resources, strokeInfos), m_d(new Private(resources)) { init(); } FreehandStrokeStrategy::FreehandStrokeStrategy(const FreehandStrokeStrategy &rhs, int levelOfDetail) : KisPainterBasedStrokeStrategy(rhs, levelOfDetail), m_d(new Private(*rhs.m_d)) { m_d->randomSource.setLevelOfDetail(levelOfDetail); } FreehandStrokeStrategy::~FreehandStrokeStrategy() { KisStrokeSpeedMonitor::instance()->notifyStrokeFinished(m_d->efficiencyMeasurer.averageCursorSpeed(), m_d->efficiencyMeasurer.averageRenderingSpeed(), m_d->efficiencyMeasurer.averageFps(), m_d->resources->currentPaintOpPreset()); KisUpdateTimeMonitor::instance()->endStrokeMeasure(); } void FreehandStrokeStrategy::init() { setSupportsWrapAroundMode(true); setSupportsMaskingBrush(true); setSupportsIndirectPainting(true); enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE); if (m_d->needsAsynchronousUpdates) { /** * In case the paintop uses asynchronous updates, we should set priority to it, * because FPS is controlled separately, not by the queue's merging algorithm. */ setBalancingRatioOverride(0.01); // set priority to updates } KisUpdateTimeMonitor::instance()->startStrokeMeasure(); m_d->efficiencyMeasurer.setEnabled(KisStrokeSpeedMonitor::instance()->haveStrokeSpeedMeasurement()); } void FreehandStrokeStrategy::initStrokeCallback() { KisPainterBasedStrokeStrategy::initStrokeCallback(); m_d->efficiencyMeasurer.notifyRenderingStarted(); } void FreehandStrokeStrategy::finishStrokeCallback() { m_d->efficiencyMeasurer.notifyRenderingFinished(); KisPainterBasedStrokeStrategy::finishStrokeCallback(); } void FreehandStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { - qDebug() << "FreehandStrokeStrategy::doStrokeCallback" << QThread::currentThread() << qApp->thread(); - if (KisAsyncronousStrokeUpdateHelper::UpdateData *d = dynamic_cast(data)) { // this job is lod-clonable in contrast to FreehandStrokeRunnableJobDataWithUpdate! tryDoUpdate(d->forceUpdate); } else if (Data *d = dynamic_cast(data)) { KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(d->strokeInfoId); KisUpdateTimeMonitor::instance()->reportPaintOpPreset(maskedPainter->preset()); KisRandomSourceSP rnd = m_d->randomSource.source(); KisPerStrokeRandomSourceSP strokeRnd = m_d->randomSource.perStrokeSource(); switch(d->type) { case Data::POINT: d->pi1.setRandomSource(rnd); d->pi1.setPerStrokeRandomSource(strokeRnd); maskedPainter->paintAt(d->pi1); m_d->efficiencyMeasurer.addSample(d->pi1.pos()); break; case Data::LINE: d->pi1.setRandomSource(rnd); d->pi2.setRandomSource(rnd); d->pi1.setPerStrokeRandomSource(strokeRnd); d->pi2.setPerStrokeRandomSource(strokeRnd); maskedPainter->paintLine(d->pi1, d->pi2); m_d->efficiencyMeasurer.addSample(d->pi2.pos()); break; case Data::CURVE: d->pi1.setRandomSource(rnd); d->pi2.setRandomSource(rnd); d->pi1.setPerStrokeRandomSource(strokeRnd); d->pi2.setPerStrokeRandomSource(strokeRnd); maskedPainter->paintBezierCurve(d->pi1, d->control1, d->control2, d->pi2); m_d->efficiencyMeasurer.addSample(d->pi2.pos()); break; case Data::POLYLINE: maskedPainter->paintPolyline(d->points, 0, d->points.size()); m_d->efficiencyMeasurer.addSamples(d->points); break; case Data::POLYGON: maskedPainter->paintPolygon(d->points); m_d->efficiencyMeasurer.addSamples(d->points); break; case Data::RECT: maskedPainter->paintRect(d->rect); m_d->efficiencyMeasurer.addSample(d->rect.topLeft()); m_d->efficiencyMeasurer.addSample(d->rect.topRight()); m_d->efficiencyMeasurer.addSample(d->rect.bottomRight()); m_d->efficiencyMeasurer.addSample(d->rect.bottomLeft()); break; case Data::ELLIPSE: maskedPainter->paintEllipse(d->rect); // TODO: add speed measures break; case Data::PAINTER_PATH: maskedPainter->paintPainterPath(d->path); // TODO: add speed measures break; case Data::QPAINTER_PATH: maskedPainter->drawPainterPath(d->path, d->pen); break; case Data::QPAINTER_PATH_FILL: maskedPainter->drawAndFillPainterPath(d->path, d->pen, d->customColor); break; }; tryDoUpdate(); } else { KisPainterBasedStrokeStrategy::doStrokeCallback(data); FreehandStrokeRunnableJobDataWithUpdate *dataWithUpdate = dynamic_cast(data); if (dataWithUpdate) { tryDoUpdate(); } } } void FreehandStrokeStrategy::tryDoUpdate(bool forceEnd) { // we should enter this function only once! std::unique_lock entryLock(m_d->updateEntryMutex, std::try_to_lock); if (!entryLock.owns_lock()) return; if (m_d->needsAsynchronousUpdates) { if (forceEnd || m_d->timeSinceLastUpdate.elapsed() > m_d->currentUpdatePeriod) { m_d->timeSinceLastUpdate.restart(); for (int i = 0; i < numMaskedPainters(); i++) { KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i); // TODO: well, we should count all N simultaneous painters for FPS rate! QVector jobs; bool needsMoreUpdates = false; std::tie(m_d->currentUpdatePeriod, needsMoreUpdates) = maskedPainter->doAsyncronousUpdate(jobs); if (!jobs.isEmpty() || maskedPainter->hasDirtyRegion() || (forceEnd && needsMoreUpdates)) { jobs.append(new KisRunnableStrokeJobData( [this] () { this->issueSetDirtySignals(); }, KisStrokeJobData::SEQUENTIAL)); if (forceEnd && needsMoreUpdates) { jobs.append(new KisRunnableStrokeJobData( [this] () { this->tryDoUpdate(true); }, KisStrokeJobData::SEQUENTIAL)); } runnableJobsInterface()->addRunnableJobs(jobs); m_d->efficiencyMeasurer.notifyFrameRenderingStarted(); } } } } else { issueSetDirtySignals(); } } void FreehandStrokeStrategy::issueSetDirtySignals() { QVector dirtyRects; for (int i = 0; i < numMaskedPainters(); i++) { KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i); dirtyRects.append(maskedPainter->takeDirtyRegion()); } if (needsMaskingUpdates()) { // optimize the rects so that they would never intersect with each other! // that is a mandatory step for the multithreaded execution of merging jobs // sanity check: updates from the brush should have already been normalized // to the wrapping rect const KisDefaultBoundsBaseSP defaultBounds = targetNode()->projection()->defaultBounds(); if (defaultBounds->wrapAroundMode()) { const QRect wrapRect = defaultBounds->bounds(); for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) { KIS_SAFE_ASSERT_RECOVER(wrapRect.contains(*it)) { ENTER_FUNCTION() << ppVar(*it) << ppVar(wrapRect); *it = *it & wrapRect; } } } const int maxPatchSizeForMaskingUpdates = 64; const QRect totalRect = std::accumulate(dirtyRects.constBegin(), dirtyRects.constEnd(), QRect(), std::bit_or()); dirtyRects = KisPaintOpUtils::splitAndFilterDabRect(totalRect, dirtyRects, maxPatchSizeForMaskingUpdates); QVector jobs = doMaskingBrushUpdates(dirtyRects); jobs.append(new KisRunnableStrokeJobData( [this, dirtyRects] () { this->targetNode()->setDirty(dirtyRects); }, KisStrokeJobData::SEQUENTIAL)); runnableJobsInterface()->addRunnableJobs(jobs); } else { targetNode()->setDirty(dirtyRects); } //KisUpdateTimeMonitor::instance()->reportJobFinished(data, dirtyRects); } KisStrokeStrategy* FreehandStrokeStrategy::createLodClone(int levelOfDetail) { if (!m_d->resources->presetAllowsLod()) return 0; FreehandStrokeStrategy *clone = new FreehandStrokeStrategy(*this, levelOfDetail); return clone; } void FreehandStrokeStrategy::notifyUserStartedStroke() { m_d->efficiencyMeasurer.notifyCursorMoveStarted(); } void FreehandStrokeStrategy::notifyUserEndedStroke() { m_d->efficiencyMeasurer.notifyCursorMoveFinished(); } diff --git a/libs/ui/widgets/kis_preset_live_preview_view.cpp b/libs/ui/widgets/kis_preset_live_preview_view.cpp index 99d71b690a..b7974e1291 100644 --- a/libs/ui/widgets/kis_preset_live_preview_view.cpp +++ b/libs/ui/widgets/kis_preset_live_preview_view.cpp @@ -1,383 +1,384 @@ /* * Copyright (c) 2017 Scott Petrovic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_paintop_settings.h" #include #include #include "KisAsyncronousStrokeUpdateHelper.h" #include +#include KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent) : QGraphicsView(parent), m_updateCompressor(100, KisSignalCompressor::FIRST_ACTIVE) { connect(&m_updateCompressor, SIGNAL(timeout()), SLOT(updateStroke())); } KisPresetLivePreviewView::~KisPresetLivePreviewView() { delete m_noPreviewText; delete m_brushPreviewScene; } void KisPresetLivePreviewView::setup() { // initializing to 0 helps check later if they actually have something in them m_noPreviewText = 0; m_sceneImageItem = 0; setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); // layer image needs to be big enough to get an entire stroke of data m_canvasSize.setWidth(this->width()); m_canvasSize.setHeight(this->height()); m_canvasCenterPoint.setX(m_canvasSize.width()*0.5); m_canvasCenterPoint.setY(m_canvasSize.height()*0.5); m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_image = new KisImage(0, m_canvasSize.width(), m_canvasSize.height(), m_colorSpace, "stroke sample image"); m_layer = new KisPaintLayer(m_image, "livePreviewStrokeSample", OPACITY_OPAQUE_U8, m_colorSpace); // set scene for the view m_brushPreviewScene = new QGraphicsScene(); setScene(m_brushPreviewScene); } void KisPresetLivePreviewView::setCurrentPreset(KisPaintOpPresetSP preset) { m_currentPreset = preset; } void KisPresetLivePreviewView::requestUpdateStroke() { m_updateCompressor.start(); } void KisPresetLivePreviewView::updateStroke() { // do not paint a stroke if we are any of these engines (they have some issue currently) if (m_currentPreset->paintOp().id() == "roundmarker" || m_currentPreset->paintOp().id() == "experimentbrush" || m_currentPreset->paintOp().id() == "duplicate") { paintBackground(); slotPreviewGenerationCompleted(); return; } if (!m_previewGenerationInProgress) { paintBackground(); setupAndPaintStroke(); } else { m_updateCompressor.start(); } } void KisPresetLivePreviewView::slotPreviewGenerationCompleted() { m_previewGenerationInProgress = false; QImage m_temp_image; m_temp_image = m_layer->paintDevice()->convertToQImage(0, m_image->bounds()); // only add the object once...then just update the pixmap so we can move the preview around if (!m_sceneImageItem) { m_sceneImageItem = m_brushPreviewScene->addPixmap(QPixmap::fromImage(m_temp_image)); } else { m_sceneImageItem->setPixmap(QPixmap::fromImage(m_temp_image)); } } void KisPresetLivePreviewView::paintBackground() { // clean up "no preview" text object if it exists. we will add it later if we need it if (m_noPreviewText) { this->scene()->removeItem(m_noPreviewText); m_noPreviewText = 0; } if (m_currentPreset->paintOp().id() == "colorsmudge" || m_currentPreset->paintOp().id() == "deformbrush" || m_currentPreset->paintOp().id() == "filter") { // easier to see deformations and smudging with alternating stripes in the background // paint the whole background with alternating stripes // filter engine may or may not show things depending on the filter...but it is better than nothing int grayStrips = 20; for (int i=0; i < grayStrips; i++ ) { float sectionPercent = 1.0 / (float)grayStrips; bool isAlternating = i % 2; KoColor fillColor(m_layer->paintDevice()->colorSpace()); if (isAlternating) { fillColor.fromQColor(QColor(80,80,80)); } else { fillColor.fromQColor(QColor(140,140,140)); } const QRect fillRect(m_layer->image()->width()*sectionPercent*i, 0, m_layer->image()->width()*(sectionPercent*i +sectionPercent), m_layer->image()->height()); m_layer->paintDevice()->fill(fillRect, fillColor); } m_paintColor = KoColor(Qt::white, m_colorSpace); } else if (m_currentPreset->paintOp().id() == "roundmarker" || m_currentPreset->paintOp().id() == "experimentbrush" || m_currentPreset->paintOp().id() == "duplicate" ) { // cases where we will not show a preview for now // roundbrush (quick) -- this isn't showing anything, disable showing preview // experimentbrush -- this creates artifacts that carry over to other previews and messes up their display // duplicate (clone) brush doesn't have a preview as it doesn't show anything) if(m_sceneImageItem) { this->scene()->removeItem(m_sceneImageItem); m_sceneImageItem = 0; } QFont font; font.setPixelSize(14); font.setBold(false); m_noPreviewText = this->scene()->addText(i18n("No Preview for this engine"),font); m_noPreviewText->setPos(50, this->height()/4); return; } else { // fill with gray first to clear out what existed from previous preview m_layer->paintDevice()->fill(m_image->bounds(), KoColor(palette().color(QPalette::Background) , m_colorSpace)); m_paintColor = KoColor(palette().color(QPalette::Text), m_colorSpace); } } class NotificationStroke : public QObject, public KisSimpleStrokeStrategy { Q_OBJECT public: NotificationStroke() : KisSimpleStrokeStrategy(QLatin1String("NotificationStroke")) { setClearsRedoOnStart(false); this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER); this->enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER); } void initStrokeCallback() { emit timeout(); } void cancelStrokeCallback() { emit cancelled(); } Q_SIGNALS: void timeout(); void cancelled(); }; void KisPresetLivePreviewView::setupAndPaintStroke() { // limit the brush stroke size. larger brush strokes just don't look good and are CPU intensive // we are making a proxy preset and setting it to the painter...otherwise setting the brush size of the original preset // will fire off signals that make this run in an infinite loop qreal previewSize = qBound(3.0, m_currentPreset->settings()->paintOpSize(), 25.0 ); // constrain live preview brush size //Except for the sketchbrush where it determine sthe history. if (m_currentPreset->paintOp().id() == "sketchbrush" || m_currentPreset->paintOp().id() == "spraybrush") { previewSize = qMax(3.0, m_currentPreset->settings()->paintOpSize()); } KisPaintOpPresetSP proxy_preset = m_currentPreset->clone().dynamicCast(); KisPaintOpSettingsSP settings = proxy_preset->settings(); settings->setPaintOpSize(previewSize); int maxTextureSize = 200; int textureOffsetX = settings->getInt("Texture/Pattern/MaximumOffsetX")*2; int textureOffsetY = settings->getInt("Texture/Pattern/MaximumOffsetY")*2; double textureScale = settings->getDouble("Texture/Pattern/Scale"); if ( textureOffsetX*textureScale> maxTextureSize || textureOffsetY*textureScale > maxTextureSize) { int maxSize = qMax(textureOffsetX, textureOffsetY); double result = qreal(maxTextureSize) / maxSize; settings->setProperty("Texture/Pattern/Scale", result); } if (proxy_preset->paintOp().id() == "spraybrush") { QDomElement element; QDomDocument d; QString brushDefinition = settings->getString("brush_definition"); if (!brushDefinition.isEmpty()) { d.setContent(brushDefinition, false); element = d.firstChildElement("Brush"); - KisBrushSP brush = KisBrush::fromXML(element); + KisBrushSP brush = KisBrush::fromXML(element, KisGlobalResourcesInterface::instance()); qreal width = brush->image().width(); qreal scale = brush->scale(); qreal diameterToBrushRatio = 1.0; qreal diameter = settings->getInt("Spray/diameter"); //hack, 1000 being the maximum possible brushsize. if (brush->filename().endsWith(".svg")) { diameterToBrushRatio = diameter/(1000.0*scale); scale = 25.0 / 1000.0; } else { if (width * scale > 25.0) { diameterToBrushRatio = diameter / (width * scale); scale = 25.0 / width; } } settings->setProperty("Spray/diameter", int(25.0 * diameterToBrushRatio)); brush->setScale(scale); d.clear(); element = d.createElement("Brush"); brush->toXML(d, element); d.appendChild(element); settings->setProperty("brush_definition", d.toString()); } } // Preset preview cannot display gradient color source: there is // no resource manager for KisResourcesSnapshot, therefore gradient is nullptr. // BUG: 385521 (Selecting "Gradient" in brush editor crashes krita) if (proxy_preset->paintOp().id() == "paintbrush") { QString colorSourceType = settings->getString("ColorSource/Type", "plain"); if (colorSourceType == "gradient") { settings->setProperty("ColorSource/Type", "plain"); } } proxy_preset->setSettings(settings); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(m_image, m_layer); resources->setOpacity(settings->paintOpOpacity()); resources->setBrush(proxy_preset); resources->setFGColorOverride(m_paintColor); KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo(); KisStrokeStrategy *stroke = new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("temp_stroke")); KisStrokeId strokeId = m_image->startStroke(stroke); // paint the stroke. The sketchbrush gets a different shape than the others to show how it works if (proxy_preset->paintOp().id() == "sketchbrush" || proxy_preset->paintOp().id() == "curvebrush" || proxy_preset->paintOp().id() == "particlebrush") { qreal startX = m_canvasCenterPoint.x() - (this->width()*0.4); qreal endX = m_canvasCenterPoint.x() + (this->width()*0.4); qreal middle = m_canvasCenterPoint.y(); KisPaintInformation pointOne; pointOne.setPressure(0.0); pointOne.setPos(QPointF(startX, middle)); KisPaintInformation pointTwo; pointTwo.setPressure(0.0); pointTwo.setPos(QPointF(startX, middle)); int repeats = 8; for (int i = 0; i < repeats; i++) { pointOne.setPos(pointTwo.pos()); pointOne.setPressure(pointTwo.pressure()); pointTwo.setPressure((1.0/repeats)*(i+1)); qreal xPos = ((1.0/repeats) * (i+1) * (endX-startX) )+startX; pointTwo.setPos(QPointF(xPos, middle)); qreal offset = (this->height()/(repeats*1.5))*(i+1); qreal handleY = middle + offset; if (i%2 == 0) { handleY = middle - offset; } m_image->addJob(strokeId, new FreehandStrokeStrategy::Data(0, pointOne, QPointF(pointOne.pos().x(), handleY), QPointF(pointTwo.pos().x(), handleY), pointTwo)); m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true)); } } else { // paint an S curve m_curvePointPI1.setPos(QPointF(m_canvasCenterPoint.x() - (this->width()*0.45), m_canvasCenterPoint.y() + (this->height()*0.2))); m_curvePointPI1.setPressure(0.0); m_curvePointPI2.setPos(QPointF(m_canvasCenterPoint.x() + (this->width()*0.4), m_canvasCenterPoint.y() - (this->height()*0.2) )); m_curvePointPI2.setPressure(1.0); m_image->addJob(strokeId, new FreehandStrokeStrategy::Data(0, m_curvePointPI1, QPointF(m_canvasCenterPoint.x(), m_canvasCenterPoint.y()-this->height()), QPointF(m_canvasCenterPoint.x(), m_canvasCenterPoint.y()+this->height()), m_curvePointPI2)); m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true)); } m_image->endStroke(strokeId); m_previewGenerationInProgress = true; NotificationStroke *notificationStroke = new NotificationStroke(); connect(notificationStroke, SIGNAL(timeout()), SLOT(slotPreviewGenerationCompleted())); KisStrokeId notificationId = m_image->startStroke(notificationStroke); m_image->endStroke(notificationId); // TODO: if we don't have any regressions because of it until 4.2.8, then // just remove this code. // even though the brush is cloned, the proxy_preset still has some connection to the original preset which will mess brush sizing // we need to return brush size to normal.The normal brush sends out a lot of extra signals, so keeping the proxy for now //proxy_preset->settings()->setPaintOpSize(originalPresetSize); } #include "kis_preset_live_preview_view.moc" diff --git a/plugins/dockers/tasksetdocker/taskset_resource.cpp b/plugins/dockers/tasksetdocker/taskset_resource.cpp index dc61cc11d2..9843760130 100644 --- a/plugins/dockers/tasksetdocker/taskset_resource.cpp +++ b/plugins/dockers/tasksetdocker/taskset_resource.cpp @@ -1,121 +1,119 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library 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; version 2 of the License, or * (at your option) any later version. * * This library 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 Lesser 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 "taskset_resource.h" #include #include #include #include #include #include #define TASKSET_VERSION 1 TasksetResource::TasksetResource(const QString& f) : KoResource(f) { } TasksetResource::~TasksetResource() { } TasksetResource::TasksetResource(const TasksetResource &rhs) : KoResource(rhs) { *this = rhs; } TasksetResource &TasksetResource::operator=(const TasksetResource &rhs) { if (*this != rhs) { m_actions = rhs.m_actions; } return *this; } KoResourceSP TasksetResource::clone() const { return KoResourceSP(new TasksetResource(*this)); } - - - - -bool TasksetResource::loadFromDevice(QIODevice *dev) +bool TasksetResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { + Q_UNUSED(resourcesInterface); + QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); QDomNode node = element.firstChild(); while (!node.isNull()) { QDomElement child = node.toElement(); if (!child.isNull() && child.tagName() == "action") { m_actions.append(child.text()); } node = node.nextSibling(); } setValid(true); return true; } QString TasksetResource::defaultFileExtension() const { return QString(".kts"); } void TasksetResource::setActionList(const QStringList actions) { m_actions = actions; } QStringList TasksetResource::actionList() { return m_actions; } bool TasksetResource::saveToDevice(QIODevice *io) const { QDomDocument doc; QDomElement root = doc.createElement("Taskset"); root.setAttribute("name", name() ); root.setAttribute("version", TASKSET_VERSION); Q_FOREACH (const QString& action, m_actions) { QDomElement element = doc.createElement("action"); element.appendChild(doc.createTextNode(action)); root.appendChild(element); } doc.appendChild(root); QTextStream textStream(io); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(io); return true; } diff --git a/plugins/dockers/tasksetdocker/taskset_resource.h b/plugins/dockers/tasksetdocker/taskset_resource.h index ff9705411e..07cc46e950 100644 --- a/plugins/dockers/tasksetdocker/taskset_resource.h +++ b/plugins/dockers/tasksetdocker/taskset_resource.h @@ -1,58 +1,58 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library 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; version 2 of the License, or * (at your option) any later version. * * This library 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 Lesser 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 TASKSET_RESOURCE_H #define TASKSET_RESOURCE_H #include #include class TasksetResource : public KoResource { public: TasksetResource(const QString& filename); ~TasksetResource() override; TasksetResource(const TasksetResource &rhs); TasksetResource &operator=(const TasksetResource &rhs); KoResourceSP clone() const override; - bool loadFromDevice(QIODevice *dev) override; + bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; QPair resourceType() const override { return QPair(ResourceType::TaskSets, ""); } void setActionList(const QStringList actions); QStringList actionList(); private: QStringList m_actions; }; typedef QSharedPointer TasksetResourceSP; #endif // TASKSET_RESOURCE_H diff --git a/plugins/impex/brush/kis_brush_import.cpp b/plugins/impex/brush/kis_brush_import.cpp index ec08599ba4..a2deb8b23a 100644 --- a/plugins/impex/brush/kis_brush_import.cpp +++ b/plugins/impex/brush/kis_brush_import.cpp @@ -1,120 +1,121 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush_import.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include K_PLUGIN_FACTORY_WITH_JSON(KisBrushImportFactory, "krita_brush_import.json", registerPlugin();) KisBrushImport::KisBrushImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisBrushImport::~KisBrushImport() { } KisImportExportErrorCode KisBrushImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) { KisBrushSP brush; if (mimeType() == "image/x-gimp-brush") { brush = KisBrushSP(new KisGbrBrush(filename())); } else if (mimeType() == "image/x-gimp-brush-animated") { brush = KisBrushSP(new KisImagePipeBrush(filename())); } else { return ImportExportCodes::FileFormatIncorrect; } - if (!brush->loadFromDevice(io)) { + if (!brush->loadFromDevice(io, KisGlobalResourcesInterface::instance())) { return ImportExportCodes::FileFormatIncorrect; } if (!brush->valid()) { return ImportExportCodes::FileFormatIncorrect;; } const KoColorSpace *colorSpace = 0; if (brush->hasColor()) { colorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), ""); } KisImageSP image = new KisImage(document->createUndoStore(), brush->width(), brush->height(), colorSpace, brush->name()); image->setProperty("brushspacing", brush->spacing()); KisImagePipeBrushSP pipeBrush = brush.dynamicCast(); if (pipeBrush) { QVector brushes = pipeBrush->brushes(); for(int i = brushes.size(); i > 0; i--) { KisGbrBrushSP subbrush = brushes.at(i - 1); const KoColorSpace *subColorSpace = 0; if (brush->hasColor()) { subColorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { subColorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), ""); } KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, subColorSpace); layer->paintDevice()->convertFromQImage(subbrush->brushTipImage(), 0, 0, 0); image->addNode(layer, image->rootLayer()); } KisAnnotationSP ann = new KisAnimatedBrushAnnotation(pipeBrush->parasite()); image->addAnnotation(ann); } else { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, colorSpace); layer->paintDevice()->convertFromQImage(brush->brushTipImage(), 0, 0, 0); image->addNode(layer, image->rootLayer(), 0); } document->setCurrentImage(image); return ImportExportCodes::OK; } #include "kis_brush_import.moc" diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp index b057bc176e..849e95ece1 100644 --- a/plugins/impex/libkra/kis_kra_loader.cpp +++ b/plugins/impex/libkra/kis_kra_loader.cpp @@ -1,1283 +1,1284 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_kra_loader.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 "lazybrush/kis_colorize_mask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceServerProvider.h" #include "kis_keyframe_channel.h" #include #include "KisReferenceImagesLayer.h" #include "KisReferenceImage.h" #include #include "KisDocument.h" #include "kis_config.h" #include "kis_kra_tags.h" #include "kis_kra_utils.h" #include "kis_kra_load_visitor.h" #include "kis_dom_utils.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_config.h" #include "KisProofingConfiguration.h" #include "kis_layer_properties_icons.h" #include "kis_node_view_color_scheme.h" #include "KisMirrorAxisConfig.h" +#include /* Color model id comparison through the ages: 2.4 2.5 2.6 ideal ALPHA ALPHA ALPHA ALPHAU8 CMYK CMYK CMYK CMYKAU8 CMYKAF32 CMYKAF32 CMYKA16 CMYKAU16 CMYKAU16 GRAYA GRAYA GRAYA GRAYAU8 GrayF32 GRAYAF32 GRAYAF32 GRAYA16 GRAYAU16 GRAYAU16 LABA LABA LABA LABAU16 LABAF32 LABAF32 LABAU8 LABAU8 RGBA RGBA RGBA RGBAU8 RGBA16 RGBA16 RGBA16 RGBAU16 RgbAF32 RGBAF32 RGBAF32 RgbAF16 RgbAF16 RGBAF16 XYZA16 XYZA16 XYZA16 XYZAU16 XYZA8 XYZA8 XYZAU8 XyzAF16 XyzAF16 XYZAF16 XyzAF32 XYZAF32 XYZAF32 YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8 YCbCrAU16 YCBCRAU16 YCBCRAU16 YCBCRF32 YCBCRF32 */ using namespace KRA; struct KisKraLoader::Private { public: KisDocument* document; QString imageName; // used to be stored in the image, is now in the documentInfo block QString imageComment; // used to be stored in the image, is now in the documentInfo block QMap layerFilenames; // temp storage during loading int syntaxVersion; // version of the fileformat we are loading vKisNodeSP selectedNodes; // the nodes that were active when saving the document. QMap assistantsFilenames; QList assistants; QMap keyframeFilenames; QVector paletteFilenames; QStringList errorMessages; QStringList warningMessages; }; void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) { if (colorspacename == "Grayscale + Alpha") { colorspacename = "GRAYA"; profileProductName.clear(); } else if (colorspacename == "RgbAF32") { colorspacename = "RGBAF32"; profileProductName.clear(); } else if (colorspacename == "RgbAF16") { colorspacename = "RGBAF16"; profileProductName.clear(); } else if (colorspacename == "CMYKA16") { colorspacename = "CMYKAU16"; } else if (colorspacename == "GrayF32") { colorspacename = "GRAYAF32"; profileProductName.clear(); } else if (colorspacename == "GRAYA16") { colorspacename = "GRAYAU16"; } else if (colorspacename == "XyzAF16") { colorspacename = "XYZAF16"; profileProductName.clear(); } else if (colorspacename == "XyzAF32") { colorspacename = "XYZAF32"; profileProductName.clear(); } else if (colorspacename == "YCbCrA") { colorspacename = "YCBCRA8"; } else if (colorspacename == "YCbCrAU16") { colorspacename = "YCBCRAU16"; } } KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion) : m_d(new Private()) { m_d->document = document; m_d->syntaxVersion = syntaxVersion; } KisKraLoader::~KisKraLoader() { delete m_d; } KisImageSP KisKraLoader::loadXML(const KoXmlElement& element) { QString attr; KisImageSP image = 0; qint32 width; qint32 height; QString profileProductName; double xres; double yres; QString colorspacename; const KoColorSpace * cs; if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) { if ((m_d->imageName = element.attribute(NAME)).isNull()) { m_d->errorMessages << i18n("Image does not have a name."); return KisImageSP(0); } if ((attr = element.attribute(WIDTH)).isNull()) { m_d->errorMessages << i18n("Image does not specify a width."); return KisImageSP(0); } width = KisDomUtils::toInt(attr); if ((attr = element.attribute(HEIGHT)).isNull()) { m_d->errorMessages << i18n("Image does not specify a height."); return KisImageSP(0); } height = KisDomUtils::toInt(attr); m_d->imageComment = element.attribute(DESCRIPTION); xres = 100.0 / 72.0; if (!(attr = element.attribute(X_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { xres = value / 72.0; } } yres = 100.0 / 72.0; if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { yres = value / 72.0; } } if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) { // An old file: take a reasonable default. // Krita didn't support anything else in those // days anyway. colorspacename = "RGBA"; } profileProductName = element.attribute(PROFILE); // A hack for an old colorspacename convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); if (profileProductName.isNull()) { // no mention of profile so get default profile"; cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName); } if (cs == 0) { // try once more without the profile cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); if (cs == 0) { m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename); return KisImageSP(0); } } KisProofingConfigurationSP proofingConfig = KisImageConfig(true).defaultProofingconfiguration(); if (!(attr = element.attribute(PROOFINGPROFILENAME)).isNull()) { proofingConfig->proofingProfile = attr; } if (!(attr = element.attribute(PROOFINGMODEL)).isNull()) { proofingConfig->proofingModel = attr; } if (!(attr = element.attribute(PROOFINGDEPTH)).isNull()) { proofingConfig->proofingDepth = attr; } if (!(attr = element.attribute(PROOFINGINTENT)).isNull()) { proofingConfig->intent = (KoColorConversionTransformation::Intent) KisDomUtils::toInt(attr); } if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) { proofingConfig->adaptationState = KisDomUtils::toDouble(attr); } if (m_d->document) { image = new KisImage(m_d->document->createUndoStore(), width, height, cs, m_d->imageName); } else { image = new KisImage(0, width, height, cs, m_d->imageName); } image->setResolution(xres, yres); loadNodes(element, image, const_cast(image->rootLayer().data())); KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == CANVASPROJECTIONCOLOR) { if (e.hasAttribute(COLORBYTEDATA)) { QByteArray colorData = QByteArray::fromBase64(e.attribute(COLORBYTEDATA).toLatin1()); KoColor color((const quint8*)colorData.data(), image->colorSpace()); image->setDefaultProjectionColor(color); } } if(e.tagName() == GLOBALASSISTANTSCOLOR) { if (e.hasAttribute(SIMPLECOLORDATA)) { QString colorData = e.attribute(SIMPLECOLORDATA); m_d->document->setAssistantsGlobalColor(KisDomUtils::qStringToQColor(colorData)); } } if(e.tagName()== PROOFINGWARNINGCOLOR) { QDomDocument dom; KoXml::asQDomElement(dom, e); QDomElement eq = dom.firstChildElement(); proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id()); } if (e.tagName().toLower() == "animation") { loadAnimationMetadata(e, image); } } image->setProofingConfiguration(proofingConfig); for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "compositions") { loadCompositions(e, image); } } } KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if (e.tagName() == "grid") { loadGrid(e); } else if (e.tagName() == "guides") { loadGuides(e); } else if (e.tagName() == MIRROR_AXIS) { loadMirrorAxis(e); } else if (e.tagName() == "assistants") { loadAssistantsList(e); } else if (e.tagName() == "audio") { loadAudio(e, image); } } // reading palettes from XML for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { QDomElement e = child.toElement(); if (e.tagName() == PALETTES) { for (QDomElement paletteElement = e.lastChildElement(); !paletteElement.isNull(); paletteElement = paletteElement.previousSiblingElement()) { QString paletteName = paletteElement.attribute("filename"); m_d->paletteFilenames.append(paletteName); } break; } } return image; } void KisKraLoader::loadBinaryData(KoStore * store, KisImageSP image, const QString & uri, bool external) { // icc profile: if present, this overrides the profile product name loaded in loadXML. QString location = external ? QString() : uri; location += m_d->imageName + ICC_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray data; data.resize(store->size()); bool res = (store->read(data.data(), store->size()) > -1); store->close(); if (res) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data); if (profile && profile->valid()) { res = image->assignImageProfile(profile, true); image->waitForDone(); } if (!res) { const QString defaultProfileId = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(image->colorSpace()->id()); profile = KoColorSpaceRegistry::instance()->profileByName(defaultProfileId); Q_ASSERT(profile && profile->valid()); image->assignImageProfile(profile, true); image->waitForDone(); } } } } //load the embed proofing profile, it only needs to be loaded into Krita, not assigned. location = external ? QString() : uri; location += m_d->imageName + ICC_PROOFING_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray proofingData; proofingData.resize(store->size()); bool proofingProfileRes = (store->read(proofingData.data(), store->size())>-1); store->close(); KisProofingConfigurationSP proofingConfig = image->proofingConfiguration(); if (!proofingConfig) { proofingConfig = KisImageConfig(true).defaultProofingconfiguration(); } if (proofingProfileRes) { const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingData); if (proofingProfile->valid()){ KoColorSpaceRegistry::instance()->addProfile(proofingProfile); } } } } // Load the layers data: if there is a profile associated with a layer it will be set now. KisKraLoadVisitor visitor(image, store, m_d->document->shapeController(), m_d->layerFilenames, m_d->keyframeFilenames, m_d->imageName, m_d->syntaxVersion); if (external) { visitor.setExternalUri(uri); } image->rootLayer()->accept(visitor); if (!visitor.errorMessages().isEmpty()) { m_d->errorMessages.append(visitor.errorMessages()); } if (!visitor.warningMessages().isEmpty()) { m_d->warningMessages.append(visitor.warningMessages()); } // annotations // exif location = external ? QString() : uri; location += m_d->imageName + EXIF_PATH; if (store->hasFile(location)) { QByteArray data; store->open(location); data = store->read(store->size()); store->close(); image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data))); } // layer styles location = external ? QString() : uri; location += m_d->imageName + LAYER_STYLES_PATH; if (store->hasFile(location)) { warnKrita << "WARNING: Asl Layer Styles cannot be read (part of resource rewrite)."; // TODO: RESOURCES: needs implementing of creation of the storage and linking it to the database etc. // Question: do we need to use KisAslStorage or the document storage? // see: QSharedPointer storage = KisResourceServerProvider::instance()->storageByName(m_d->document->uniqueID()); // and then: storage->addResource(aslStyle); // or through the server: get the server, add everything directly to the server // but I believe it should go through the KisAslStorage? // tiar // //KisPSDLayerStyleSP collection(new KisPSDLayerStyle("Embedded Styles.asl")); // collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName)); // KIS_ASSERT_RECOVER_NOOP(!collection->valid()); // store->open(location); // { // KoStoreDevice device(store); // device.open(QIODevice::ReadOnly); // /** // * ASL loading code cannot work with non-sequential IO devices, // * so convert the device beforehand! // */ // QByteArray buf = device.readAll(); // QBuffer raDevice(&buf); // raDevice.open(QIODevice::ReadOnly); // collection->loadFromDevice(&raDevice); // } // store->close(); // if (collection->valid()) { // KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer(); // server->addResource(collection, false); // collection->assignAllLayerStyles(image->root()); // } else { // warnKrita << "WARNING: Couldn't load layer styles library from .kra!"; // } } if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull()) m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName); if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull()) m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment); loadAssistants(store, uri, external); } void KisKraLoader::loadPalettes(KoStore *store, KisDocument *doc) { QList list; Q_FOREACH (const QString &filename, m_d->paletteFilenames) { KoColorSetSP newPalette(new KoColorSet(filename)); store->open(m_d->imageName + PALETTE_PATH + filename); QByteArray data = store->read(store->size()); - newPalette->fromByteArray(data); + newPalette->fromByteArray(data, KisGlobalResourcesInterface::instance()); newPalette->setIsGlobal(false); newPalette->setIsEditable(true); store->close(); list.append(newPalette); } doc->setPaletteList(list); } vKisNodeSP KisKraLoader::selectedNodes() const { return m_d->selectedNodes; } QList KisKraLoader::assistants() const { return m_d->assistants; } QStringList KisKraLoader::errorMessages() const { return m_d->errorMessages; } QStringList KisKraLoader::warningMessages() const { return m_d->warningMessages; } QString KisKraLoader::imageName() const { return m_d->imageName; } void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external) { QString file_path; QString location; QMap handleMap; KisPaintingAssistant* assistant = 0; const QColor globalColor = m_d->document->assistantsGlobalColor(); QMap::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin(); while (loadedAssistant != m_d->assistantsFilenames.constEnd()){ const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value()); if (factory) { assistant = factory->createPaintingAssistant(); location = external ? QString() : uri; location += m_d->imageName + ASSISTANTS_PATH; file_path = location + loadedAssistant.key(); assistant->loadXml(store, handleMap, file_path); assistant->setAssistantGlobalColorCache(globalColor); //If an assistant has too few handles than it should according to it's own setup, just don't load it// if (assistant->handles().size()==assistant->numHandles()){ m_d->assistants.append(toQShared(assistant)); } } loadedAssistant++; } } void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageSP image) { QDomDocument qDom; KoXml::asQDomElement(qDom, element); QDomElement qElement = qDom.firstChildElement(); float framerate; KisTimeRange range; int currentTime; KisImageAnimationInterface *animation = image->animationInterface(); if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) { animation->setFramerate(framerate); } if (KisDomUtils::loadValue(qElement, "range", &range)) { animation->setFullClipRange(range); } if (KisDomUtils::loadValue(qElement, "currentTime", ¤tTime)) { animation->switchCurrentTimeAsync(currentTime); } } KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent) { KoXmlNode node = element.firstChild(); KoXmlNode child; if (!node.isNull()) { if (node.isElement()) { if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) { for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) { KisNodeSP node = loadNode(child.toElement(), image); if (node) { image->nextLayerName(); // Make sure the nameserver is current with the number of nodes. image->addNode(node, parent); if (node->inherits("KisLayer") && KoXml::childNodesCount(child) > 0) { loadNodes(child.toElement(), image, node); } } } } } } return parent; } KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageSP image) { // Nota bene: If you add new properties to layers, you should // ALWAYS define a default value in case the property is not // present in the layer definition: this helps a LOT with backward // compatibility. QString name = element.attribute(NAME, "No Name"); QUuid id = QUuid(element.attribute(UUID, QUuid().toString())); qint32 x = element.attribute(X, "0").toInt(); qint32 y = element.attribute(Y, "0").toInt(); qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt(); if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8; if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8; const KoColorSpace* colorSpace = 0; if ((element.attribute(COLORSPACE_NAME)).isNull()) { dbgFile << "No attribute color space for layer: " << name; colorSpace = image->colorSpace(); } else { QString colorspacename = element.attribute(COLORSPACE_NAME); QString profileProductName; convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name; // use default profile - it will be replaced later in completeLoading colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); dbgFile << "found colorspace" << colorSpace; if (!colorSpace) { m_d->warningMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename); return 0; } } const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true; const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true; const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true; int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt(); QVector labels = KisNodeViewColorScheme::instance()->allColorLabels(); if (colorLabelIndex >= labels.size()) { colorLabelIndex = labels.size() - 1; } // Now find out the layer type and do specific handling QString nodeType; if (m_d->syntaxVersion == 1) { nodeType = element.attribute("layertype"); if (nodeType.isEmpty()) { nodeType = PAINT_LAYER; } } else { nodeType = element.attribute(NODE_TYPE); } if (nodeType.isEmpty()) { m_d->warningMessages << i18n("Layer %1 has an unsupported type.", name); return 0; } KisNodeSP node = 0; if (nodeType == PAINT_LAYER) node = loadPaintLayer(element, image, name, colorSpace, opacity); else if (nodeType == GROUP_LAYER) node = loadGroupLayer(element, image, name, colorSpace, opacity); else if (nodeType == ADJUSTMENT_LAYER) node = loadAdjustmentLayer(element, image, name, colorSpace, opacity); else if (nodeType == SHAPE_LAYER) node = loadShapeLayer(element, image, name, colorSpace, opacity); else if (nodeType == GENERATOR_LAYER) node = loadGeneratorLayer(element, image, name, colorSpace, opacity); else if (nodeType == CLONE_LAYER) node = loadCloneLayer(element, image, name, colorSpace, opacity); else if (nodeType == FILTER_MASK) node = loadFilterMask(element); else if (nodeType == TRANSFORM_MASK) node = loadTransformMask(element); else if (nodeType == TRANSPARENCY_MASK) node = loadTransparencyMask(element); else if (nodeType == SELECTION_MASK) node = loadSelectionMask(image, element); else if (nodeType == COLORIZE_MASK) node = loadColorizeMask(image, element, colorSpace); else if (nodeType == FILE_LAYER) node = loadFileLayer(element, image, name, opacity); else if (nodeType == REFERENCE_IMAGES_LAYER) node = loadReferenceImagesLayer(element, image); else { m_d->warningMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType); return 0; } // Loading the node went wrong. Return empty node and leave to // upstream to complain to the user if (!node) { m_d->warningMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType); return 0; } node->setVisible(visible, true); node->setUserLocked(locked); node->setCollapsed(collapsed); node->setColorLabelIndex(colorLabelIndex); node->setX(x); node->setY(y); node->setName(name); if (! id.isNull()) // if no uuid in file, new one has been generated already node->setUuid(id); if (node->inherits("KisLayer") || node->inherits("KisColorizeMask")) { QString compositeOpName = element.attribute(COMPOSITE_OP, "normal"); node->setCompositeOpId(compositeOpName); } if (node->inherits("KisLayer")) { KisLayer* layer = qobject_cast(node.data()); QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount()); layer->setChannelFlags(channelFlags); if (element.hasAttribute(LAYER_STYLE_UUID)) { QString uuidString = element.attribute(LAYER_STYLE_UUID); QUuid uuid(uuidString); if (!uuid.isNull()) { KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle()); dumbLayerStyle->setUuid(uuid); layer->setLayerStyle(dumbLayerStyle); } else { warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString; } } } if (node->inherits("KisGroupLayer")) { if (element.hasAttribute(PASS_THROUGH_MODE)) { bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0"; KisGroupLayer *group = qobject_cast(node.data()); group->setPassThroughMode(value); } } const bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true; node->setUseInTimeline(timelineEnabled); if (node->inherits("KisPaintLayer")) { KisPaintLayer* layer = qobject_cast(node.data()); QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount()); layer->setChannelLockFlags(channelLockFlags); bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true; layer->setOnionSkinEnabled(onionEnabled); } if (element.attribute(FILE_NAME).isNull()) { m_d->layerFilenames[node.data()] = name; } else { m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME); } if (element.hasAttribute("selected") && element.attribute("selected") == "true") { m_d->selectedNodes.append(node); } if (element.hasAttribute(KEYFRAME_FILE)) { m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE)); } return node; } KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); KisPaintLayer* layer; layer = new KisPaintLayer(image, name, opacity, cs); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageSP image, const QString& name, quint32 opacity) { QString filename = element.attribute("source", QString()); if (filename.isNull()) return 0; bool scale = (element.attribute("scale", "true") == "true"); int scalingMethod = element.attribute("scalingmethod", "-1").toInt(); if (scalingMethod < 0) { if (scale) { scalingMethod = KisFileLayer::ToImagePPI; } else { scalingMethod = KisFileLayer::None; } } QString documentPath; if (m_d->document) { documentPath = m_d->document->url().toLocalFile(); } QFileInfo info(documentPath); QString basePath = info.absolutePath(); QString fullPath = QDir(basePath).filePath(QDir::cleanPath(filename)); if (!QFileInfo(fullPath).exists()) { qApp->setOverrideCursor(Qt::ArrowCursor); QString msg = i18nc( "@info", "The file associated to a file layer with the name \"%1\" is not found.\n\n" "Expected path:\n" "%2\n\n" "Do you want to locate it manually?", name, fullPath); int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); dialog.setDefaultDir(basePath); QString url = dialog.filename(); if (!QFileInfo(basePath).exists()) { filename = url; } else { QDir d(basePath); filename = d.relativeFilePath(url); } } qApp->restoreOverrideCursor(); } KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KisGroupLayer* layer; layer = new KisGroupLayer(image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { // XXX: do something with filterversion? Q_UNUSED(cs); QString attr; KisAdjustmentLayer* layer; QString filtername; QString legacy = filtername; if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid adjustmentlayer! We should warn about it! warnFile << "No filter in adjustment layer"; return 0; } //get deprecated filters. if (filtername=="brightnesscontrast") { legacy = filtername; filtername = "perchannel"; } if (filtername=="left edge detections" || filtername=="right edge detections" || filtername=="top edge detections" || filtername=="bottom edge detections") { legacy = filtername; filtername = "edge detection"; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfigurationSP kfc = f->factoryConfiguration(); kfc->setProperty("legacy", legacy); if (legacy=="brightnesscontrast") { kfc->setProperty("colorModel", cs->colorModelId().id()); } // We'll load the configuration and the selection later. layer = new KisAdjustmentLayer(image, name, kfc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KoShapeControllerBase * shapeController = 0; if (m_d->document) { shapeController = m_d->document->shapeController(); } KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); // XXX: do something with generator version? KisGeneratorLayer* layer; QString generatorname = element.attribute(GENERATOR_NAME); if (generatorname.isNull()) { // XXX: Invalid generator layer! We should warn about it! warnFile << "No generator in generator layer"; return 0; } KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname); if (!generator) { warnFile << "No generator for generatorname" << generatorname << ""; return 0; // XXX: We don't have this generator. We should warn about it! } KisFilterConfigurationSP kgc = generator->factoryConfiguration(); // We'll load the configuration and the selection later. layer = new KisGeneratorLayer(image, name, kgc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity); KisNodeUuidInfo info; if (! (element.attribute(CLONE_FROM_UUID)).isNull()) { info = KisNodeUuidInfo(QUuid(element.attribute(CLONE_FROM_UUID))); } else { if ((element.attribute(CLONE_FROM)).isNull()) { return 0; } else { info = KisNodeUuidInfo(element.attribute(CLONE_FROM)); } } layer->setCopyFromInfo(info); if ((element.attribute(CLONE_TYPE)).isNull()) { return 0; } else { layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt()); } return layer; } KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element) { QString attr; KisFilterMask* mask; QString filtername; // XXX: should we check the version? if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid filter layer! We should warn about it! warnFile << "No filter in filter layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfigurationSP kfc = f->factoryConfiguration(); // We'll load the configuration and the selection later. mask = new KisFilterMask(); mask->setFilter(kfc); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element) { Q_UNUSED(element); KisTransformMask* mask; /** * We'll load the transform configuration later on a stage * of binary data loading */ mask = new KisTransformMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element) { Q_UNUSED(element); KisTransparencyMask* mask = new KisTransparencyMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadSelectionMask(KisImageSP image, const KoXmlElement& element) { KisSelectionMaskSP mask = new KisSelectionMask(image); bool active = element.attribute(ACTIVE, "1") == "0" ? false : true; mask->setActive(active); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadColorizeMask(KisImageSP image, const KoXmlElement& element, const KoColorSpace *colorSpace) { KisColorizeMaskSP mask = new KisColorizeMask(); const bool editKeystrokes = element.attribute(COLORIZE_EDIT_KEYSTROKES, "1") == "0" ? false : true; const bool showColoring = element.attribute(COLORIZE_SHOW_COLORING, "1") == "0" ? false : true; KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, editKeystrokes, image); KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, showColoring, image); const bool useEdgeDetection = KisDomUtils::toInt(element.attribute(COLORIZE_USE_EDGE_DETECTION, "0")); const qreal edgeDetectionSize = KisDomUtils::toDouble(element.attribute(COLORIZE_EDGE_DETECTION_SIZE, "4")); const qreal radius = KisDomUtils::toDouble(element.attribute(COLORIZE_FUZZY_RADIUS, "0")); const int cleanUp = KisDomUtils::toInt(element.attribute(COLORIZE_CLEANUP, "0")); const bool limitToDevice = KisDomUtils::toInt(element.attribute(COLORIZE_LIMIT_TO_DEVICE, "0")); mask->setUseEdgeDetection(useEdgeDetection); mask->setEdgeDetectionSize(edgeDetectionSize); mask->setFuzzyRadius(radius); mask->setCleanUpAmount(qreal(cleanUp) / 100.0); mask->setLimitToDeviceBounds(limitToDevice); delete mask->setColorSpace(colorSpace); mask->setImage(image); return mask; } void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageSP image) { KoXmlNode child; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString name = e.attribute("name"); bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true; KisLayerCompositionSP composition(new KisLayerComposition(image, name)); composition->setExportEnabled(exportEnabled); KoXmlNode value; for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) { KoXmlElement e = value.toElement(); QUuid uuid(e.attribute("uuid")); bool visible = e.attribute("visible", "1") == "0" ? false : true; composition->setVisible(uuid, visible); bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true; composition->setCollapsed(uuid, collapsed); } image->addComposition(composition); } } void KisKraLoader::loadAssistantsList(const KoXmlElement &elem) { KoXmlNode child; int count = 0; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString type = e.attribute("type"); QString file_name = e.attribute("filename"); m_d->assistantsFilenames.insert(file_name,type); count++; } } void KisKraLoader::loadGrid(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGridConfig config; config.loadDynamicDataFromXml(domElement); config.loadStaticData(); m_d->document->setGridConfig(config); } void KisKraLoader::loadGuides(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGuidesConfig guides; guides.loadFromXml(domElement); m_d->document->setGuidesConfig(guides); } void KisKraLoader::loadMirrorAxis(const KoXmlElement &elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisMirrorAxisConfig mirrorAxis; mirrorAxis.loadFromXml(domElement); m_d->document->setMirrorAxisConfig(mirrorAxis); } void KisKraLoader::loadAudio(const KoXmlElement& elem, KisImageSP image) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement qElement = dom.firstChildElement(); QString fileName; if (KisDomUtils::loadValue(qElement, "masterChannelPath", &fileName)) { fileName = QDir::toNativeSeparators(fileName); QDir baseDirectory = QFileInfo(m_d->document->localFilePath()).absoluteDir(); fileName = baseDirectory.absoluteFilePath(fileName); QFileInfo info(fileName); if (!info.exists()) { qApp->setOverrideCursor(Qt::ArrowCursor); QString msg = i18nc( "@info", "Audio channel file \"%1\" doesn't exist!\n\n" "Expected path:\n" "%2\n\n" "Do you want to locate it manually?", info.fileName(), info.absoluteFilePath()); int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { info.setFile(KisImportExportManager::askForAudioFileName(info.absolutePath(), 0)); } qApp->restoreOverrideCursor(); } if (info.exists()) { image->animationInterface()->setAudioChannelFileName(info.absoluteFilePath()); } } bool audioMuted = false; if (KisDomUtils::loadValue(qElement, "audioMuted", &audioMuted)) { image->animationInterface()->setAudioMuted(audioMuted); } qreal audioVolume = 0.5; if (KisDomUtils::loadValue(qElement, "audioVolume", &audioVolume)) { image->animationInterface()->setAudioVolume(audioVolume); } } KisNodeSP KisKraLoader::loadReferenceImagesLayer(const KoXmlElement &elem, KisImageSP image) { KisSharedPtr layer = new KisReferenceImagesLayer(m_d->document->shapeController(), image); m_d->document->setReferenceImagesLayer(layer, false); for (QDomElement child = elem.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { if (child.nodeName().toLower() == "referenceimage") { auto* reference = KisReferenceImage::fromXml(child); layer->addShape(reference); } } return layer; } diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp index 89e3b6e569..4b7ca8a4ee 100644 --- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp +++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp @@ -1,121 +1,122 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_colorsmudgeop_settings.h" #include "kis_smudge_option.h" struct KisColorSmudgeOpSettings::Private { QList uniformProperties; }; -KisColorSmudgeOpSettings::KisColorSmudgeOpSettings() - : m_d(new Private) +KisColorSmudgeOpSettings::KisColorSmudgeOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface), + m_d(new Private) { } KisColorSmudgeOpSettings::~KisColorSmudgeOpSettings() { } #include #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_curve_option_uniform_property.h" #include "kis_smudge_radius_option.h" QList KisColorSmudgeOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisComboBasedPaintOpPropertyCallback *prop = new KisComboBasedPaintOpPropertyCallback( "smudge_mode", i18n("Smudge Mode"), settings, 0); QList modes; modes << i18n("Smearing"); modes << i18n("Dulling"); prop->setItems(modes); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisSmudgeOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.getMode())); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisSmudgeOption option; option.readOptionSetting(prop->settings().data()); option.setMode(KisSmudgeOption::Mode(prop->value().toInt())); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisCurveOptionUniformProperty *prop = new KisCurveOptionUniformProperty( "smudge_length", new KisSmudgeOption(), settings, 0); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisCurveOptionUniformProperty *prop = new KisCurveOptionUniformProperty( "smudge_radius", new KisSmudgeRadiusOption(), settings, 0); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisCurveOptionUniformProperty *prop = new KisCurveOptionUniformProperty( "smudge_color_rate", new KisRateOption("ColorRate", KisPaintOpOption::GENERAL, false), settings, 0); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisBrushBasedPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h index 5bef9202ae..c00b65ac55 100644 --- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h +++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_COLORSMUDGEOP_SETTINGS_H #define __KIS_COLORSMUDGEOP_SETTINGS_H #include #include class KisColorSmudgeOpSettings : public KisBrushBasedPaintOpSettings { public: - KisColorSmudgeOpSettings(); + KisColorSmudgeOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisColorSmudgeOpSettings() override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; typedef KisSharedPtr KisColorSmudgeOpSettingsSP; #endif /* __KIS_COLORSMUDGEOP_SETTINGS_H */ diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp index 6ba0de794e..4e2b71f3bd 100644 --- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp +++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp @@ -1,91 +1,92 @@ /* * Copyright (C) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_colorsmudgeop_settings_widget.h" #include "kis_brush_based_paintop_settings.h" #include "kis_overlay_mode_option.h" #include "kis_rate_option.h" #include "kis_smudge_option_widget.h" #include "kis_smudge_radius_option.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" #include "kis_pressure_hsv_option.h" #include "kis_colorsmudgeop_settings.h" +#include KisColorSmudgeOpSettingsWidget::KisColorSmudgeOpSettingsWidget(QWidget* parent): KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); m_smudgeOptionWidget = new KisSmudgeOptionWidget(); addPaintOpOption(m_smudgeOptionWidget, i18n("Smudge Length")); addPaintOpOption(new KisCurveOptionWidget(new KisSmudgeRadiusOption(), i18n("0.0"), i18n("1.0")), i18n("Smudge Radius")); addPaintOpOption(new KisCurveOptionWidget(new KisRateOption("ColorRate", KisPaintOpOption::GENERAL, false), i18n("0.0"), i18n("1.0")), i18n("Color Rate")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter")); addPaintOpOption(new KisOverlayModeOptionWidget(), i18n("Overlay Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureGradientOption(), i18n("0%"), i18n("100%")), i18n("Gradient")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); } KisColorSmudgeOpSettingsWidget::~KisColorSmudgeOpSettingsWidget() { } KisPropertiesConfigurationSP KisColorSmudgeOpSettingsWidget::configuration() const { - KisColorSmudgeOpSettingsSP config = new KisColorSmudgeOpSettings(); + KisColorSmudgeOpSettingsSP config = new KisColorSmudgeOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "colorsmudge"); writeConfiguration(config); return config; } void KisColorSmudgeOpSettingsWidget::notifyPageChanged() { KisBrushSP brush = this->brush(); bool pierced = brush ? brush->isPiercedApprox() : false; m_smudgeOptionWidget->updateBrushPierced(pierced); } diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp index 6489c6c3bc..62acd99992 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp +++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp @@ -1,202 +1,203 @@ /* * Copyright (c) 2008,2009 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_curve_line_option.h" struct KisCurvePaintOpSettings::Private { QList uniformProperties; }; -KisCurvePaintOpSettings::KisCurvePaintOpSettings() - : m_d(new Private) +KisCurvePaintOpSettings::KisCurvePaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisPaintOpSettings(resourcesInterface), + m_d(new Private) { } KisCurvePaintOpSettings::~KisCurvePaintOpSettings() { } void KisCurvePaintOpSettings::setPaintOpSize(qreal value) { KisCurveOptionProperties option; option.readOptionSetting(this); option.curve_line_width = value; option.writeOptionSetting(this); } qreal KisCurvePaintOpSettings::paintOpSize() const { KisCurveOptionProperties option; option.readOptionSetting(this); return option.curve_line_width; } bool KisCurvePaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisCurvePaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "curve_linewidth", i18n("Line Width"), settings, 0); prop->setRange(1, 100); prop->setSingleStep(1); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_line_width); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_line_width = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "curve_historysize", i18n("History Size"), settings, 0); prop->setRange(2, 300); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_stroke_history_size); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_stroke_history_size = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "curve_lineopacity", i18n("Line Opacity"), settings, 0); prop->setRange(0, 100.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_curves_opacity * 100.0); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_curves_opacity = prop->value().toReal() / 100.0; option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "curve_connectionline", i18n("Connection Line"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_paint_connection_line); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_paint_connection_line = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings.h b/plugins/paintops/curvebrush/kis_curve_paintop_settings.h index 1e7128fa2d..2609373a63 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop_settings.h +++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings.h @@ -1,43 +1,43 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008 Lukas Tvrdy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_CURVE_PAINTOP_SETTINGS_H_ #define KIS_CURVE_PAINTOP_SETTINGS_H_ #include #include class KisCurvePaintOpSettings : public KisPaintOpSettings { public: - KisCurvePaintOpSettings(); + KisCurvePaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisCurvePaintOpSettings() override; void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; bool paintIncremental() override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp index dd75f2ca82..f63fcb1fc6 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp +++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp @@ -1,55 +1,56 @@ /* * Copyright (c) 2008,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_curve_line_option.h" #include #include #include #include #include #include "kis_curves_opacity_option.h" +#include KisCurvePaintOpSettingsWidget:: KisCurvePaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { m_curveOption = new KisCurveOpOption(); addPaintOpOption(m_curveOption, i18nc("Brush settings curve value", "Value")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisLineWidthOption(), i18n("0%"), i18n("100%")), i18n("Line width")); addPaintOpOption(new KisCurveOptionWidget(new KisCurvesOpacityOption(), i18n("0%"), i18n("100%")), i18n("Curves opacity")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); } KisCurvePaintOpSettingsWidget::~ KisCurvePaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisCurvePaintOpSettingsWidget::configuration() const { - KisCurvePaintOpSettings* config = new KisCurvePaintOpSettings(); + KisCurvePaintOpSettings* config = new KisCurvePaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "curvebrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp index 5da1c8ea48..9a1b29a4e6 100644 --- a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp +++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp @@ -1,95 +1,95 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisBrushOpResources.h" #include #include #include "kis_color_source.h" #include "kis_pressure_mix_option.h" #include "kis_pressure_darken_option.h" #include "kis_pressure_hsv_option.h" #include "kis_color_source_option.h" #include "kis_pressure_sharpness_option.h" #include "kis_texture_option.h" #include "kis_painter.h" #include "kis_paintop_settings.h" struct KisBrushOpResources::Private { QList hsvOptions; KoColorTransformation *hsvTransformation = 0; KisPressureMixOption mixOption; KisPressureDarkenOption darkenOption; }; KisBrushOpResources::KisBrushOpResources(const KisPaintOpSettingsSP settings, KisPainter *painter) : m_d(new Private) { KisColorSourceOption colorSourceOption; colorSourceOption.readOptionSetting(settings); colorSource.reset(colorSourceOption.createColorSource(painter)); sharpnessOption.reset(new KisPressureSharpnessOption()); sharpnessOption->readOptionSetting(settings); sharpnessOption->resetAllSensors(); textureOption.reset(new KisTextureProperties(painter->device()->defaultBounds()->currentLevelOfDetail())); - textureOption->fillProperties(settings); + textureOption->fillProperties(settings, settings->resourcesInterface()); m_d->hsvOptions.append(KisPressureHSVOption::createHueOption()); m_d->hsvOptions.append(KisPressureHSVOption::createSaturationOption()); m_d->hsvOptions.append(KisPressureHSVOption::createValueOption()); Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->readOptionSetting(settings); option->resetAllSensors(); if (option->isChecked() && !m_d->hsvTransformation) { m_d->hsvTransformation = painter->backgroundColor().colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } } m_d->darkenOption.readOptionSetting(settings); m_d->mixOption.readOptionSetting(settings); m_d->darkenOption.resetAllSensors(); m_d->mixOption.resetAllSensors(); } KisBrushOpResources::~KisBrushOpResources() { qDeleteAll(m_d->hsvOptions); delete m_d->hsvTransformation; } void KisBrushOpResources::syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info) { colorSource->selectColor(m_d->mixOption.apply(info), info); m_d->darkenOption.apply(colorSource.data(), info); if (m_d->hsvTransformation) { Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->apply(m_d->hsvTransformation, info); } colorSource->applyColorTransformation(m_d->hsvTransformation); } KisDabCacheUtils::DabRenderingResources::syncResourcesToSeqNo(seqNo, info); } diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp index f7573c09a5..686a5c5cfd 100644 --- a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp +++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp @@ -1,25 +1,30 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisBrushOpSettings.h" +KisBrushOpSettings::KisBrushOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface) +{ +} + bool KisBrushOpSettings::needsAsynchronousUpdates() const { return true; } diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h index f4dcd7ed66..c9fd160b7b 100644 --- a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h +++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h @@ -1,31 +1,32 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISBRUSHOPSETTINGS_H #define KISBRUSHOPSETTINGS_H #include "kis_brush_based_paintop_settings.h" class KisBrushOpSettings : public KisBrushBasedPaintOpSettings { public: + KisBrushOpSettings(KisResourcesInterfaceSP resourcesInterface); bool needsAsynchronousUpdates() const; }; #endif // KISBRUSHOPSETTINGS_H diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp index b0d7b31147..3fdf0710bd 100644 --- a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp +++ b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp @@ -1,164 +1,165 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brushop_settings_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" #include #include #include +#include KisBrushOpSettingsWidget::KisBrushOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); // Brush tip options addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisFlowOpacityOptionWidget(), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureFlowOption(), i18n("0%"), i18n("100%")), i18n("Flow")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRatioOption(), i18n("0%"), i18n("100%")), i18n("Ratio")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSoftnessOption(), i18n("Soft"), i18n("Hard")), i18n("Softness")); addPaintOpOption(new KisPressureSharpnessOptionWidget(), i18n("Sharpness")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter")); // Colors options addPaintOpOption(new KisColorSourceOptionWidget(), i18n("Source")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureDarkenOption(), i18n("0.0"), i18n("1.0")), i18n("Darken")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureMixOption(), i18n("Foreground"), i18n("Background")), i18n("Mix")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value")); addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); KisPaintActionTypeOption *actionTypeOption = new KisPaintActionTypeOption(); addPaintOpOption(actionTypeOption, i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); KisMaskingBrushOption::MasterBrushSizeAdapter sizeAdapter = [this] () { return this->brush()->userEffectiveSize(); }; KisMaskingBrushOption *maskingOption = new KisMaskingBrushOption(sizeAdapter); addPaintOpOption(maskingOption, i18n("Brush Tip")); connect(maskingOption, SIGNAL(sigCheckedChanged(bool)), actionTypeOption, SLOT(slotForceWashMode(bool))); { KisCurveOption *maskingSizeOption = new KisPressureSizeOption(); maskingSizeOption->setChecked(false); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSizeOption, i18n("0%"), i18n("100%")), i18n("Size"), KisPaintOpOption::MASKING_BRUSH); } addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Opacity"), KisPaintOpOption::MASKING_BRUSH); KisCurveOption *maskingFlowOption = new KisPressureFlowOption(); maskingFlowOption->setChecked(false); KisCurveOption *maskingRatioOption = new KisPressureRatioOption(); maskingRatioOption->setChecked(false); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingFlowOption, i18n("0%"), i18n("100%")), i18n("Flow"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingRatioOption, i18n("0%"), i18n("100%")), i18n("Ratio"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Mirror"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Scatter"), KisPaintOpOption::MASKING_BRUSH); } KisBrushOpSettingsWidget::~KisBrushOpSettingsWidget() { } KisPropertiesConfigurationSP KisBrushOpSettingsWidget::configuration() const { - KisBrushBasedPaintOpSettingsSP config = new KisBrushOpSettings(); + KisBrushBasedPaintOpSettingsSP config = new KisBrushOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "paintbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp index d7df7773a6..1aec19c6ff 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp @@ -1,261 +1,263 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_duplicateop_settings.h" #include "kis_duplicateop_option.h" #include "kis_duplicateop_settings_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include -KisDuplicateOpSettings::KisDuplicateOpSettings() - : m_isOffsetNotUptodate(false), m_duringPaintingStroke(false) +KisDuplicateOpSettings::KisDuplicateOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface), + m_isOffsetNotUptodate(false), + m_duringPaintingStroke(false) { } KisDuplicateOpSettings::~KisDuplicateOpSettings() { } bool KisDuplicateOpSettings::paintIncremental() { return false; } QString KisDuplicateOpSettings::indirectPaintingCompositeOp() const { return COMPOSITE_COPY; } QPointF KisDuplicateOpSettings::offset() const { return m_offset; } QPointF KisDuplicateOpSettings::position() const { return m_position; } bool KisDuplicateOpSettings::mousePressEvent(const KisPaintInformation &info, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) { bool ignoreEvent = true; if (modifiers & Qt::ControlModifier) { if (!m_sourceNode || !(modifiers & Qt::AltModifier)) { m_sourceNode = currentNode; } m_position = info.pos(); m_isOffsetNotUptodate = true; ignoreEvent = false; } else { bool resetOrigin = getBool(DUPLICATE_RESET_SOURCE_POINT); if (m_isOffsetNotUptodate || resetOrigin) { m_offset = info.pos() - m_position; m_isOffsetNotUptodate = false; } m_duringPaintingStroke = true; ignoreEvent = true; } return ignoreEvent; } bool KisDuplicateOpSettings::mouseReleaseEvent() { m_duringPaintingStroke = false; bool ignoreEvent = true; return ignoreEvent; } KisNodeWSP KisDuplicateOpSettings::sourceNode() const { return m_sourceNode; } void KisDuplicateOpSettings::activate() { } void KisDuplicateOpSettings::fromXML(const QDomElement& elt) { // First, call the parent class fromXML to make sure all the // properties are saved to the map KisPaintOpSettings::fromXML(elt); m_offset.setX(KisDomUtils::toDouble(elt.attribute("OffsetX", "0.0"))); m_offset.setY(KisDomUtils::toDouble(elt.attribute("OffsetY", "0.0"))); m_isOffsetNotUptodate = false; } void KisDuplicateOpSettings::toXML(QDomDocument& doc, QDomElement& rootElt) const { // Then call the parent class fromXML KisPropertiesConfiguration::toXML(doc, rootElt); rootElt.setAttribute("OffsetX", QString::number(m_offset.x())); rootElt.setAttribute("OffsetY", QString::number(m_offset.y())); } KisPaintOpSettingsSP KisDuplicateOpSettings::clone() const { KisPaintOpSettingsSP setting = KisBrushBasedPaintOpSettings::clone(); KisDuplicateOpSettings* s = static_cast(setting.data()); s->m_offset = m_offset; s->m_isOffsetNotUptodate = m_isOffsetNotUptodate; s->m_position = m_position; s->m_sourceNode = m_sourceNode; s->m_duringPaintingStroke = m_duringPaintingStroke; return setting; } QPainterPath KisDuplicateOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; OutlineMode forcedMode = mode; if (!forcedMode.isVisible) { forcedMode.isVisible = true; forcedMode.forceCircle = true; } // clone tool should always show an outline path = KisBrushBasedPaintOpSettings::brushOutlineImpl(info, forcedMode, alignForZoom, 1.0); QPainterPath copy(path); QRectF rect2 = copy.boundingRect(); bool shouldStayInOrigin = m_isOffsetNotUptodate // the clone brush right now waits for first stroke with a new origin, so stays at origin point || !getBool(DUPLICATE_MOVE_SOURCE_POINT) // the brush always use the same source point, so stays at origin point || (!m_duringPaintingStroke && getBool(DUPLICATE_RESET_SOURCE_POINT)); // during the stroke, with reset Origin selected, outline should stay at origin point if (shouldStayInOrigin) { copy.translate(m_position - info.pos()); } else { copy.translate(-m_offset); } path.addPath(copy); qreal dx = rect2.width() / 4.0; qreal dy = rect2.height() / 4.0; rect2.adjust(dx, dy, -dx, -dy); path.moveTo(rect2.topLeft()); path.lineTo(rect2.bottomRight()); path.moveTo(rect2.topRight()); path.lineTo(rect2.bottomLeft()); return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisDuplicateOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_uniformProperties); if (props.isEmpty()) { { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "clone_healing", i18n("Healing"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.duplicate_healing); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); option.duplicate_healing = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "clone_movesource", i18n("Move `Source"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.duplicate_move_source_point); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); option.duplicate_move_source_point = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h index caab483d54..d255cf6d02 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h @@ -1,86 +1,86 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_DUPLICATEOP_SETTINGS_H_ #define KIS_DUPLICATEOP_SETTINGS_H_ #include #include #include class QDomElement; class KisDuplicateOpSettings : public KisBrushBasedPaintOpSettings { public: using KisPaintOpSettings::fromXML; using KisPaintOpSettings::toXML; - KisDuplicateOpSettings(); + KisDuplicateOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisDuplicateOpSettings() override; bool paintIncremental() override; QString indirectPaintingCompositeOp() const override; QPointF offset() const; QPointF position() const; /** * This function is called by a tool when the mouse is pressed. * Returns false if picking new origin is in action, * and returns true otherwise (i.e. if brush is starting a new stroke). * See kis_tool_freehand:tryPickByPaintOp() */ bool mousePressEvent(const KisPaintInformation& pos, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) override; /** * This function is called by a tool when the mouse is released. * If the tool is supposed to ignore the event, the paint op should return true * and if the tool is supposed to use the event, return false. */ bool mouseReleaseEvent() override; void activate() override; void fromXML(const QDomElement& elt) override; void toXML(QDomDocument& doc, QDomElement& rootElt) const override; KisPaintOpSettingsSP clone() const override; using KisBrushBasedPaintOpSettings::brushOutline; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; KisNodeWSP sourceNode() const; QList uniformProperties(KisPaintOpSettingsSP settings) override; public: Q_DISABLE_COPY(KisDuplicateOpSettings) QPointF m_offset; bool m_isOffsetNotUptodate; // true between the act of setting a new origin and the first stroke bool m_duringPaintingStroke; // true if the stroke is begin painted now, false otherwise QPointF m_position; // Give the position of the last alt-click KisNodeWSP m_sourceNode; // Give the node of the source point (origin) QList m_uniformProperties; }; typedef KisSharedPtr KisDuplicateOpSettingsSP; #endif // KIS_DUPLICATEOP_SETTINGS_H_ diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp index 84b7fb0ce4..cb28b69095 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp @@ -1,76 +1,76 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_duplicateop_settings_widget.h" #include "kis_duplicateop_settings.h" #include "kis_duplicateop_option.h" #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" #include - +#include KisDuplicateOpSettingsWidget::KisDuplicateOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisDuplicateOpOption(), i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); } KisDuplicateOpSettingsWidget::~KisDuplicateOpSettingsWidget() { } KisPropertiesConfigurationSP KisDuplicateOpSettingsWidget::configuration() const { - KisDuplicateOpSettings *config = new KisDuplicateOpSettings(); + KisDuplicateOpSettings *config = new KisDuplicateOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "duplicate"); // XXX: make this a const id string writeConfiguration(config); return config; } KisPaintopLodLimitations KisDuplicateOpSettingsWidget::lodLimitations() const { KisPaintopLodLimitations l = KisBrushBasedPaintopOptionWidget::lodLimitations(); l.blockers << KoID("clone-brush", i18nc("PaintOp instant preview limitation", "Clone Brush (temporarily disabled)")); return l; } diff --git a/plugins/paintops/deform/kis_deform_paintop_settings.cpp b/plugins/paintops/deform/kis_deform_paintop_settings.cpp index 69c6800f9d..c5ac437a97 100644 --- a/plugins/paintops/deform/kis_deform_paintop_settings.cpp +++ b/plugins/paintops/deform/kis_deform_paintop_settings.cpp @@ -1,222 +1,223 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 struct KisDeformPaintOpSettings::Private { QList uniformProperties; }; -KisDeformPaintOpSettings::KisDeformPaintOpSettings() +KisDeformPaintOpSettings::KisDeformPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | - KisCurrentOutlineFetcher::ROTATION_OPTION), + KisCurrentOutlineFetcher::ROTATION_OPTION, + resourcesInterface), m_d(new Private) { } KisDeformPaintOpSettings::~KisDeformPaintOpSettings() { } void KisDeformPaintOpSettings::setPaintOpSize(qreal value) { KisBrushSizeOptionProperties option; option.readOptionSetting(this); option.brush_diameter = value; option.writeOptionSetting(this); } qreal KisDeformPaintOpSettings::paintOpSize() const { KisBrushSizeOptionProperties option; option.readOptionSetting(this); return option.brush_diameter; } bool KisDeformPaintOpSettings::paintIncremental() { return true; } bool KisDeformPaintOpSettings::isAirbrushing() const { // version 2.3 if (hasProperty(AIRBRUSH_ENABLED)) { return getBool(AIRBRUSH_ENABLED); } else { return getBool(DEFORM_USE_MOVEMENT_PAINT); } } QPainterPath KisDeformPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { qreal width = getInt(BRUSH_DIAMETER); qreal height = getInt(BRUSH_DIAMETER) * getDouble(BRUSH_ASPECT); path = ellipseOutline(width, height, getDouble(BRUSH_SCALE), getDouble(BRUSH_ROTATION)); path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom); if (mode.showTiltDecoration) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisDeformPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "deform_amount", i18n("Amount"), settings, 0); prop->setRange(0.01, 1.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.deform_amount); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); option.deform_amount = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisComboBasedPaintOpPropertyCallback *prop = new KisComboBasedPaintOpPropertyCallback( "deform_mode", i18n("Deform Mode"), settings, 0); QList modes; modes << i18n("Grow"); modes << i18n("Shrink"); modes << i18n("Swirl CW"); modes << i18n("Swirl CCW"); modes << i18n("Move"); modes << i18n("Lens Zoom In"); modes << i18n("Lens Zoom Out"); modes << i18n("Color Deformation"); prop->setItems(modes); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.deform_action - 1)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); option.deform_action = prop->value().toInt() + 1; option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "deform_angle", i18n("Angle"), settings, 0); const QString degree = QChar(Qt::Key_degree); prop->setRange(0, 360); prop->setSingleStep(1); prop->setSuffix(degree); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushSizeOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.brush_rotation)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushSizeOptionProperties option; option.readOptionSetting(prop->settings().data()); option.brush_rotation = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id() || prop->id() == size.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/deform/kis_deform_paintop_settings.h b/plugins/paintops/deform/kis_deform_paintop_settings.h index 6ea39db433..7cf29bb222 100644 --- a/plugins/paintops/deform/kis_deform_paintop_settings.h +++ b/plugins/paintops/deform/kis_deform_paintop_settings.h @@ -1,48 +1,48 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008,2009 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_DEFORM_PAINTOP_SETTINGS_H_ #define KIS_DEFORM_PAINTOP_SETTINGS_H_ #include #include #include #include class KisDeformPaintOpSettings : public KisOutlineGenerationPolicy { public: - KisDeformPaintOpSettings(); + KisDeformPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisDeformPaintOpSettings() override; void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; bool paintIncremental() override; bool isAirbrushing() const override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp b/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp index bf7f8ea73d..680f1a55ff 100644 --- a/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp +++ b/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp @@ -1,63 +1,64 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_deform_paintop_settings.h" #include "kis_deform_paintop_settings_widget.h" #include "kis_deform_option.h" #include #include #include #include #include #include #include #include #include +#include KisDeformPaintOpSettingsWidget::KisDeformPaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { m_deformOption = new KisDeformOption(); m_brushSizeOption = new KisBrushSizeOption(); m_brushSizeOption->setDiameter(200); addPaintOpOption(m_brushSizeOption, i18n("Brush size")); addPaintOpOption(m_deformOption, i18n("Deform Options")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); } KisDeformPaintOpSettingsWidget::~ KisDeformPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisDeformPaintOpSettingsWidget::configuration() const { - KisDeformPaintOpSettings* config = new KisDeformPaintOpSettings(); + KisDeformPaintOpSettings* config = new KisDeformPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "deformBrush"); writeConfiguration(config); return config; } diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp b/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp index 27ee13f959..774944dc84 100644 --- a/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp +++ b/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp @@ -1,266 +1,267 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_experiment_paintop_settings.h" #include "kis_current_outline_fetcher.h" #include "kis_algebra_2d.h" struct KisExperimentPaintOpSettings::Private { QList uniformProperties; }; -KisExperimentPaintOpSettings::KisExperimentPaintOpSettings() - : m_d(new Private) +KisExperimentPaintOpSettings::KisExperimentPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisNoSizePaintOpSettings(resourcesInterface), + m_d(new Private) { } KisExperimentPaintOpSettings::~KisExperimentPaintOpSettings() { } bool KisExperimentPaintOpSettings::lodSizeThresholdSupported() const { return false; } bool KisExperimentPaintOpSettings::paintIncremental() { /** * The experiment brush supports working in the * WASH mode only! */ return false; } QPainterPath KisExperimentPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { QRectF ellipse(0, 0, 3, 3); ellipse.translate(-ellipse.center()); path.addEllipse(ellipse); ellipse.setRect(0,0, 12, 12); ellipse.translate(-ellipse.center()); path.addEllipse(ellipse); if (mode.showTiltDecoration) { path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 3.0)); } path.translate(KisAlgebra2D::alignForZoom(info.pos(), alignForZoom)); } return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_experimentop_option.h" #include "kis_standard_uniform_properties_factory.h" QList KisExperimentPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "shape_speed", i18n("Speed"), settings, 0); prop->setRange(0, 100); prop->setSingleStep(1); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.speed)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); option.speed = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) -> bool { ExperimentOption option; option.readOptionSetting(prop->settings().data()); return option.isSpeedEnabled; }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "shape_smooth", i18n("Smooth"), settings, 0); prop->setRange(0, 100); prop->setSingleStep(1); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.smoothing)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); option.smoothing = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); return option.isSmoothingEnabled; }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "shape_displace", i18n("Displace"), settings, 0); prop->setRange(0, 100); prop->setSingleStep(1); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.displacement)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); option.displacement = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); return option.isDisplacementEnabled; }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "shape_windingfill", i18n("Winding Fill"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.windingFill); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); option.windingFill = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "shape_hardedge", i18n("Hard Edge"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.hardEdge); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ExperimentOption option; option.readOptionSetting(prop->settings().data()); option.hardEdge = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings.h b/plugins/paintops/experiment/kis_experiment_paintop_settings.h index 3020c771b4..5e46d338fa 100644 --- a/plugins/paintops/experiment/kis_experiment_paintop_settings.h +++ b/plugins/paintops/experiment/kis_experiment_paintop_settings.h @@ -1,43 +1,43 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_EXPERIMENT_PAINTOP_SETTINGS_H_ #define KIS_EXPERIMENT_PAINTOP_SETTINGS_H_ #include #include class KisExperimentPaintOpSettings : public KisNoSizePaintOpSettings { public: - KisExperimentPaintOpSettings(); + KisExperimentPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisExperimentPaintOpSettings() override; bool lodSizeThresholdSupported() const override; bool paintIncremental() override; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp b/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp index c7d123c0b4..b797a845fb 100644 --- a/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp +++ b/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp @@ -1,52 +1,53 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_experiment_paintop_settings_widget.h" #include "kis_experimentop_option.h" #include "kis_experiment_paintop_settings.h" #include #include #include #include #include #include #include +#include KisExperimentPaintOpSettingsWidget:: KisExperimentPaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { addPaintOpOption(new KisExperimentOpOption(), i18n("Experiment option")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); //addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); } KisExperimentPaintOpSettingsWidget::~ KisExperimentPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisExperimentPaintOpSettingsWidget::configuration() const { - KisExperimentPaintOpSettings* config = new KisExperimentPaintOpSettings(); + KisExperimentPaintOpSettings* config = new KisExperimentPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "experimentbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/filterop/kis_filterop_settings.cpp b/plugins/paintops/filterop/kis_filterop_settings.cpp index 13aeef995e..1250464c4d 100644 --- a/plugins/paintops/filterop/kis_filterop_settings.cpp +++ b/plugins/paintops/filterop/kis_filterop_settings.cpp @@ -1,88 +1,89 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_filterop_settings.h" #include #include #include #include #include #include #include #include #include -KisFilterOpSettings::KisFilterOpSettings() +KisFilterOpSettings::KisFilterOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface) { setPropertyNotSaved(FILTER_CONFIGURATION); } KisFilterOpSettings::~KisFilterOpSettings() { } bool KisFilterOpSettings::paintIncremental() { return true; // We always paint on the existing data } KisFilterConfigurationSP KisFilterOpSettings::filterConfig() const { if (hasProperty(FILTER_ID)) { KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID)); if (filter) { KisFilterConfigurationSP configuration = filter->factoryConfiguration(); configuration->fromXML(getString(FILTER_CONFIGURATION)); return configuration; } } return 0; } void KisFilterOpSettings::toXML(QDomDocument& doc, QDomElement& root) const { KisPaintOpSettings::toXML(doc, root); KisFilterConfigurationSP configuration = filterConfig(); if (configuration) { QDomElement e = doc.createElement("filterconfig"); configuration->toXML(doc, e); root.appendChild(e); } } void KisFilterOpSettings::fromXML(const QDomElement& e) { KisPaintOpSettings::fromXML(e); QDomElement element = e.firstChildElement("filterconfig"); if (hasProperty(FILTER_ID)) { KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID)); if (filter) { KisFilterConfigurationSP configuration = filter->factoryConfiguration(); configuration->fromXML(element); setProperty(FILTER_CONFIGURATION, configuration->toXML()); } } } diff --git a/plugins/paintops/filterop/kis_filterop_settings.h b/plugins/paintops/filterop/kis_filterop_settings.h index a50b005773..b0ebc88374 100644 --- a/plugins/paintops/filterop/kis_filterop_settings.h +++ b/plugins/paintops/filterop/kis_filterop_settings.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_FILTEROP_SETTINGS_H_ #define KIS_FILTEROP_SETTINGS_H_ #include #include #include "kis_filterop_settings_widget.h" class QDomElement; class KisFilterConfiguration; class KisFilterOpSettings : public KisBrushBasedPaintOpSettings { public: - KisFilterOpSettings(); + KisFilterOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisFilterOpSettings() override; bool paintIncremental() override; KisFilterConfigurationSP filterConfig() const; using KisPaintOpSettings::toXML; void toXML(QDomDocument& doc, QDomElement& root) const override; using KisPaintOpSettings::fromXML; void fromXML(const QDomElement& e) override; }; #endif // KIS_FILTEROP_SETTINGS_H_ diff --git a/plugins/paintops/filterop/kis_filterop_settings_widget.cpp b/plugins/paintops/filterop/kis_filterop_settings_widget.cpp index 85f41da250..bf28d92ff3 100644 --- a/plugins/paintops/filterop/kis_filterop_settings_widget.cpp +++ b/plugins/paintops/filterop/kis_filterop_settings_widget.cpp @@ -1,70 +1,71 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_filterop_settings_widget.h" #include "kis_filterop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" +#include KisFilterOpSettingsWidget::KisFilterOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setObjectName("filter option widget"); setPrecisionEnabled(true); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); m_filterOption = new KisFilterOption(); addPaintOpOption(m_filterOption, i18nc("option name", "Filter")); } KisFilterOpSettingsWidget::~KisFilterOpSettingsWidget() { } KisPropertiesConfigurationSP KisFilterOpSettingsWidget::configuration() const { - KisFilterOpSettings *config = new KisFilterOpSettings(); + KisFilterOpSettings *config = new KisFilterOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "filter"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp index 5ba7220b99..d651531d69 100644 --- a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp +++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp @@ -1,129 +1,130 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_grid_paintop_settings.h" #include "kis_grid_paintop_settings_widget.h" #include "kis_gridop_option.h" #include "kis_grid_shape_option.h" #include struct KisGridPaintOpSettings::Private { QList uniformProperties; }; -KisGridPaintOpSettings::KisGridPaintOpSettings() - : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::NO_OPTION), +KisGridPaintOpSettings::KisGridPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::NO_OPTION, + resourcesInterface), m_d(new Private) { } void KisGridPaintOpSettings::setPaintOpSize(qreal value) { KisGridOpProperties option; option.readOptionSetting(this); option.grid_width = value; option.grid_height = value; option.writeOptionSetting(this); } qreal KisGridPaintOpSettings::paintOpSize() const { KisGridOpProperties option; option.readOptionSetting(this); return option.grid_width; } KisGridPaintOpSettings::~KisGridPaintOpSettings() { } bool KisGridPaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } QPainterPath KisGridPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { qreal sizex = getInt(GRID_WIDTH) * getDouble(GRID_SCALE); qreal sizey = getInt(GRID_HEIGHT) * getDouble(GRID_SCALE); QRectF rc(0, 0, sizex, sizey); rc.translate(-rc.center()); path.addRect(rc); path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom); if (mode.showTiltDecoration) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), sizex * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" QList KisGridPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "grid_divisionlevel", i18n("Division Level"), settings, 0); prop->setRange(1, 25); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisGridOpProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.grid_division_level)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisGridOpProperties option; option.readOptionSetting(prop->settings().data()); option.grid_division_level = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings.h b/plugins/paintops/gridbrush/kis_grid_paintop_settings.h index 131a735b2e..9c42201b29 100644 --- a/plugins/paintops/gridbrush/kis_grid_paintop_settings.h +++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings.h @@ -1,55 +1,55 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_GRID_PAINTOP_SETTINGS_H_ #define KIS_GRID_PAINTOP_SETTINGS_H_ #include #include #include #include #include "kis_grid_paintop_settings_widget.h" class KisGridPaintOpSettings : public KisOutlineGenerationPolicy { public: - KisGridPaintOpSettings(); + KisGridPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisGridPaintOpSettings() override; void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; bool paintIncremental() override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; typedef KisSharedPtr KisGridPaintOpSettingsSP; #endif diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp b/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp index 4b756a1927..e7f0ed3fa4 100644 --- a/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp +++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp @@ -1,56 +1,57 @@ /* * Copyright (c) 2009 Lukáš Tvrdý (lukast.dev@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_grid_paintop_settings_widget.h" #include "kis_gridop_option.h" #include "kis_grid_paintop_settings.h" #include "kis_grid_shape_option.h" #include #include #include #include #include +#include KisGridPaintOpSettingsWidget:: KisGridPaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { m_gridOption = new KisGridOpOption(); m_gridShapeOption = new KisGridShapeOption(); m_ColorOption = new KisColorOption(); addPaintOpOption(m_gridOption, i18n("Brush size")); addPaintOpOption(m_gridShapeOption, i18n("Particle type")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(m_ColorOption, i18n("Color options")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); } KisGridPaintOpSettingsWidget::~ KisGridPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisGridPaintOpSettingsWidget::configuration() const { - KisGridPaintOpSettings* config = new KisGridPaintOpSettings(); + KisGridPaintOpSettings* config = new KisGridPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "gridbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp index bd53194983..46023465cc 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp @@ -1,170 +1,175 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_hairy_paintop.h" #include "kis_hairy_paintop_settings.h" #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include #include #include #include #include #include #include #include #include "kis_brush.h" KisHairyPaintOp::KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { - Q_UNUSED(image) + Q_UNUSED(image); Q_ASSERT(settings); m_dev = node ? node->paintDevice() : 0; KisBrushOptionProperties brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, settings->resourcesInterface()); KisBrushSP brush = brushOption.brush(); KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); // properly initialize fake paint information to avoid warnings KisPaintInformation fakePaintInformation; fakePaintInformation.setRandomSource(new KisRandomSource()); fakePaintInformation.setPerStrokeRandomSource(new KisPerStrokeRandomSource()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->paintDevice(source()->colorSpace(), KisDabShape(), fakePaintInformation); } else { brush->mask(dab, painter->paintColor(), KisDabShape(), fakePaintInformation); } m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01); m_brush.setInkColor(painter->paintColor()); loadSettings(static_cast(settings.data())); m_brush.setProperties(&m_properties); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } +QList KisHairyPaintOp::prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) +{ + KisBrushOptionProperties brushOption; + return brushOption.prepareResources(settings, resourcesInterface); +} void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings *settings) { m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT); //TODO: wait for the transfer function with variable size m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount); m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED); m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION); m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY); m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS); m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0; m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0; m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0; m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT); m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK); m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE); m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR); m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM); m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE); m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD); m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING); m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING); m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED); } KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) { return updateSpacingImpl(info); } KisSpacingInformation KisHairyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { Q_UNUSED(info); return KisSpacingInformation(0.5); } void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } /** * Even though we don't use spacing in hairy brush, we should still * initialize its distance information to ensure drawing angle and * other history-based sensors work fine. */ KisPaintInformation pi(pi2); KisPaintInformation::DistanceInformationRegistrar r = pi.registerDistanceInformation(currentDistance); // Hairy Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(pi); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(pi); quint8 origOpacity = m_opacityOption.apply(painter(), pi); const bool mirrorFlip = pi1.canvasMirroredH() != pi1.canvasMirroredV(); // we don't use spacing here (the brush itself is used only once // during initialization), so we should just skip the distance info // update m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_properties.scaleFactor, mirrorFlip ? -rotation : rotation); //QRect rc = m_dab->exactBounds(); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); // we don't use spacing in hairy brush, but history is // still important for us currentDistance->registerPaintedDab(pi, KisSpacingInformation(), KisTimingInformation()); } diff --git a/plugins/paintops/hairy/kis_hairy_paintop.h b/plugins/paintops/hairy/kis_hairy_paintop.h index e9de44d51d..9d4d47f408 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.h +++ b/plugins/paintops/hairy/kis_hairy_paintop.h @@ -1,63 +1,65 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_HAIRYPAINTOP_H_ #define KIS_HAIRYPAINTOP_H_ #include #include #include #include #include "hairy_brush.h" #include #include #include class KisPainter; class KisBrushBasedPaintOpSettings; +class KisResourcesInterface; class KisHairyPaintOp : public KisPaintOp { public: KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image); void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; + static QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface); protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; private: KisHairyProperties m_properties; KisPaintDeviceSP m_dab; KisPaintDeviceSP m_dev; HairyBrush m_brush; KisPressureRotationOption m_rotationOption; KisPressureSizeOption m_sizeOption; KisPressureOpacityOption m_opacityOption; void loadSettings(const KisBrushBasedPaintOpSettings* settings); }; #endif // KIS_HAIRYPAINTOP_H_ diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp b/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp index 2484b1f995..34e89a2348 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp @@ -1,36 +1,37 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_image.h" #include "kis_hairy_paintop_settings.h" #include "kis_hairy_bristle_option.h" #include "kis_brush_based_paintop_options_widget.h" #include "kis_boundary.h" -KisHairyPaintOpSettings::KisHairyPaintOpSettings() +KisHairyPaintOpSettings::KisHairyPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface) { } QPainterPath KisHairyPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { return brushOutlineImpl(info, mode, alignForZoom, getDouble(HAIRY_BRISTLE_SCALE)); } diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings.h b/plugins/paintops/hairy/kis_hairy_paintop_settings.h index 132ff2a067..345a313113 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop_settings.h +++ b/plugins/paintops/hairy/kis_hairy_paintop_settings.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_HAIRYPAINTOP_SETTINGS_H_ #define KIS_HAIRYPAINTOP_SETTINGS_H_ #include #include #include class KisHairyPaintOpSettings : public KisBrushBasedPaintOpSettings { public: using KisPaintOpSettings::fromXML; - KisHairyPaintOpSettings(); + KisHairyPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); using KisBrushBasedPaintOpSettings::brushOutline; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; }; #endif diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp b/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp index 3ded30acfe..dce14d39d6 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp @@ -1,73 +1,74 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_hairy_paintop_settings_widget.h" #include "kis_hairy_paintop_settings.h" #include "kis_hairy_ink_option.h" #include #include "kis_hairy_bristle_option.h" #include #include #include #include #include #include +#include KisHairyPaintOpSettingsWidget:: KisHairyPaintOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { addPaintOpOption(new KisHairyBristleOption(), i18n("Bristle options")); addPaintOpOption(new KisHairyInkOption(), i18n("Ink depletion")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); KisBrushOptionWidget *brushWidget = brushOptionWidget(); QStringList hiddenOptions; hiddenOptions << "KisBrushChooser/lblSpacing" << "KisBrushChooser/Spacing" << "KisBrushChooser/ColorAsMask" << "KisAutoBrushWidget/btnAntiAliasing" << "KisAutoBrushWidget/grpFade" << "KisAutoBrushWidget/lblDensity" << "KisAutoBrushWidget/density" << "KisAutoBrushWidget/lblSpacing" << "KisAutoBrushWidget/spacingWidget" << "KisAutoBrushWidget/lblRandomness" << "KisAutoBrushWidget/inputRandomness" ; brushWidget->hideOptions(hiddenOptions); } KisHairyPaintOpSettingsWidget::~ KisHairyPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisHairyPaintOpSettingsWidget::configuration() const { - KisHairyPaintOpSettings* config = new KisHairyPaintOpSettings(); + KisHairyPaintOpSettings* config = new KisHairyPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "hairybrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/hatching/kis_hatching_paintop.cpp b/plugins/paintops/hatching/kis_hatching_paintop.cpp index 5d70a05a08..9a16040fb2 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop.cpp @@ -1,213 +1,213 @@ /* * Copyright (c) 2008,2009 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_hatching_paintop.h" #include "kis_hatching_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisHatchingPaintOp::KisHatchingPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP /*image*/) : KisBrushBasedPaintOp(settings, painter) { Q_UNUSED(node); - m_settings = new KisHatchingPaintOpSettings(); + m_settings = static_cast(settings->clone().data()); static_cast(settings.data())->initializeTwin(m_settings); m_hatchingBrush = new HatchingBrush(m_settings); m_angleOption.readOptionSetting(settings); m_crosshatchingOption.readOptionSetting(settings); m_separationOption.readOptionSetting(settings); m_thicknessOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_angleOption.resetAllSensors(); m_crosshatchingOption.resetAllSensors(); m_separationOption.resetAllSensors(); m_thicknessOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } KisHatchingPaintOp::~KisHatchingPaintOp() { delete m_hatchingBrush; } KisSpacingInformation KisHatchingPaintOp::paintAt(const KisPaintInformation& info) { //------START SIMPLE ERROR CATCHING------- if (!painter()->device()) return KisSpacingInformation(1.0); if (!m_hatchedDab) m_hatchedDab = source()->createCompositionSourceDevice(); else m_hatchedDab->clear(); //Simple convenience renaming, I'm thinking of removing these inherited quirks KisBrushSP brush = m_brush; KisPaintDeviceSP device = painter()->device(); //Macro to catch errors Q_ASSERT(brush); //----------SIMPLE error catching code, maybe it's not even needed------ if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); //SENSOR-depending settings m_settings->anglesensorvalue = m_angleOption.apply(info); m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info); m_settings->separationsensorvalue = m_separationOption.apply(info); m_settings->thicknesssensorvalue = m_thicknessOption.apply(info); const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); const double scale = additionalScale * m_sizeOption.apply(info); if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return KisSpacingInformation(1.0); KisDabShape shape(scale, 1.0, 0.0); quint8 origOpacity = m_opacityOption.apply(painter(), info); /*----Fetch the Dab----*/ static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP maskDab = m_dabCache->fetchDab(cs, color, info.pos(), shape, info, 1.0, &dstRect); // sanity check KIS_ASSERT_RECOVER_NOOP(dstRect.size() == maskDab->bounds().size()); /*-----Convenient renaming for the limits of the maskDab, this will be used to hatch a dab of just the right size------*/ qint32 x, y, sw, sh; dstRect.getRect(&x, &y, &sw, &sh); //------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor if (m_settings->opaquebackground) { KoColor aersh = painter()->backgroundColor(); m_hatchedDab->fill(0, 0, (sw - 1), (sh - 1), aersh.data()); //this plus yellow background = french fry brush } /* If block describing how to stack hatching passes to generate crosshatching according to user specifications */ if (m_settings->enabledcurvecrosshatching) { if (m_settings->perpendicular) { if (m_settings->crosshatchingsensorvalue > 0.5) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 360), painter()->paintColor(), additionalScale); } } else { if (m_settings->perpendicular) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor(), additionalScale); } } if (m_settings->enabledcurveangle) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->anglesensorvalue)*360+m_settings->angle), painter()->paintColor(), additionalScale); // The base hatch... unless moiré or angle if (!m_settings->moirepattern && !m_settings->enabledcurveangle) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_settings->angle, painter()->paintColor(), additionalScale); // The most important line, the one that paints to the screen. painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh); painter()->renderMirrorMaskSafe(QRect(QPoint(x, y), QSize(sw, sh)), m_hatchedDab, 0, 0, maskDab, !m_dabCache->needSeparateOriginal()); painter()->setOpacity(origOpacity); return effectiveSpacing(scale); } KisSpacingInformation KisHatchingPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { const qreal scale = KisLodTransform::lodToScale(painter()->device()) * m_sizeOption.apply(info); return effectiveSpacing(scale); } double KisHatchingPaintOp::spinAngle(double spin) { double tempangle = m_settings->angle + spin; qint8 factor = 1; if (tempangle < 0) factor = -1; tempangle = fabs(fmod(tempangle, 180)); if ((tempangle >= 0) && (tempangle <= 90)) return factor * tempangle; else if ((tempangle > 90) && (tempangle <= 180)) return factor * -(180 - tempangle); return 0; // this should never be executed except if NAN } diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp index fabf330bb2..e3446ca6fb 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp @@ -1,220 +1,221 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_hatching_paintop_settings.h" #include #include #include const QString HATCHING_VERSION = "Hatching/Version"; struct KisHatchingPaintOpSettings::Private { QList uniformProperties; }; -KisHatchingPaintOpSettings::KisHatchingPaintOpSettings() - : m_d(new Private) +KisHatchingPaintOpSettings::KisHatchingPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface), + m_d(new Private) { setProperty(HATCHING_VERSION, "2"); } KisHatchingPaintOpSettings::~KisHatchingPaintOpSettings() { } void KisHatchingPaintOpSettings::initializeTwin(KisPaintOpSettingsSP settings) const { // XXX: this is a nice way to reinvent the copy constructor? /*--------DO NOT REMOVE please, use this to review the XML config tree QMap rofl = QMap(getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); /----------DO NOT REMOVE----------------*/ KisHatchingPaintOpSettings *convenienttwin = static_cast(settings.data()); convenienttwin->enabledcurveangle = getBool("PressureAngle"); convenienttwin->enabledcurvecrosshatching = getBool("PressureCrosshatching"); convenienttwin->enabledcurveopacity = getBool("PressureOpacity"); convenienttwin->enabledcurveseparation = getBool("PressureSeparation"); convenienttwin->enabledcurvesize = getBool("PressureSize"); convenienttwin->enabledcurvethickness = getBool("PressureThickness"); convenienttwin->angle = getDouble("Hatching/angle"); convenienttwin->separation = getDouble("Hatching/separation"); convenienttwin->thickness = getDouble("Hatching/thickness"); convenienttwin->origin_x = getDouble("Hatching/origin_x"); convenienttwin->origin_y = getDouble("Hatching/origin_y"); convenienttwin->nocrosshatching = getBool("Hatching/bool_nocrosshatching"); convenienttwin->perpendicular = getBool("Hatching/bool_perpendicular"); convenienttwin->minusthenplus = getBool("Hatching/bool_minusthenplus"); convenienttwin->plusthenminus = getBool("Hatching/bool_plusthenminus"); convenienttwin->moirepattern = getBool("Hatching/bool_moirepattern"); convenienttwin->separationintervals = getInt("Hatching/separationintervals"); //convenienttwin->trigonometryalgebra = getBool("Hatching/bool_trigonometryalgebra"); //convenienttwin->scratchoff = getBool("Hatching/bool_scratchoff"); convenienttwin->antialias = getBool("Hatching/bool_antialias"); convenienttwin->opaquebackground = getBool("Hatching/bool_opaquebackground"); convenienttwin->subpixelprecision = getBool("Hatching/bool_subpixelprecision"); if (getBool("Hatching/bool_nocrosshatching")) convenienttwin->crosshatchingstyle = 0; else if (getBool("Hatching/bool_perpendicular")) convenienttwin->crosshatchingstyle = 1; else if (getBool("Hatching/bool_minusthenplus")) convenienttwin->crosshatchingstyle = 2; else if (getBool("Hatching/bool_plusthenminus")) convenienttwin->crosshatchingstyle = 3; if (getBool("Hatching/bool_moirepattern")) convenienttwin->crosshatchingstyle = 4; } void KisHatchingPaintOpSettings::fromXML(const QDomElement& elt) { setProperty(HATCHING_VERSION, "1"); // This make sure that fromXML will override HAIRY_VERSION with 2, or will default to 1 KisBrushBasedPaintOpSettings::fromXML(elt); QVariant v; if (!getProperty(HATCHING_VERSION, v) || v == "1") { setProperty("Hatching/thickness", 2.0 * getDouble("Hatching/thickness")); } setProperty(HATCHING_VERSION, "2"); // make sure it's saved as version 2 next time } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_hatching_options.h" QList KisHatchingPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_angle", i18n("Hatching Angle"), settings, 0); const QString degree = QChar(Qt::Key_degree); prop->setRange(-90, 90); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(degree); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.angle); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.angle = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_separation", i18n("Separation"), settings, 0); prop->setRange(1.0, 30); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.separation); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.separation = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_thickness", i18n("Thickness"), settings, 0); prop->setRange(1.0, 30); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.thickness); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.thickness = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.h b/plugins/paintops/hatching/kis_hatching_paintop_settings.h index 089b3ed3bc..2cc3b0fb63 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings.h +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.h @@ -1,89 +1,89 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_HATCHING_PAINTOP_SETTINGS_H_ #define KIS_HATCHING_PAINTOP_SETTINGS_H_ #include #include #include "kis_hatching_paintop_settings_widget.h" #include class KisHatchingPaintOpSettings : public KisBrushBasedPaintOpSettings { public: - KisHatchingPaintOpSettings(); + KisHatchingPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisHatchingPaintOpSettings() override; //Dialogs enabled bool enabledcurveangle; bool enabledcurvecrosshatching; bool enabledcurveopacity; bool enabledcurveseparation; bool enabledcurvesize; bool enabledcurvethickness; //Hatching Options double angle; double separation; double thickness; double origin_x; double origin_y; bool nocrosshatching; bool perpendicular; bool minusthenplus; bool plusthenminus; bool moirepattern; int crosshatchingstyle; int separationintervals; //Hatching Preferences //bool trigonometryalgebra; //bool scratchoff; bool antialias; bool subpixelprecision; bool opaquebackground; //Angle, Crosshatching, Separation and Thickness curves double anglesensorvalue; double crosshatchingsensorvalue; double separationsensorvalue; double thicknesssensorvalue; void initializeTwin(KisPaintOpSettingsSP convenienttwin) const; using KisPropertiesConfiguration::fromXML; void fromXML(const QDomElement&) override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: Q_DISABLE_COPY(KisHatchingPaintOpSettings) struct Private; const QScopedPointer m_d; }; typedef KisSharedPtr KisHatchingPaintOpSettingsSP; #endif diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp index a1b67a70af..3b61c29b58 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp @@ -1,138 +1,139 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_hatching_paintop_settings_widget.h" #include "kis_hatching_options.h" #include "kis_hatching_preferences.h" #include "kis_hatching_paintop_settings.h" #include "kis_hatching_pressure_angle_option.h" #include "kis_hatching_pressure_crosshatching_option.h" #include "kis_hatching_pressure_separation_option.h" #include "kis_hatching_pressure_thickness_option.h" #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" +#include #include #include KisHatchingPaintOpSettingsWidget:: KisHatchingPaintOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setPrecisionEnabled(true); //-------Adding widgets to the screen------------ addPaintOpOption(new KisHatchingOptions(), i18n("Hatching options")); addPaintOpOption(new KisHatchingPreferences(), i18n("Hatching preferences")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureSeparationOption(), i18n("0.0"), i18n("1.0")), i18n("Separation")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureThicknessOption(), i18n("0.0"), i18n("1.0")), i18n("Thickness")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureAngleOption(), i18n("0.0"), i18n("1.0")), i18n("Angle")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureCrosshatchingOption(), i18n("0.0"), i18n("1.0")), i18n("Crosshatching")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); //-----Useful to read first:------ /* Below you will encounter a reasonably correct solution to the problem of changing the default presets of the "BrushTip" popup configuration dialogue. In my (Pentalis) opinion, the best solution is code refactoring (simpler ways to change the defaults). On the meanwhile, copypasting this code won't give your class a charisma penalty. In kis_hatching_paintop_settings.cpp you will find a snippet of code to discover the structure of your XML config tree if you need to edit it at build time like here. */ //---------START ALTERING DEFAULT VALUES----------- //As the name implies, reconfigurationCourier is the KisPropertiesConfigurationSP //we'll use as an intermediary to edit the default settings KisPropertiesConfigurationSP reconfigurationCourier = configuration(); /*xMLAnalyzer is an empty document we'll use to analyze and edit the config string part by part I know the important string is "brush_definition" because I read the tree with the snippet in kis_hatching_paintop_settings.cpp */ QDomDocument xMLAnalyzer; xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition")); /*More things I know by reading the XML tree. At this point you can just read it with: dbgKrita << xMLAnalyzer.toString() ; those QDomElements are the way to navigate the XML tree, read https://doc.qt.io/qt-5/qdomdocument.html for more information */ QDomElement firstTag = xMLAnalyzer.documentElement(); QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement(); // SET THE DEFAULT VALUES firstTag.attributeNode("spacing").setValue("0.4"); firstTagsChild.attributeNode("diameter").setValue("30"); //Write them into the intermediary config file reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString()); KisCubicCurve CurveSize; CurveSize.fromString("0,1;1,0.1;"); //dbgKrita << "\n\n\n" << CurveSize.toString() << "\n\n\n"; QVariant QVCurveSize = QVariant::fromValue(CurveSize); reconfigurationCourier->setProperty("CurveSize", QVCurveSize); setConfiguration(reconfigurationCourier); // Finished. /* Debugging block QMap rofl = QMap(reconfigurationCourier->getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); */ } KisHatchingPaintOpSettingsWidget::~ KisHatchingPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisHatchingPaintOpSettingsWidget::configuration() const { - KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings(); + KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "hatchingbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp index c180d5962c..f1bfb917b7 100644 --- a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp +++ b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp @@ -1,129 +1,130 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisMaskingBrushOption.h" #include "kis_brush_chooser.h" #include "kis_brush_selection_widget.h" #include #include #include #include #include #include "kis_brush.h" #include "kis_image.h" #include "kis_brush_option.h" #include "KisMaskingBrushOptionProperties.h" #include #include +#include struct KisMaskingBrushOption::Private { Private() : ui(new QWidget()) { QVBoxLayout *l = new QVBoxLayout(); QHBoxLayout *compositeOpLayout = new QHBoxLayout(); compositeSelector = new QComboBox(ui.data()); const QStringList supportedComposites = KisMaskingBrushCompositeOpFactory::supportedCompositeOpIds(); Q_FOREACH (const QString &id, supportedComposites) { const QString name = KoCompositeOpRegistry::instance().getKoID(id).name(); compositeSelector->addItem(name, id); } compositeSelector->setCurrentIndex(0); compositeOpLayout->addWidget(new QLabel(i18n("Blending Mode:")), 0); compositeOpLayout->addWidget(compositeSelector, 1); l->addLayout(compositeOpLayout, 0); brushChooser = new KisBrushSelectionWidget(ui.data()); l->addWidget(brushChooser, 1); ui->setLayout(l); } QScopedPointer ui; KisBrushSelectionWidget *brushChooser = 0; QComboBox *compositeSelector = 0; MasterBrushSizeAdapter masterBrushSizeAdapter; }; KisMaskingBrushOption::KisMaskingBrushOption(MasterBrushSizeAdapter masterBrushSizeAdapter) : KisPaintOpOption(KisPaintOpOption::MASKING_BRUSH, false), m_d(new Private()) { m_d->masterBrushSizeAdapter = masterBrushSizeAdapter; setObjectName("KisMaskingBrushOption"); setConfigurationPage(m_d->ui.data()); connect(m_d->brushChooser, SIGNAL(sigBrushChanged()), SLOT(emitSettingChanged())); connect(m_d->compositeSelector, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); } KisMaskingBrushOption::~KisMaskingBrushOption() { } void KisMaskingBrushOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { KisMaskingBrushOptionProperties props; props.isEnabled = isChecked(); props.brush = m_d->brushChooser->brush(); props.compositeOpId = m_d->compositeSelector->currentData().toString(); props.write(setting.data(), m_d->masterBrushSizeAdapter()); } void KisMaskingBrushOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { KisMaskingBrushOptionProperties props; - props.read(setting.data(), m_d->masterBrushSizeAdapter()); + props.read(setting.data(), m_d->masterBrushSizeAdapter(), KisGlobalResourcesInterface::instance()); setChecked(props.isEnabled); const int selectedIndex = qMax(0, m_d->compositeSelector->findData(props.compositeOpId)); m_d->compositeSelector->setCurrentIndex(selectedIndex); if (props.brush) { m_d->brushChooser->setCurrentBrush(props.brush); } } void KisMaskingBrushOption::setImage(KisImageWSP image) { m_d->brushChooser->setImage(image); } void KisMaskingBrushOption::lodLimitations(KisPaintopLodLimitations *l) const { KisBrushSP brush = m_d->brushChooser->brush(); if (brush) { brush->lodLimitations(l); } } diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp index 649be6140b..9ed26773a4 100644 --- a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp +++ b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp @@ -1,87 +1,93 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisMaskingBrushOptionProperties.h" #include "kis_brush_option.h" #include #include KisMaskingBrushOptionProperties::KisMaskingBrushOptionProperties() : compositeOpId(COMPOSITE_MULT) { } void KisMaskingBrushOptionProperties::write(KisPropertiesConfiguration *setting, qreal masterBrushSize) const { setting->setProperty(KisPaintOpUtils::MaskingBrushEnabledTag, isEnabled); setting->setProperty(KisPaintOpUtils::MaskingBrushCompositeOpTag, compositeOpId); setting->setProperty(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, useMasterSize); const qreal masterSizeCoeff = brush && masterBrushSize > 0 ? brush->userEffectiveSize() / masterBrushSize : 1.0; setting->setProperty(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, masterSizeCoeff); // TODO: skip saving in some cases? // if (!isEnabled) return; if (brush) { KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration(); { KisBrushOptionProperties option; option.setBrush(brush); option.writeOptionSetting(embeddedConfig); } setting->setPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig); const QString brushFileName = brush->filename(); if (!brushFileName.isEmpty()) { QStringList requiredFiles = setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << brushFileName; setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles); } } } -void KisMaskingBrushOptionProperties::read(const KisPropertiesConfiguration *setting, qreal masterBrushSize) +QList KisMaskingBrushOptionProperties::prepareResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) +{ + KisBrushOptionProperties option; + return option.prepareResources(settings, resourcesInterface); +} + +void KisMaskingBrushOptionProperties::read(const KisPropertiesConfiguration *setting, qreal masterBrushSize, KisResourcesInterfaceSP resourcesInterface) { isEnabled = setting->getBool(KisPaintOpUtils::MaskingBrushEnabledTag); compositeOpId = setting->getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT); useMasterSize = setting->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true); KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration(); setting->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig); KisBrushOptionProperties option; - option.readOptionSetting(embeddedConfig); + option.readOptionSetting(embeddedConfig, resourcesInterface); brush = option.brush(); if (brush && useMasterSize) { const qreal masterSizeCoeff = setting->getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0); brush->setUserEffectiveSize(masterSizeCoeff * masterBrushSize); } } diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h index 8258761540..e9a4c8aadd 100644 --- a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h +++ b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h @@ -1,39 +1,44 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISMASKINGBRUSHOPTIONPROPERTIES_H #define KISMASKINGBRUSHOPTIONPROPERTIES_H #include "kritapaintop_export.h" #include #include +class KisResourcesInterface; +using KisResourcesInterfaceSP = QSharedPointer; + + struct PAINTOP_EXPORT KisMaskingBrushOptionProperties { KisMaskingBrushOptionProperties(); bool isEnabled = false; KisBrushSP brush; QString compositeOpId; bool useMasterSize = true; void write(KisPropertiesConfiguration *setting, qreal masterBrushSize) const; - void read(const KisPropertiesConfiguration *setting, qreal masterBrushSize); + void read(const KisPropertiesConfiguration *setting, qreal masterBrushSize, KisResourcesInterfaceSP resourcesInterface); + QList prepareResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface); }; #endif // KISMASKINGBRUSHOPTIONPROPERTIES_H diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp index 74ac1d9b1f..2b91ac3234 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp @@ -1,229 +1,229 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "KisTextureMaskInfo.h" #include #include #include "kis_embedded_pattern_manager.h" #include #include #include #include #include #include /**********************************************************************/ /* KisTextureMaskInfo */ /**********************************************************************/ KisTextureMaskInfo::KisTextureMaskInfo(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { } KisTextureMaskInfo::KisTextureMaskInfo(const KisTextureMaskInfo &rhs) : m_levelOfDetail(rhs.m_levelOfDetail), m_pattern(rhs.m_pattern), m_scale(rhs.m_scale), m_brightness(rhs.m_brightness), m_contrast(rhs.m_contrast), m_invert(rhs.m_invert), m_cutoffLeft(rhs.m_cutoffLeft), m_cutoffRight(rhs.m_cutoffRight), m_cutoffPolicy(rhs.m_cutoffPolicy) { } KisTextureMaskInfo::~KisTextureMaskInfo() { } bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs) { return lhs.m_levelOfDetail == rhs.m_levelOfDetail && (lhs.m_pattern == rhs.m_pattern || (lhs.m_pattern && rhs.m_pattern && lhs.m_pattern->md5() == rhs.m_pattern->md5())) && qFuzzyCompare(lhs.m_scale, rhs.m_scale) && qFuzzyCompare(lhs.m_brightness, rhs.m_brightness) && qFuzzyCompare(lhs.m_contrast, rhs.m_contrast) && lhs.m_invert == rhs.m_invert && lhs.m_cutoffLeft == rhs.m_cutoffLeft && lhs.m_cutoffRight == rhs.m_cutoffRight && lhs.m_cutoffPolicy == rhs.m_cutoffPolicy; } KisTextureMaskInfo &KisTextureMaskInfo::operator=(const KisTextureMaskInfo &rhs) { m_levelOfDetail = rhs.m_levelOfDetail; m_pattern = rhs.m_pattern; m_scale = rhs.m_scale; m_brightness = rhs.m_brightness; m_contrast = rhs.m_contrast; m_invert = rhs.m_invert; m_cutoffLeft = rhs.m_cutoffLeft; m_cutoffRight = rhs.m_cutoffRight; m_cutoffPolicy = rhs.m_cutoffPolicy; return *this; } int KisTextureMaskInfo::levelOfDetail() const { return m_levelOfDetail; } bool KisTextureMaskInfo::hasMask() const { return m_mask; } KisPaintDeviceSP KisTextureMaskInfo::mask() { return m_mask; } QRect KisTextureMaskInfo::maskBounds() const { return m_maskBounds; } -bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting) +bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { return false; } - m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); + m_pattern = KisEmbeddedPatternManager::tryFetchPattern(setting, resourcesInterface); if (!m_pattern) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; return false; } m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); m_brightness = setting->getDouble("Texture/Pattern/Brightness"); m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0); m_invert = setting->getBool("Texture/Pattern/Invert"); m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); return true; } void KisTextureMaskInfo::recalculateMask() { if (!m_pattern) return; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); if (!m_mask) { m_mask = new KisPaintDevice(cs); } QImage mask = m_pattern->pattern(); if ((mask.format() != QImage::Format_RGB32) | (mask.format() != QImage::Format_ARGB32)) { mask = mask.convertToFormat(QImage::Format_ARGB32); } qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail); if (!qFuzzyCompare(scale, 0.0)) { QTransform tf; tf.scale(scale, scale); QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } const QRgb* pixel = reinterpret_cast(mask.constBits()); const int width = mask.width(); const int height = mask.height(); KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { const QRgb currentPixel = pixel[row * width + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); float alpha = qAlpha(currentPixel) / 255.0; const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; float maskValue = (grayValue / 255.0) * alpha + (1 - alpha); maskValue = maskValue - m_brightness; maskValue = ((maskValue - 0.5)*m_contrast)+0.5; if (maskValue > 1.0) {maskValue = 1;} else if (maskValue < 0) {maskValue = 0;} if (m_invert) { maskValue = 1 - maskValue; } if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { // mask out the dab if it's outside the pattern's cuttoff points maskValue = OPACITY_TRANSPARENT_F; } else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { maskValue = OPACITY_OPAQUE_F; } cs->setOpacity(iter->rawData(), maskValue, 1); iter->nextPixel(); } iter->nextRow(); } m_maskBounds = QRect(0, 0, width, height); } /**********************************************************************/ /* KisTextureMaskInfoCache */ /**********************************************************************/ Q_GLOBAL_STATIC(KisTextureMaskInfoCache, s_instance) KisTextureMaskInfoCache *KisTextureMaskInfoCache::instance() { return s_instance; } KisTextureMaskInfoSP KisTextureMaskInfoCache::fetchCachedTextureInfo(KisTextureMaskInfoSP info) { QMutexLocker locker(&m_mutex); KisTextureMaskInfoSP &cachedInfo = info->levelOfDetail() > 0 ? m_lodInfo : m_mainInfo; if (!cachedInfo || *cachedInfo != *info) { cachedInfo = info; cachedInfo->recalculateMask(); } return cachedInfo; } diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.h b/plugins/paintops/libpaintop/KisTextureMaskInfo.h index 60096a7563..c2c8a98326 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.h +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.h @@ -1,90 +1,91 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KISTEXTUREMASKINFO_H #define KISTEXTUREMASKINFO_H #include #include #include #include #include class KisTextureMaskInfo; +class KisResourcesInterface; class KisTextureMaskInfo : public boost::equality_comparable { public: KisTextureMaskInfo(int levelOfDetail); KisTextureMaskInfo(const KisTextureMaskInfo &rhs); ~KisTextureMaskInfo(); friend bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs); KisTextureMaskInfo& operator=(const KisTextureMaskInfo &rhs); int levelOfDetail() const; bool hasMask() const; KisPaintDeviceSP mask(); QRect maskBounds() const; - bool fillProperties(const KisPropertiesConfigurationSP setting); + bool fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); void recalculateMask(); private: int m_levelOfDetail = 0; KoPatternSP m_pattern = 0; qreal m_scale = 1.0; qreal m_brightness = 0.0; qreal m_contrast = 1.0; bool m_invert = false; int m_cutoffLeft = 0; int m_cutoffRight = 255; int m_cutoffPolicy = 0; KisPaintDeviceSP m_mask; QRect m_maskBounds; }; typedef QSharedPointer KisTextureMaskInfoSP; struct KisTextureMaskInfoCache { static KisTextureMaskInfoCache *instance(); KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info); private: QMutex m_mutex; QSharedPointer m_lodInfo; QSharedPointer m_mainInfo; }; #endif // KISTEXTUREMASKINFO_H diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index eca8aff271..395df346d2 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,185 +1,196 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include #include "kis_painter.h" #include #include "kis_paintop_utils.h" #include "kis_paintop_plugin_utils.h" #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } -void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings) +void TextBrushInitializationWorkaround::preinitialize(KisPaintOpSettingsSP settings) { if (KisBrushOptionProperties::isTextBrush(settings.data())) { KisBrushOptionProperties brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, settings->resourcesInterface()); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings) { return (settings && settings == m_settings ? m_brush : 0); } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} - - void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ -KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter) +KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOptionProperties brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, settings->resourcesInterface()); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); - m_textureProperties.fillProperties(settings); + m_textureProperties.fillProperties(settings, settings->resourcesInterface()); m_dabCache->setTexturePostprocessing(&m_textureProperties); m_precisionOption.setHasImprecisePositionOptions( m_precisionOption.hasImprecisePositionOptions() | m_mirrorOption.isChecked() | m_textureProperties.m_enabled); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } +QList KisBrushBasedPaintOp::prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) +{ + QList resources; + + KisBrushOptionProperties brushOption; + resources << brushOption.prepareResources(settings, resourcesInterface); + + KisTextureProperties textureProperties(0); + resources << textureProperties.prepareResources(settings, resourcesInterface); + + return resources; +} + bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height()); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, &spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const { bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing(); MirrorProperties prop = m_mirrorOption.apply(pi); const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror; // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(), isotropicSpacing, rotation, implicitFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device()), airbrushOption, spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const { return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, true, isotropicSpacing, rotation, axesFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device())); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.h b/plugins/paintops/libpaintop/kis_brush_based_paintop.h index c884c3be46..cc18e6610a 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.h +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.h @@ -1,103 +1,107 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_BRUSH_BASED_PAINTOP_H #define KIS_BRUSH_BASED_PAINTOP_H #include "kritapaintop_export.h" #include #include "kis_dab_cache.h" #include "kis_brush.h" #include "kis_texture_option.h" #include "kis_precision_option.h" #include "kis_airbrush_option_widget.h" #include "kis_pressure_mirror_option.h" #include class KisPropertiesConfiguration; class KisPressureSpacingOption; class KisPressureRateOption; class KisDabCache; +class KisResourcesInterface; /// Internal class TextBrushInitializationWorkaround { public: TextBrushInitializationWorkaround(); ~TextBrushInitializationWorkaround(); static TextBrushInitializationWorkaround* instance(); - void preinitialize(KisPropertiesConfigurationSP settings); + void preinitialize(KisPaintOpSettingsSP settings); KisBrushSP tryGetBrush(const KisPropertiesConfigurationSP settings); private: KisBrushSP m_brush; KisPropertiesConfigurationSP m_settings; }; /** * This is a base class for paintops that use a KisBrush or derived * brush to paint with. This is mainly important for the spacing * generation. */ class PAINTOP_EXPORT KisBrushBasedPaintOp : public KisPaintOp { public: - KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter); + KisBrushBasedPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter); ~KisBrushBasedPaintOp() override; bool checkSizeTooSmall(qreal scale); KisSpacingInformation effectiveSpacing(qreal scale) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const; ///Reimplemented, false if brush is 0 bool canPaint() const override; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND typedef int needs_preinitialization; static void preinitializeOpStatically(KisPaintOpSettingsSP settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ + static QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface); + + private: KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const; protected: // XXX: make private! KisDabCache *m_dabCache; KisBrushSP m_brush; private: KisTextureProperties m_textureProperties; protected: KisPressureMirrorOption m_mirrorOption; KisPrecisionOption m_precisionOption; }; #endif diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp index ba2f00e0cc..070861d217 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp @@ -1,340 +1,342 @@ /* * Copyright (c) 2010 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush_based_paintop_settings.h" #include #include #include "kis_brush_based_paintop_options_widget.h" #include #include "KisBrushServerProvider.h" #include #include "kis_signals_blocker.h" #include "kis_brush_option.h" #include #include +#include struct BrushReader { BrushReader(const KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { - m_option.readOptionSetting(m_parent); + m_option.readOptionSetting(m_parent, parent->resourcesInterface()); } KisBrushSP brush() { return m_option.brush(); } const KisBrushBasedPaintOpSettings *m_parent; KisBrushOptionProperties m_option; }; struct BrushWriter { BrushWriter(KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { - m_option.readOptionSetting(m_parent); + m_option.readOptionSetting(m_parent, parent->resourcesInterface()); } ~BrushWriter() { m_option.writeOptionSetting(m_parent); } KisBrushSP brush() { return m_option.brush(); } KisBrushBasedPaintOpSettings *m_parent; KisBrushOptionProperties m_option; }; -KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings() +KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | - KisCurrentOutlineFetcher::ROTATION_OPTION | - KisCurrentOutlineFetcher::MIRROR_OPTION | - KisCurrentOutlineFetcher::SHARPNESS_OPTION) + KisCurrentOutlineFetcher::ROTATION_OPTION | + KisCurrentOutlineFetcher::MIRROR_OPTION | + KisCurrentOutlineFetcher::SHARPNESS_OPTION, + resourcesInterface) { } bool KisBrushBasedPaintOpSettings::paintIncremental() { if (hasProperty("PaintOpAction")) { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } return true; } KisPaintOpSettingsSP KisBrushBasedPaintOpSettings::clone() const { KisPaintOpSettingsSP _settings = KisOutlineGenerationPolicy::clone(); KisBrushBasedPaintOpSettingsSP settings = dynamic_cast(_settings.data()); settings->m_savedBrush = 0; return settings; } KisBrushSP KisBrushBasedPaintOpSettings::brush() const { KisBrushSP brush = m_savedBrush; if (!brush) { BrushReader w(this); brush = w.brush(); m_savedBrush = brush; } return brush; } QPainterPath KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom, qreal additionalScale) { QPainterPath path; if (mode.isVisible) { KisBrushSP brush = this->brush(); if (!brush) return path; qreal finalScale = brush->scale() * additionalScale; QPainterPath realOutline = brush->outline(); if (mode.forceCircle) { QPainterPath ellipse; ellipse.addEllipse(realOutline.boundingRect()); realOutline = ellipse; } path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale, brush->angle()); if (mode.showTiltDecoration) { const QPainterPath tiltLine = makeTiltIndicator(info, realOutline.boundingRect().center(), realOutline.boundingRect().width() * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y())); } } return path; } QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { return brushOutlineImpl(info, mode, alignForZoom, 1.0); } bool KisBrushBasedPaintOpSettings::isValid() const { QStringList files = getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); files << getString(KisPaintOpUtils::RequiredBrushFileTag); Q_FOREACH (const QString &file, files) { if (!file.isEmpty()) { - KisBrushSP brush = KisBrushServerProvider::instance()->brushServer()->resourceByFilename(file); + KisBrushSP brush = resourcesInterface()->source(ResourceType::Brushes).resourceForFilename(file); if (!brush) { return false; } } } return true; } bool KisBrushBasedPaintOpSettings::isLoadable() { return (KisBrushServerProvider::instance()->brushServer()->resourceCount() > 0); } void KisBrushBasedPaintOpSettings::setAngle(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAngle(value); } qreal KisBrushBasedPaintOpSettings::angle() { return this->brush()->angle(); } void KisBrushBasedPaintOpSettings::setSpacing(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setSpacing(value); } qreal KisBrushBasedPaintOpSettings::spacing() { return this->brush()->spacing(); } void KisBrushBasedPaintOpSettings::setAutoSpacing(bool active, qreal coeff) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAutoSpacing(active, coeff); } bool KisBrushBasedPaintOpSettings::autoSpacingActive() { return this->brush()->autoSpacingActive(); } qreal KisBrushBasedPaintOpSettings::autoSpacingCoeff() { return this->brush()->autoSpacingCoeff(); } void KisBrushBasedPaintOpSettings::setPaintOpSize(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setUserEffectiveSize(value); } qreal KisBrushBasedPaintOpSettings::paintOpSize() const { return this->brush()->userEffectiveSize(); } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" QList KisBrushBasedPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "angle", "Angle", settings, 0); prop->setRange(0, 360); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); const qreal angleResult = kisRadiansToDegrees(s->angle()); prop->setValue(angleResult); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAngle(kisDegreesToRadians(prop->value().toReal())); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "auto_spacing", "Auto Spacing", settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); prop->setValue(s->autoSpacingActive()); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAutoSpacing(prop->value().toBool(), s->autoSpacingCoeff()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spacing", "Spacing", settings, 0); prop->setRange(0.01, 10); prop->setSingleStep(0.01); prop->setExponentRatio(3.0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); if (s) { const qreal value = s->autoSpacingActive() ? s->autoSpacingCoeff() : s->spacing(); prop->setValue(value); } }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); if (s) { if (s->autoSpacingActive()) { s->setAutoSpacing(true, prop->value().toReal()); } else { s->setSpacing(prop->value().toReal()); } } }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } void KisBrushBasedPaintOpSettings::onPropertyChanged() { m_savedBrush.clear(); KisOutlineGenerationPolicy::onPropertyChanged(); } diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h index a5b4e4d79f..cc931703a7 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h @@ -1,84 +1,84 @@ /* * Copyright (c) 2010 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_BRUSH_BASED_PAINTOP_SETTINGS_H #define KIS_BRUSH_BASED_PAINTOP_SETTINGS_H #include #include #include #include #include #include class PAINTOP_EXPORT KisBrushBasedPaintOpSettings : public KisOutlineGenerationPolicy { public: - KisBrushBasedPaintOpSettings(); + KisBrushBasedPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisBrushBasedPaintOpSettings() override {} ///Reimplemented bool paintIncremental() override; using KisPaintOpSettings::brushOutline; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; ///Reimplemented bool isValid() const override; ///Reimplemented bool isLoadable() override; KisBrushSP brush() const; KisPaintOpSettingsSP clone() const override; void setAngle(qreal value); qreal angle(); void setSpacing(qreal spacing); qreal spacing(); void setAutoSpacing(bool active, qreal coeff); bool autoSpacingActive(); qreal autoSpacingCoeff(); void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; QList uniformProperties(KisPaintOpSettingsSP settings) override; protected: void onPropertyChanged() override; QPainterPath brushOutlineImpl(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom, qreal additionalScale); mutable KisBrushSP m_savedBrush; QList m_uniformProperties; private: Q_DISABLE_COPY(KisBrushBasedPaintOpSettings) }; class KisBrushBasedPaintOpSettings; typedef KisPinnedSharedPtr KisBrushBasedPaintOpSettingsSP; #endif // KIS_BRUSH_BASED_PAINTOP_SETTINGS_H diff --git a/plugins/paintops/libpaintop/kis_brush_chooser.cpp b/plugins/paintops/libpaintop/kis_brush_chooser.cpp index f47fcacbd1..f7b455f051 100644 --- a/plugins/paintops/libpaintop/kis_brush_chooser.cpp +++ b/plugins/paintops/libpaintop/kis_brush_chooser.cpp @@ -1,417 +1,418 @@ /* * Copyright (c) 2004 Adrian Page * Copyright (c) 2009 Sven Langkamp * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_brush_chooser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisBrushServerProvider.h" #include "widgets/kis_slider_spin_box.h" #include "widgets/kis_multipliers_double_slider_spinbox.h" #include "kis_spacing_selection_widget.h" #include "kis_signals_blocker.h" #include "kis_imagepipe_brush.h" #include "kis_custom_brush_widget.h" #include "kis_clipboard_brush_widget.h" #include #include "kis_global.h" #include "kis_gbr_brush.h" #include "kis_debug.h" #include "kis_image.h" +#include /// The resource item delegate for rendering the resource preview class KisBrushDelegate : public QAbstractItemDelegate { public: KisBrushDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {} ~KisBrushDelegate() override {} /// reimplemented void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; /// reimplemented QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override { return option.decorationSize; } }; void KisBrushDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { if (! index.isValid()) return; QImage thumbnail = index.data(Qt::UserRole + KisResourceModel::Image).value(); QRect itemRect = option.rect; if (thumbnail.height() > itemRect.height() || thumbnail.width() > itemRect.width()) { thumbnail = thumbnail.scaled(itemRect.size() , Qt::KeepAspectRatio, Qt::SmoothTransformation); } painter->save(); int dx = (itemRect.width() - thumbnail.width()) / 2; int dy = (itemRect.height() - thumbnail.height()) / 2; painter->drawImage(itemRect.x() + dx, itemRect.y() + dy, thumbnail); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlight(), 2.0)); painter->drawRect(option.rect); painter->setCompositionMode(QPainter::CompositionMode_HardLight); painter->setOpacity(0.65); painter->fillRect(option.rect, option.palette.highlight()); } painter->restore(); } KisPredefinedBrushChooser::KisPredefinedBrushChooser(QWidget *parent, const char *name) : QWidget(parent), m_stampBrushWidget(0), m_clipboardBrushWidget(0) { setObjectName(name); setupUi(this); brushSizeSpinBox->setRange(0, KSharedConfig::openConfig()->group("").readEntry("maximumBrushSize", 1000), 2); brushSizeSpinBox->setValue(5); brushSizeSpinBox->setExponentRatio(3.0); brushSizeSpinBox->setSuffix(i18n(" px")); brushSizeSpinBox->setExponentRatio(3.0); QObject::connect(brushSizeSpinBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemSize(qreal))); brushRotationSpinBox->setRange(0, 360, 0); brushRotationSpinBox->setValue(0); brushRotationSpinBox->setSuffix(QChar(Qt::Key_degree)); QObject::connect(brushRotationSpinBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemRotation(qreal))); brushSpacingSelectionWidget->setSpacing(true, 1.0); connect(brushSpacingSelectionWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged())); QObject::connect(useColorAsMaskCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetItemUseColorAsMask(bool))); m_itemChooser = new KisResourceItemChooser(ResourceType::Brushes, false, this); m_itemChooser->setObjectName("brush_selector"); m_itemChooser->showTaggingBar(true); m_itemChooser->setRowHeight(30); m_itemChooser->setItemDelegate(new KisBrushDelegate(this)); m_itemChooser->setCurrentItem(0); m_itemChooser->setSynced(true); m_itemChooser->setMinimumWidth(100); m_itemChooser->setMinimumHeight(150); m_itemChooser->showButtons(false); // turn the import and delete buttons since we want control over them addPresetButton->setIcon(KisIconUtils::loadIcon("list-add")); deleteBrushTipButton->setIcon(KisIconUtils::loadIcon("trash-empty")); connect(addPresetButton, SIGNAL(clicked(bool)), this, SLOT(slotImportNewBrushResource())); connect(deleteBrushTipButton, SIGNAL(clicked(bool)), this, SLOT(slotDeleteBrushResource())); presetsLayout->addWidget(m_itemChooser); connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(updateBrushTip(KoResourceSP ))); stampButton->setIcon(KisIconUtils::loadIcon("list-add")); stampButton->setToolTip(i18n("Creates a brush tip from the current image selection." "\n If no selection is present the whole image will be used.")); clipboardButton->setIcon(KisIconUtils::loadIcon("list-add")); clipboardButton->setToolTip(i18n("Creates a brush tip from the image in the clipboard.")); connect(stampButton, SIGNAL(clicked()), this, SLOT(slotOpenStampBrush())); connect(clipboardButton, SIGNAL(clicked()), SLOT(slotOpenClipboardBrush())); QGridLayout *spacingLayout = new QGridLayout(); spacingLayout->setObjectName("spacing grid layout"); resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0")); connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush())); updateBrushTip(m_itemChooser->currentResource()); } KisPredefinedBrushChooser::~KisPredefinedBrushChooser() { } void KisPredefinedBrushChooser::setBrush(KisBrushSP brush) { /** * Warning: since the brushes are always cloned after loading from XML or * fetching from the server, we cannot just ask for that brush explicitly. * Instead, we should search for the brush with the same filename and/or name * and load it. Please take it into account that after selecting the brush * explicitly in the chooser, m_itemChooser->currentResource() might be * **not** the same as the value in m_brush. * * Ideally, if the resource is not found on the server, we should add it, but * it might lead to a set of weird consequences. So for now we just * select nothing. */ KoResourceServer* server = KisBrushServerProvider::instance()->brushServer(); KoResourceSP resource = server->resourceByFilename(brush->filename()); if (!resource) { resource = server->resourceByName(brush->name()); } if (!resource) { resource = brush; } m_itemChooser->setCurrentResource(resource); updateBrushTip(brush, true); } void KisPredefinedBrushChooser::slotResetBrush() { /** * The slot also resets the brush on the server * * TODO: technically, after we refactored all the brushes to be forked, * we can just re-update the brush from the server without reloading. * But it needs testing. */ KisBrushSP brush = m_itemChooser->currentResource().dynamicCast(); if (brush) { - brush->load(); + brush->load(KisGlobalResourcesInterface::instance()); brush->setScale(1.0); brush->setAngle(0.0); updateBrushTip(brush); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotSetItemSize(qreal sizeValue) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush); if (m_brush) { int brushWidth = m_brush->width(); m_brush->setScale(sizeValue / qreal(brushWidth)); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotSetItemRotation(qreal rotationValue) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush); if (m_brush) { m_brush->setAngle(rotationValue / 180.0 * M_PI); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotSpacingChanged() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush); if (m_brush) { m_brush->setSpacing(brushSpacingSelectionWidget->spacing()); m_brush->setAutoSpacing(brushSpacingSelectionWidget->autoSpacingActive(), brushSpacingSelectionWidget->autoSpacingCoeff()); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotSetItemUseColorAsMask(bool useColorAsMask) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush); KisGbrBrush *brush = dynamic_cast(m_brush.data()); if (brush) { brush->setUseColorAsMask(useColorAsMask); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotOpenStampBrush() { if(!m_stampBrushWidget) { m_stampBrushWidget = new KisCustomBrushWidget(this, i18n("Stamp"), m_image); m_stampBrushWidget->setModal(false); connect(m_stampBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )), SLOT(slotNewPredefinedBrush(KoResourceSP ))); } else { m_stampBrushWidget->setImage(m_image); } QDialog::DialogCode result = (QDialog::DialogCode)m_stampBrushWidget->exec(); if(result) { updateBrushTip(m_itemChooser->currentResource()); } } void KisPredefinedBrushChooser::slotOpenClipboardBrush() { if(!m_clipboardBrushWidget) { m_clipboardBrushWidget = new KisClipboardBrushWidget(this, i18n("Clipboard"), m_image); m_clipboardBrushWidget->setModal(true); connect(m_clipboardBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )), SLOT(slotNewPredefinedBrush(KoResourceSP ))); } QDialog::DialogCode result = (QDialog::DialogCode)m_clipboardBrushWidget->exec(); if(result) { updateBrushTip(m_itemChooser->currentResource()); } } void KisPredefinedBrushChooser::updateBrushTip(KoResourceSP resource, bool isChangingBrushPresets) { QString animatedBrushTipSelectionMode; // incremental, random, etc { KisBrushSP brush = resource.dynamicCast(); m_brush = brush ? brush->clone().dynamicCast() : 0; } if (m_brush) { brushTipNameLabel->setText(i18n(m_brush->name().toUtf8().data())); QString brushTypeString = ""; if (m_brush->brushType() == INVALID) { brushTypeString = i18n("Invalid"); } else if (m_brush->brushType() == MASK) { brushTypeString = i18n("Mask"); } else if (m_brush->brushType() == IMAGE) { brushTypeString = i18n("GBR"); } else if (m_brush->brushType() == PIPE_MASK ) { brushTypeString = i18n("Animated Mask"); // GIH brush // cast to GIH brush and grab parasite name //m_brush KisImagePipeBrushSP pipeBrush = resource.dynamicCast(); animatedBrushTipSelectionMode = pipeBrush->parasiteSelection(); } else if (m_brush->brushType() == PIPE_IMAGE ) { brushTypeString = i18n("Animated Image"); } QString brushDetailsText = QString("%1 (%2 x %3) %4") .arg(brushTypeString) .arg(m_brush->width()) .arg(m_brush->height()) .arg(animatedBrushTipSelectionMode); brushDetailsLabel->setText(brushDetailsText); // keep the current preset's tip settings if we are preserving it // this will set the brush's model data to keep what it currently has for size, spacing, etc. if (preserveBrushPresetSettings->isChecked() && !isChangingBrushPresets) { m_brush->setAutoSpacing(brushSpacingSelectionWidget->autoSpacingActive(), brushSpacingSelectionWidget->autoSpacingCoeff()); m_brush->setAngle(brushRotationSpinBox->value() * M_PI / 180); m_brush->setSpacing(brushSpacingSelectionWidget->spacing()); m_brush->setUserEffectiveSize(brushSizeSpinBox->value()); } brushSpacingSelectionWidget->setSpacing(m_brush->autoSpacingActive(), m_brush->autoSpacingActive() ? m_brush->autoSpacingCoeff() : m_brush->spacing()); brushRotationSpinBox->setValue(m_brush->angle() * 180 / M_PI); brushSizeSpinBox->setValue(m_brush->width() * m_brush->scale()); // useColorAsMask support is only in gimp brush so far bool prevColorAsMaskState = useColorAsMaskCheckbox->isChecked(); KisGbrBrush *gimpBrush = dynamic_cast(m_brush.data()); if (gimpBrush) { useColorAsMaskCheckbox->setChecked(gimpBrush->useColorAsMask() || prevColorAsMaskState); gimpBrush->setUseColorAsMask(prevColorAsMaskState); } useColorAsMaskCheckbox->setEnabled(m_brush->hasColor() && gimpBrush); emit sigBrushChanged(); } } void KisPredefinedBrushChooser::slotNewPredefinedBrush(KoResourceSP resource) { m_itemChooser->setCurrentResource(resource); updateBrushTip(resource); } void KisPredefinedBrushChooser::setBrushSize(qreal xPixels, qreal yPixels) { Q_UNUSED(yPixels); qreal oldWidth = m_brush->width() * m_brush->scale(); qreal newWidth = oldWidth + xPixels; newWidth = qMax(newWidth, qreal(0.1)); brushSizeSpinBox->setValue(newWidth); } void KisPredefinedBrushChooser::setImage(KisImageWSP image) { m_image = image; } void KisPredefinedBrushChooser::slotImportNewBrushResource() { m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Import); } void KisPredefinedBrushChooser::slotDeleteBrushResource() { m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Remove); } #include "moc_kis_brush_chooser.cpp" diff --git a/plugins/paintops/libpaintop/kis_brush_option.cpp b/plugins/paintops/libpaintop/kis_brush_option.cpp index d1284774dd..578467df7e 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.cpp +++ b/plugins/paintops/libpaintop/kis_brush_option.cpp @@ -1,104 +1,121 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_brush_option.h" #include #include #include "kis_properties_configuration.h" #include +#include void KisBrushOptionProperties::writeOptionSettingImpl(KisPropertiesConfiguration *setting) const { if (!m_brush) return; QDomDocument d; QDomElement e = d.createElement("Brush"); m_brush->toXML(d, e); d.appendChild(e); setting->setProperty("brush_definition", d.toString()); QString brushFileName = !m_brush->filename().isEmpty() ? m_brush->filename() : QString(); setting->setProperty(KisPaintOpUtils::RequiredBrushFileTag, brushFileName); { QStringList requiredFiles = setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << brushFileName; setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles); } } QDomElement getBrushXMLElement(const KisPropertiesConfiguration *setting) { QDomElement element; QString brushDefinition = setting->getString("brush_definition"); if (!brushDefinition.isEmpty()) { QDomDocument d; d.setContent(brushDefinition, false); element = d.firstChildElement("Brush"); } return element; } -void KisBrushOptionProperties::readOptionSettingImpl(const KisPropertiesConfiguration *setting) +void KisBrushOptionProperties::readOptionSettingResourceImpl(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface) { QDomElement element = getBrushXMLElement(setting); if (!element.isNull()) { - m_brush = KisBrush::fromXML(element); + m_brush = KisBrush::fromXML(element, resourcesInterface); } } +QList KisBrushOptionProperties::prepareResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const +{ + QList resources; + + QDomElement element = getBrushXMLElement(settings); + if (element.isNull()) return resources; + + KisBrushSP brush = KisBrush::fromXML(element, resourcesInterface); + // TODO: check if ephemeral and don't add it! + if (brush) { + resources << brush; + } + + return resources; +} + #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND #include "kis_text_brush_factory.h" bool KisBrushOptionProperties::isTextBrush(const KisPropertiesConfiguration *setting) { static QString textBrushId = KisTextBrushFactory().id(); QDomElement element = getBrushXMLElement(setting); QString brushType = element.attribute("type"); return brushType == textBrushId; } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushSP KisBrushOptionProperties::brush() const { return m_brush; } void KisBrushOptionProperties::setBrush(KisBrushSP brush) { m_brush = brush; } diff --git a/plugins/paintops/libpaintop/kis_brush_option.h b/plugins/paintops/libpaintop/kis_brush_option.h index ac34d1e30b..0cc8312723 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.h +++ b/plugins/paintops/libpaintop/kis_brush_option.h @@ -1,50 +1,51 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_OPTION_H_ #define KIS_BRUSH_OPTION_H_ #include #include #include #include - +#include #include -class PAINTOP_EXPORT KisBrushOptionProperties : public KisPaintopPropertiesBase +class PAINTOP_EXPORT KisBrushOptionProperties : public KisPaintopPropertiesResourcesBase { public: void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override; - void readOptionSettingImpl(const KisPropertiesConfiguration *setting) override; + void readOptionSettingResourceImpl(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface) override; + QList prepareResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override; KisBrushSP brush() const; void setBrush(KisBrushSP brush); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND static bool isTextBrush(const KisPropertiesConfiguration *setting); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ private: KisBrushSP m_brush; }; #endif diff --git a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp index 770f822b4d..1d9f46882d 100644 --- a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp +++ b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp @@ -1,112 +1,112 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_brush_option_widget.h" #include #include #include "kis_brush_selection_widget.h" #include "kis_brush.h" - +#include KisBrushOptionWidget::KisBrushOptionWidget() : KisPaintOpOption(KisPaintOpOption::GENERAL, true) { m_checkable = false; m_brushSelectionWidget = new KisBrushSelectionWidget(); connect(m_brushSelectionWidget, SIGNAL(sigPrecisionChanged()), SLOT(emitSettingChanged())); connect(m_brushSelectionWidget, SIGNAL(sigBrushChanged()), SLOT(brushChanged())); m_brushSelectionWidget->hide(); setConfigurationPage(m_brushSelectionWidget); m_brushOption.setBrush(brush()); setObjectName("KisBrushOptionWidget"); } KisBrushSP KisBrushOptionWidget::brush() const { return m_brushSelectionWidget->brush(); } void KisBrushOptionWidget::setAutoBrush(bool on) { m_brushSelectionWidget->setAutoBrush(on); } void KisBrushOptionWidget::setPredefinedBrushes(bool on) { m_brushSelectionWidget->setPredefinedBrushes(on); } void KisBrushOptionWidget::setCustomBrush(bool on) { m_brushSelectionWidget->setCustomBrush(on); } void KisBrushOptionWidget::setTextBrush(bool on) { m_brushSelectionWidget->setTextBrush(on); } void KisBrushOptionWidget::setImage(KisImageWSP image) { m_brushSelectionWidget->setImage(image); } void KisBrushOptionWidget::setPrecisionEnabled(bool value) { m_brushSelectionWidget->setPrecisionEnabled(value); } void KisBrushOptionWidget::writeOptionSetting(KisPropertiesConfigurationSP settings) const { m_brushSelectionWidget->writeOptionSetting(settings); m_brushOption.writeOptionSetting(settings); } void KisBrushOptionWidget::readOptionSetting(const KisPropertiesConfigurationSP setting) { m_brushSelectionWidget->readOptionSetting(setting); - m_brushOption.readOptionSetting(setting); + m_brushOption.readOptionSetting(setting, KisGlobalResourcesInterface::instance()); m_brushSelectionWidget->setCurrentBrush(m_brushOption.brush()); } void KisBrushOptionWidget::lodLimitations(KisPaintopLodLimitations *l) const { KisBrushSP brush = this->brush(); brush->lodLimitations(l); } void KisBrushOptionWidget::brushChanged() { m_brushOption.setBrush(brush()); emitSettingChanged(); } bool KisBrushOptionWidget::presetIsValid() { return m_brushSelectionWidget->presetIsValid(); } void KisBrushOptionWidget::hideOptions(const QStringList &options) { m_brushSelectionWidget->hideOptions(options); } #include "moc_kis_brush_option_widget.cpp" diff --git a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp index 431d814e2d..985e09fed4 100644 --- a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp +++ b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp @@ -1,104 +1,113 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_embedded_pattern_manager.h" #include #include #include #include #include +#include struct KisEmbeddedPatternManager::Private { static KoPatternSP tryLoadEmbeddedPattern(const KisPropertiesConfigurationSP setting) { KoPatternSP pattern; QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toLatin1()); QImage img; img.loadFromData(ba, "PNG"); QString name = setting->getString("Texture/Pattern/Name"); QString filename = setting->getString("Texture/Pattern/PatternFileName"); if (name.isEmpty() || name != QFileInfo(name).fileName()) { QFileInfo info(filename); name = info.completeBaseName(); } if (!img.isNull()) { pattern = KoPatternSP(new KoPattern(img, name, KoResourceServerProvider::instance()->patternServer()->saveLocation())); } return pattern; } }; void KisEmbeddedPatternManager::saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPatternSP pattern) { QByteArray patternMD5 = pattern->md5(); /** * The process of saving a pattern may be quite expensive, so * we won't rewrite the pattern if has the same md5-sum and at * least some data is present */ QByteArray existingMD5 = QByteArray::fromBase64(setting->getString("Texture/Pattern/PatternMD5").toLatin1()); QString existingPatternBase64 = setting->getString("Texture/Pattern/PatternMD5").toLatin1(); if (patternMD5 == existingMD5 && !existingPatternBase64.isEmpty()) { return; } setting->setProperty("Texture/Pattern/PatternMD5", patternMD5.toBase64()); setting->setProperty("Texture/Pattern/PatternFileName", pattern->filename()); setting->setProperty("Texture/Pattern/Name", pattern->name()); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); pattern->pattern().save(&buffer, "PNG"); setting->setProperty("Texture/Pattern/Pattern", ba.toBase64()); } -KoPatternSP KisEmbeddedPatternManager::loadEmbeddedPattern(const KisPropertiesConfigurationSP setting) +KoPatternSP KisEmbeddedPatternManager::tryFetchPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) { KoPatternSP pattern; - KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); + auto server = resourcesInterface->source(ResourceType::Patterns); QByteArray md5 = QByteArray::fromBase64(setting->getString("Texture/Pattern/PatternMD5").toLatin1()); - pattern = server->resourceByMD5(md5); + pattern = server.resourceForMD5(md5); if (pattern) return pattern; QString name = setting->getString("Texture/Pattern/Name"); - pattern = server->resourceByName(name); + pattern = server.resourceForName(name); if (pattern) return pattern; QString fileName = setting->getString("Texture/Pattern/PatternFileName"); - pattern = server->resourceByFilename(fileName); + pattern = server.resourceForFilename(fileName); + + return pattern; +} + +KoPatternSP KisEmbeddedPatternManager::loadEmbeddedPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) +{ + KoPatternSP pattern = tryFetchPattern(setting, resourcesInterface); if (pattern) return pattern; pattern = Private::tryLoadEmbeddedPattern(setting); if (pattern) { - KoResourceServerProvider::instance()->patternServer()->addResource(pattern, false); + // TODO: implement addition of the embedded resources in KisResourceLocator::resource() + //KoResourceServerProvider::instance()->patternServer()->addResource(pattern, false); } return pattern; } diff --git a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h index ceef455347..a3bda14707 100644 --- a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h +++ b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h @@ -1,38 +1,41 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_EMBEDDED_PATTERN_MANAGER_H #define __KIS_EMBEDDED_PATTERN_MANAGER_H #include #include #include class KoAbstractGradient; +class KisResourcesInterface; +using KisResourcesInterfaceSP = QSharedPointer; class PAINTOP_EXPORT KisEmbeddedPatternManager { public: static void saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPatternSP pattern); - static KoPatternSP loadEmbeddedPattern(const KisPropertiesConfigurationSP setting); + static KoPatternSP loadEmbeddedPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); + static KoPatternSP tryFetchPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); private: struct Private; }; #endif /* __KIS_EMBEDDED_PATTERN_MANAGER_H */ diff --git a/plugins/paintops/libpaintop/kis_outline_generation_policy.h b/plugins/paintops/libpaintop/kis_outline_generation_policy.h index 386531ebe5..e311f71f2a 100644 --- a/plugins/paintops/libpaintop/kis_outline_generation_policy.h +++ b/plugins/paintops/libpaintop/kis_outline_generation_policy.h @@ -1,63 +1,65 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_OUTLINE_GENERATION_POLICY_H #define __KIS_OUTLINE_GENERATION_POLICY_H #include /** * This is a policy class that adds an ability to have a Outline Generator * to a KisPaintOpSettings-based class. * * \see Andrei Alexandrescu "Modern C++ Design: Generic Programming and Design Patterns Applied" */ template class KisOutlineGenerationPolicy : public ParentClass { public: - KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::Options options) - : m_outlineFetcher(options) + KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::Options options, + KisResourcesInterfaceSP resourcesInterface) + : ParentClass(resourcesInterface), + m_outlineFetcher(options) { } ~KisOutlineGenerationPolicy() override { } KisOutlineGenerationPolicy(const KisOutlineGenerationPolicy &rhs) : ParentClass(rhs) { } const KisCurrentOutlineFetcher *outlineFetcher() const { return &m_outlineFetcher; } void onPropertyChanged() override { m_outlineFetcher.setDirty(); ParentClass::onPropertyChanged(); } private: KisCurrentOutlineFetcher m_outlineFetcher; }; #endif /* __KIS_OUTLINE_GENERATION_POLICY_H */ diff --git a/plugins/paintops/libpaintop/kis_simple_paintop_factory.h b/plugins/paintops/libpaintop/kis_simple_paintop_factory.h index ac15f79dd4..4dc18264e6 100644 --- a/plugins/paintops/libpaintop/kis_simple_paintop_factory.h +++ b/plugins/paintops/libpaintop/kis_simple_paintop_factory.h @@ -1,123 +1,156 @@ /* * Copyright (c) 2009 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SIMPLE_PAINTOP_FACTORY_H #define KIS_SIMPLE_PAINTOP_FACTORY_H #include #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND namespace detail { template< class, class = std::void_t<> > struct has_preinitialize_statically : std::false_type { }; template< class T > struct has_preinitialize_statically().preinitializeOpStatically(KisPaintOpSettingsSP()))>> : std::true_type { }; template void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename std::enable_if_t::value> * = 0) { T::preinitializeOpStatically(settings); } template void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename std::enable_if_t::value> * = 0) { Q_UNUSED(settings); // noop } } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ +namespace detail { + +template< class, class = std::void_t<> > +struct has_prepare_resources : std::false_type { }; + +template< class T > +struct has_prepare_resources().prepareResources(KisPaintOpSettingsSP(),KisResourcesInterfaceSP()))>> : std::true_type { }; + +template +QList prepareResources(const KisPaintOpSettingsSP settings, + KisResourcesInterfaceSP resourcesInterface, + std::enable_if_t::value> * = 0) +{ + return T::prepareResources(settings, resourcesInterface); +} + +template +QList prepareResources(const KisPaintOpSettingsSP settings, + KisResourcesInterfaceSP resourcesInterface, + std::enable_if_t::value> * = 0) +{ + Q_UNUSED(settings); + Q_UNUSED(resourcesInterface); + // noop + return {}; +} + +} + /** * Base template class for simple paintop factories */ template class KisSimplePaintOpFactory : public KisPaintOpFactory { public: KisSimplePaintOpFactory(const QString& id, const QString& name, const QString& category, const QString& pixmap, const QString& model = QString(), const QStringList& whiteListedCompositeOps = QStringList(), int priority = 100) : KisPaintOpFactory(whiteListedCompositeOps) , m_id(id) , m_name(name) , m_category(category) , m_pixmap(pixmap) , m_model(model) { setPriority(priority); } ~KisSimplePaintOpFactory() override { } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND void preinitializePaintOpIfNeeded(const KisPaintOpSettingsSP settings) override { detail::preinitializeOpStatically(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisPaintOp *createOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) override { KisPaintOp * op = new Op(settings, painter, node, image); Q_CHECK_PTR(op); return op; } - KisPaintOpSettingsSP createSettings() override { - KisPaintOpSettingsSP settings = new OpSettings(); + QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) { + return detail::prepareResources(settings, resourcesInterface); + } + + KisPaintOpSettingsSP createSettings(KisResourcesInterfaceSP resourcesInterface) override { + KisPaintOpSettingsSP settings = new OpSettings(resourcesInterface); settings->setModelName(m_model); return settings; } KisPaintOpConfigWidget* createConfigWidget(QWidget* parent) override { return new OpSettingsWidget(parent); } QString id() const override { return m_id; } QString name() const override { return m_name; } QIcon icon() override { return KisIconUtils::loadIcon(id()); } QString category() const override { return m_category; } private: QString m_id; QString m_name; QString m_category; QString m_pixmap; QString m_model; }; #endif // KIS_SIMPLE_PAINTOP_FACTORY_H diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 557e36797e..4cb29727c8 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,273 +1,289 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * Copyright (C) Mohit Goyal , (C) 2014 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_texture_option.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_embedded_pattern_manager.h" #include #include "kis_texture_chooser.h" #include - - +#include "kis_signals_blocker.h" +#include KisTextureOption::KisTextureOption() : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) , m_textureOptions(new KisTextureChooser()) { setObjectName("KisTextureOption"); setConfigurationPage(m_textureOptions); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(resetGUI(KoResourceSP ))); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(emitSettingChanged())); connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_textureOptions->textureSelectorWidget->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_textureOptions; } void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { - m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking - if (!m_textureOptions->textureSelectorWidget->currentResource()) return; - KoPatternSP pattern = m_textureOptions->textureSelectorWidget->currentResource().staticCast(); - m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking - if (!pattern) return; + KoPatternSP pattern; + + { + KisSignalsBlocker b(m_textureOptions->textureSelectorWidget); + KoResourceSP resource = m_textureOptions->textureSelectorWidget->currentResource(); + if (!resource) return; + + pattern = resource.staticCast(); + if (!pattern) return; + } setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_textureOptions->scaleSlider->value(); qreal brightness = m_textureOptions->brightnessSlider->value(); qreal contrast = m_textureOptions->contrastSlider->value(); int offsetX = m_textureOptions->offsetSliderX->value(); if (m_textureOptions ->randomOffsetX->isChecked()) { m_textureOptions->offsetSliderX ->setEnabled(false); m_textureOptions->offsetSliderX ->blockSignals(true); m_textureOptions->offsetSliderX ->setValue(offsetX); m_textureOptions->offsetSliderX ->blockSignals(false); } else { m_textureOptions->offsetSliderX ->setEnabled(true); } int offsetY = m_textureOptions->offsetSliderY->value(); if (m_textureOptions ->randomOffsetY->isChecked()) { m_textureOptions->offsetSliderY ->setEnabled(false); m_textureOptions->offsetSliderY ->blockSignals(true); m_textureOptions->offsetSliderY ->setValue(offsetY); m_textureOptions->offsetSliderY ->blockSignals(false); } else { m_textureOptions->offsetSliderY ->setEnabled(true); } int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex(); bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/Brightness", brightness); setting->setProperty("Texture/Pattern/Contrast", contrast); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } - KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); + KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, KisGlobalResourcesInterface::instance()); if (!pattern) { pattern =m_textureOptions->textureSelectorWidget->currentResource().staticCast(); } m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern); m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness")); m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0)); m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY")); m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const { l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)")); } void KisTextureOption::resetGUI(KoResourceSP res) { KoPatternSP pattern = res.staticCast(); if (!pattern) return; m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } /**********************************************************************/ /* KisTextureProperties */ /**********************************************************************/ KisTextureProperties::KisTextureProperties(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { } -void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting) +void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) { - if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { m_enabled = false; return; } m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail)); - if (!m_maskInfo->fillProperties(setting)) { + if (!m_maskInfo->fillProperties(setting, resourcesInterface)) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; m_enabled = false; return; } m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo); m_enabled = setting->getBool("Texture/Pattern/Enabled", false); m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); m_strengthOption.readOptionSetting(setting); m_strengthOption.resetAllSensors(); } +QList KisTextureProperties::prepareResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) +{ + QList resources; + + KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, resourcesInterface); + if (pattern) { + resources << pattern; + } + + return resources; +} + void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) { if (!m_enabled) return; KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); KisPaintDeviceSP mask = m_maskInfo->mask(); const QRect maskBounds = m_maskInfo->maskBounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); fillPainter.end(); qreal pressure = m_strengthOption.apply(info); quint8 *dabData = dab->data(); KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { if (m_texturingMode == MULTIPLY) { dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1); } else { int pressureOffset = (1.0 - pressure) * 255; qint16 maskA = *iter->oldRawData() + pressureOffset; quint8 dabA = dab->colorSpace()->opacityU8(dabData); dabA = qMax(0, (qint16)dabA - maskA); dab->colorSpace()->setOpacity(dabData, dabA, 1); } iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h index 0df28f2510..8c90c4d213 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,98 +1,100 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_TEXTURE_OPTION_H #define KIS_TEXTURE_OPTION_H #include #include #include #include "kis_paintop_option.h" #include "kis_pressure_texture_strength_option.h" #include "KisTextureMaskInfo.h" #include class KisTextureChooser; class KoPattern; class KoResource; class KisPropertiesConfiguration; class KisPaintopLodLimitations; +class KisResourcesInterface; class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption { Q_OBJECT public: explicit KisTextureOption(); ~KisTextureOption() override; public Q_SLOTS: void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void lodLimitations(KisPaintopLodLimitations *l) const override; private Q_SLOTS: void resetGUI(KoResourceSP ); /// called when a new pattern is selected private: /// UI Widget that stores all the texture options KisTextureChooser* m_textureOptions; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties(int levelOfDetail); enum TexturingMode { MULTIPLY, SUBTRACT }; bool m_enabled; /** * @brief apply combine the texture map with the dab * @param dab the colored, final representation of the dab, after mirroring and everything. * @param offset the position of the dab on the image. used to calculate the position of the mask pattern * @param info the paint information */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); - void fillProperties(const KisPropertiesConfigurationSP setting); + void fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); + QList prepareResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); private: int m_offsetX; int m_offsetY; TexturingMode m_texturingMode; int m_levelOfDetail; private: KisPressureTextureStrengthOption m_strengthOption; KisTextureMaskInfoSP m_maskInfo; }; #endif // KIS_TEXTURE_OPTION_H diff --git a/plugins/paintops/particle/kis_particle_paintop_settings.cpp b/plugins/paintops/particle/kis_particle_paintop_settings.cpp index 0b1cb96980..5ce49c2f83 100644 --- a/plugins/paintops/particle/kis_particle_paintop_settings.cpp +++ b/plugins/paintops/particle/kis_particle_paintop_settings.cpp @@ -1,256 +1,257 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_particle_paintop_settings.h" #include "kis_particle_paintop_settings_widget.h" #include "kis_particleop_option.h" #include #include struct KisParticlePaintOpSettings::Private { QList uniformProperties; }; -KisParticlePaintOpSettings::KisParticlePaintOpSettings() - : m_d(new Private) +KisParticlePaintOpSettings::KisParticlePaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisNoSizePaintOpSettings(resourcesInterface), + m_d(new Private) { } KisParticlePaintOpSettings::~KisParticlePaintOpSettings() { } bool KisParticlePaintOpSettings::lodSizeThresholdSupported() const { return false; } bool KisParticlePaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisParticlePaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "particle_particles", i18n("Particles"), settings, 0); prop->setRange(1, 500); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.particle_count)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_count = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "particle_opecityweight", i18n("Opacity Weight"), settings, 0); prop->setRange(0.01, 1.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.particle_weight); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_weight = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "particle_dx_scale", i18n("dx scale"), settings, 0); prop->setRange(-2, 2); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.particle_scale_x); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_scale_x = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "particle_dy_scale", i18n("dy scale"), settings, 0); prop->setRange(-2, 2); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.particle_scale_y); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_scale_y = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "particle_gravity", i18n("Gravity"), settings, 0); prop->setRange(0.01, 1.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.particle_gravity); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_gravity = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "particle_iterations", i18n("Iterations"), settings, 0); prop->setRange(1, 300); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.particle_iterations)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { ParticleOption option; option.readOptionSetting(prop->settings().data()); option.particle_iterations = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/particle/kis_particle_paintop_settings.h b/plugins/paintops/particle/kis_particle_paintop_settings.h index 57772c3e83..ca7fd76db4 100644 --- a/plugins/paintops/particle/kis_particle_paintop_settings.h +++ b/plugins/paintops/particle/kis_particle_paintop_settings.h @@ -1,44 +1,44 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_PARTICLE_PAINTOP_SETTINGS_H_ #define KIS_PARTICLE_PAINTOP_SETTINGS_H_ #include #include #include class KisParticlePaintOpSettings : public KisNoSizePaintOpSettings { public: - KisParticlePaintOpSettings(); + KisParticlePaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisParticlePaintOpSettings() override; bool lodSizeThresholdSupported() const override; bool paintIncremental() override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp b/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp index 8f8a5acc4e..9697a87b2f 100644 --- a/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp +++ b/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp @@ -1,56 +1,57 @@ /* * Copyright (c) 2008 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_particle_paintop_settings_widget.h" #include "kis_particleop_option.h" #include "kis_particle_paintop_settings.h" #include #include #include #include #include #include +#include KisParticlePaintOpSettingsWidget:: KisParticlePaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { m_paintActionTypeOption = new KisPaintActionTypeOption(); m_particleOption = new KisParticleOpOption(); addPaintOpOption(m_particleOption, i18n("Brush size")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisAirbrushOptionWidget(false, false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); addPaintOpOption(m_paintActionTypeOption, i18n("Painting Mode")); } KisParticlePaintOpSettingsWidget::~ KisParticlePaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisParticlePaintOpSettingsWidget::configuration() const { - KisParticlePaintOpSettings* config = new KisParticlePaintOpSettings(); + KisParticlePaintOpSettings* config = new KisParticlePaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "particlebrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp index e54272ef11..80af598109 100644 --- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp +++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp @@ -1,103 +1,104 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_roundmarkerop_settings.h" #include "kis_roundmarker_option.h" struct KisRoundMarkerOpSettings::Private { }; -KisRoundMarkerOpSettings::KisRoundMarkerOpSettings() +KisRoundMarkerOpSettings::KisRoundMarkerOpSettings(KisResourcesInterfaceSP resourcesInterface) : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | KisCurrentOutlineFetcher::ROTATION_OPTION | - KisCurrentOutlineFetcher::MIRROR_OPTION), + KisCurrentOutlineFetcher::MIRROR_OPTION, + resourcesInterface), m_d(new Private) { } KisRoundMarkerOpSettings::~KisRoundMarkerOpSettings() { } bool KisRoundMarkerOpSettings::paintIncremental() { return false; } void KisRoundMarkerOpSettings::setPaintOpSize(qreal value) { RoundMarkerOption op; op.readOptionSetting(*this); op.diameter = value; op.writeOptionSetting(this); } qreal KisRoundMarkerOpSettings::paintOpSize() const { RoundMarkerOption op; op.readOptionSetting(*this); return op.diameter; } QPainterPath KisRoundMarkerOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { qreal finalScale = 1.0; RoundMarkerOption op; op.readOptionSetting(*this); const qreal radius = 0.5 * op.diameter; QPainterPath realOutline; realOutline.addEllipse(QPointF(), radius, radius); path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale); if (mode.showTiltDecoration) { QPainterPath tiltLine = makeTiltIndicator(info, realOutline.boundingRect().center(), realOutline.boundingRect().width() * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y())); } } return path; } #include "kis_standard_uniform_properties_factory.h" QList KisRoundMarkerOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props; { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() != flow.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h index eff10ec1ce..566840a9fd 100644 --- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h +++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h @@ -1,57 +1,57 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 __KIS_ROUNDMARKEROP_SETTINGS_H #define __KIS_ROUNDMARKEROP_SETTINGS_H #include #include #include class KisRoundMarkerOpSettings : public KisOutlineGenerationPolicy { public: - KisRoundMarkerOpSettings(); + KisRoundMarkerOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisRoundMarkerOpSettings() override; bool paintIncremental() override; qreal paintOpSize() const override; void setPaintOpSize(qreal value) override; bool isAirbrushing() const override { return false; } qreal airbrushInterval() const override { return 1000.0; } QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_ROUNDMARKEROP_SETTINGS_H */ diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp b/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp index 63034b0bbc..1e58a8d7b5 100644 --- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp +++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp @@ -1,58 +1,59 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_roundmarkerop_settings_widget.h" #include "kis_brush_based_paintop_settings.h" #include #include #include #include #include #include //#include "kis_texture_option.h" //#include "kis_pressure_texture_strength_option.h" #include "kis_roundmarkerop_settings.h" #include "kis_roundmarker_option.h" +#include KisRoundMarkerOpSettingsWidget::KisRoundMarkerOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { setObjectName("roundmarker option widget"); //setPrecisionEnabled(true); addPaintOpOption(new KisRoundMarkerOption(), i18n("Brush")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); //addPaintOpOption(new KisTextureOption(), i18n("Pattern")); //addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); } KisRoundMarkerOpSettingsWidget::~KisRoundMarkerOpSettingsWidget() { } KisPropertiesConfigurationSP KisRoundMarkerOpSettingsWidget::configuration() const { - KisRoundMarkerOpSettings *config = new KisRoundMarkerOpSettings(); + KisRoundMarkerOpSettings *config = new KisRoundMarkerOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "roundmarker"); writeConfiguration(config); return config; } diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp index fb20f0f335..248ec831d7 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp @@ -1,325 +1,331 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 Ricardo Cabello * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_sketch_paintop.h" #include "kis_sketch_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include /* * Based on Harmony project https://github.com/mrdoob/harmony/ */ // chrome : diff 0.2, sketchy : 0.3, fur: 0.5 // fur : distance / thresholdDistance // shaded: opacity per line :/ // ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0 // chrome: color per line :/ //this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )"; // long fur // from: count + offset * -random // to: i point - (offset * -random) + random * 2 // probability distance / thresholdDistnace // shaded: probability : paint always - 0.0 density KisSketchPaintOp::KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); m_airbrushOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_sketchProperties.readOptionSetting(settings); - m_brushOption.readOptionSetting(settings); + m_brushOption.readOptionSetting(settings, settings->resourcesInterface()); m_densityOption.readOptionSetting(settings); m_lineWidthOption.readOptionSetting(settings); m_offsetScaleOption.readOptionSetting(settings); m_brush = m_brushOption.brush(); m_dabCache = new KisDabCache(m_brush); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_painter = 0; m_count = 0; } KisSketchPaintOp::~KisSketchPaintOp() { delete m_painter; delete m_dabCache; } +QList KisSketchPaintOp::prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) +{ + KisBrushOptionProperties brushOption; + return brushOption.prepareResources(settings, resourcesInterface); +} + void KisSketchPaintOp::drawConnection(const QPointF& start, const QPointF& end, double lineWidth) { if (lineWidth == 1.0) { m_painter->drawThickLine(start, end, lineWidth, lineWidth); } else { m_painter->drawLine(start, end, lineWidth, true); } } void KisSketchPaintOp::updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation) { QRect dstRect; m_maskDab = m_dabCache->fetchDab(m_dab->colorSpace(), painter()->paintColor(), info.pos(), KisDabShape(scale, 1.0, rotation), info, 1.0, &dstRect); m_brushBoundingBox = dstRect; m_hotSpot = QPointF(0.5 * m_brushBoundingBox.width(), 0.5 * m_brushBoundingBox.height()); } void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { // Use superclass behavior for lines of zero length. Otherwise, airbrushing can happen faster // than it is supposed to. if (pi1.pos() == pi2.pos()) { KisPaintOp::paintLine(pi1, pi2, currentDistance); } else { doPaintLine(pi1, pi2); } } void KisSketchPaintOp::doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { if (!m_brush || !painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); m_painter = new KisPainter(m_dab); m_painter->setPaintColor(painter()->paintColor()); } else { m_dab->clear(); } QPointF prevMouse = pi1.pos(); QPointF mousePosition = pi2.pos(); m_points.append(mousePosition); const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device()); const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2); if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return; const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth)); const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset); const double rotation = m_rotationOption.apply(pi2); const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability); // shaded: does not draw this line, chrome does, fur does if (m_sketchProperties.makeConnection) { drawConnection(prevMouse, mousePosition, currentLineWidth); } qreal thresholdDistance = 0.0; // update the mask for simple mode only once // determine the radius if (m_count == 0 && m_sketchProperties.simpleMode) { updateBrushMask(pi2, 1.0, 0.0); //m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5; m_radius = 0.5 * qMax(m_brush->width(), m_brush->height()); } if (!m_sketchProperties.simpleMode) { updateBrushMask(pi2, scale, rotation); m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5; thresholdDistance = pow(m_radius, 2); } if (m_sketchProperties.simpleMode) { // update the radius according scale in simple mode thresholdDistance = pow(m_radius * scale, 2); } // determine density const qreal density = thresholdDistance * currentProbability; // probability behaviour qreal probability = 1.0 - currentProbability; QColor painterColor = painter()->paintColor().toQColor(); QColor randomColor; KoColor color(m_dab->colorSpace()); int w = m_maskDab->bounds().width(); quint8 opacityU8 = 0; quint8 * pixel; qreal distance; QPoint positionInMask; QPointF diff; int size = m_points.size(); // MAIN LOOP for (int i = 0; i < size; i++) { diff = m_points.at(i) - mousePosition; distance = diff.x() * diff.x() + diff.y() * diff.y(); // circle test bool makeConnection = false; if (m_sketchProperties.simpleMode) { if (distance < thresholdDistance) { makeConnection = true; } // mask test } else { if (m_brushBoundingBox.contains(m_points.at(i))) { positionInMask = (diff + m_hotSpot).toPoint(); uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize()); if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) { pixel = m_maskDab->data() + pos; opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel); if (opacityU8 != 0) { makeConnection = true; } } } } if (!makeConnection) { // check next point continue; } if (m_sketchProperties.distanceDensity) { probability = distance / density; } KisRandomSourceSP randomSource = pi2.randomSource(); // density check if (randomSource->generateNormalized() >= probability) { QPointF offsetPt = diff * currentOffsetScale; if (m_sketchProperties.randomRGB) { /** * Since the order of calculation of function * parameters is not defined by C++ standard, we * should generate values in an external code snippet * which has a definite order of execution. */ qreal r1 = randomSource->generateNormalized(); qreal r2 = randomSource->generateNormalized(); qreal r3 = randomSource->generateNormalized(); // some color transformation per line goes here randomColor.setRgbF(r1 * painterColor.redF(), r2 * painterColor.greenF(), r3 * painterColor.blueF()); color.fromQColor(randomColor); m_painter->setPaintColor(color); } // distance based opacity quint8 opacity = OPACITY_OPAQUE_U8; if (m_sketchProperties.distanceOpacity) { opacity *= qRound((1.0 - (distance / thresholdDistance))); } if (m_sketchProperties.randomOpacity) { opacity *= randomSource->generateNormalized(); } m_painter->setOpacity(opacity); if (m_sketchProperties.magnetify) { drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth); } else { drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth); } } }// end of MAIN LOOP m_count++; QRect rc = m_dab->extent(); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } KisSpacingInformation KisSketchPaintOp::paintAt(const KisPaintInformation& info) { doPaintLine(info, info); return updateSpacingImpl(info); } KisSpacingInformation KisSketchPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveSpacing(0.0, 0.0, true, 0.0, false, 0.0, false, 0.0, KisLodTransform::lodToScale(painter()->device()), &m_airbrushOption, nullptr, info); } KisTimingInformation KisSketchPaintOp::updateTimingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } diff --git a/plugins/paintops/sketch/kis_sketch_paintop.h b/plugins/paintops/sketch/kis_sketch_paintop.h index f760983b95..1cd9644b72 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.h +++ b/plugins/paintops/sketch/kis_sketch_paintop.h @@ -1,94 +1,96 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SKETCH_PAINTOP_H_ #define KIS_SKETCH_PAINTOP_H_ #include #include #include "kis_density_option.h" #include "kis_sketchop_option.h" #include "kis_sketch_paintop_settings.h" #include "kis_painter.h" #include #include #include #include #include #include "kis_linewidth_option.h" #include "kis_offset_scale_option.h" class KisDabCache; class KisSketchPaintOp : public KisPaintOp { public: KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image); ~KisSketchPaintOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; + static QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface); + protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: // pixel buffer KisPaintDeviceSP m_dab; // mask detection area KisFixedPaintDeviceSP m_maskDab; QRectF m_brushBoundingBox; QPointF m_hotSpot; // simple mode qreal m_radius; KisPressureOpacityOption m_opacityOption; KisPressureSizeOption m_sizeOption; KisPressureRotationOption m_rotationOption; KisPressureRateOption m_rateOption; KisDensityOption m_densityOption; KisLineWidthOption m_lineWidthOption; KisOffsetScaleOption m_offsetScaleOption; KisAirbrushOptionProperties m_airbrushOption; KisBrushOptionProperties m_brushOption; SketchProperties m_sketchProperties; QVector m_points; int m_count; KisPainter * m_painter; KisBrushSP m_brush; KisDabCache *m_dabCache; private: void drawConnection(const QPointF &start, const QPointF &end, double lineWidth); void updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation); void doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2); }; #endif // KIS_SKETCH_PAINTOP_H_ diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp b/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp index 848889c00d..b183c3cec6 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp @@ -1,62 +1,63 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_sketch_paintop_settings.h" #include #include #include "kis_current_outline_fetcher.h" -KisSketchPaintOpSettings::KisSketchPaintOpSettings() +KisSketchPaintOpSettings::KisSketchPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) + : KisBrushBasedPaintOpSettings(resourcesInterface) { } bool KisSketchPaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } QPainterPath KisSketchPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { bool isSimpleMode = getBool(SKETCH_USE_SIMPLE_MODE); if (!isSimpleMode) { return KisBrushBasedPaintOpSettings::brushOutline(info, mode, alignForZoom); } QPainterPath path; KisBrushSP brush = this->brush(); if (brush && mode.isVisible) { // just circle supported qreal diameter = qMax(brush->width(), brush->height()); path = ellipseOutline(diameter, diameter, 1.0, 0.0); path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom); if (mode.showTiltDecoration) { QPainterPath tiltLine = makeTiltIndicator(info, path.boundingRect().center(), diameter * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, path.boundingRect().center().x(), path.boundingRect().center().y())); } } return path; } diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings.h b/plugins/paintops/sketch/kis_sketch_paintop_settings.h index 1ad2541caa..9b7d5b2a63 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop_settings.h +++ b/plugins/paintops/sketch/kis_sketch_paintop_settings.h @@ -1,44 +1,44 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SKETCH_PAINTOP_SETTINGS_H_ #define KIS_SKETCH_PAINTOP_SETTINGS_H_ #include #include #include "kis_sketch_paintop_settings_widget.h" #include class KisSketchPaintOpSettings : public KisBrushBasedPaintOpSettings { public: - KisSketchPaintOpSettings(); + KisSketchPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisSketchPaintOpSettings() override {} QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; bool paintIncremental() override; }; typedef KisSharedPtr KisSketchPaintOpSettingsSP; #endif diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp b/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp index a739832dd7..3ca9032759 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp @@ -1,88 +1,89 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_sketch_paintop_settings_widget.h" #include "kis_sketchop_option.h" #include "kis_sketch_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_density_option.h" #include "kis_linewidth_option.h" #include "kis_offset_scale_option.h" +#include KisSketchPaintOpSettingsWidget::KisSketchPaintOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { m_sketchOption = new KisSketchOpOption(); addPaintOpOption(m_sketchOption, i18n("Brush size")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisCurveOptionWidget(new KisLineWidthOption() , i18n("0%"), i18n("100%")), i18n("Line width")); addPaintOpOption(new KisCurveOptionWidget(new KisOffsetScaleOption(), i18n("0%"), i18n("100%")), i18n("Offset scale")); addPaintOpOption(new KisCurveOptionWidget(new KisDensityOption(), i18n("0%"), i18n("100%")), i18n("Density")); addPaintOpOption(new KisAirbrushOptionWidget(false, false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); m_paintActionType = new KisPaintActionTypeOption(); KisPropertiesConfigurationSP defaultSetting = new KisPropertiesConfiguration(); defaultSetting->setProperty("PaintOpAction", BUILDUP); m_paintActionType->readOptionSetting(defaultSetting); addPaintOpOption(m_paintActionType, i18n("Painting Mode")); KisPropertiesConfigurationSP reconfigurationCourier = configuration(); QDomDocument xMLAnalyzer; xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition")); QDomElement firstTag = xMLAnalyzer.documentElement(); QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement(); firstTagsChild.attributeNode("diameter").setValue("128"); reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString()); setConfiguration(reconfigurationCourier); } KisSketchPaintOpSettingsWidget::~ KisSketchPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisSketchPaintOpSettingsWidget::configuration() const { - KisSketchPaintOpSettingsSP config = new KisSketchPaintOpSettings(); + KisSketchPaintOpSettingsSP config = new KisSketchPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "sketchbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/spray/kis_spray_paintop.cpp b/plugins/paintops/spray/kis_spray_paintop.cpp index ff83355007..e6fc14b06e 100644 --- a/plugins/paintops/spray/kis_spray_paintop.cpp +++ b/plugins/paintops/spray/kis_spray_paintop.cpp @@ -1,153 +1,159 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_spray_paintop.h" #include "kis_spray_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSprayPaintOp::KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) , m_isPresetValid(true) , m_node(node) { Q_ASSERT(settings); Q_ASSERT(painter); Q_UNUSED(image); m_airbrushOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rateOption.resetAllSensors(); - m_brushOption.readOptionSetting(settings); + m_brushOption.readOptionSetting(settings, settings->resourcesInterface()); m_colorProperties.fillProperties(settings); m_properties.readOptionSetting(settings); // first load tip properties as shape properties are dependent on diameter/scale/aspect m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale); // TODO: what to do with proportional sizes? m_shapeDynamicsProperties.loadSettings(settings); if (!m_shapeProperties.enabled && !m_brushOption.brush()) { // in case the preset does not contain the definition for KisBrush m_isPresetValid = false; dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset."; } m_sprayBrush.setProperties(&m_properties, &m_colorProperties, &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush()); m_sprayBrush.setFixedDab(cachedDab()); // spacing if ((m_properties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisSprayPaintOp::~KisSprayPaintOp() { } +QList KisSprayPaintOp::prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) +{ + KisBrushOptionProperties brushOption; + return brushOption.prepareResources(settings, resourcesInterface); +} + KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info) { if (!painter() || !m_isPresetValid) { return KisSpacingInformation(m_spacing); } if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } qreal rotation = m_rotationOption.apply(info); quint8 origOpacity = m_opacityOption.apply(painter(), info); // Spray Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed const qreal scale = m_sizeOption.apply(info); const qreal lodScale = KisLodTransform::lodToScale(painter()->device()); m_sprayBrush.paint(m_dab, m_node->paintDevice(), info, rotation, scale, lodScale, painter()->paintColor(), painter()->backgroundColor()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); return computeSpacing(info, lodScale); } KisSpacingInformation KisSprayPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return computeSpacing(info, KisLodTransform::lodToScale(painter()->device())); } KisTimingInformation KisSprayPaintOp::updateTimingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } KisSpacingInformation KisSprayPaintOp::computeSpacing(const KisPaintInformation &info, qreal lodScale) const { return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false, m_spacing * lodScale, false, 1.0, lodScale, &m_airbrushOption, nullptr, info); } diff --git a/plugins/paintops/spray/kis_spray_paintop.h b/plugins/paintops/spray/kis_spray_paintop.h index 600f6bcf48..6bc10b6bf9 100644 --- a/plugins/paintops/spray/kis_spray_paintop.h +++ b/plugins/paintops/spray/kis_spray_paintop.h @@ -1,75 +1,77 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SPRAY_PAINTOP_H_ #define KIS_SPRAY_PAINTOP_H_ #include #include #include "spray_brush.h" #include "kis_spray_paintop_settings.h" #include "kis_brush_option.h" #include #include #include #include #include class KisPainter; class KisSprayPaintOp : public KisPaintOp { public: KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisSprayPaintOp() override; + static QList prepareResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface); + protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: KisSpacingInformation computeSpacing(const KisPaintInformation &info, qreal lodScale) const; private: KisShapeProperties m_shapeProperties; KisSprayOptionProperties m_properties; KisShapeDynamicsProperties m_shapeDynamicsProperties; KisColorProperties m_colorProperties; KisBrushOptionProperties m_brushOption; KisPaintDeviceSP m_dab; SprayBrush m_sprayBrush; qreal m_xSpacing, m_ySpacing, m_spacing; bool m_isPresetValid; KisAirbrushOptionProperties m_airbrushOption; KisPressureRotationOption m_rotationOption; KisPressureSizeOption m_sizeOption; KisPressureOpacityOption m_opacityOption; KisPressureRateOption m_rateOption; KisNodeSP m_node; }; #endif // KIS_SPRAY_PAINTOP_H_ diff --git a/plugins/paintops/spray/kis_spray_paintop_settings.cpp b/plugins/paintops/spray/kis_spray_paintop_settings.cpp index 0069aa3978..e6cecadae2 100644 --- a/plugins/paintops/spray/kis_spray_paintop_settings.cpp +++ b/plugins/paintops/spray/kis_spray_paintop_settings.cpp @@ -1,218 +1,219 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_spray_paintop_settings.h" #include "kis_sprayop_option.h" #include "kis_spray_shape_option.h" #include struct KisSprayPaintOpSettings::Private { QList uniformProperties; }; -KisSprayPaintOpSettings::KisSprayPaintOpSettings() +KisSprayPaintOpSettings::KisSprayPaintOpSettings(KisResourcesInterfaceSP resourcesInterface) : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | - KisCurrentOutlineFetcher::ROTATION_OPTION), + KisCurrentOutlineFetcher::ROTATION_OPTION, + resourcesInterface), m_d(new Private) { } KisSprayPaintOpSettings::~KisSprayPaintOpSettings() { } void KisSprayPaintOpSettings::setPaintOpSize(qreal value) { KisSprayOptionProperties option; option.readOptionSetting(this); option.diameter = value; option.writeOptionSetting(this); } qreal KisSprayPaintOpSettings::paintOpSize() const { KisSprayOptionProperties option; option.readOptionSetting(this); return option.diameter; } bool KisSprayPaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } QPainterPath KisSprayPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) { QPainterPath path; if (mode.isVisible) { qreal width = getInt(SPRAY_DIAMETER); qreal height = getInt(SPRAY_DIAMETER) * getDouble(SPRAY_ASPECT); path = ellipseOutline(width, height, getDouble(SPRAY_SCALE), getDouble(SPRAY_ROTATION)); path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom); if (mode.forceFullSize) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisSprayPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spacing", i18n("Spacing"), settings, 0); prop->setRange(0.01, 10); prop->setSingleStep(0.01); prop->setExponentRatio(3.0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.spacing); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.spacing = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "spray_particlecount", i18n("Particle Count"), settings, 0); prop->setRange(0, 1000); prop->setExponentRatio(3); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.particleCount)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.particleCount = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); return !option.useDensity; }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spray_density", i18n("Density"), settings, 0); prop->setRange(0.1, 100); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setExponentRatio(3); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.coverage); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.coverage = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); return option.useDensity; }); QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id() || prop->id() == size.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/spray/kis_spray_paintop_settings.h b/plugins/paintops/spray/kis_spray_paintop_settings.h index c9b0111aed..e034c105d9 100644 --- a/plugins/paintops/spray/kis_spray_paintop_settings.h +++ b/plugins/paintops/spray/kis_spray_paintop_settings.h @@ -1,62 +1,62 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 KIS_SPRAY_PAINTOP_SETTINGS_H_ #define KIS_SPRAY_PAINTOP_SETTINGS_H_ #include #include #include #include #include "kis_spray_paintop_settings_widget.h" class KisSprayPaintOpSettings : public KisOutlineGenerationPolicy { public: - KisSprayPaintOpSettings(); + KisSprayPaintOpSettings(KisResourcesInterfaceSP resourcesInterface); ~KisSprayPaintOpSettings() override; void setPaintOpSize(qreal value) override; qreal paintOpSize() const override; QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; QString modelName() const override { return "airbrush"; } bool paintIncremental() override; protected: QList uniformProperties(KisPaintOpSettingsSP settings) override; private: Q_DISABLE_COPY(KisSprayPaintOpSettings) struct Private; const QScopedPointer m_d; }; typedef KisSharedPtr KisSprayPaintOpSettingsSP; #endif diff --git a/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp b/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp index b5f345cf99..19b0470090 100644 --- a/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp +++ b/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp @@ -1,69 +1,70 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 "kis_spray_paintop_settings_widget.h" #include "kis_sprayop_option.h" #include "kis_spray_paintop_settings.h" #include "kis_spray_shape_option.h" #include #include #include #include #include #include #include #include #include #include "kis_spray_shape_dynamics.h" #include #include +#include KisSprayPaintOpSettingsWidget:: KisSprayPaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) , m_sprayArea(new KisSprayOpOption()) { addPaintOpOption(m_sprayArea, i18n("Spray Area")); addPaintOpOption(new KisSprayShapeOption(), i18n("Spray shape")); addPaintOpOption(new KisBrushOptionWidget(), i18n("Brush Tip")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisSprayShapeDynamicsOption(), i18n("Shape dynamics")); addPaintOpOption(new KisColorOption(), i18n("Color options")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); } KisSprayPaintOpSettingsWidget::~ KisSprayPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisSprayPaintOpSettingsWidget::configuration() const { - KisSprayPaintOpSettings* config = new KisSprayPaintOpSettings(); + KisSprayPaintOpSettings* config = new KisSprayPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "spraybrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp index dd181346ed..6dd556bd1b 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp +++ b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp @@ -1,84 +1,84 @@ /* * Copyright (C) 2015 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 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 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 "kis_tangent_normal_paintop_settings_widget.h" #include "kis_brush_based_paintop_settings.h" #include "kis_tangent_tilt_option.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" - +#include KisTangentNormalPaintOpSettingsWidget::KisTangentNormalPaintOpSettingsWidget(QWidget* parent): KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureFlowOption(), i18n("0%"), i18n("100%")), i18n("Flow")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisTangentTiltOption(), i18n("Tangent Tilt")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSoftnessOption(), i18n("Soft"), i18n("Hard")), i18n("Softness")); addPaintOpOption(new KisPressureSharpnessOptionWidget(), i18n("Sharpness")); addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); } KisTangentNormalPaintOpSettingsWidget::~KisTangentNormalPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisTangentNormalPaintOpSettingsWidget::configuration() const { - KisBrushBasedPaintOpSettingsSP config = new KisBrushBasedPaintOpSettings(); + KisBrushBasedPaintOpSettingsSP config = new KisBrushBasedPaintOpSettings(KisGlobalResourcesInterface::instance()); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "tangentnormal"); writeConfiguration(config); return config; }