diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 03da27f889..bfa76be1cc 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,437 +1,397 @@ /* 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 #include "kis_texture_chooser.h" #include "KoMixColorsOp.h" #include #include "kis_signals_blocker.h" #include KisTextureOption::KisTextureOption() : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) , m_textureOptions(new KisTextureChooser()) { setObjectName("KisTextureOption"); setConfigurationPage(m_textureOptions); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(resetGUI(KoResourceSP ))); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(emitSettingChanged())); connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->neutralPointSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_textureOptions->textureSelectorWidget->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_textureOptions; } void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { KoPatternSP pattern; { KisSignalsBlocker b(m_textureOptions->textureSelectorWidget); KoResourceSP resource = m_textureOptions->textureSelectorWidget->currentResource(); if (!resource) return; pattern = resource.staticCast(); if (!pattern) return; } setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_textureOptions->scaleSlider->value(); qreal brightness = m_textureOptions->brightnessSlider->value(); qreal contrast = m_textureOptions->contrastSlider->value(); qreal neutralPoint = m_textureOptions->neutralPointSlider->value(); int offsetX = m_textureOptions->offsetSliderX->value(); if (m_textureOptions ->randomOffsetX->isChecked()) { m_textureOptions->offsetSliderX ->setEnabled(false); m_textureOptions->offsetSliderX ->blockSignals(true); m_textureOptions->offsetSliderX ->setValue(offsetX); m_textureOptions->offsetSliderX ->blockSignals(false); } else { m_textureOptions->offsetSliderX ->setEnabled(true); } int offsetY = m_textureOptions->offsetSliderY->value(); if (m_textureOptions ->randomOffsetY->isChecked()) { m_textureOptions->offsetSliderY ->setEnabled(false); m_textureOptions->offsetSliderY ->blockSignals(true); m_textureOptions->offsetSliderY ->setValue(offsetY); m_textureOptions->offsetSliderY ->blockSignals(false); } else { m_textureOptions->offsetSliderY ->setEnabled(true); } int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex(); bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/Brightness", brightness); setting->setProperty("Texture/Pattern/Contrast", contrast); setting->setProperty("Texture/Pattern/NeutralPoint", neutralPoint); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, KisGlobalResourcesInterface::instance()); if (!pattern) { pattern =m_textureOptions->textureSelectorWidget->currentResource().staticCast(); } m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern); m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness")); m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0)); m_textureOptions->neutralPointSlider->setValue(setting->getDouble("Texture/Pattern/NeutralPoint", 0.5)); m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY")); m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const { l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)")); } void KisTextureOption::resetGUI(KoResourceSP res) { KoPatternSP pattern = res.staticCast(); if (!pattern) return; m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } /**********************************************************************/ /* KisTextureProperties */ /**********************************************************************/ KisTextureProperties::KisTextureProperties(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { KoResourceServer* rserver = KoResourceServerProvider::instance()->gradientServer(); m_gradient = dynamic_cast(rserver->resources().first()); } void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { m_enabled = false; return; } m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail)); if (!m_maskInfo->fillProperties(setting, resourcesInterface)) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; m_enabled = false; return; } m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo); m_enabled = setting->getBool("Texture/Pattern/Enabled", false); m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); m_strengthOption.readOptionSetting(setting); m_strengthOption.resetAllSensors(); } QList KisTextureProperties::prepareEmbeddedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface) { QList resources; KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, resourcesInterface); if (pattern) { resources << pattern; } return resources; } void KisTextureProperties::setTextureGradient(KoAbstractGradientSP gradient) { if (gradient) { m_gradient = gradient; m_cachedGradient.setGradient(gradient, 256); } } -//Convert a pixel to a QColor. We need this instead of calling ColorSpace::toQColor() because toQColor() is extremely slow, -//even though it works pretty much exactly the same way... -void KisTextureProperties::createQColorFromPixel(QColor& dest, const quint8* pixel, const KoColorSpace *cs) { - QVector channelValuesF(4); - - //Probably doesn't need to check for RGBA, because the only time we call it is for - //the mask, which we explicitly set as rbg8 colorspace before calling this. It's here from previous testing - //where it was necessary, and left in just in case it's needed in the future. - QString csid = cs->id(); - if (csid.contains("RGBA")) { - cs->normalisedChannelsValue(pixel, channelValuesF); - } - else { - const int rgbPixelSize = sizeof(KoBgrU16Traits::Pixel); - quint8* pixelRGBA = new quint8[rgbPixelSize]; - cs->toRgbA16(pixel, pixelRGBA, 1); - KoColorSpaceTrait::normalisedChannelsValue(pixelRGBA, channelValuesF); - delete pixelRGBA; - } - dest.setRgbF(channelValuesF[2], channelValuesF[1], channelValuesF[0], channelValuesF[3]); -} - void KisTextureProperties::applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { if (!m_enabled) return; KisPaintDeviceSP mask = m_maskInfo->mask(); - bool maskHasAlpha = m_maskInfo->hasAlpha(); const QRect maskBounds = m_maskInfo->maskBounds(); - KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(mask->colorSpace()); - QRect rect = dab->bounds(); + KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); + const QRect rect = dab->bounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; KisFillPainter fillMaskPainter(fillMaskDevice); fillMaskPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); fillMaskPainter.end(); qreal pressure = m_strengthOption.apply(info); quint8* dabData = dab->data(); - KisHLineIteratorSP iter = fillMaskDevice->createHLineIteratorNG(x, y, rect.width()); - for (int row = 0; row < rect.height(); ++row) { - for (int col = 0; col < rect.width(); ++col) { - const quint8* maskData = iter->oldRawData(); - QColor maskColor; - if (maskHasAlpha) { - createQColorFromPixel(maskColor, maskData, mask->colorSpace()); - } else { - quint8 gray = *maskData; - maskColor = QColor::fromRgb(gray, gray, gray); - } - QRgb maskQRgb = maskColor.rgba(); - dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, &maskQRgb, dabData, pressure, 1); - - iter->nextPixel(); - dabData += dab->pixelSize(); - } - iter->nextRow(); + KisSequentialConstIterator it(fillMaskDevice, QRect(x, y, rect.width(), rect.height())); + while (it.nextPixel()) { + const QRgb *maskQRgb = reinterpret_cast(it.oldRawData()); + dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, maskQRgb, dabData, pressure, 1); + dabData += dab->pixelSize(); } } void KisTextureProperties::applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { if (!m_enabled) return; + KIS_SAFE_ASSERT_RECOVER_RETURN(m_gradient && m_gradient->valid()); + KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); KisPaintDeviceSP mask = m_maskInfo->mask(); const QRect maskBounds = m_maskInfo->maskBounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); fillPainter.end(); qreal pressure = m_strengthOption.apply(info); quint8* dabData = dab->data(); //for gradient textures... KoMixColorsOp* colorMix = dab->colorSpace()->mixColorsOp(); qint16 colorWeights[2]; colorWeights[0] = qRound(pressure * 255); colorWeights[1] = 255 - colorWeights[0]; quint8* colors[2]; KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { - if (m_gradient && m_gradient->valid()) { - qreal gradientvalue = qreal(*iter->oldRawData()) / 255.0; - KoColor paintcolor; - if (m_useCachedGradient) { - paintcolor.setColor(m_cachedGradient.cachedAt(gradientvalue), m_gradient->colorSpace()); - } - else { - paintcolor = KoColor(m_gradient->colorSpace()); - m_gradient->colorAt(paintcolor, gradientvalue); - } - paintcolor.setOpacity(qMin(paintcolor.opacityF(), dab->colorSpace()->opacityF(dabData))); - paintcolor.convertTo(dab->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - colors[0] = paintcolor.data(); - KoColor dabColor(dabData, dab->colorSpace()); - colors[1] = dabColor.data(); - colorMix->mixColors(colors, colorWeights, 2, dabData); - } + + qreal gradientvalue = qreal(*iter->oldRawData()) / 255.0; + KoColor paintcolor; + paintcolor.setColor(m_cachedGradient.cachedAt(gradientvalue), m_gradient->colorSpace()); + paintcolor.setOpacity(qMin(paintcolor.opacityF(), dab->colorSpace()->opacityF(dabData))); + paintcolor.convertTo(dab->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + colors[0] = paintcolor.data(); + KoColor dabColor(dabData, dab->colorSpace()); + colors[1] = dabColor.data(); + colorMix->mixColors(colors, colorWeights, 2, dabData); + iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) { if (!m_enabled) return; if (m_texturingMode == LIGHTNESS) { applyLightness(dab, offset, info); return; } else if (m_texturingMode == GRADIENT) { applyGradient(dab, offset, info); return; } KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); KisPaintDeviceSP mask = m_maskInfo->mask(); const QRect maskBounds = m_maskInfo->maskBounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); fillPainter.end(); qreal pressure = m_strengthOption.apply(info); quint8* dabData = dab->data(); KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { if (m_texturingMode == MULTIPLY) { dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1); } else { int pressureOffset = (1.0 - pressure) * 255; qint16 maskA = *iter->oldRawData() + pressureOffset; quint8 dabA = dab->colorSpace()->opacityU8(dabData); dabA = qMax(0, (qint16)dabA - maskA); dab->colorSpace()->setOpacity(dabData, dabA, 1); } iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h index 4362112802..b04a8b24fe 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,115 +1,113 @@ /* 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 #include #include "KisTextureMaskInfo.h" #include class KisTextureChooser; class KoPattern; class KoResource; class KisPropertiesConfiguration; class KisPaintopLodLimitations; class KisResourcesInterface; class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption { Q_OBJECT public: explicit KisTextureOption(); ~KisTextureOption() override; public Q_SLOTS: void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void lodLimitations(KisPaintopLodLimitations *l) const override; private Q_SLOTS: void resetGUI(KoResourceSP ); /// called when a new pattern is selected private: /// UI Widget that stores all the texture options KisTextureChooser* m_textureOptions; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties(int levelOfDetail); enum TexturingMode { MULTIPLY, SUBTRACT, LIGHTNESS, GRADIENT }; bool m_enabled; /** * @brief apply combine the texture map with the dab * @param dab the colored, final representation of the dab, after mirroring and everything. * @param offset the position of the dab on the image. used to calculate the position of the mask pattern * @param info the paint information */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); void fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); QList prepareEmbeddedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface); void setTextureGradient(const KoAbstractGradientSP gradient); private: - void createQColorFromPixel(QColor& dest, const quint8* pixel, const KoColorSpace *cs); void applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info); void applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info); private: int m_offsetX; int m_offsetY; TexturingMode m_texturingMode; KoAbstractGradientSP m_gradient; KoCachedGradient m_cachedGradient; - bool m_useCachedGradient = true; int m_levelOfDetail; private: KisPressureTextureStrengthOption m_strengthOption; KisTextureMaskInfoSP m_maskInfo; }; #endif // KIS_TEXTURE_OPTION_H