diff --git a/krita/plugins/paintops/libpaintop/kis_dab_cache.cpp b/krita/plugins/paintops/libpaintop/kis_dab_cache.cpp index 628e9a0cda7..ee31c71779a 100644 --- a/krita/plugins/paintops/libpaintop/kis_dab_cache.cpp +++ b/krita/plugins/paintops/libpaintop/kis_dab_cache.cpp @@ -1,420 +1,420 @@ /* * Copyright (c) 2012 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_dab_cache.h" #include #include "kis_color_source.h" #include "kis_paint_device.h" #include "kis_brush.h" #include #include #include #include #include #include #include struct PrecisionValues { qreal angle; qreal sizeFrac; qreal subPixel; qreal softnessFactor; }; const qreal eps = 1e-6; static const PrecisionValues precisionLevels[] = { {M_PI / 180, 0.05, 1, 0.01}, {M_PI / 180, 0.01, 1, 0.01}, {M_PI / 180, 0, 1, 0.01}, {M_PI / 180, 0, 0.5, 0.01}, {eps, 0, eps, eps} }; struct KisDabCache::SavedDabParameters { KoColor color; qreal angle; int width; int height; qreal subPixelX; qreal subPixelY; qreal softnessFactor; int index; MirrorProperties mirrorProperties; bool compare(const SavedDabParameters &rhs, int precisionLevel) const { const PrecisionValues &prec = precisionLevels[precisionLevel]; return color == rhs.color && qAbs(angle - rhs.angle) <= prec.angle && qAbs(width - rhs.width) <= (int)(prec.sizeFrac * width) && qAbs(height - rhs.height) <= (int)(prec.sizeFrac * height) && qAbs(subPixelX - rhs.subPixelX) <= prec.subPixel && qAbs(subPixelY - rhs.subPixelY) <= prec.subPixel && qAbs(softnessFactor - rhs.softnessFactor) <= prec.softnessFactor && index == rhs.index && mirrorProperties.horizontalMirror == rhs.mirrorProperties.horizontalMirror && mirrorProperties.verticalMirror == rhs.mirrorProperties.verticalMirror; } }; struct KisDabCache::Private { Private(KisBrushSP brush) : brush(brush), mirrorOption(0), sharpnessOption(0), textureOption(0), precisionOption(0), subPixelPrecisionDisabled(false), cachedDabParameters(new SavedDabParameters) {} KisFixedPaintDeviceSP dab; KisFixedPaintDeviceSP dabOriginal; KisBrushSP brush; KisPaintDeviceSP colorSourceDevice; KisPressureMirrorOption *mirrorOption; KisPressureSharpnessOption *sharpnessOption; KisTextureProperties *textureOption; KisPrecisionOption *precisionOption; bool subPixelPrecisionDisabled; SavedDabParameters *cachedDabParameters; }; KisDabCache::KisDabCache(KisBrushSP brush) : m_d(new Private(brush)) { } KisDabCache::~KisDabCache() { delete m_d->cachedDabParameters; delete m_d; } void KisDabCache::setMirrorPostprocessing(KisPressureMirrorOption *option) { m_d->mirrorOption = option; } void KisDabCache::setSharpnessPostprocessing(KisPressureSharpnessOption *option) { m_d->sharpnessOption = option; } void KisDabCache::setTexturePostprocessing(KisTextureProperties *option) { m_d->textureOption = option; } void KisDabCache::setPrecisionOption(KisPrecisionOption *option) { m_d->precisionOption = option; } void KisDabCache::disableSubpixelPrecision() { m_d->subPixelPrecisionDisabled = true; } inline KisDabCache::SavedDabParameters KisDabCache::getDabParameters(const KoColor& color, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor, MirrorProperties mirrorProperties) { SavedDabParameters params; params.color = color; params.angle = angle; params.width = m_d->brush->maskWidth(scaleX, angle, subPixelX, subPixelY, info); params.height = m_d->brush->maskHeight(scaleY, angle, subPixelX, subPixelY, info); params.subPixelX = subPixelX; params.subPixelY = subPixelY; params.softnessFactor = softnessFactor; params.index = m_d->brush->brushIndex(info); params.mirrorProperties = mirrorProperties; return params; } KisFixedPaintDeviceSP KisDabCache::fetchDab(const KoColorSpace *cs, const KisColorSource *colorSource, const QPointF &cursorPoint, double scaleX, double scaleY, double angle, const KisPaintInformation& info, qreal softnessFactor, QRect *dstDabRect) { return fetchDabCommon(cs, colorSource, KoColor(), cursorPoint, scaleX, scaleY, angle, info, softnessFactor, dstDabRect); } KisFixedPaintDeviceSP KisDabCache::fetchDab(const KoColorSpace *cs, const KoColor& color, const QPointF &cursorPoint, double scaleX, double scaleY, double angle, const KisPaintInformation& info, qreal softnessFactor, QRect *dstDabRect) { return fetchDabCommon(cs, 0, color, cursorPoint, scaleX, scaleY, angle, info, softnessFactor, dstDabRect); } bool KisDabCache::needSeparateOriginal() { - return (m_d->textureOption && m_d->textureOption->enabled) || + return (m_d->textureOption && m_d->textureOption->m_enabled) || (m_d->sharpnessOption && m_d->sharpnessOption->isChecked()); } struct KisDabCache::DabPosition { DabPosition(const QRect &_rect, const QPointF &_subPixel, qreal _realAngle) : rect(_rect), subPixel(_subPixel), realAngle(_realAngle) { } QRect rect; QPointF subPixel; qreal realAngle; }; inline QRect KisDabCache::correctDabRectWhenFetchedFromCache(const QRect &dabRect, const QSize &realDabSize) { int diffX = (realDabSize.width() - dabRect.width()) / 2; int diffY = (realDabSize.height() - dabRect.height()) / 2; return QRect(dabRect.x() - diffX, dabRect.y() - diffY, realDabSize.width() , realDabSize.height()); } inline KisFixedPaintDeviceSP KisDabCache::tryFetchFromCache(const SavedDabParameters ¶ms, const KisPaintInformation& info, QRect *dstDabRect) { int precisionLevel = m_d->precisionOption ? m_d->precisionOption->precisionLevel() - 1 : 3; if (!params.compare(*m_d->cachedDabParameters, precisionLevel)) { return 0; } if (needSeparateOriginal()) { *m_d->dab = *m_d->dabOriginal; *dstDabRect = correctDabRectWhenFetchedFromCache(*dstDabRect, m_d->dab->bounds().size()); postProcessDab(m_d->dab, dstDabRect->topLeft(), info); } else { *dstDabRect = correctDabRectWhenFetchedFromCache(*dstDabRect, m_d->dab->bounds().size()); } m_d->brush->notifyCachedDabPainted(); return m_d->dab; } qreal positiveFraction(qreal x) { qint32 unused = 0; qreal fraction = 0.0; KisPaintOp::splitCoordinate(x, &unused, &fraction); return fraction; } inline KisDabCache::DabPosition KisDabCache::calculateDabRect(const QPointF &cursorPoint, double scaleX, double scaleY, double angle, const KisPaintInformation& info, const MirrorProperties &mirrorProperties) { qint32 x = 0, y = 0; qreal subPixelX = 0.0, subPixelY = 0.0; if (mirrorProperties.coordinateSystemFlipped) { angle = 2 * M_PI - angle; } QPointF hotSpot = m_d->brush->hotSpot(scaleX, scaleY, angle, info); QPointF pt = cursorPoint - hotSpot; if (m_d->sharpnessOption) { m_d->sharpnessOption->apply(info, pt, x, y, subPixelX, subPixelY); } else { KisPaintOp::splitCoordinate(pt.x(), &x, &subPixelX); KisPaintOp::splitCoordinate(pt.y(), &y, &subPixelY); } if (m_d->subPixelPrecisionDisabled) { subPixelX = 0; subPixelY = 0; } int width = m_d->brush->maskWidth(scaleX, angle, subPixelX, subPixelY, info); int height = m_d->brush->maskHeight(scaleY, angle, subPixelX, subPixelY, info); if (mirrorProperties.horizontalMirror) { subPixelX = positiveFraction(-(cursorPoint.x() + hotSpot.x())); width = m_d->brush->maskWidth(scaleX, angle, subPixelX, subPixelY, info); x = qRound(cursorPoint.x() + subPixelX + hotSpot.x()) - width; } if (mirrorProperties.verticalMirror) { subPixelY = positiveFraction(-(cursorPoint.y() + hotSpot.y())); height = m_d->brush->maskHeight(scaleY, angle, subPixelX, subPixelY, info); y = qRound(cursorPoint.y() + subPixelY + hotSpot.y()) - height; } return DabPosition(QRect(x, y, width, height), QPointF(subPixelX, subPixelY), angle); } inline KisFixedPaintDeviceSP KisDabCache::fetchDabCommon(const KoColorSpace *cs, const KisColorSource *colorSource, const KoColor& color, const QPointF &cursorPoint, double scaleX, double scaleY, double initialAngle, const KisPaintInformation& info, qreal softnessFactor, QRect *dstDabRect) { Q_ASSERT(dstDabRect); MirrorProperties mirrorProperties; if (m_d->mirrorOption) { mirrorProperties = m_d->mirrorOption->apply(info); } DabPosition position = calculateDabRect(cursorPoint, scaleX, scaleY, initialAngle, info, mirrorProperties); *dstDabRect = position.rect; bool cachingIsPossible = !colorSource || colorSource->isUniformColor(); KoColor paintColor = colorSource && colorSource->isUniformColor() ? colorSource->uniformColor() : color; SavedDabParameters newParams = getDabParameters(paintColor, scaleX, scaleY, position.realAngle, info, position.subPixel.x(), position.subPixel.y(), softnessFactor, mirrorProperties); if (!m_d->dab || !(*m_d->dab->colorSpace() == *cs)) { m_d->dab = new KisFixedPaintDevice(cs); } else if (cachingIsPossible) { KisFixedPaintDeviceSP cachedDab = tryFetchFromCache(newParams, info, dstDabRect); if (cachedDab) return cachedDab; } if (m_d->brush->brushType() == IMAGE || m_d->brush->brushType() == PIPE_IMAGE) { m_d->dab = m_d->brush->paintDevice(cs, scaleX, position.realAngle, info, position.subPixel.x(), position.subPixel.y()); } else if (cachingIsPossible) { *m_d->cachedDabParameters = newParams; m_d->brush->mask(m_d->dab, paintColor, scaleX, scaleY, position.realAngle, info, position.subPixel.x(), position.subPixel.y(), softnessFactor); } else { if (!m_d->colorSourceDevice || !(*cs == *m_d->colorSourceDevice->colorSpace())) { m_d->colorSourceDevice = new KisPaintDevice(cs); } else { m_d->colorSourceDevice->clear(); } QRect maskRect(QPoint(), position.rect.size()); colorSource->colorize(m_d->colorSourceDevice, maskRect, info.pos().toPoint()); delete m_d->colorSourceDevice->convertTo(cs); m_d->brush->mask(m_d->dab, m_d->colorSourceDevice, scaleX, scaleY, position.realAngle, info, position.subPixel.x(), position.subPixel.y(), softnessFactor); } if (!mirrorProperties.isEmpty()) { m_d->dab->mirror(mirrorProperties.horizontalMirror, mirrorProperties.verticalMirror); } if (needSeparateOriginal()) { if (!m_d->dabOriginal || !(*cs == *m_d->dabOriginal->colorSpace())) { m_d->dabOriginal = new KisFixedPaintDevice(cs); } *m_d->dabOriginal = *m_d->dab; } postProcessDab(m_d->dab, position.rect.topLeft(), info); return m_d->dab; } void KisDabCache::postProcessDab(KisFixedPaintDeviceSP dab, const QPoint &dabTopLeft, const KisPaintInformation& info) { if (m_d->sharpnessOption) { m_d->sharpnessOption->applyThreshold(dab); } if (m_d->textureOption) { m_d->textureOption->apply(dab, dabTopLeft, info); } } diff --git a/krita/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp b/krita/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp index d1bd8a01da4..f78c711d2f3 100644 --- a/krita/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp +++ b/krita/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp @@ -1,113 +1,130 @@ /* * 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 struct KisEmbeddedPatternManager::Private { static KoPattern* tryLoadEmbeddedPattern(const KisPropertiesConfiguration* setting) { KoPattern *pattern = 0; 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.baseName(); } if (!img.isNull()) { pattern = new KoPattern(img, name, KoResourceServerProvider::instance()->patternServer()->saveLocation()); } return pattern; } static KoPattern* tryFetchPatternByMd5(const QByteArray &md5) { KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); return server->resourceByMD5(md5); } -}; + static KoPattern* tryFetchPatternByName(const QString &name) { + KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); + return server->resourceByName(name); + } + + static KoPattern* tryFetchPatternByFileName(const QString &fileName) { + KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); + return server->resourceByFilename(fileName); + } +}; void KisEmbeddedPatternManager::saveEmbeddedPattern(KisPropertiesConfiguration* setting, const KoPattern *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()) { - + 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()); - setting->setProperty("Texture/Pattern/PatternFileName", pattern->filename()); - setting->setProperty("Texture/Pattern/Name", pattern->name()); + } KoPattern* KisEmbeddedPatternManager::loadEmbeddedPattern(const KisPropertiesConfiguration* setting) { KoPattern *pattern = 0; QByteArray md5 = QByteArray::fromBase64(setting->getString("Texture/Pattern/PatternMD5").toLatin1()); pattern = Private::tryFetchPatternByMd5(md5); + if (pattern) return pattern; - if (!pattern) { - pattern = Private::tryLoadEmbeddedPattern(setting); - if (pattern) { - KoPattern *existingPattern = Private::tryFetchPatternByMd5(pattern->md5()); - if (existingPattern) { - delete pattern; - pattern = existingPattern; - } - else { - KoResourceServerProvider::instance()->patternServer()->addResource(pattern, true); - } + QString name = setting->getString("Texture/Pattern/Name"); + pattern = Private::tryFetchPatternByName(name); + if (pattern) return pattern; + + QString fileName = setting->getString("Texture/Pattern/PatternFileName"); + pattern = Private::tryFetchPatternByFileName(fileName); + if (pattern) return pattern; + + + pattern = Private::tryLoadEmbeddedPattern(setting); + if (pattern) { + KoPattern *existingPattern = Private::tryFetchPatternByMd5(pattern->md5()); + if (existingPattern) { + delete pattern; + pattern = existingPattern; + } + else { + KoResourceServerProvider::instance()->patternServer()->addResource(pattern, false); } } + return pattern; } diff --git a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp index c92af80c5c5..b0e7b03b6ef 100644 --- a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,400 +1,398 @@ /* 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 #include #include #include "kis_embedded_pattern_manager.h" #include "kis_algebra_2d.h" #include class KisTextureOptionWidget : public QWidget { public: KisTextureOptionWidget(QWidget *parent = 0) : QWidget(parent) { QFormLayout *formLayout = new QFormLayout(this); formLayout->setMargin(0); chooser = new KisPatternChooser(this); chooser->setGrayscalePreview(true); chooser->setMaximumHeight(250); chooser->setCurrentItem(0, 0); formLayout->addRow(chooser); scaleSlider = new KisMultipliersDoubleSliderSpinBox(this); scaleSlider->setRange(0.0, 2.0, 2); scaleSlider->setValue(1.0); scaleSlider->addMultiplier(0.1); scaleSlider->addMultiplier(2); scaleSlider->addMultiplier(10); formLayout->addRow(i18n("Scale:"), scaleSlider); QBoxLayout *offsetLayoutX = new QBoxLayout(QBoxLayout::LeftToRight); offsetSliderX = new KisSliderSpinBox(this); offsetSliderX->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); randomOffsetX = new QCheckBox(i18n("Random Offset"),this); offsetLayoutX->addWidget(offsetSliderX,1,0); offsetLayoutX->addWidget(randomOffsetX,0,0); formLayout->addRow(i18n("Horizontal Offset:"), offsetLayoutX); QBoxLayout *offsetLayoutY = new QBoxLayout(QBoxLayout::LeftToRight); offsetSliderY = new KisSliderSpinBox(this); offsetSliderY->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); randomOffsetY = new QCheckBox(i18n("Random Offset"),this); offsetLayoutY->addWidget(offsetSliderY,1,0); offsetLayoutY->addWidget(randomOffsetY,0,0); formLayout->addRow(i18n("Vertical Offset:"), offsetLayoutY); cmbTexturingMode = new QComboBox(this); cmbTexturingMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QStringList texturingModes; texturingModes << i18n("Multiply") << i18n("Subtract"); cmbTexturingMode->addItems(texturingModes); formLayout->addRow(i18n("Texturing Mode:"), cmbTexturingMode); cmbTexturingMode->setCurrentIndex(KisTextureProperties::SUBTRACT); cmbCutoffPolicy = new QComboBox(this); cmbCutoffPolicy->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QStringList cutOffPolicies; cutOffPolicies << i18n("Cut Off Disabled") << i18n("Cut Off Brush") << i18n("Cut Off Pattern"); cmbCutoffPolicy->addItems(cutOffPolicies); formLayout->addRow(i18n("Cutoff Policy:"), cmbCutoffPolicy); cutoffSlider = new KisGradientSlider(this); cutoffSlider->setMinimumSize(256, 30); cutoffSlider->enableGamma(false); cutoffSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified" " by the slider, the cut-off policy will be applied.")); formLayout->addRow(i18n("Cutoff:"), cutoffSlider); chkInvert = new QCheckBox(this); chkInvert->setChecked(false); formLayout->addRow(i18n("Invert Pattern:"), chkInvert); setLayout(formLayout); } KisPatternChooser *chooser; KisMultipliersDoubleSliderSpinBox *scaleSlider; KisSliderSpinBox *offsetSliderX; QCheckBox *randomOffsetX; KisSliderSpinBox *offsetSliderY; QCheckBox *randomOffsetY; QComboBox *cmbTexturingMode; KisGradientSlider *cutoffSlider; QComboBox *cmbCutoffPolicy; QCheckBox *chkInvert; }; KisTextureOption::KisTextureOption(QObject *) : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) { setObjectName("KisTextureOption"); setChecked(false); m_optionWidget = new KisTextureOptionWidget; m_optionWidget->hide(); setConfigurationPage(m_optionWidget); connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged())); connect(m_optionWidget->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_optionWidget->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_optionWidget->chooser->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_optionWidget; } void KisTextureOption::writeOptionSetting(KisPropertiesConfiguration* setting) const { m_optionWidget->chooser->blockSignals(true); // Checking if (!m_optionWidget->chooser->currentResource()) return; KoPattern *pattern = static_cast(m_optionWidget->chooser->currentResource()); m_optionWidget->chooser->blockSignals(false); // Checking if (!pattern) return; setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_optionWidget->scaleSlider->value(); int offsetX = m_optionWidget->offsetSliderX->value(); if (m_optionWidget ->randomOffsetX->isChecked()) { m_optionWidget -> offsetSliderX ->setEnabled(false); m_optionWidget -> offsetSliderX ->blockSignals(true); m_optionWidget -> offsetSliderX ->setValue(offsetX); m_optionWidget -> offsetSliderX ->blockSignals(false); srand(time(0)); } else { m_optionWidget -> offsetSliderX ->setEnabled(true); } int offsetY = m_optionWidget->offsetSliderY->value(); if (m_optionWidget ->randomOffsetY->isChecked()) { m_optionWidget -> offsetSliderY ->setEnabled(false); m_optionWidget -> offsetSliderY ->blockSignals(true); m_optionWidget -> offsetSliderY ->setValue(offsetY); m_optionWidget -> offsetSliderY ->blockSignals(false); srand(time(0)); } else { m_optionWidget -> offsetSliderY ->setEnabled(true); } int texturingMode = m_optionWidget->cmbTexturingMode->currentIndex(); bool invert = (m_optionWidget->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_optionWidget->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_optionWidget->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_optionWidget->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_optionWidget -> offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_optionWidget -> offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_optionWidget ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_optionWidget ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } - - void KisTextureOption::readOptionSetting(const KisPropertiesConfiguration* setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!pattern) { pattern = static_cast(m_optionWidget->chooser->currentResource()); } m_optionWidget->chooser->setCurrentPattern(pattern); m_optionWidget->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_optionWidget->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_optionWidget->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_optionWidget->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_optionWidget->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_optionWidget->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_optionWidget->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_optionWidget->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::resetGUI(KoResource* res) { KoPattern *pattern = static_cast(res); if (!pattern) return; m_optionWidget->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_optionWidget->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } void KisTextureProperties::recalculateMask() { - if (!pattern) return; + if (!m_pattern) return; m_mask = 0; - QImage mask = pattern->pattern(); + QImage mask = m_pattern->pattern(); if (mask.format() != QImage::Format_RGB32 || mask.format() != QImage::Format_ARGB32) { mask = mask.convertToFormat(QImage::Format_ARGB32); } - if (!qFuzzyCompare(scale, 0.0)) { + if (!qFuzzyCompare(m_scale, 0.0)) { QTransform tf; - tf.scale(scale, scale); + tf.scale(m_scale, m_scale); QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } #if QT_VERSION >= 0x040700 const QRgb* pixel = reinterpret_cast(mask.constBits()); #else const QRgb* pixel = reinterpret_cast(mask.bits()); #endif int width = mask.width(); int height = mask.height(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); m_mask = new KisPaintDevice(cs); 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); - if (invert) { + if (m_invert) { maskValue = 1 - maskValue; } - if (cutoffPolicy == 1 && (maskValue < (cutoffLeft / 255.0) || maskValue > (cutoffRight / 255.0))) { + 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 (cutoffPolicy == 2 && (maskValue < (cutoffLeft / 255.0) || maskValue > (cutoffRight / 255.0))) { + 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); } void KisTextureProperties::fillProperties(const KisPropertiesConfiguration *setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { - enabled = false; + m_enabled = false; return; } - pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); + m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); - if (!pattern) { + if (!m_pattern) { qWarning() << "WARNING: Couldn't load the pattern for a stroke"; - enabled = false; + m_enabled = false; return; } - enabled = setting->getBool("Texture/Pattern/Enabled", false); - scale = setting->getDouble("Texture/Pattern/Scale", 1.0); - offsetX = setting->getInt("Texture/Pattern/OffsetX"); - offsetY = setting->getInt("Texture/Pattern/OffsetY"); - texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); - invert = setting->getBool("Texture/Pattern/Invert"); - cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); - cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); - cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); + m_enabled = setting->getBool("Texture/Pattern/Enabled", false); + m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); + m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); + m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); + m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); + 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); m_strengthOption.readOptionSetting(setting); m_strengthOption.resetAllSensors(); recalculateMask(); } void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) { - if (!enabled) return; + if (!m_enabled) return; KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); - int x = offset.x() % m_maskBounds.width() - offsetX; - int y = offset.y() % m_maskBounds.height() - offsetY; + int x = offset.x() % m_maskBounds.width() - m_offsetX; + int y = offset.y() % m_maskBounds.height() - m_offsetY; KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, m_mask, m_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 (texturingMode == MULTIPLY) { + 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/krita/plugins/paintops/libpaintop/kis_texture_option.h b/krita/plugins/paintops/libpaintop/kis_texture_option.h index d32331a5dd3..f755e0e386c 100644 --- a/krita/plugins/paintops/libpaintop/kis_texture_option.h +++ b/krita/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,101 +1,101 @@ /* 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 class KisTextureOptionWidget; class KoPattern; class KoResource; class KisPropertiesConfiguration; class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption { Q_OBJECT public: explicit KisTextureOption(QObject *parent = 0); virtual ~KisTextureOption(); public Q_SLOTS: virtual void writeOptionSetting(KisPropertiesConfiguration* setting) const; virtual void readOptionSetting(const KisPropertiesConfiguration* setting); private Q_SLOTS: void resetGUI(KoResource*); /// called when a new pattern is selected private: KisTextureOptionWidget *m_optionWidget; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties() - : pattern(0) + : m_pattern(0) {} enum TexturingMode { MULTIPLY, SUBTRACT }; - bool enabled; + 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 */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); void fillProperties(const KisPropertiesConfiguration *setting); private: - qreal scale; - int offsetX; - int offsetY; - TexturingMode texturingMode; - bool invert; - KoPattern *pattern; - int cutoffLeft; - int cutoffRight; - int cutoffPolicy; + qreal m_scale; + int m_offsetX; + int m_offsetY; + TexturingMode m_texturingMode; + bool m_invert; + KoPattern *m_pattern; + int m_cutoffLeft; + int m_cutoffRight; + int m_cutoffPolicy; private: KisPressureTextureStrengthOption m_strengthOption; QRect m_maskBounds; // this can be different from the extent if we mask out too many pixels in a big mask! KisPaintDeviceSP m_mask; void recalculateMask(); }; #endif // KIS_TEXTURE_OPTION_H