From kde-kimageshop Fri Oct 17 13:16:04 2014 From: Dmitry Kazakov Date: Fri, 17 Oct 2014 13:16:04 +0000 To: kde-kimageshop Subject: [calligra/krita-chili-kazakov] krita: [FEATURE] Implemented Wash mode for the liquify transform tool Message-Id: X-MARC-Message: https://marc.info/?l=kde-kimageshop&m=141355178113156 Git commit 16d55c02bb56c766892717d3508f7f13620f947d by Dmitry Kazakov. Committed on 17/10/2014 at 13:15. Pushed by dkazakov into branch 'krita-chili-kazakov'. [FEATURE] Implemented Wash mode for the liquify transform tool There is a fundamental difference between Wash mode and BuildUp mode of the liquify brush. When you paint on the same place with in BuildUp (default) mode it adds deformations infinitely, until you hardly see the original piece. When you use Wash mode, all deformations are limited by the Amount value, that is they will not exceed this level. You can also control how fast the deformations will rush to the maximum level by adjusting 'Flow' parameter, which is available only in this mode. CCMAIL:kimageshop@kde.org M +78 -10 krita/image/kis_liquify_transform_worker.cpp M +9 -3 krita/image/kis_liquify_transform_worker.h M +6 -6 krita/image/tests/kis_liquify_transform_worker_test.cpp M +6 -5 krita/plugins/tools/tool_transform2/kis_liquify_paintop.cpp M +5 -0 krita/plugins/tools/tool_transform2/kis_liquify_properties.cpp M +20 -1 krita/plugins/tools/tool_transform2/kis_liquify_properties.h M +47 -0 krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp M +2 -0 krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h M +22 -5 krita/plugins/tools/tool_transform2/wdg_tool_transform.ui http://commits.kde.org/calligra/16d55c02bb56c766892717d3508f7f13620f947d diff --git a/krita/image/kis_liquify_transform_worker.cpp b/krita/image/kis_liquify_transform_worker.cpp index a8ed648..3e4c850 100644 --- a/krita/image/kis_liquify_transform_worker.cpp +++ b/krita/image/kis_liquify_transform_worker.cpp @@ -45,9 +45,22 @@ struct KisLiquifyTransformWorker::Private struct MapIndexesOp; template + void processTransformedPixelsBuildUp(ProcessOp op, + const QPointF &base, + qreal sigma); + + template + void processTransformedPixelsWash(ProcessOp op, + const QPointF &base, + qreal sigma, + qreal flow); + + template void processTransformedPixels(ProcessOp op, const QPointF &base, - qreal sigma); + qreal sigma, + bool useWashMode, + qreal flow); }; KisLiquifyTransformWorker::KisLiquifyTransformWorker(const QRect &srcBounds, @@ -159,9 +172,9 @@ void KisLiquifyTransformWorker::undoPoints(const QPointF &base, template void KisLiquifyTransformWorker::Private:: -processTransformedPixels(ProcessOp op, - const QPointF &base, - qreal sigma) +processTransformedPixelsBuildUp(ProcessOp op, + const QPointF &base, + qreal sigma) { const qreal maxDist = ProcessOp::maxDistCoeff * sigma; QRectF clipRect(base.x() - maxDist, base.y() - maxDist, @@ -182,6 +195,55 @@ processTransformedPixels(ProcessOp op, } } +template +void KisLiquifyTransformWorker::Private:: +processTransformedPixelsWash(ProcessOp op, + const QPointF &base, + qreal sigma, + qreal flow) +{ + const qreal maxDist = ProcessOp::maxDistCoeff * sigma; + QRectF clipRect(base.x() - maxDist, base.y() - maxDist, + 2 * maxDist, 2 * maxDist); + + QVector::iterator it = transformedPoints.begin(); + QVector::iterator end = transformedPoints.end(); + + QVector::iterator refIt = originalPoints.begin(); + KIS_ASSERT_RECOVER_RETURN(originalPoints.size() == + transformedPoints.size()); + + for (; it != end; ++it, ++refIt) { + if (!clipRect.contains(*it)) continue; + + QPointF diff = *refIt - base; + qreal dist = KisAlgebra2D::norm(diff); + if (dist > maxDist) continue; + + const qreal lambda = exp(-0.5 * pow2(dist / sigma)); + QPointF dstPt = op(*refIt, base, diff, lambda); + + if (kisDistance(dstPt, *refIt) > kisDistance(*it, *refIt)) { + *it = (1.0 - flow) * (*it) + flow * dstPt; + } + } +} + +template +void KisLiquifyTransformWorker::Private:: +processTransformedPixels(ProcessOp op, + const QPointF &base, + qreal sigma, + bool useWashMode, + qreal flow) +{ + if (useWashMode) { + processTransformedPixelsWash(op, base, sigma, flow); + } else { + processTransformedPixelsBuildUp(op, base, sigma); + } +} + struct TranslateOp { TranslateOp(const QPointF &offset) : m_offset(offset) {} @@ -248,26 +310,32 @@ struct RotateOp void KisLiquifyTransformWorker::translatePoints(const QPointF &base, const QPointF &offset, - qreal sigma) + qreal sigma, + bool useWashMode, + qreal flow) { TranslateOp op(offset); - m_d->processTransformedPixels(op, base, sigma); + m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } void KisLiquifyTransformWorker::scalePoints(const QPointF &base, qreal scale, - qreal sigma) + qreal sigma, + bool useWashMode, + qreal flow) { ScaleOp op(scale); - m_d->processTransformedPixels(op, base, sigma); + m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } void KisLiquifyTransformWorker::rotatePoints(const QPointF &base, qreal angle, - qreal sigma) + qreal sigma, + bool useWashMode, + qreal flow) { RotateOp op(angle); - m_d->processTransformedPixels(op, base, sigma); + m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } struct KisLiquifyTransformWorker::Private::MapIndexesOp { diff --git a/krita/image/kis_liquify_transform_worker.h b/krita/image/kis_liquify_transform_worker.h index 8977097..b6f269b 100644 --- a/krita/image/kis_liquify_transform_worker.h +++ b/krita/image/kis_liquify_transform_worker.h @@ -45,15 +45,21 @@ public: void translatePoints(const QPointF &base, const QPointF &offset, - qreal sigma); + qreal sigma, + bool useWashMode, + qreal flow); void scalePoints(const QPointF &base, qreal scale, - qreal sigma); + qreal sigma, + bool useWashMode, + qreal flow); void rotatePoints(const QPointF &base, qreal angle, - qreal sigma); + qreal sigma, + bool useWashMode, + qreal flow); void undoPoints(const QPointF &base, qreal amount, diff --git a/krita/image/tests/kis_liquify_transform_worker_test.cpp b/krita/image/tests/kis_liquify_transform_worker_test.cpp index 507c0a4..180efac 100644 --- a/krita/image/tests/kis_liquify_transform_worker_test.cpp +++ b/krita/image/tests/kis_liquify_transform_worker_test.cpp @@ -148,11 +148,11 @@ void KisLiquifyTransformWorkerTest::testPoints() QBENCHMARK_ONCE { worker.translatePoints(QPointF(100,100), QPointF(50, 0), - 50); + 50, false, 0.2); worker.scalePoints(QPointF(400,100), 0.9, - 50); + 50, false, 0.2); worker.undoPoints(QPointF(400,100), 1.0, @@ -160,15 +160,15 @@ void KisLiquifyTransformWorkerTest::testPoints() worker.scalePoints(QPointF(400,300), 0.5, - 50); + 50, false, 0.2); worker.scalePoints(QPointF(100,300), -0.5, - 30); + 30, false, 0.2); worker.rotatePoints(QPointF(100,500), M_PI / 4, - 50); + 50, false, 0.2); } worker.run(dev); @@ -198,7 +198,7 @@ void KisLiquifyTransformWorkerTest::testPointsQImage() worker.translatePoints(QPointF(100,100), QPointF(50, 0), - 50); + 50, false, 0.2); QRect rc = dev->exactBounds(); dev->setX(50); diff --git a/krita/plugins/tools/tool_transform2/kis_liquify_paintop.cpp b/krita/plugins/tools/tool_transform2/kis_liquify_paintop.cpp index 478dc73..a7e07c7 100644 --- a/krita/plugins/tools/tool_transform2/kis_liquify_paintop.cpp +++ b/krita/plugins/tools/tool_transform2/kis_liquify_paintop.cpp @@ -121,32 +121,33 @@ KisSpacingInformation KisLiquifyPaintop::paintAt(const KisPaintInformation &pi) pi.pressure() * reverseCoeff * m_d->props.amount(): reverseCoeff * m_d->props.amount(); - + const bool useWashMode = m_d->props.useWashMode(); + const qreal flow = m_d->props.flow(); switch (m_d->props.mode()) { case KisLiquifyProperties::MOVE: { const qreal offsetLength = size * amount; m_d->worker->translatePoints(pi.pos(), pi.drawingDirectionVector() * offsetLength, - size); + size, useWashMode, flow); break; } case KisLiquifyProperties::SCALE: m_d->worker->scalePoints(pi.pos(), amount, - size); + size, useWashMode, flow); break; case KisLiquifyProperties::ROTATE: m_d->worker->rotatePoints(pi.pos(), 2.0 * M_PI * amount, - size); + size, useWashMode, flow); break; case KisLiquifyProperties::OFFSET: { const qreal offsetLength = size * amount; m_d->worker->translatePoints(pi.pos(), KisAlgebra2D::rightUnitNormal(pi.drawingDirectionVector()) * offsetLength, - size); + size, useWashMode, flow); break; } case KisLiquifyProperties::UNDO: diff --git a/krita/plugins/tools/tool_transform2/kis_liquify_properties.cpp b/krita/plugins/tools/tool_transform2/kis_liquify_properties.cpp index ad497fe..8f468a4 100644 --- a/krita/plugins/tools/tool_transform2/kis_liquify_properties.cpp +++ b/krita/plugins/tools/tool_transform2/kis_liquify_properties.cpp @@ -61,6 +61,9 @@ void KisLiquifyProperties::saveMode() const cfg.writeEntry("sizeHasPressure", m_sizeHasPressure); cfg.writeEntry("amountHasPressure", m_amountHasPressure); cfg.writeEntry("reverseDirection", m_reverseDirection); + cfg.writeEntry("useWashMode", m_useWashMode); + cfg.writeEntry("flow", m_flow); + } void KisLiquifyProperties::loadMode() @@ -74,4 +77,6 @@ void KisLiquifyProperties::loadMode() m_sizeHasPressure = cfg.readEntry("sizeHasPressure", m_sizeHasPressure); m_amountHasPressure = cfg.readEntry("amountHasPressure", m_amountHasPressure); m_reverseDirection = cfg.readEntry("reverseDirection", m_reverseDirection); + m_useWashMode = cfg.readEntry("useWashMode", m_useWashMode); + m_flow = cfg.readEntry("flow", m_flow); } diff --git a/krita/plugins/tools/tool_transform2/kis_liquify_properties.h b/krita/plugins/tools/tool_transform2/kis_liquify_properties.h index ab888aa..08e32c0 100644 --- a/krita/plugins/tools/tool_transform2/kis_liquify_properties.h +++ b/krita/plugins/tools/tool_transform2/kis_liquify_properties.h @@ -39,7 +39,9 @@ public: m_spacing(0.2), m_sizeHasPressure(false), m_amountHasPressure(false), - m_reverseDirection(false) + m_reverseDirection(false), + m_useWashMode(false), + m_flow(0.2) { } @@ -100,6 +102,20 @@ public: m_reverseDirection = value; } + bool useWashMode() const { + return m_useWashMode; + } + void setUseWashMode(bool value) { + m_useWashMode = value; + } + + qreal flow() const { + return m_flow; + } + void setFlow(qreal value) { + m_flow = value; + } + void saveMode() const; void loadMode(); @@ -111,6 +127,9 @@ private: bool m_sizeHasPressure; bool m_amountHasPressure; bool m_reverseDirection; + + bool m_useWashMode; + qreal m_flow; }; #endif /* __KIS_LIQUIFY_PROPERTIES_H */ diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp index ca1623f..841e0a2 100644 --- a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp +++ b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp @@ -144,6 +144,15 @@ KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionP connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal))); liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get")); + liquifyFlowSlider->setRange(0.0, 1.0, 2); + liquifyFlowSlider->setValue(1.0); + connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal))); + liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached.")); + + liquifyBuildUpBox->setChecked(true); + connect(liquifyBuildUpBox, SIGNAL(toggled(bool)), this, SLOT(liquifyBuildUpChanged(bool))); + liquifyBuildUpBox->setToolTip(i18nc("@info:tooltip", "Switch between Build Up and Wash mode of painting. Build Up mode adds deformations one on top of the other without any limits. Wash mode gradually deforms the piece to the selected deformation level.")); + liquifySpacingSlider->setRange(0.0, 3.0, 2); liquifySizeSlider->setExponentRatio(3); liquifySpacingSlider->setSingleStep(0.01); @@ -265,8 +274,12 @@ void KisToolTransformConfigWidget::updateLiquifyControls() KisLiquifyProperties *props = config->liquifyProperties(); + const bool useWashMode = props->useWashMode(); + liquifySizeSlider->setValue(props->size()); liquifyAmountSlider->setValue(props->amount()); + liquifyFlowSlider->setValue(props->flow()); + liquifyBuildUpBox->setChecked(!useWashMode); liquifySpacingSlider->setValue(props->spacing()); liquifySizePressureBox->setChecked(props->sizeHasPressure()); liquifyAmountPressureBox->setChecked(props->amountHasPressure()); @@ -279,7 +292,14 @@ void KisToolTransformConfigWidget::updateLiquifyControls() bool canInverseDirection = mode != KisLiquifyProperties::UNDO; + bool canUseWashMode = mode != KisLiquifyProperties::UNDO; + liquifyReverseDirectionChk->setEnabled(canInverseDirection); + liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode); + liquifyBuildUpBox->setEnabled(canUseWashMode); + + const qreal maxAmount = canUseWashMode ? 5.0 : 1.0; + liquifyAmountSlider->setRange(0.0, maxAmount, 2); unblockUiSlots(); } @@ -330,6 +350,33 @@ void KisToolTransformConfigWidget::liquifyAmountChanged(qreal value) notifyConfigChanged(); } +void KisToolTransformConfigWidget::liquifyFlowChanged(qreal value) +{ + if (m_uiSlotsBlocked) return; + + ToolTransformArgs *config = m_transaction->currentConfig(); + KisLiquifyProperties *props = + config->liquifyProperties(); + + props->setFlow(value); + notifyConfigChanged(); +} + +void KisToolTransformConfigWidget::liquifyBuildUpChanged(bool value) +{ + if (m_uiSlotsBlocked) return; + + ToolTransformArgs *config = m_transaction->currentConfig(); + KisLiquifyProperties *props = + config->liquifyProperties(); + + props->setUseWashMode(!value); + notifyConfigChanged(); + + // we need to enable/disable flow slider + updateLiquifyControls(); +} + void KisToolTransformConfigWidget::liquifySpacingChanged(qreal value) { if (m_uiSlotsBlocked) return; diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h index 94cf109..6dc79f1 100644 --- a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h +++ b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h @@ -90,6 +90,8 @@ public slots: void liquifySizeChanged(qreal value); void liquifyAmountChanged(qreal value); + void liquifyFlowChanged(qreal value); + void liquifyBuildUpChanged(bool value); void liquifySpacingChanged(qreal value); void liquifySizePressureChanged(bool value); void liquifyAmountPressureChanged(bool value); diff --git a/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui b/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui index 47f13ea..52284c9 100755 --- a/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui +++ b/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui @@ -1054,7 +1054,7 @@ big! true - buttonGroup + buttonGroup @@ -1120,7 +1120,7 @@ big! true - buttonGroup + buttonGroup @@ -1373,17 +1373,17 @@ big! - + Spacing: - + - + @@ -1399,6 +1399,23 @@ big! + + + + Flow: + + + + + + + + + + Build Up + + + _______________________________________________ Krita mailing list kimageshop@kde.org https://mail.kde.org/mailman/listinfo/kimageshop