diff --git a/plugins/paintops/libpaintop/CMakeLists.txt b/plugins/paintops/libpaintop/CMakeLists.txt index ea0af1519b..12bd12575d 100644 --- a/plugins/paintops/libpaintop/CMakeLists.txt +++ b/plugins/paintops/libpaintop/CMakeLists.txt @@ -1,104 +1,105 @@ set(kritalibpaintop_LIB_SRCS kis_airbrush_option.cpp kis_auto_brush_widget.cpp kis_spacing_selection_widget.cpp kis_bidirectional_mixing_option.cpp kis_bidirectional_mixing_option_widget.cpp kis_brush_based_paintop.cpp kis_brush_chooser.cpp kis_brush_option_widget.cpp kis_brush_option.cpp kis_brush_selection_widget.cpp kis_color_option.cpp kis_color_source.cpp kis_color_source_option.cpp kis_color_source_option_widget.cpp kis_curve_option.cpp kis_curve_option_widget.cpp kis_curve_option_uniform_property.cpp kis_custom_brush_widget.cpp kis_clipboard_brush_widget.cpp kis_dynamic_sensor.cc KisDabCacheUtils.cpp kis_dab_cache_base.cpp kis_dab_cache.cpp kis_filter_option.cpp kis_multi_sensors_model_p.cpp kis_multi_sensors_selector.cpp kis_paint_action_type_option.cpp kis_precision_option.cpp kis_pressure_darken_option.cpp kis_pressure_hsv_option.cpp kis_pressure_opacity_option.cpp kis_pressure_flow_option.cpp kis_pressure_mirror_option.cpp kis_pressure_scatter_option.cpp kis_pressure_scatter_option_widget.cpp kis_pressure_sharpness_option.cpp kis_pressure_sharpness_option_widget.cpp kis_pressure_mirror_option_widget.cpp kis_pressure_rotation_option.cpp kis_pressure_size_option.cpp kis_pressure_spacing_option.cpp kis_pressure_rate_option.cpp kis_pressure_softness_option.cpp kis_pressure_mix_option.cpp kis_pressure_gradient_option.cpp kis_pressure_flow_opacity_option.cpp kis_pressure_flow_opacity_option_widget.cpp kis_pressure_spacing_option_widget.cpp kis_pressure_ratio_option.cpp kis_current_outline_fetcher.cpp kis_text_brush_chooser.cpp kis_brush_based_paintop_options_widget.cpp kis_brush_based_paintop_settings.cpp kis_compositeop_option.cpp kis_texture_option.cpp kis_texture_chooser.cpp + KisTextureMaskInfo.cpp kis_pressure_texture_strength_option.cpp kis_embedded_pattern_manager.cpp sensors/kis_dynamic_sensors.cc sensors/kis_dynamic_sensor_drawing_angle.cpp sensors/kis_dynamic_sensor_distance.cc sensors/kis_dynamic_sensor_time.cc sensors/kis_dynamic_sensor_fade.cpp sensors/kis_dynamic_sensor_fuzzy.cpp ) ki18n_wrap_ui(kritalibpaintop_LIB_SRCS forms/wdgautobrush.ui forms/wdgBrushSizeOptions.ui forms/wdgcurveoption.ui forms/wdgcustombrush.ui forms/wdgclipboardbrush.ui forms/wdgtextbrush.ui forms/wdgincremental.ui forms/wdgmultisensorsselector.ui forms/wdgairbrush.ui forms/wdgfilteroption.ui forms/wdgcoloroptions.ui forms/wdgbrushchooser.ui forms/wdgpredefinedbrushchooser.ui forms/wdgtexturechooser.ui forms/wdgCompositeOpOption.ui forms/wdgflowopacityoption.ui sensors/SensorDistanceConfiguration.ui sensors/SensorTimeConfiguration.ui sensors/SensorFadeConfiguration.ui ) add_library(kritalibpaintop SHARED ${kritalibpaintop_LIB_SRCS} ) generate_export_header(kritalibpaintop BASE_NAME kritapaintop EXPORT_MACRO_NAME PAINTOP_EXPORT) target_link_libraries(kritalibpaintop kritaui kritalibbrush kritawidgetutils) target_link_libraries(kritalibpaintop LINK_INTERFACE_LIBRARIES kritaui kritalibbrush) set_target_properties(kritalibpaintop PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritalibpaintop ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(tests) diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp new file mode 100644 index 0000000000..74ac1d9b1f --- /dev/null +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisTextureMaskInfo.h" + +#include +#include +#include "kis_embedded_pattern_manager.h" + +#include +#include + +#include +#include +#include + +#include + +/**********************************************************************/ +/* KisTextureMaskInfo */ +/**********************************************************************/ + + +KisTextureMaskInfo::KisTextureMaskInfo(int levelOfDetail) + : m_levelOfDetail(levelOfDetail) +{ +} + +KisTextureMaskInfo::KisTextureMaskInfo(const KisTextureMaskInfo &rhs) + : m_levelOfDetail(rhs.m_levelOfDetail), + m_pattern(rhs.m_pattern), + m_scale(rhs.m_scale), + m_brightness(rhs.m_brightness), + m_contrast(rhs.m_contrast), + m_invert(rhs.m_invert), + m_cutoffLeft(rhs.m_cutoffLeft), + m_cutoffRight(rhs.m_cutoffRight), + m_cutoffPolicy(rhs.m_cutoffPolicy) +{ +} + +KisTextureMaskInfo::~KisTextureMaskInfo() +{ +} + +bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs) { + return + lhs.m_levelOfDetail == rhs.m_levelOfDetail && + (lhs.m_pattern == rhs.m_pattern || + (lhs.m_pattern && + rhs.m_pattern && + lhs.m_pattern->md5() == rhs.m_pattern->md5())) && + qFuzzyCompare(lhs.m_scale, rhs.m_scale) && + qFuzzyCompare(lhs.m_brightness, rhs.m_brightness) && + qFuzzyCompare(lhs.m_contrast, rhs.m_contrast) && + lhs.m_invert == rhs.m_invert && + lhs.m_cutoffLeft == rhs.m_cutoffLeft && + lhs.m_cutoffRight == rhs.m_cutoffRight && + lhs.m_cutoffPolicy == rhs.m_cutoffPolicy; +} + +KisTextureMaskInfo &KisTextureMaskInfo::operator=(const KisTextureMaskInfo &rhs) +{ + m_levelOfDetail = rhs.m_levelOfDetail; + m_pattern = rhs.m_pattern; + m_scale = rhs.m_scale; + m_brightness = rhs.m_brightness; + m_contrast = rhs.m_contrast; + m_invert = rhs.m_invert; + m_cutoffLeft = rhs.m_cutoffLeft; + m_cutoffRight = rhs.m_cutoffRight; + m_cutoffPolicy = rhs.m_cutoffPolicy; + + return *this; +} + +int KisTextureMaskInfo::levelOfDetail() const { + return m_levelOfDetail; +} + +bool KisTextureMaskInfo::hasMask() const { + return m_mask; +} + +KisPaintDeviceSP KisTextureMaskInfo::mask() { + return m_mask; +} + +QRect KisTextureMaskInfo::maskBounds() const { + return m_maskBounds; +} + +bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting) +{ + + if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { + return false; + } + + m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); + + if (!m_pattern) { + warnKrita << "WARNING: Couldn't load the pattern for a stroke"; + return false; + } + + m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); + m_brightness = setting->getDouble("Texture/Pattern/Brightness"); + m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0); + m_invert = setting->getBool("Texture/Pattern/Invert"); + m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); + m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); + m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); + + return true; +} + +void KisTextureMaskInfo::recalculateMask() +{ + if (!m_pattern) return; + + const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); + + if (!m_mask) { + m_mask = new KisPaintDevice(cs); + } + + QImage mask = m_pattern->pattern(); + + if ((mask.format() != QImage::Format_RGB32) | + (mask.format() != QImage::Format_ARGB32)) { + + mask = mask.convertToFormat(QImage::Format_ARGB32); + } + + qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail); + + if (!qFuzzyCompare(scale, 0.0)) { + QTransform tf; + tf.scale(scale, scale); + QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); + mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + const QRgb* pixel = reinterpret_cast(mask.constBits()); + const int width = mask.width(); + const int height = mask.height(); + + KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); + + for (int row = 0; row < height; ++row) { + for (int col = 0; col < width; ++col) { + const QRgb currentPixel = pixel[row * width + col]; + + const int red = qRed(currentPixel); + const int green = qGreen(currentPixel); + const int blue = qBlue(currentPixel); + float alpha = qAlpha(currentPixel) / 255.0; + + const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; + float maskValue = (grayValue / 255.0) * alpha + (1 - alpha); + + maskValue = maskValue - m_brightness; + + maskValue = ((maskValue - 0.5)*m_contrast)+0.5; + + if (maskValue > 1.0) {maskValue = 1;} + else if (maskValue < 0) {maskValue = 0;} + + if (m_invert) { + maskValue = 1 - maskValue; + } + + if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { + // mask out the dab if it's outside the pattern's cuttoff points + maskValue = OPACITY_TRANSPARENT_F; + } + else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { + maskValue = OPACITY_OPAQUE_F; + } + + cs->setOpacity(iter->rawData(), maskValue, 1); + iter->nextPixel(); + } + iter->nextRow(); + } + + m_maskBounds = QRect(0, 0, width, height); +} + +/**********************************************************************/ +/* KisTextureMaskInfoCache */ +/**********************************************************************/ + +Q_GLOBAL_STATIC(KisTextureMaskInfoCache, s_instance) + +KisTextureMaskInfoCache *KisTextureMaskInfoCache::instance() +{ + return s_instance; +} + +KisTextureMaskInfoSP KisTextureMaskInfoCache::fetchCachedTextureInfo(KisTextureMaskInfoSP info) { + QMutexLocker locker(&m_mutex); + + KisTextureMaskInfoSP &cachedInfo = + info->levelOfDetail() > 0 ? m_lodInfo : m_mainInfo; + + if (!cachedInfo || *cachedInfo != *info) { + cachedInfo = info; + cachedInfo->recalculateMask(); + } + + return cachedInfo; +} diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.h b/plugins/paintops/libpaintop/KisTextureMaskInfo.h new file mode 100644 index 0000000000..9ac1834388 --- /dev/null +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISTEXTUREMASKINFO_H +#define KISTEXTUREMASKINFO_H + + +#include +#include +#include + + +#include + +class KoPattern; +class KisTextureMaskInfo; + +class KisTextureMaskInfo : public boost::equality_comparable +{ +public: + KisTextureMaskInfo(int levelOfDetail); + KisTextureMaskInfo(const KisTextureMaskInfo &rhs); + + ~KisTextureMaskInfo(); + + friend bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs); + + KisTextureMaskInfo& operator=(const KisTextureMaskInfo &rhs); + + int levelOfDetail() const; + + bool hasMask() const; + + KisPaintDeviceSP mask(); + + QRect maskBounds() const; + + bool fillProperties(const KisPropertiesConfigurationSP setting); + + void recalculateMask(); + +private: + int m_levelOfDetail = 0; + + KoPattern *m_pattern = 0; + + qreal m_scale = 1.0; + qreal m_brightness = 0.0; + qreal m_contrast = 1.0; + bool m_invert = false; + + int m_cutoffLeft = 0; + int m_cutoffRight = 255; + int m_cutoffPolicy = 0; + + KisPaintDeviceSP m_mask; + QRect m_maskBounds; + +}; + +typedef QSharedPointer KisTextureMaskInfoSP; + +struct KisTextureMaskInfoCache +{ + static KisTextureMaskInfoCache *instance(); + KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info); + +private: + QMutex m_mutex; + QSharedPointer m_lodInfo; + QSharedPointer m_mainInfo; +}; + +#endif // KISTEXTUREMASKINFO_H diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 97586937c5..36e202c68b 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,349 +1,273 @@ /* 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 "kis_lod_transform.h" #include #include "kis_texture_chooser.h" #include + KisTextureOption::KisTextureOption() : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) , m_textureOptions(new KisTextureChooser()) { setObjectName("KisTextureOption"); setConfigurationPage(m_textureOptions); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged())); connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_textureOptions->textureSelectorWidget->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_textureOptions; } void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking if (!m_textureOptions->textureSelectorWidget->currentResource()) return; KoPattern *pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking if (!pattern) return; setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_textureOptions->scaleSlider->value(); qreal brightness = m_textureOptions->brightnessSlider->value(); qreal contrast = m_textureOptions->contrastSlider->value(); int offsetX = m_textureOptions->offsetSliderX->value(); if (m_textureOptions ->randomOffsetX->isChecked()) { m_textureOptions->offsetSliderX ->setEnabled(false); m_textureOptions->offsetSliderX ->blockSignals(true); m_textureOptions->offsetSliderX ->setValue(offsetX); m_textureOptions->offsetSliderX ->blockSignals(false); } else { m_textureOptions->offsetSliderX ->setEnabled(true); } int offsetY = m_textureOptions->offsetSliderY->value(); if (m_textureOptions ->randomOffsetY->isChecked()) { m_textureOptions->offsetSliderY ->setEnabled(false); m_textureOptions->offsetSliderY ->blockSignals(true); m_textureOptions->offsetSliderY ->setValue(offsetY); m_textureOptions->offsetSliderY ->blockSignals(false); } else { m_textureOptions->offsetSliderY ->setEnabled(true); } int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex(); bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/Brightness", brightness); setting->setProperty("Texture/Pattern/Contrast", contrast); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!pattern) { pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); } m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern); m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness")); m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0)); m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY")); m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const { l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)")); } void KisTextureOption::resetGUI(KoResource* res) { KoPattern *pattern = static_cast(res); if (!pattern) return; m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } -KisTextureProperties::KisTextureProperties(int levelOfDetail) - : m_pattern(0), - m_levelOfDetail(levelOfDetail) -{ -} - -void KisTextureProperties::recalculateMask() -{ - if (!m_pattern) return; - - m_mask = 0; - - QImage mask = m_pattern->pattern(); - - if ((mask.format() != QImage::Format_RGB32) | - (mask.format() != QImage::Format_ARGB32)) { - - mask = mask.convertToFormat(QImage::Format_ARGB32); - } - - qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail); - - if (!qFuzzyCompare(scale, 0.0)) { - QTransform tf; - tf.scale(scale, scale); - QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); - mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - const QRgb* pixel = reinterpret_cast(mask.constBits()); - 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); - - maskValue = maskValue - m_brightness; +/**********************************************************************/ +/* KisTextureProperties */ +/**********************************************************************/ - maskValue = ((maskValue - 0.5)*m_contrast)+0.5; - if (maskValue > 1.0) {maskValue = 1;} - else if (maskValue < 0) {maskValue = 0;} - - if (m_invert) { - maskValue = 1 - maskValue; - } - - if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { - // mask out the dab if it's outside the pattern's cuttoff points - maskValue = OPACITY_TRANSPARENT_F; - } - else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { - maskValue = OPACITY_OPAQUE_F; - } - - cs->setOpacity(iter->rawData(), maskValue, 1); - iter->nextPixel(); - } - iter->nextRow(); - } - - m_maskBounds = QRect(0, 0, width, height); +KisTextureProperties::KisTextureProperties(int levelOfDetail) + : m_levelOfDetail(levelOfDetail) +{ } - void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { m_enabled = false; return; } - m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); - - if (!m_pattern) { + m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail)); + if (!m_maskInfo->fillProperties(setting)) { 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_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); - m_brightness = setting->getDouble("Texture/Pattern/Brightness"); - m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0); m_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 (!m_enabled) return; KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); - int x = offset.x() % m_maskBounds.width() - m_offsetX; - int y = offset.y() % m_maskBounds.height() - m_offsetY; + 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, m_mask, m_maskBounds); + 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 b37c0f33d0..e6ff1c5873 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,106 +1,98 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_TEXTURE_OPTION_H #define KIS_TEXTURE_OPTION_H #include #include #include #include "kis_paintop_option.h" #include "kis_pressure_texture_strength_option.h" +#include "KisTextureMaskInfo.h" + #include class KisTextureChooser; class KisTextureOptionWidget; class KoPattern; class KoResource; class KisPropertiesConfiguration; class KisPaintopLodLimitations; 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(KoResource*); /// called when a new pattern is selected private: /// UI Widget that stores all the texture options KisTextureChooser* m_textureOptions; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties(int levelOfDetail); enum TexturingMode { MULTIPLY, SUBTRACT }; bool m_enabled; /** * @brief apply combine the texture map with the dab * @param dab the colored, final representation of the dab, after mirroring and everything. * @param offset the position of the dab on the image. used to calculate the position of the mask pattern */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); void fillProperties(const KisPropertiesConfigurationSP setting); private: - qreal m_scale; int m_offsetX; int m_offsetY; - qreal m_brightness; - qreal m_contrast; TexturingMode m_texturingMode; - bool m_invert; - KoPattern *m_pattern; - int m_cutoffLeft; - int m_cutoffRight; - int m_cutoffPolicy; int m_levelOfDetail; 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(); + KisTextureMaskInfoSP m_maskInfo; }; #endif // KIS_TEXTURE_OPTION_H