1 /* This file is part of the KDE project
2  * Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2012
3  * Copyright (C) Mohit Goyal <mohit.bits2011@gmail.com>, (C) 2014
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 #include "kis_texture_option.h"
21 
22 #include <QWidget>
23 #include <QString>
24 #include <QByteArray>
25 #include <QCheckBox>
26 #include <QBuffer>
27 #include <QFormLayout>
28 #include <QLabel>
29 #include <QComboBox>
30 #include <QTransform>
31 #include <QPainter>
32 #include <QBoxLayout>
33 
34 #include <klocalizedstring.h>
35 
36 #include <kis_pattern_chooser.h>
37 #include <kis_slider_spin_box.h>
38 #include <kis_multipliers_double_slider_spinbox.h>
39 #include <resources/KoPattern.h>
40 #include <resources/KoAbstractGradient.h>
41 #include <resources/KoResource.h>
42 #include <KoResourceServerProvider.h>
43 #include <kis_paint_device.h>
44 #include <kis_fill_painter.h>
45 #include <kis_painter.h>
46 #include <kis_iterator_ng.h>
47 #include <kis_fixed_paint_device.h>
48 #include <KisGradientSlider.h>
49 #include "kis_embedded_pattern_manager.h"
50 #include <brushengine/kis_paintop_lod_limitations.h>
51 #include "kis_texture_chooser.h"
52 #include "KoMixColorsOp.h"
53 #include <time.h>
54 
55 
56 
KisTextureOption()57 KisTextureOption::KisTextureOption()
58     : KisPaintOpOption(KisPaintOpOption::TEXTURE, true)
59     , m_textureOptions(new KisTextureChooser())
60 {
61     setObjectName("KisTextureOption");
62     setConfigurationPage(m_textureOptions);
63 
64     connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*)));
65     connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged()));
66     connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
67     connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
68     connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
69     connect(m_textureOptions->neutralPointSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
70     connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
71     connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
72     connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
73     connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
74     connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
75     connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
76     connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged()));
77     connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged()));
78     connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
79     resetGUI(m_textureOptions->textureSelectorWidget->currentResource());
80 
81 }
82 
~KisTextureOption()83 KisTextureOption::~KisTextureOption()
84 {
85     delete m_textureOptions;
86 }
87 
writeOptionSetting(KisPropertiesConfigurationSP setting) const88 void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const
89 {
90      m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking
91     if (!m_textureOptions->textureSelectorWidget->currentResource()) return;
92     KoPattern *pattern = static_cast<KoPattern*>(m_textureOptions->textureSelectorWidget->currentResource());
93     m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking
94     if (!pattern) return;
95 
96     setting->setProperty("Texture/Pattern/Enabled", isChecked());
97     if (!isChecked()) {
98         return;
99     }
100 
101     qreal scale = m_textureOptions->scaleSlider->value();
102 
103     qreal brightness = m_textureOptions->brightnessSlider->value();
104 
105     qreal contrast = m_textureOptions->contrastSlider->value();
106 
107     qreal neutralPoint = m_textureOptions->neutralPointSlider->value();
108 
109     int offsetX = m_textureOptions->offsetSliderX->value();
110     if (m_textureOptions ->randomOffsetX->isChecked()) {
111 
112         m_textureOptions->offsetSliderX ->setEnabled(false);
113         m_textureOptions->offsetSliderX ->blockSignals(true);
114         m_textureOptions->offsetSliderX ->setValue(offsetX);
115         m_textureOptions->offsetSliderX ->blockSignals(false);
116     }
117     else {
118         m_textureOptions->offsetSliderX ->setEnabled(true);
119     }
120 
121     int offsetY = m_textureOptions->offsetSliderY->value();
122     if (m_textureOptions ->randomOffsetY->isChecked()) {
123 
124         m_textureOptions->offsetSliderY ->setEnabled(false);
125         m_textureOptions->offsetSliderY ->blockSignals(true);
126         m_textureOptions->offsetSliderY ->setValue(offsetY);
127         m_textureOptions->offsetSliderY ->blockSignals(false);
128     }
129     else {
130         m_textureOptions->offsetSliderY ->setEnabled(true);
131     }
132 
133     int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex();
134     bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked);
135 
136     setting->setProperty("Texture/Pattern/Scale", scale);
137     setting->setProperty("Texture/Pattern/Brightness", brightness);
138     setting->setProperty("Texture/Pattern/Contrast", contrast);
139     setting->setProperty("Texture/Pattern/NeutralPoint", neutralPoint);
140     setting->setProperty("Texture/Pattern/OffsetX", offsetX);
141     setting->setProperty("Texture/Pattern/OffsetY", offsetY);
142     setting->setProperty("Texture/Pattern/TexturingMode", texturingMode);
143     setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black());
144     setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white());
145     setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex());
146     setting->setProperty("Texture/Pattern/Invert", invert);
147 
148     setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum());
149     setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum());
150     setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked());
151     setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked());
152 
153     KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern);
154 }
155 
readOptionSetting(const KisPropertiesConfigurationSP setting)156 void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting)
157 {
158 
159     setChecked(setting->getBool("Texture/Pattern/Enabled"));
160     if (!isChecked()) {
161         return;
162     }
163     KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
164 
165     if (!pattern) {
166         pattern = static_cast<KoPattern*>(m_textureOptions->textureSelectorWidget->currentResource());
167     }
168 
169     m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern);
170 
171     m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0));
172     m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness"));
173     m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0));
174     m_textureOptions->neutralPointSlider->setValue(setting->getDouble("Texture/Pattern/NeutralPoint", 0.5));
175     m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX"));
176     m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY"));
177     m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX"));
178     m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY"));
179     m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY));
180     m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy"));
181     m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0));
182     m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255));
183     m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert"));
184 
185 }
186 
lodLimitations(KisPaintopLodLimitations * l) const187 void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const
188 {
189     l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)"));
190 }
191 
192 
resetGUI(KoResource * res)193 void KisTextureOption::resetGUI(KoResource* res)
194 {
195     KoPattern *pattern = static_cast<KoPattern *>(res);
196     if (!pattern) return;
197 
198     m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2);
199     m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2);
200 }
201 
202 /**********************************************************************/
203 /*       KisTextureProperties                                         */
204 /**********************************************************************/
205 
206 
KisTextureProperties(int levelOfDetail)207 KisTextureProperties::KisTextureProperties(int levelOfDetail)
208     : m_levelOfDetail(levelOfDetail),
209       m_gradient(0)
210 {
211 }
212 
fillProperties(const KisPropertiesConfigurationSP setting)213 void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting)
214 {
215 
216     if (!setting->hasProperty("Texture/Pattern/PatternMD5")) {
217         m_enabled = false;
218         return;
219     }
220 
221     m_texturingMode = (TexturingMode)setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY);
222 
223     bool preserveAlpha = m_texturingMode == LIGHTNESS || m_texturingMode == GRADIENT;
224 
225     m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail, preserveAlpha));
226     if (!m_maskInfo->fillProperties(setting)) {
227         warnKrita << "WARNING: Couldn't load the pattern for a stroke";
228         m_enabled = false;
229         return;
230     }
231 
232     m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo);
233 
234     m_enabled = setting->getBool("Texture/Pattern/Enabled", false);
235     m_offsetX = setting->getInt("Texture/Pattern/OffsetX");
236     m_offsetY = setting->getInt("Texture/Pattern/OffsetY");
237 
238     m_strengthOption.readOptionSetting(setting);
239     m_strengthOption.resetAllSensors();
240 }
241 
setTextureGradient(const KoAbstractGradient * gradient)242 void KisTextureProperties::setTextureGradient(const KoAbstractGradient* gradient) {
243     if (gradient) {
244         m_gradient = gradient;
245         m_cachedGradient.setGradient(gradient, 256);
246     }
247 }
248 
applyingGradient() const249 bool KisTextureProperties::applyingGradient() const
250 {
251     return m_texturingMode == GRADIENT;
252 }
253 
applyLightness(KisFixedPaintDeviceSP dab,const QPoint & offset,const KisPaintInformation & info)254 void KisTextureProperties::applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) {
255     if (!m_enabled) return;
256 
257     KisPaintDeviceSP mask = m_maskInfo->mask();
258     const QRect maskBounds = m_maskInfo->maskBounds();
259 
260     KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
261     const QRect rect = dab->bounds();
262 
263     KIS_SAFE_ASSERT_RECOVER_RETURN(mask);
264 
265     int x = offset.x() % maskBounds.width() - m_offsetX;
266     int y = offset.y() % maskBounds.height() - m_offsetY;
267 
268     KisFillPainter fillMaskPainter(fillMaskDevice);
269     fillMaskPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds);
270     fillMaskPainter.end();
271 
272     qreal pressure = m_strengthOption.apply(info);
273     quint8* dabData = dab->data();
274 
275     KisSequentialConstIterator it(fillMaskDevice, QRect(x, y, rect.width(), rect.height()));
276     while (it.nextPixel()) {
277         const QRgb *maskQRgb = reinterpret_cast<const QRgb*>(it.oldRawData());
278         dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, maskQRgb, dabData, pressure, 1);
279         dabData += dab->pixelSize();
280     }
281 }
282 
applyGradient(KisFixedPaintDeviceSP dab,const QPoint & offset,const KisPaintInformation & info)283 void KisTextureProperties::applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) {
284     if (!m_enabled) return;
285 
286     KIS_SAFE_ASSERT_RECOVER_RETURN(m_gradient && m_gradient->valid());
287 
288     KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
289     QRect rect = dab->bounds();
290 
291     KisPaintDeviceSP mask = m_maskInfo->mask();
292     const QRect maskBounds = m_maskInfo->maskBounds();
293 
294     KIS_SAFE_ASSERT_RECOVER_RETURN(mask);
295 
296     int x = offset.x() % maskBounds.width() - m_offsetX;
297     int y = offset.y() % maskBounds.height() - m_offsetY;
298 
299 
300     KisFillPainter fillPainter(fillDevice);
301     fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds);
302     fillPainter.end();
303 
304     qreal pressure = m_strengthOption.apply(info);
305     quint8* dabData = dab->data();
306 
307     //for gradient textures...
308     KoMixColorsOp* colorMix = dab->colorSpace()->mixColorsOp();
309     qint16 colorWeights[2];
310     colorWeights[0] = qRound(pressure * 255);
311     colorWeights[1] = 255 - colorWeights[0];
312     quint8* colors[2];
313     m_cachedGradient.setColorSpace(dab->colorSpace()); //Change colorspace here so we don't have to convert each pixel drawn
314 
315     KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
316     for (int row = 0; row < rect.height(); ++row) {
317         for (int col = 0; col < rect.width(); ++col) {
318 
319             const QRgb* maskQRgb = reinterpret_cast<const QRgb*>(iter->oldRawData());
320             qreal gradientvalue = qreal(qGray(*maskQRgb))/255.0;//qreal(*iter->oldRawData()) / 255.0;
321             KoColor paintcolor;
322             paintcolor.setColor(m_cachedGradient.cachedAt(gradientvalue), dab->colorSpace());
323             qreal paintOpacity = paintcolor.opacityF() * (qreal(qAlpha(*maskQRgb)) / 255.0);
324             paintcolor.setOpacity(qMin(paintOpacity, dab->colorSpace()->opacityF(dabData)));
325             colors[0] = paintcolor.data();
326             KoColor dabColor(dabData, dab->colorSpace());
327             colors[1] = dabColor.data();
328             colorMix->mixColors(colors, colorWeights, 2, dabData);
329 
330             iter->nextPixel();
331             dabData += dab->pixelSize();
332         }
333         iter->nextRow();
334     }
335 }
336 
apply(KisFixedPaintDeviceSP dab,const QPoint & offset,const KisPaintInformation & info)337 void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info)
338 {
339     if (!m_enabled) return;
340 
341     if (m_texturingMode == LIGHTNESS) {
342         applyLightness(dab, offset, info);
343         return;
344     }
345     else if (m_texturingMode == GRADIENT) {
346         applyGradient(dab, offset, info);
347         return;
348     }
349 
350     KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
351     QRect rect = dab->bounds();
352 
353     KisPaintDeviceSP mask = m_maskInfo->mask();
354     const QRect maskBounds = m_maskInfo->maskBounds();
355 
356     KIS_SAFE_ASSERT_RECOVER_RETURN(mask);
357 
358     int x = offset.x() % maskBounds.width() - m_offsetX;
359     int y = offset.y() % maskBounds.height() - m_offsetY;
360 
361 
362     KisFillPainter fillPainter(fillDevice);
363     fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds);
364     fillPainter.end();
365 
366     qreal pressure = m_strengthOption.apply(info);
367     quint8* dabData = dab->data();
368 
369     KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
370     for (int row = 0; row < rect.height(); ++row) {
371         for (int col = 0; col < rect.width(); ++col) {
372             if (m_texturingMode == MULTIPLY) {
373                 dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1);
374             }
375             else {
376                 int pressureOffset = (1.0 - pressure) * 255;
377 
378                 qint16 maskA = *iter->oldRawData() + pressureOffset;
379                 quint8 dabA = dab->colorSpace()->opacityU8(dabData);
380 
381                 dabA = qMax(0, (qint16)dabA - maskA);
382                 dab->colorSpace()->setOpacity(dabData, dabA, 1);
383             }
384 
385             iter->nextPixel();
386             dabData += dab->pixelSize();
387         }
388         iter->nextRow();
389     }
390 }
391