diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt index ef8877b212..b4572181b1 100644 --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -1,129 +1,130 @@ project(kritapigment) # we have to repeat platform specifics from top-level if (WIN32) include_directories(${CMAKE_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/compositeops) set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIRS}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set(LINK_VC_LIB) if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR}) set(LINK_VC_LIB ${Vc_LIBRARIES}) ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp) ko_compile_for_all_implementations(__per_arch_alpha_applicator_factory_objs KoAlphaMaskApplicatorFactoryImpl.cpp) message("Following objects are generated from the per-arch lib") message("${__per_arch_factory_objs}") else() set(__per_arch_alpha_applicator_factory_objs KoAlphaMaskApplicatorFactoryImpl.cpp) endif() add_subdirectory(tests) add_subdirectory(benchmarks) set(kritapigment_SRCS DebugPigment.cpp KoBasicHistogramProducers.cpp KoAlphaMaskApplicatorBase.cpp KoColor.cpp KoColorDisplayRendererInterface.cpp KoColorConversionAlphaTransformation.cpp KoColorConversionCache.cpp KoColorConversions.cpp KoColorConversionSystem.cpp KoColorConversionTransformation.cpp KoColorProofingConversionTransformation.cpp KoColorConversionTransformationFactory.cpp KoColorModelStandardIds.cpp KoColorProfile.cpp KoColorSpace.cpp KoColorSpaceEngine.cpp KoColorSpaceFactory.cpp KoColorSpaceMaths.cpp KoCmykColorSpaceMaths.cpp KoLabColorSpaceMaths.cpp KoColorSpaceRegistry.cpp KoColorProfileStorage.cpp KoColorTransformation.cpp KoColorTransformationFactory.cpp KoColorTransformationFactoryRegistry.cpp KoCompositeColorTransformation.cpp KoCompositeOp.cpp KoCompositeOpRegistry.cpp KoCopyColorConversionTransformation.cpp KoFallBackColorTransformation.cpp KoHistogramProducer.cpp KoMultipleColorConversionTransformation.cpp KoUniqueNumberForIdServer.cpp colorspaces/KoAlphaColorSpace.cpp colorspaces/KoLabColorSpace.cpp colorspaces/KoRgbU16ColorSpace.cpp colorspaces/KoRgbU8ColorSpace.cpp colorspaces/KoSimpleColorSpaceEngine.cpp compositeops/KoOptimizedCompositeOpFactory.cpp compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp compositeops/KoAlphaDarkenParamsWrapper.cpp ${__per_arch_factory_objs} ${__per_arch_alpha_applicator_factory_objs} KoAlphaMaskApplicatorFactory.cpp colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp resources/KisSwatch.cpp resources/KisSwatchGroup.cpp resources/KoPattern.cpp resources/KoStopGradient.cpp resources/KoSegmentGradient.cpp + resources/KoCachedGradient.h ) set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB}) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() add_library(kritapigment SHARED ${kritapigment_SRCS}) generate_export_header(kritapigment) target_include_directories( kritapigment PUBLIC $ $ ) target_link_libraries( kritapigment PUBLIC kritaplugin kritastore kritaglobal kritaresources ${EXTRA_LIBRARIES} KF5::I18n KF5::ConfigCore Qt5::Core Qt5::Gui Qt5::Xml ${WIN32_PLATFORM_NET_LIBS} ) set_target_properties(kritapigment PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/pigment/resources/KoCachedGradient.h b/libs/pigment/resources/KoCachedGradient.h new file mode 100644 index 0000000000..815298d438 --- /dev/null +++ b/libs/pigment/resources/KoCachedGradient.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2019 Miguel Lopez + * + * 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. + */ + + + //Adapted from CachedGradient in libs/image/kis_gradient_painter.cc. TODO: fix kis_gradient_painter to use this instead + +class KoCachedGradient : public KoAbstractGradient +{ + +public: + KoCachedGradient() : KoAbstractGradient("") + {} + + KoCachedGradient(const KoAbstractGradient* gradient, qint32 steps, const KoColorSpace* cs) + : KoAbstractGradient(gradient->filename()) + { + setGradient(gradient, steps, cs); + } + + ~KoCachedGradient() override {} + + KoAbstractGradient* clone() const override { + return new KoCachedGradient(m_subject, m_max + 1, m_colorSpace); + } + + /** + * Creates a QGradient from the gradient. + * The resulting QGradient might differ from original gradient + */ + QGradient* toQGradient() const override + { + return m_subject->toQGradient(); + } + + void setGradient(const KoAbstractGradient* gradient, qint32 steps, const KoColorSpace* cs) { + m_subject = gradient; + m_max = steps - 1; + m_colorSpace = cs; + + m_black = KoColor(cs); + + KoColor tmpColor(m_colorSpace); + for (qint32 i = 0; i < steps; i++) { + m_subject->colorAt(tmpColor, qreal(i) / m_max); + m_colors << tmpColor; + } + } + + void setGradient(const KoAbstractGradient* gradient, qint32 steps) { + setGradient(gradient, steps, gradient->colorSpace()); + } + + /// gets the color data at position 0 <= t <= 1 + const quint8* cachedAt(qreal t) const + { + qint32 tInt = t * m_max + 0.5; + if (m_colors.size() > tInt) { + return m_colors[tInt].data(); + } + else { + return m_black.data(); + } + } + + /// allow access to the actual color at position 0 <= t <= 1, instead of the cached color + void colorAt(KoColor& color, qreal t) const override + { + m_subject->colorAt(color, t); + } + void setColorSpace(KoColorSpace* colorSpace) { m_colorSpace = colorSpace; } + const KoColorSpace* colorSpace() const { return m_colorSpace; } + + QByteArray generateMD5() const override { return QByteArray(); } + +private: + + const KoAbstractGradient* m_subject; + const KoColorSpace* m_colorSpace; + qint32 m_max; + QVector m_colors; + KoColor m_black; +}; \ No newline at end of file diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 21326a8371..a43868e6e0 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,365 +1,432 @@ /* 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::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) -{ +void KisTextureProperties::applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { if (!m_enabled) return; - KisPaintDeviceSP fillAlphaDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); 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 fillMaskPainter(fillMaskDevice); + fillMaskPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillMaskPainter.end(); - if (m_texturingMode == LIGHTNESS) { - 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 lightIter = 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 = lightIter->oldRawData(); + QColor maskColor; + createQColorFromPixel(maskColor, maskData, mask->colorSpace()); + + QRgb maskQRgb = maskColor.rgba(); + dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, &maskQRgb, dabData, pressure, 1); + + lightIter->nextPixel(); + dabData += dab->pixelSize(); + } + lightIter->nextRow(); } +} - KisFillPainter fillAlphaPainter(fillAlphaDevice); - fillAlphaPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); - fillAlphaPainter.end(); +void KisTextureProperties::applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { + if (!m_enabled) return; + + KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); + QRect rect = dab->bounds(); + + KisPaintDeviceSP mask = m_maskInfo->mask(); + const QRect maskBounds = m_maskInfo->maskBounds(); + + KIS_SAFE_ASSERT_RECOVER_RETURN(mask); + + int x = offset.x() % maskBounds.width() - m_offsetX; + int y = offset.y() % maskBounds.height() - m_offsetY; + + + KisFillPainter fillPainter(fillDevice); + fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillPainter.end(); qreal pressure = m_strengthOption.apply(info); - quint8 *dabData = dab->data(); + quint8* dabData = dab->data(); //for gradient textures... - KoMixColorsOp *colorMix = dab->colorSpace()->mixColorsOp(); + KoMixColorsOp* colorMix = dab->colorSpace()->mixColorsOp(); qint16 colorWeights[2]; + colorWeights[0] = pressure * 255; + colorWeights[1] = (1.0 - pressure) * 255; quint8* colors[2]; - KisHLineIteratorSP iter = fillAlphaDevice->createHLineIteratorNG(x, y, rect.width()); - KisHLineIteratorSP lightIter = fillMaskDevice->createHLineIteratorNG(x, y, rect.width()); + 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); + } + 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 if (m_texturingMode == SUBTRACT) { + 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); } - else if (m_texturingMode == LIGHTNESS) { - const quint8* maskData = lightIter->oldRawData(); - QColor maskColor; - createQColorFromPixel(maskColor, maskData, mask->colorSpace()); - QRgb maskQRgb = maskColor.rgba(); - dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, &maskQRgb, dabData, pressure, 1); - } - else { - if (m_gradient && m_gradient->valid()) { - KoColor paintcolor(m_gradient->colorSpace()); - qreal gradientvalue = qreal(*iter->oldRawData()) / 255.0; - m_gradient->colorAt(paintcolor, gradientvalue); - paintcolor.setOpacity(qMin(paintcolor.opacityF(), dab->colorSpace()->opacityF(dabData))); - paintcolor.convertTo(dab->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - colorWeights[0] = pressure * 255; - colorWeights[1] = (1.0 - pressure) * 255; - colors[0] = paintcolor.data(); - KoColor dabColor(dabData, dab->colorSpace()); - colors[1] = dabColor.data(); - colorMix->mixColors(colors, colorWeights, 2, dabData); - } - } - lightIter->nextPixel(); iter->nextPixel(); dabData += dab->pixelSize(); } - lightIter->nextRow(); iter->nextRow(); } } diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h index 74d3e2f458..4362112802 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,110 +1,115 @@ /* 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