diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -82,6 +82,7 @@ private: qreal m_scale; + int m_rotation; int m_offsetX; int m_offsetY; TexturingMode m_texturingMode; diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ #include +#include #include class KisTextureOptionWidget : public QWidget @@ -80,6 +82,15 @@ formLayout->addRow(i18n("Scale:"), scaleSlider); + QBoxLayout *rotationLayout = new QBoxLayout(QBoxLayout::LeftToRight); + rotationSlider = new KisSliderSpinBox(this); + rotationSlider->setRange(0, 359); + rotationSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + rotationSlider->setSuffix(i18n(" deg")); + rotationLayout->addWidget(rotationSlider,1,0); + formLayout->addRow(i18n("Rotation:"), rotationLayout); + + QBoxLayout *offsetLayoutX = new QBoxLayout(QBoxLayout::LeftToRight); offsetSliderX = new KisSliderSpinBox(this); offsetSliderX->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -130,6 +141,7 @@ } KisPatternChooser *chooser; KisMultipliersDoubleSliderSpinBox *scaleSlider; + KisSliderSpinBox *rotationSlider; KisSliderSpinBox *offsetSliderX; QCheckBox *randomOffsetX; KisSliderSpinBox *offsetSliderY; @@ -153,6 +165,7 @@ connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged())); connect(m_optionWidget->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); + connect(m_optionWidget->rotationSlider, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_optionWidget->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); @@ -183,6 +196,7 @@ return; } + qreal rotation = m_optionWidget->rotationSlider->value(); qreal scale = m_optionWidget->scaleSlider->value(); int offsetX = m_optionWidget->offsetSliderX->value(); @@ -213,6 +227,7 @@ bool invert = (m_optionWidget->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); + setting->setProperty("Texture/Pattern/Rotation", rotation); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); @@ -244,6 +259,7 @@ m_optionWidget->chooser->setCurrentPattern(pattern); m_optionWidget->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); + m_optionWidget->rotationSlider->setValue(setting->getInt("Texture/Pattern/Rotation")); m_optionWidget->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_optionWidget->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_optionWidget->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); @@ -358,6 +374,7 @@ m_enabled = setting->getBool("Texture/Pattern/Enabled", false); m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); + m_rotation = setting->getInt("Texture/Pattern/Rotation"); m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); @@ -378,24 +395,53 @@ 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; + qreal pressure = m_strengthOption.apply(info); + quint8 *dabData = dab->data(); KisFillPainter fillPainter(fillDevice); - fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, m_mask, m_maskBounds); - fillPainter.end(); + int x, y; + int rw = rect.width(); + int rh = rect.height(); + + if (m_rotation == 0) { + // default way for large patterns or for rotation of 0 deg. + x = offset.x() % m_maskBounds.width() - m_offsetX; + y = offset.y() % m_maskBounds.height() - m_offsetY; + + fillPainter.fillRect(x - 1, y - 1, rw + 2, rh + 2, m_mask, m_maskBounds); + fillPainter.end(); + } else { + // Find the coordinates within the pattern which correspond to the dab location + QTransform backwards; + backwards.rotate(-m_rotation); + QPointF uv = backwards.map(QPointF(offset)); + float u = fmod(uv.x(), m_maskBounds.width()); + float v = fmod(uv.y(), m_maskBounds.height()); + if (u < 0) u += m_maskBounds.width(); + if (v < 0) v += m_maskBounds.height(); + + // Around said point, fill a region in fillDevice with the pattern + // TODO: use a more fitting fill area + double r = sqrt(rw * rw + rh * rh); + fillPainter.fillRect(u - r - 1, v - r - 1, 2*(r + 1), 2*(r + 1), m_mask, m_maskBounds); + fillPainter.end(); + + // Rotate fillDevice and move the point (u, v) to the origin + QTransform tf; + tf.rotate(m_rotation); + tf.translate(-u, -v); + KisPerspectiveTransformWorker worker(fillDevice, tf, NULL); + worker.run(); - qreal pressure = m_strengthOption.apply(info); - quint8 *dabData = dab->data(); + x = y = 0; + } - KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); - for (int row = 0; row < rect.height(); ++row) { - for (int col = 0; col < rect.width(); ++col) { + KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rw); + for (int row = 0; row < rh; ++row) { + for (int col = 0; col < rw; ++col) { if (m_texturingMode == MULTIPLY) { dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1); - } - else { + } else { int pressureOffset = (1.0 - pressure) * 255; qint16 maskA = *iter->oldRawData() + pressureOffset; @@ -404,11 +450,9 @@ dabA = qMax(0, (qint16)dabA - maskA); dab->colorSpace()->setOpacity(dabData, dabA, 1); } - iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } -