diff --git a/libs/brush/kis_brush.cpp b/libs/brush/kis_brush.cpp index f969c80e10..6034ae4f84 100644 --- a/libs/brush/kis_brush.cpp +++ b/libs/brush/kis_brush.cpp @@ -1,648 +1,651 @@ /* * 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 #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() : brushType(INVALID) + , width(0) + , height(0) + , spacing (1.0) , hasColor(false) , preserveLightness(false) , angle(0) , scale(1.0) , autoSpacingActive(false) , autoSpacingCoeff(1.0) , threadingAllowed(true) {} Private(const Private &rhs) : brushType(rhs.brushType), width(rhs.width), height(rhs.height), spacing(rhs.spacing), hotSpot(rhs.hotSpot), hasColor(rhs.hasColor), preserveLightness(rhs.preserveLightness), angle(rhs.angle), scale(rhs.scale), autoSpacingActive(rhs.autoSpacingActive), autoSpacingCoeff(rhs.autoSpacingCoeff), threadingAllowed(rhs.threadingAllowed), brushTipImage(rhs.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! */ brushPyramid(rhs.brushPyramid) { // don't copy the boundary, it will be regenerated -- see bug 291910 } ~Private() { } mutable QScopedPointer boundary; enumBrushType brushType; qint32 width; qint32 height; double spacing; QPointF hotSpot; bool hasColor; bool preserveLightness; qreal angle; qreal scale; bool autoSpacingActive; qreal autoSpacingCoeff; bool threadingAllowed; QImage brushTipImage; mutable QSharedPointer brushPyramid; }; 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(*rhs.d)) { } KisBrush::~KisBrush() { delete d; } QImage KisBrush::brushTipImage() const { KIS_SAFE_ASSERT_RECOVER_NOOP(!d->brushTipImage.isNull()); 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::preserveLightness() const { return d->preserveLightness; } void KisBrush::setPreserveLightness(bool preserveLightness) { if (d->preserveLightness != preserveLightness) { d->preserveLightness = preserveLightness; clearBrushPyramid(); } } 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())); e.setAttribute("preserveLightness", QString::number((int)preserveLightness())); } void KisBrush::toXML(QDomDocument& /*document*/ , QDomElement& element) const { element.setAttribute("BrushVersion", "2"); } KisBrushSP KisBrush::fromXML(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface) { 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); } namespace { void fetchPremultipliedRed(const QRgb* src, quint8 *dst, int maskWidth) { for (int x = 0; x < maskWidth; x++) { *dst = KoColorSpaceMaths::multiply(255 - *src, qAlpha(*src)); src++; dst++; } } } 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(); KIS_SAFE_ASSERT_RECOVER_RETURN(coloringInformation); quint8* color = 0; if (dynamic_cast(coloringInformation)) { color = const_cast(coloringInformation->color()); } const KoColorSpace *cs = dst->colorSpace(); const quint32 pixelSize = cs->pixelSize(); quint8 *rowPointer = dst->data(); const bool preserveLightness = this->preserveLightness(); for (int y = 0; y < maskHeight; y++) { const quint8* maskPointer = outputImage.constScanLine(y); if (color) { if (preserveLightness) { cs->fillGrayBrushWithColorAndLightnessOverlay(rowPointer, reinterpret_cast(maskPointer), color, maskWidth); } else { cs->fillGrayBrushWithColor(rowPointer, reinterpret_cast(maskPointer), color, maskWidth); } } else { { quint8 *dst = rowPointer; for (int x = 0; x < maskWidth; x++) { memcpy(dst, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); dst += pixelSize; } } QScopedArrayPointer alphaArray(new quint8[maskWidth]); fetchPremultipliedRed(reinterpret_cast(maskPointer), alphaArray.data(), maskWidth); cs->applyAlphaU8Mask(rowPointer, alphaArray.data(), maskWidth); } rowPointer += maskWidth * pixelSize; if (!color) { coloringInformation->nextRow(); } } } 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() { d->boundary.reset(); } 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.reset(new KisBoundary(dev)); d->boundary->generateBoundary(); } const KisBoundary* KisBrush::boundary() const { if (!d->boundary) generateBoundary(); return d->boundary.data(); } 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/image/kis_distance_information.cpp b/libs/image/kis_distance_information.cpp index f8633ab396..5f0b134ec0 100644 --- a/libs/image/kis_distance_information.cpp +++ b/libs/image/kis_distance_information.cpp @@ -1,635 +1,636 @@ /* * Copyright (c) 2010 Cyrille Berger * 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 #include #include "kis_spacing_information.h" #include "kis_timing_information.h" #include "kis_debug.h" #include #include #include #include "kis_algebra_2d.h" #include "kis_dom_utils.h" #include "kis_lod_transform.h" const qreal MIN_DISTANCE_SPACING = 0.5; // Smallest allowed interval when timed spacing is enabled, in milliseconds. const qreal MIN_TIMED_INTERVAL = 0.5; // Largest allowed interval when timed spacing is enabled, in milliseconds. const qreal MAX_TIMED_INTERVAL = LONG_TIME; struct Q_DECL_HIDDEN KisDistanceInformation::Private { Private() : accumDistance(), accumTime(0.0), spacingUpdateInterval(LONG_TIME), timeSinceSpacingUpdate(0.0), timingUpdateInterval(LONG_TIME), timeSinceTimingUpdate(0.0), + lastAngle(0.0), lastDabInfoValid(false), lastPaintInfoValid(false), totalDistance(0.0), currentDabSeqNo(0), levelOfDetail(0) { } // Accumulators of time/distance passed since the last painted dab QPointF accumDistance; qreal accumTime; KisSpacingInformation spacing; qreal spacingUpdateInterval; // Accumulator of time passed since the last spacing update qreal timeSinceSpacingUpdate; KisTimingInformation timing; qreal timingUpdateInterval; // Accumulator of time passed since the last timing update qreal timeSinceTimingUpdate; // Information about the last position considered (not necessarily a painted dab) QPointF lastPosition; qreal lastAngle; bool lastDabInfoValid; // Information about the last painted dab KisPaintInformation lastPaintInformation; bool lastPaintInfoValid; qreal totalDistance; boost::optional lockedDrawingAngleOptional; int currentDabSeqNo; int levelOfDetail; }; struct Q_DECL_HIDDEN KisDistanceInitInfo::Private { Private() : hasLastInfo(false), lastPosition(), lastAngle(0.0), spacingUpdateInterval(LONG_TIME), timingUpdateInterval(LONG_TIME), currentDabSeqNo(0) { } // Indicates whether lastPosition, and lastAngle are valid or not. bool hasLastInfo; QPointF lastPosition; qreal lastAngle; qreal spacingUpdateInterval; qreal timingUpdateInterval; int currentDabSeqNo; }; KisDistanceInitInfo::KisDistanceInitInfo() : m_d(new Private) { } KisDistanceInitInfo::KisDistanceInitInfo(qreal spacingUpdateInterval, qreal timingUpdateInterval, int currentDabSeqNo) : m_d(new Private) { m_d->spacingUpdateInterval = spacingUpdateInterval; m_d->timingUpdateInterval = timingUpdateInterval; m_d->currentDabSeqNo = currentDabSeqNo; } KisDistanceInitInfo::KisDistanceInitInfo(const QPointF &lastPosition, qreal lastAngle, int currentDabSeqNo) : m_d(new Private) { m_d->hasLastInfo = true; m_d->lastPosition = lastPosition; m_d->lastAngle = lastAngle; m_d->currentDabSeqNo = currentDabSeqNo; } KisDistanceInitInfo::KisDistanceInitInfo(const QPointF &lastPosition, qreal lastAngle, qreal spacingUpdateInterval, qreal timingUpdateInterval, int currentDabSeqNo) : m_d(new Private) { m_d->hasLastInfo = true; m_d->lastPosition = lastPosition; m_d->lastAngle = lastAngle; m_d->spacingUpdateInterval = spacingUpdateInterval; m_d->timingUpdateInterval = timingUpdateInterval; m_d->currentDabSeqNo = currentDabSeqNo; } KisDistanceInitInfo::KisDistanceInitInfo(const KisDistanceInitInfo &rhs) : m_d(new Private(*rhs.m_d)) { } KisDistanceInitInfo::~KisDistanceInitInfo() { delete m_d; } bool KisDistanceInitInfo::operator==(const KisDistanceInitInfo &other) const { if (m_d->spacingUpdateInterval != other.m_d->spacingUpdateInterval || m_d->timingUpdateInterval != other.m_d->timingUpdateInterval || m_d->hasLastInfo != other.m_d->hasLastInfo) { return false; } if (m_d->hasLastInfo) { if (m_d->lastPosition != other.m_d->lastPosition || m_d->lastAngle != other.m_d->lastAngle) { return false; } } if (m_d->currentDabSeqNo != other.m_d->currentDabSeqNo) { return false; } return true; } bool KisDistanceInitInfo::operator!=(const KisDistanceInitInfo &other) const { return !(*this == other); } KisDistanceInitInfo &KisDistanceInitInfo::operator=(const KisDistanceInitInfo &rhs) { *m_d = *rhs.m_d; return *this; } KisDistanceInformation KisDistanceInitInfo::makeDistInfo() { if (m_d->hasLastInfo) { return KisDistanceInformation(m_d->lastPosition, m_d->lastAngle, m_d->spacingUpdateInterval, m_d->timingUpdateInterval, m_d->currentDabSeqNo); } else { return KisDistanceInformation(m_d->spacingUpdateInterval, m_d->timingUpdateInterval, m_d->currentDabSeqNo); } } void KisDistanceInitInfo::toXML(QDomDocument &doc, QDomElement &elt) const { elt.setAttribute("spacingUpdateInterval", QString::number(m_d->spacingUpdateInterval, 'g', 15)); elt.setAttribute("timingUpdateInterval", QString::number(m_d->timingUpdateInterval, 'g', 15)); elt.setAttribute("currentDabSeqNo", QString::number(m_d->currentDabSeqNo)); if (m_d->hasLastInfo) { QDomElement lastInfoElt = doc.createElement("LastInfo"); lastInfoElt.setAttribute("lastPosX", QString::number(m_d->lastPosition.x(), 'g', 15)); lastInfoElt.setAttribute("lastPosY", QString::number(m_d->lastPosition.y(), 'g', 15)); lastInfoElt.setAttribute("lastAngle", QString::number(m_d->lastAngle, 'g', 15)); elt.appendChild(lastInfoElt); } } KisDistanceInitInfo KisDistanceInitInfo::fromXML(const QDomElement &elt) { const qreal spacingUpdateInterval = qreal(KisDomUtils::toDouble(elt.attribute("spacingUpdateInterval", QString::number(LONG_TIME, 'g', 15)))); const qreal timingUpdateInterval = qreal(KisDomUtils::toDouble(elt.attribute("timingUpdateInterval", QString::number(LONG_TIME, 'g', 15)))); const qreal currentDabSeqNo = KisDomUtils::toInt(elt.attribute("currentDabSeqNo", "0")); const QDomElement lastInfoElt = elt.firstChildElement("LastInfo"); const bool hasLastInfo = !lastInfoElt.isNull(); if (hasLastInfo) { const qreal lastPosX = qreal(KisDomUtils::toDouble(lastInfoElt.attribute("lastPosX", "0.0"))); const qreal lastPosY = qreal(KisDomUtils::toDouble(lastInfoElt.attribute("lastPosY", "0.0"))); const qreal lastAngle = qreal(KisDomUtils::toDouble(lastInfoElt.attribute("lastAngle", "0.0"))); return KisDistanceInitInfo(QPointF(lastPosX, lastPosY), lastAngle, spacingUpdateInterval, timingUpdateInterval, currentDabSeqNo); } else { return KisDistanceInitInfo(spacingUpdateInterval, timingUpdateInterval, currentDabSeqNo); } } KisDistanceInformation::KisDistanceInformation() : m_d(new Private) { } KisDistanceInformation::KisDistanceInformation(qreal spacingUpdateInterval, qreal timingUpdateInterval, int currentDabSeqNo) : m_d(new Private) { m_d->spacingUpdateInterval = spacingUpdateInterval; m_d->timingUpdateInterval = timingUpdateInterval; m_d->currentDabSeqNo = currentDabSeqNo; } KisDistanceInformation::KisDistanceInformation(const QPointF &lastPosition, qreal lastAngle) : m_d(new Private) { m_d->lastPosition = lastPosition; m_d->lastAngle = lastAngle; m_d->lastDabInfoValid = true; } KisDistanceInformation::KisDistanceInformation(const QPointF &lastPosition, qreal lastAngle, qreal spacingUpdateInterval, qreal timingUpdateInterval, int currentDabSeqNo) : KisDistanceInformation(lastPosition, lastAngle) { m_d->spacingUpdateInterval = spacingUpdateInterval; m_d->timingUpdateInterval = timingUpdateInterval; m_d->currentDabSeqNo = currentDabSeqNo; } KisDistanceInformation::KisDistanceInformation(const KisDistanceInformation &rhs) : m_d(new Private(*rhs.m_d)) { } KisDistanceInformation::KisDistanceInformation(const KisDistanceInformation &rhs, int levelOfDetail) : m_d(new Private(*rhs.m_d)) { KIS_ASSERT_RECOVER_NOOP(!m_d->lastPaintInfoValid && "The distance information " "should be cloned before the " "actual painting is started"); m_d->levelOfDetail = levelOfDetail; KisLodTransform t(levelOfDetail); m_d->lastPosition = t.map(m_d->lastPosition); } KisDistanceInformation& KisDistanceInformation::operator=(const KisDistanceInformation &rhs) { *m_d = *rhs.m_d; return *this; } void KisDistanceInformation::overrideLastValues(const QPointF &lastPosition, qreal lastAngle) { m_d->lastPosition = lastPosition; m_d->lastAngle = lastAngle; m_d->lastDabInfoValid = true; } KisDistanceInformation::~KisDistanceInformation() { delete m_d; } const KisSpacingInformation& KisDistanceInformation::currentSpacing() const { return m_d->spacing; } void KisDistanceInformation::updateSpacing(const KisSpacingInformation &spacing) { m_d->spacing = spacing; m_d->timeSinceSpacingUpdate = 0.0; } bool KisDistanceInformation::needsSpacingUpdate() const { return m_d->timeSinceSpacingUpdate >= m_d->spacingUpdateInterval; } const KisTimingInformation &KisDistanceInformation::currentTiming() const { return m_d->timing; } void KisDistanceInformation::updateTiming(const KisTimingInformation &timing) { m_d->timing = timing; m_d->timeSinceTimingUpdate = 0.0; } bool KisDistanceInformation::needsTimingUpdate() const { return m_d->timeSinceTimingUpdate >= m_d->timingUpdateInterval; } bool KisDistanceInformation::hasLastDabInformation() const { return m_d->lastDabInfoValid; } QPointF KisDistanceInformation::lastPosition() const { return m_d->lastPosition; } qreal KisDistanceInformation::lastDrawingAngle() const { return m_d->lastAngle; } bool KisDistanceInformation::hasLastPaintInformation() const { return m_d->lastPaintInfoValid; } const KisPaintInformation& KisDistanceInformation::lastPaintInformation() const { return m_d->lastPaintInformation; } int KisDistanceInformation::currentDabSeqNo() const { return m_d->currentDabSeqNo; } bool KisDistanceInformation::isStarted() const { return m_d->lastPaintInfoValid; } void KisDistanceInformation::registerPaintedDab(const KisPaintInformation &info, const KisSpacingInformation &spacing, const KisTimingInformation &timing) { m_d->totalDistance += KisAlgebra2D::norm(info.pos() - m_d->lastPosition) * KisLodTransform::lodToInvScale(m_d->levelOfDetail); m_d->lastPaintInformation = info; m_d->lastPaintInfoValid = true; m_d->lastAngle = info.drawingAngle(false); m_d->lastPosition = info.pos(); m_d->lastDabInfoValid = true; m_d->spacing = spacing; m_d->timing = timing; m_d->currentDabSeqNo++; } qreal KisDistanceInformation::getNextPointPosition(const QPointF &start, const QPointF &end, qreal startTime, qreal endTime) { // Compute interpolation factor based on distance. qreal distanceFactor = -1.0; if (m_d->spacing.isDistanceSpacingEnabled()) { distanceFactor = m_d->spacing.isIsotropic() ? getNextPointPositionIsotropic(start, end) : getNextPointPositionAnisotropic(start, end); } // Compute interpolation factor based on time. qreal timeFactor = -1.0; if (m_d->timing.isTimedSpacingEnabled()) { timeFactor = getNextPointPositionTimed(startTime, endTime); } // Return the distance-based or time-based factor, whichever is smallest. qreal t = -1.0; if (distanceFactor < 0.0) { t = timeFactor; } else if (timeFactor < 0.0) { t = distanceFactor; } else { t = qMin(distanceFactor, timeFactor); } // If we aren't ready to paint a dab, accumulate time for the spacing/timing updates that might // be needed between dabs. if (t < 0.0) { m_d->timeSinceSpacingUpdate += endTime - startTime; m_d->timeSinceTimingUpdate += endTime - startTime; } // If we are ready to paint a dab, reset the accumulated time for spacing/timing updates. else { m_d->timeSinceSpacingUpdate = 0.0; m_d->timeSinceTimingUpdate = 0.0; } return t; } qreal KisDistanceInformation::getSpacingInterval() const { return m_d->spacingUpdateInterval; } qreal KisDistanceInformation::getTimingUpdateInterval() const { return m_d->timingUpdateInterval; } qreal KisDistanceInformation::getNextPointPositionIsotropic(const QPointF &start, const QPointF &end) { qreal distance = m_d->accumDistance.x(); qreal spacing = qMax(MIN_DISTANCE_SPACING, m_d->spacing.distanceSpacing().x()); if (start == end) { return -1; } qreal dragVecLength = QVector2D(end - start).length(); qreal nextPointDistance = spacing - distance; qreal t; // nextPointDistance can sometimes be negative if the spacing info has been modified since the // last interpolation attempt. In that case, have a point painted immediately. if (nextPointDistance <= 0.0) { resetAccumulators(); t = 0.0; } else if (nextPointDistance <= dragVecLength) { t = nextPointDistance / dragVecLength; resetAccumulators(); } else { t = -1; m_d->accumDistance.rx() += dragVecLength; } return t; } qreal KisDistanceInformation::getNextPointPositionAnisotropic(const QPointF &start, const QPointF &end) { if (start == end) { return -1; } qreal a_rev = 1.0 / qMax(MIN_DISTANCE_SPACING, m_d->spacing.distanceSpacing().x()); qreal b_rev = 1.0 / qMax(MIN_DISTANCE_SPACING, m_d->spacing.distanceSpacing().y()); qreal x = m_d->accumDistance.x(); qreal y = m_d->accumDistance.y(); qreal gamma = pow2(x * a_rev) + pow2(y * b_rev) - 1; // If the distance accumulator is already past the spacing ellipse, have a point painted // immediately. This can happen if the spacing info has been modified since the last // interpolation attempt. if (gamma >= 0.0) { resetAccumulators(); return 0.0; } static const qreal eps = 2e-3; // < 0.2 deg qreal currentRotation = m_d->spacing.rotation(); if (m_d->spacing.coordinateSystemFlipped()) { currentRotation = 2 * M_PI - currentRotation; } QPointF diff = end - start; if (currentRotation > eps) { QTransform rot; // since the ellipse is symmetrical, the sign // of rotation doesn't matter rot.rotateRadians(currentRotation); diff = rot.map(diff); } qreal dx = qAbs(diff.x()); qreal dy = qAbs(diff.y()); qreal alpha = pow2(dx * a_rev) + pow2(dy * b_rev); qreal beta = x * dx * a_rev * a_rev + y * dy * b_rev * b_rev; qreal D_4 = pow2(beta) - alpha * gamma; qreal t = -1.0; if (D_4 >= 0) { qreal k = (-beta + qSqrt(D_4)) / alpha; if (k >= 0.0 && k <= 1.0) { t = k; resetAccumulators(); } else { m_d->accumDistance += KisAlgebra2D::abs(diff); } } else { warnKrita << "BUG: No solution for elliptical spacing equation has been found. This shouldn't have happened."; } return t; } qreal KisDistanceInformation::getNextPointPositionTimed(qreal startTime, qreal endTime) { // If start time is not before end time, do not interpolate. if (!(startTime < endTime)) { return -1.0; } qreal timedSpacingInterval = qBound(MIN_TIMED_INTERVAL, m_d->timing.timedSpacingInterval(), MAX_TIMED_INTERVAL); qreal nextPointInterval = timedSpacingInterval - m_d->accumTime; qreal t = -1.0; // nextPointInterval can sometimes be negative if the spacing info has been modified since the // last interpolation attempt. In that case, have a point painted immediately. if (nextPointInterval <= 0.0) { resetAccumulators(); t = 0.0; } else if (nextPointInterval <= endTime - startTime) { resetAccumulators(); t = nextPointInterval / (endTime - startTime); } else { m_d->accumTime += endTime - startTime; t = -1.0; } return t; } void KisDistanceInformation::resetAccumulators() { m_d->accumDistance = QPointF(); m_d->accumTime = 0.0; } boost::optional KisDistanceInformation::lockedDrawingAngleOptional() const { return m_d->lockedDrawingAngleOptional; } void KisDistanceInformation::lockCurrentDrawingAngle(const KisPaintInformation &info) const { const qreal angle = info.drawingAngle(false); qreal newAngle = angle; if (m_d->lockedDrawingAngleOptional) { const qreal stabilizingCoeff = 20.0; const qreal dist = stabilizingCoeff * m_d->spacing.scalarApprox(); const qreal alpha = qMax(0.0, dist - scalarDistanceApprox()) / dist; const qreal oldAngle = *m_d->lockedDrawingAngleOptional; if (shortestAngularDistance(oldAngle, newAngle) < M_PI / 6) { newAngle = (1.0 - alpha) * oldAngle + alpha * newAngle; } else { newAngle = oldAngle; } } m_d->lockedDrawingAngleOptional = newAngle; } qreal KisDistanceInformation::scalarDistanceApprox() const { return m_d->totalDistance; } diff --git a/libs/image/kis_filter_strategy.h b/libs/image/kis_filter_strategy.h index 09699cfc07..e76a2e89c9 100644 --- a/libs/image/kis_filter_strategy.h +++ b/libs/image/kis_filter_strategy.h @@ -1,210 +1,210 @@ /* * Copyright (c) 2004 Michael Thaler * Copyright (c) 2005 C. Boemann * Copyright (c) 2013 Juan Palacios * * 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_FILTER_STRATEGY_H_ #define KIS_FILTER_STRATEGY_H_ #include #include "KoGenericRegistry.h" #include "KoID.h" #include "kritaimage_export.h" class KRITAIMAGE_EXPORT KisFilterStrategy { public: KisFilterStrategy(KoID id) : m_id(id) {} virtual ~KisFilterStrategy() { } QString id() { return m_id.id(); } QString name() { return m_id.name(); } virtual qreal valueAt(qreal t, qreal weightsPositionScale) const { Q_UNUSED(t); Q_UNUSED(weightsPositionScale); return 0; } virtual qint32 intValueAt(qint32 t, qreal weightsPositionScale) const { return qint32(255*valueAt(t / 256.0, weightsPositionScale)); } virtual qreal support(qreal weightsPositionScale) { Q_UNUSED(weightsPositionScale); return supportVal; } virtual qint32 intSupport(qreal weightsPositionScale) { Q_UNUSED(weightsPositionScale); return intSupportVal; } virtual QString description() { return QString(); } protected: - qreal supportVal; - qint32 intSupportVal; + qreal supportVal {0.0}; + qint32 intSupportVal {0}; KoID m_id; }; class KRITAIMAGE_EXPORT KisHermiteFilterStrategy : public KisFilterStrategy { public: KisHermiteFilterStrategy() : KisFilterStrategy(KoID("Hermite", i18n("Hermite"))) { supportVal = 1.0; intSupportVal = 256; } ~KisHermiteFilterStrategy() override {} qint32 intValueAt(qint32 t, qreal weightsPositionScale) const override; qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisBicubicFilterStrategy : public KisFilterStrategy { public: KisBicubicFilterStrategy() : KisFilterStrategy(KoID("Bicubic", i18n("Bicubic"))) { supportVal = 2.0; intSupportVal = 512; } ~KisBicubicFilterStrategy() override {} QString description() override { return i18n("Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear."); } qint32 intValueAt(qint32 t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisBoxFilterStrategy : public KisFilterStrategy { public: KisBoxFilterStrategy() : KisFilterStrategy(KoID("NearestNeighbor", i18n("Nearest Neighbor"))) { // 0.5 and 128, but with a bit of margin to ensure the correct pixel will be used // even in case of calculation errors supportVal = 0.51; intSupportVal = 129; } ~KisBoxFilterStrategy() override {} QString description() override { return i18n("Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects."); } virtual qreal support(qreal weightsPositionScale) override; virtual qint32 intSupport(qreal weightsPositionScale) override; qint32 intValueAt(qint32 t, qreal weightsPositionScale) const override; qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisBilinearFilterStrategy : public KisFilterStrategy { public: KisBilinearFilterStrategy() : KisFilterStrategy(KoID("Bilinear", i18n("Bilinear"))) { supportVal = 1.0; intSupportVal = 256; } ~KisBilinearFilterStrategy() override {} QString description() override { return i18n("Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size."); } qint32 intValueAt(qint32 t, qreal weightsPositionScale) const override; qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisBellFilterStrategy : public KisFilterStrategy { public: KisBellFilterStrategy() : KisFilterStrategy(KoID("Bell", i18n("Bell"))) { supportVal = 1.5; intSupportVal = 128 + 256; } ~KisBellFilterStrategy() override {} qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisBSplineFilterStrategy : public KisFilterStrategy { public: KisBSplineFilterStrategy() : KisFilterStrategy(KoID("BSpline", i18n("BSpline"))) { supportVal = 2.0; intSupportVal = 512; } ~KisBSplineFilterStrategy() override {} qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisLanczos3FilterStrategy : public KisFilterStrategy { public: KisLanczos3FilterStrategy() : KisFilterStrategy(KoID("Lanczos3", i18n("Lanczos3"))) { supportVal = 3.0; intSupportVal = 768; } ~KisLanczos3FilterStrategy() override {} QString description() override { return i18n("Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges."); } qreal valueAt(qreal t, qreal weightsPositionScale) const override; private: qreal sinc(qreal x) const; }; class KRITAIMAGE_EXPORT KisMitchellFilterStrategy : public KisFilterStrategy { public: KisMitchellFilterStrategy() : KisFilterStrategy(KoID("Mitchell", i18n("Mitchell"))) { supportVal = 2.0; intSupportVal = 256; } ~KisMitchellFilterStrategy() override {} qreal valueAt(qreal t, qreal weightsPositionScale) const override; }; class KRITAIMAGE_EXPORT KisFilterStrategyRegistry : public KoGenericRegistry { public: KisFilterStrategyRegistry(); ~KisFilterStrategyRegistry() override; static KisFilterStrategyRegistry* instance(); /** * 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; /** * This function return a string formatted in HTML that contains the descriptions of all objects * (with a non empty description) stored in the registry. */ QString formattedDescriptions() const; private: KisFilterStrategyRegistry(const KisFilterStrategyRegistry&); KisFilterStrategyRegistry operator=(const KisFilterStrategyRegistry&); }; #endif // KIS_FILTER_STRATEGY_H_ diff --git a/libs/image/kis_node_query_path.cc b/libs/image/kis_node_query_path.cc index 1804f2b0db..e56104b91e 100644 --- a/libs/image/kis_node_query_path.cc +++ b/libs/image/kis_node_query_path.cc @@ -1,225 +1,225 @@ /* * Copyright (c) 2009 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_node_query_path.h" #include #include #include #include struct PathElement { enum Type { Wildcard, Parent, Index }; PathElement(Type _type) : type(_type) { Q_ASSERT(type == Wildcard || type == Parent); } PathElement(int _i) : type(Index), index(_i) {} Type type; - unsigned int index; + unsigned int index {0}; }; struct Q_DECL_HIDDEN KisNodeQueryPath::Private { QList elements; bool relative; /// This function will remove unneeded call to parent, for instance, "1/../3/../5" => "5" void simplifyPath() { // No elements then return if (elements.isEmpty()) return; QList newelements; int i = 0; for (; i < elements.count() && elements[i].type == PathElement::Parent; ++i) { newelements.push_back(PathElement::Parent); } // Loop ofver the element of the list for (; i < elements.count(); ++i) { PathElement pe = elements[i]; // If it's the last element, or the next element isn't a parent if (pe.type != PathElement::Parent) { newelements.push_back(pe); } else { if (newelements.isEmpty() || newelements.last().type == PathElement::Parent) { newelements.push_back(PathElement::Parent); } else { newelements.removeLast(); } } } // Set the new list elements = newelements; } void queryLevel(int _level, KisNodeSP _node, QList& _result) { if (_level >= elements.size()) { _result.push_back(_node); } else { PathElement pe = elements[_level]; switch (pe.type) { case PathElement::Wildcard: { for (KisNodeSP child = _node->firstChild(); child != 0; child = child->nextSibling()) { queryLevel(_level + 1, child, _result); } } break; case PathElement::Parent: { if (_node->parent()) { queryLevel(_level + 1, _node->parent(), _result); } else { dbgKrita << "No parent"; } break; } case PathElement::Index: { if (pe.index < _node->childCount()) { queryLevel(_level + 1, _node->at(pe.index), _result); } else { dbgKrita << "No parent"; } break; } } } } }; KisNodeQueryPath::KisNodeQueryPath() : d(new Private) { } KisNodeQueryPath::~KisNodeQueryPath() { delete d; } KisNodeQueryPath::KisNodeQueryPath(const KisNodeQueryPath& nqp) : d(new Private(*nqp.d)) { } KisNodeQueryPath& KisNodeQueryPath::operator=(const KisNodeQueryPath & nqp) { *d = *nqp.d; return *this; } bool KisNodeQueryPath::isRelative() const { return d->relative; } QList KisNodeQueryPath::queryNodes(KisImageWSP image, KisNodeSP currentNode) const { KisNodeSP _node; if (d->relative) { _node = currentNode; } else { _node = image->root(); } QList result; d->queryLevel(0, _node, result); return result; } KisNodeSP KisNodeQueryPath::queryUniqueNode(KisImageWSP image, KisNodeSP currentNode) const { QList result = queryNodes(image, currentNode); KIS_ASSERT_RECOVER_NOOP(result.size() <= 1); return !result.isEmpty() ? result.first() : 0; } QString KisNodeQueryPath::toString() const { QString str; if (!d->relative) { str = '/'; } else if (d->elements.count() == 0) { return QString('.'); } for (int i = 0; i < d->elements.count(); ++i) { PathElement pe = d->elements[i]; switch (pe.type) { case PathElement::Wildcard: str += '*'; break; case PathElement::Parent: str += ".."; break; case PathElement::Index: str += QString::number(pe.index); break; } if (i != d->elements.count() - 1) { str += '/'; } } return str; } KisNodeQueryPath KisNodeQueryPath::fromString(const QString& _path) { KisNodeQueryPath path; if (_path.size() == 0 || _path == ".") { path.d->relative = true; return path; } if (_path == "/") { path.d->relative = false; return path; } path.d->relative = !(_path.at(0) == '/'); QStringList indexes = _path.split('/'); if (!path.d->relative) { indexes.pop_front(); // In case of an absolute path "/1/2", the list is "", "1", "2" which is not good } Q_FOREACH (const QString& index, indexes) { if (index == "*") { path.d->elements.push_back(PathElement::Wildcard); } else if (index == "..") { path.d->elements.push_back(PathElement::Parent); } else { path.d->elements.push_back(index.toInt()); } } path.d->simplifyPath(); return path; } KisNodeQueryPath KisNodeQueryPath::absolutePath(KisNodeSP node) { KisNodeQueryPath path; path.d->relative = false; KisNodeSP parent = 0; while ((parent = node->parent())) { int index = parent->index(node); if (index >= 0) { path.d->elements.push_front(index); } node = parent; } return path; } diff --git a/libs/pigment/KoColorConversionSystem_p.h b/libs/pigment/KoColorConversionSystem_p.h index df152013b8..6db16f0491 100644 --- a/libs/pigment/KoColorConversionSystem_p.h +++ b/libs/pigment/KoColorConversionSystem_p.h @@ -1,335 +1,335 @@ /* * Copyright (c) 2007-2008 Cyrille Berger * * 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; 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 KOCOLORCONVERSIONSYSTEM_P_H #define KOCOLORCONVERSIONSYSTEM_P_H #include "DebugPigment.h" #include "KoColorSpaceRegistry.h" #include "KoColorModelStandardIds.h" #include "KoColorConversionTransformationFactory.h" #include "KoColorSpaceEngine.h" #include struct KoColorConversionSystem::Node { Node() : isHdr(false) , isInitialized(false) , referenceDepth(0) , isGray(false) , crossingCost(1) , colorSpaceFactory(0) , isEngine(false) , engine(0) {} void init(const KoColorSpaceFactory* _colorSpaceFactory) { dbgPigment << "Initialise " << modelId << " " << depthId << " " << profileName; if (isInitialized) { dbgPigment << "Re-initializing node. Old factory" << colorSpaceFactory << "new factory" << _colorSpaceFactory; } isInitialized = true; if (_colorSpaceFactory) { isHdr = _colorSpaceFactory->isHdr(); colorSpaceFactory = _colorSpaceFactory; referenceDepth = _colorSpaceFactory->referenceDepth(); isGray = (_colorSpaceFactory->colorModelId() == GrayAColorModelID || _colorSpaceFactory->colorModelId() == GrayColorModelID || _colorSpaceFactory->colorModelId() == AlphaColorModelID); } } void init(const KoColorSpaceEngine* _engine) { Q_ASSERT(!isInitialized); isEngine = true; isInitialized = true; isHdr = true; engine = _engine; } QString id() const { return modelId + " " + depthId + " " + profileName; } QString modelId; QString depthId; QString profileName; bool isHdr; bool isInitialized; int referenceDepth; QList outputVertexes; bool isGray; int crossingCost; const KoColorSpaceFactory* colorSpaceFactory; bool isEngine; const KoColorSpaceEngine* engine; }; Q_DECLARE_TYPEINFO(KoColorConversionSystem::Node, Q_MOVABLE_TYPE); struct KoColorConversionSystem::Vertex { Vertex(Node* _srcNode, Node* _dstNode) : srcNode(_srcNode) , dstNode(_dstNode) , factoryFromSrc(0) , factoryFromDst(0) { } ~Vertex() { if (factoryFromSrc == factoryFromDst) { delete factoryFromSrc; } else { delete factoryFromSrc; delete factoryFromDst; } } void setFactoryFromSrc(KoColorConversionTransformationFactory* factory) { factoryFromSrc = factory; initParameter(factoryFromSrc); } void setFactoryFromDst(KoColorConversionTransformationFactory* factory) { factoryFromDst = factory; if (!factoryFromSrc) initParameter(factoryFromDst); } void initParameter(KoColorConversionTransformationFactory* transfo) { conserveColorInformation = transfo->conserveColorInformation(); conserveDynamicRange = transfo->conserveDynamicRange(); } KoColorConversionTransformationFactory* factory() { if (factoryFromSrc) return factoryFromSrc; return factoryFromDst; } Node* srcNode; Node* dstNode; - bool conserveColorInformation; - bool conserveDynamicRange; + bool conserveColorInformation {true}; + bool conserveDynamicRange {true}; private: KoColorConversionTransformationFactory* factoryFromSrc; // Factory provided by the destination node KoColorConversionTransformationFactory* factoryFromDst; // Factory provided by the destination node }; struct KoColorConversionSystem::NodeKey { NodeKey(const QString &_modelId, const QString &_depthId, const QString &_profileName) : modelId(_modelId) , depthId(_depthId) , profileName(_profileName) {} bool operator==(const KoColorConversionSystem::NodeKey& rhs) const { return modelId == rhs.modelId && depthId == rhs.depthId && profileName == rhs.profileName; } QString modelId; QString depthId; QString profileName; }; Q_DECLARE_TYPEINFO(KoColorConversionSystem::NodeKey, Q_MOVABLE_TYPE); struct KoColorConversionSystem::Path { Path() : respectColorCorrectness(true) , referenceDepth(0) , keepDynamicRange(true) , isGood(false) , cost(0) {} Node* startNode() { return vertexes.size() > 0 ? (vertexes.first())->srcNode : 0; } bool operator==(const Path &other) const { return other.vertexes == vertexes; } const Node* startNode() const { return vertexes.size() > 0 ? (vertexes.first())->srcNode : 0; } Node* endNode() { return vertexes.size() > 0 ? (vertexes.last())->dstNode : 0; } const Node* endNode() const { return vertexes.size() > 0 ? (vertexes.last())->dstNode : 0; } bool isEmpty() const { return vertexes.isEmpty(); } void appendVertex(Vertex* v) { if (vertexes.empty()) { referenceDepth = v->srcNode->referenceDepth; } vertexes.append(v); if (!v->conserveColorInformation) respectColorCorrectness = false; if (!v->conserveDynamicRange) keepDynamicRange = false; referenceDepth = qMin(referenceDepth, v->dstNode->referenceDepth); cost += v->dstNode->crossingCost; } // Compress path to hide the Engine node and correctly select the factory typedef QPair node2factory; QList< node2factory > compressedPath() const { QList< node2factory > nodes; nodes.push_back(node2factory(vertexes.first()->srcNode , vertexes.first()->factory())); const KoColorConversionTransformationAbstractFactory* previousFactory = 0; Q_FOREACH (Vertex* vertex, vertexes) { // Unless the node is the icc node, add it to the path Node* n = vertex->dstNode; if (n->isEngine) { previousFactory = n->engine; } else { nodes.push_back( node2factory(n, previousFactory ? previousFactory : vertex->factory())); previousFactory = 0; } } return nodes; } int length() const { return vertexes.size(); } bool contains(Node* n) const { Q_FOREACH (Vertex* v, vertexes) { if (v->srcNode == n || v->dstNode == n) { return true; } } return false; } QList vertexes; bool respectColorCorrectness; int referenceDepth; bool keepDynamicRange; bool isGood; int cost; }; Q_DECLARE_TYPEINFO(KoColorConversionSystem::Path, Q_MOVABLE_TYPE); inline QDebug operator<<(QDebug dbg, const KoColorConversionSystem::Path &path) { bool havePrintedFirst = false; Q_FOREACH (const KoColorConversionSystem::Vertex *v, path.vertexes) { if (!havePrintedFirst) { dbg.nospace() << v->srcNode->id(); havePrintedFirst = true; } dbg.nospace() << "->" << v->dstNode->id(); } return dbg.space(); } typedef QHash Node2PathHash; uint qHash(const KoColorConversionSystem::NodeKey &key) { return qHash(key.modelId) + qHash(key.depthId); } struct Q_DECL_HIDDEN KoColorConversionSystem::Private { Private(RegistryInterface *_registryInterface) : registryInterface(_registryInterface) {} QHash graph; QList vertexes; RegistryInterface *registryInterface; }; #define CHECK_ONE_AND_NOT_THE_OTHER(name) \ if(path1. name && !path2. name) \ { \ return true; \ } \ if(!path1. name && path2. name) \ { \ return false; \ } struct PathQualityChecker { PathQualityChecker(int _referenceDepth, bool _ignoreHdr, bool _ignoreColorCorrectness) : referenceDepth(_referenceDepth) , ignoreHdr(_ignoreHdr) , ignoreColorCorrectness(_ignoreColorCorrectness) {} /// @return true if the path maximize all the criteria (except length) inline bool isGoodPath(const KoColorConversionSystem::Path & path) const { return (path.respectColorCorrectness || ignoreColorCorrectness) && (path.referenceDepth >= referenceDepth) && (path.keepDynamicRange || ignoreHdr); } /** * Compare two paths. */ inline bool lessWorseThan(const KoColorConversionSystem::Path &path1, const KoColorConversionSystem::Path &path2) const { // There is no point in comparing two paths which doesn't start from the same node or doesn't end at the same node if (!ignoreHdr) { CHECK_ONE_AND_NOT_THE_OTHER(keepDynamicRange) } if (!ignoreColorCorrectness) { CHECK_ONE_AND_NOT_THE_OTHER(respectColorCorrectness) } if (path1.referenceDepth == path2.referenceDepth) { return path1.cost < path2.cost; // if they have the same cost, well anyway you have to choose one, and there is no point in keeping one and not the other } return path1.referenceDepth > path2.referenceDepth; } int referenceDepth; bool ignoreHdr; bool ignoreColorCorrectness; }; #undef CHECK_ONE_AND_NOT_THE_OTHER #endif diff --git a/libs/ui/kis_paint_ops_model.h b/libs/ui/kis_paint_ops_model.h index 8bf2034fb5..4521ced7df 100644 --- a/libs/ui/kis_paint_ops_model.h +++ b/libs/ui/kis_paint_ops_model.h @@ -1,91 +1,91 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * 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. */ #ifndef _KIS_PAINTOP_LIST_MODEL_H_ #define _KIS_PAINTOP_LIST_MODEL_H_ #include #include #include #include "kis_categorized_list_model.h" #include class KisPaintOpFactory; struct KRITAUI_EXPORT KisPaintOpInfo { KisPaintOpInfo() { } KisPaintOpInfo(const QString& _id, const QString& _name, const QString& _category, const QIcon& _icon, qint32 _priority): id(_id), name(_name), category(_category), icon(_icon), priority(_priority) { } KisPaintOpInfo(const QString& _id): id(_id) { } bool operator==(const KisPaintOpInfo info) const{ return (info.id == id); } QString id; QString name; QString category; QIcon icon; - qint32 priority; + qint32 priority {0}; }; struct PaintOpInfoToQStringConverter { QString operator() (const KisPaintOpInfo &info) { return info.name; } }; typedef KisCategorizedListModel BasePaintOpCategorizedListModel; class KRITAUI_EXPORT KisPaintOpListModel : public BasePaintOpCategorizedListModel { public: KisPaintOpListModel(QObject *parent); QVariant data(const QModelIndex& idx, int role = Qt::DisplayRole) const override; void fill(const QList& list); }; class KRITAUI_EXPORT KisSortedPaintOpListModel : public KisSortedCategorizedListModel { public: KisSortedPaintOpListModel(QObject *parent) : KisSortedCategorizedListModel(parent), m_model(new KisPaintOpListModel(this)) { initializeModel(m_model); } void fill(const QList &list) { m_model->fill(list); } protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override { return lessThanPriority(left, right, KisPaintOpFactory::categoryStable()); } private: KisPaintOpListModel *m_model; }; #endif //_KIS_PAINTOP_LIST_MODEL_H_ diff --git a/libs/ui/tool/kis_tool_paint.h b/libs/ui/tool/kis_tool_paint.h index 084fd10127..9820a0d088 100644 --- a/libs/ui/tool/kis_tool_paint.h +++ b/libs/ui/tool/kis_tool_paint.h @@ -1,201 +1,201 @@ /* * Copyright (c) 2003 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_TOOL_PAINT_H_ #define KIS_TOOL_PAINT_H_ #include "kis_tool.h" #include #include #include #include #include #include #include #include #include #include #include "kis_signal_compressor_with_param.h" #include #include class QGridLayout; class KoCompositeOp; class KoCanvasBase; class KRITAUI_EXPORT KisToolPaint : public KisTool { Q_OBJECT public: KisToolPaint(KoCanvasBase *canvas, const QCursor &cursor); ~KisToolPaint() override; int flags() const override; void mousePressEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; protected: void setMode(ToolMode mode) override; void canvasResourceChanged(int key, const QVariant &v) override; void paint(QPainter &gc, const KoViewConverter &converter) override; void activatePrimaryAction() override; void deactivatePrimaryAction() override; void activateAlternateAction(AlternateAction action) override; void deactivateAlternateAction(AlternateAction action) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event); /** If the paint tool support outline like brushes, set to true. * If not (e.g. gradient tool), set to false. Default is false. */ void setSupportOutline(bool supportOutline) { m_supportOutline = supportOutline; } virtual QPainterPath getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode); protected: bool isOutlineEnabled() const; void setOutlineEnabled(bool enabled); bool pickColor(const QPointF &documentPixel, AlternateAction action); /// Add the tool-specific layout to the default option widget layout. void addOptionWidgetLayout(QLayout *layout); /// Add a widget and a label to the current option widget layout. virtual void addOptionWidgetOption(QWidget *control, QWidget *label = 0); void showControl(QWidget *control, bool value); void enableControl(QWidget *control, bool value); QWidget * createOptionWidget() override; /** * Quick help is a short help text about the way the tool functions. */ virtual QString quickHelp() const { return QString(); } const KoCompositeOp* compositeOp(); public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void slotPopupQuickHelp(); void increaseBrushSize(); void decreaseBrushSize(); void activatePickColorDelayed(); void slotColorPickingFinished(KoColor color); protected: quint8 m_opacity; - bool m_paintOutline; + bool m_paintOutline {false}; QPointF m_outlineDocPoint; QPainterPath m_currentOutline; QRectF m_oldOutlineRect; bool m_showColorPreview; QRectF m_oldColorPreviewRect; QRectF m_oldColorPreviewUpdateRect; QColor m_colorPreviewCurrentColor; bool m_colorPreviewShowComparePlate; QColor m_colorPreviewBaseColor; private: QPainterPath tryFixBrushOutline(const QPainterPath &originalOutline); void setOpacity(qreal opacity); void activatePickColor(AlternateAction action); void deactivatePickColor(AlternateAction action); void pickColorWasOverridden(); int colorPreviewResourceId(AlternateAction action); QRectF colorPreviewDocRect(const QPointF &outlineDocPoint); bool isPickingAction(AlternateAction action); struct PickingJob { PickingJob() {} PickingJob(QPointF _documentPixel, AlternateAction _action) : documentPixel(_documentPixel), action(_action) {} QPointF documentPixel; AlternateAction action; }; void addPickerJob(const PickingJob &pickingJob); private: bool m_specialHoverModifier; QGridLayout *m_optionsWidgetLayout; bool m_supportOutline; /** * Used as a switch for pickColor */ // used to skip some of the tablet events and don't update the colour that often QTimer m_colorPickerDelayTimer; - AlternateAction delayedAction; + AlternateAction delayedAction {AlternateAction::NONE}; bool m_isOutlineEnabled; std::vector m_standardBrushSizes; KisStrokeId m_pickerStrokeId; - int m_pickingResource; + int m_pickingResource {0}; typedef KisSignalCompressorWithParam PickingCompressor; QScopedPointer m_colorPickingCompressor; qreal m_localOpacity {1.0}; qreal m_oldOpacity {1.0}; Q_SIGNALS: void sigPaintingFinished(); }; #endif // KIS_TOOL_PAINT_H_ diff --git a/plugins/extensions/rotateimage/dlg_rotateimage.h b/plugins/extensions/rotateimage/dlg_rotateimage.h index 758776aec8..bf1012ee81 100644 --- a/plugins/extensions/rotateimage/dlg_rotateimage.h +++ b/plugins/extensions/rotateimage/dlg_rotateimage.h @@ -1,75 +1,75 @@ /* * dlg_rotateimage.h -- part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Michael Thaler * * 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 DLG_ROTATEIMAGE #define DLG_ROTATEIMAGE #include #include "kis_global.h" #include "ui_wdg_rotateimage.h" enum enumRotationDirection { CLOCKWISE, COUNTERCLOCKWISE }; class WdgRotateImage : public QWidget, public Ui::WdgRotateImage { Q_OBJECT public: WdgRotateImage(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgRotateImage: public KoDialog { Q_OBJECT public: DlgRotateImage(QWidget * parent = 0, const char* name = 0); ~DlgRotateImage() override; void setAngle(quint32 w); double angle(); void setDirection(enumRotationDirection direction); enumRotationDirection direction(); private Q_SLOTS: void okClicked(); void resetPreview(); void slotAngleValueChanged(double); private: WdgRotateImage * m_page; - double m_oldAngle; + double m_oldAngle {0.0}; bool m_lock; }; #endif // DLG_ROTATEIMAGE diff --git a/plugins/extensions/shearimage/dlg_shearimage.h b/plugins/extensions/shearimage/dlg_shearimage.h index b8cf81a202..ec1ff98062 100644 --- a/plugins/extensions/shearimage/dlg_shearimage.h +++ b/plugins/extensions/shearimage/dlg_shearimage.h @@ -1,65 +1,65 @@ /* * dlg_shearimage.h -- part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Michael Thaler * * 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 DLG_SHEARIMAGE #define DLG_SHEARIMAGE #include #include "ui_wdg_shearimage.h" class WdgShearImage : public QWidget, public Ui::WdgShearImage { Q_OBJECT public: WdgShearImage(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgShearImage: public KoDialog { Q_OBJECT public: DlgShearImage(QWidget * parent = 0, const char* name = 0); ~DlgShearImage() override; void setAngleX(quint32 w); void setAngleY(quint32 w); qint32 angleX(); qint32 angleY(); private Q_SLOTS: void okClicked(); private: WdgShearImage * m_page; - double m_oldAngle; + double m_oldAngle {0.0}; bool m_lock; }; #endif // DLG_SHEARIMAGE diff --git a/plugins/tools/basictools/kis_tool_colorpicker.h b/plugins/tools/basictools/kis_tool_colorpicker.h index 75415d625e..f52510cef9 100644 --- a/plugins/tools/basictools/kis_tool_colorpicker.h +++ b/plugins/tools/basictools/kis_tool_colorpicker.h @@ -1,141 +1,141 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2018 Emmet & Eoin O'Neill * * 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_TOOL_COLOR_PICKER_H_ #define KIS_TOOL_COLOR_PICKER_H_ #include #include "KoToolFactoryBase.h" #include "ui_wdgcolorpicker.h" #include "kis_tool.h" #include #include namespace KisToolUtils { struct ColorPickerConfig; } class ColorPickerOptionsWidget : public QWidget, public Ui::ColorPickerOptionsWidget { Q_OBJECT public: ColorPickerOptionsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisToolColorPicker : public KisTool { Q_OBJECT Q_PROPERTY(bool toForeground READ toForeground WRITE setToForeground NOTIFY toForegroundChanged) public: KisToolColorPicker(KoCanvasBase *canvas); ~KisToolColorPicker() override; public: struct Configuration { Configuration(); bool toForegroundColor; bool updateColor; bool addPalette; bool normaliseValues; bool sampleMerged; int radius; int blend; void save(ToolActivation activation) const; void load(ToolActivation activation); }; public: QWidget* createOptionWidget() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter &gc, const KoViewConverter &converter) override; bool toForeground() const; Q_SIGNALS: void toForegroundChanged(); protected: void activate(ToolActivation activation, const QSet &) override; void deactivate() override; public Q_SLOTS: void setToForeground(bool newValue); void slotSetUpdateColor(bool); void slotSetNormaliseValues(bool); void slotSetAddPalette(bool); void slotChangeRadius(int); void slotChangeBlend(int); void slotAddPalette(KoResourceSP resource); void slotSetColorSource(int value); private: void displayPickedColor(); bool pickColor(const QPointF& pos); void updateOptionWidget(); // Configuration QScopedPointer m_config; - ToolActivation m_toolActivationSource; + ToolActivation m_toolActivationSource {ToolActivation::DefaultActivation}; bool m_isActivated; KoColor m_pickedColor; // Used to skip some tablet events and update color less often QTimer m_colorPickerDelayTimer; ColorPickerOptionsWidget *m_optionsWidget; QList m_palettes; }; class KisToolColorPickerFactory : public KoToolFactoryBase { public: KisToolColorPickerFactory() : KoToolFactoryBase("KritaSelected/KisToolColorPicker") { setToolTip(i18n("Color Selector Tool")); setSection(TOOL_TYPE_FILL); setPriority(2); setIconName(koIconNameCStr("krita_tool_color_picker")); setShortcut(QKeySequence(Qt::Key_P)); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolColorPickerFactory() override {} KoToolBase *createTool(KoCanvasBase *canvas) override { return new KisToolColorPicker(canvas); } }; #endif // KIS_TOOL_COLOR_PICKER_H_