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