Commit f9de97b5 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Ported KisTransformVisitor to the processings framework

This fixes a very tricky bug as well: the clones store the shift
internally, so when we transform a source layer, we should transform
the clone's offset as well. So we need to represent our transformation
with a matrix to do this. Now there is a matrix and a test for it in
KisTransformWorker.

WARNING: there is a bug(?) in the worker. Its transformation differs 1-3
pixels from the theoretical values. Looks like there are some rounding
problem. Anyway, we can use it even in such a rought way.

The bug will be closed when the visitor will be added to KisImage
CCBUG:280502
parent 16cd7879
......@@ -93,6 +93,7 @@ set(kritaimage_LIB_SRCS
commands_new/kis_image_resize_command.cpp
commands_new/kis_update_command.cpp
processing/kis_crop_processing_visitor.cpp
processing/kis_transform_processing_visitor.cpp
filter/kis_filter.cc
filter/kis_filter_configuration.cc
filter/kis_filter_job.cpp
......
......@@ -95,6 +95,10 @@ public:
}
}
const QList<KisCloneLayerWSP> registeredClones() const {
return m_clonesList;
}
private:
QList<KisCloneLayerWSP> m_clonesList;
};
......@@ -249,6 +253,11 @@ void KisLayer::unregisterClone(KisCloneLayerWSP clone)
m_d->clonesList.removeClone(clone);
}
const QList<KisCloneLayerWSP> KisLayer::registeredClones() const
{
return m_d->clonesList.registeredClones();
}
KisSelectionMaskSP KisLayer::selectionMask() const
{
KoProperties properties;
......
......@@ -177,6 +177,13 @@ public:
* \see registerClone()
*/
void unregisterClone(KisCloneLayerWSP clone);
/**
* Return the list of the clones of this node. Be careful
* with the list, because it is not thread safe.
*/
const QList<KisCloneLayerWSP> registeredClones() const;
public:
qint32 x() const;
qint32 y() const;
......
......@@ -24,6 +24,8 @@
#include <qmath.h>
#include <klocale.h>
#include <QTransform>
#include <KoProgressUpdater.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
......@@ -68,6 +70,17 @@ KisTransformWorker::~KisTransformWorker()
{
}
QTransform KisTransformWorker::transform() const
{
QTransform TS = QTransform::fromTranslate(m_xshearOrigin, m_yshearOrigin);
QTransform S; S.shear(0, m_yshear); S.shear(m_xshear, 0);
QTransform SC = QTransform::fromScale(m_xscale, m_yscale);
QTransform R; R.rotateRadians(m_rotation);
QTransform T = QTransform::fromTranslate(m_xtranslate, m_ytranslate);
return TS.inverted() * S * TS * SC * R * T;
}
void KisTransformWorker::rotateNone(KisPaintDeviceSP src, KisPaintDeviceSP dst)
{
qint32 pixelSize = src->pixelSize();
......
......@@ -32,6 +32,7 @@ typedef QPointer<KoUpdater> KoUpdaterPtr;
class KisPaintDevice;
class KisFilterStrategy;
class KisSelection;
class QTransform;
class KRITAIMAGE_EXPORT KisTransformWorker
{
......@@ -74,6 +75,27 @@ public:
// returns false if interrupted
bool run();
/**
* Returns a matrix of the transformation executed by the worker.
* Resulting transformation has the following form (in Qt's matrix
* notation (all the matrices are trasposed)):
*
* transform = TS.inverted() * S * TS * SC * R * T
*
* ,where:
* TS - shear origin transpose
* S - shear itself (shearX * shearY)
* SC - scale
* R - rotation (@rotation parameter)
* T - transpose (@xtranslate, @ytranslate)
*
* WARNING: due to some rounding problems in the worker
* the work it does does not correspond to the matrix exactly!
* The result always differs 1-3 pixel. So be careful with it
* (or fix it)
*/
QTransform transform() const;
private:
// XXX (BSAR): Why didn't we use the shared-pointer versions of the paint device classes?
// CBR: because the template functions used within don't work if it's not true pointers
......
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 "kis_transform_processing_visitor.h"
#include "klocale.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "generator/kis_generator_layer.h"
#include "kis_transparency_mask.h"
#include "kis_filter_mask.h"
#include "kis_selection_mask.h"
#include "kis_external_layer_iface.h"
#include "kis_paint_device.h"
#include "kis_transaction.h"
#include "kis_undo_adapter.h"
#include "kis_transform_worker.h"
#include "commands_new/kis_node_move_command2.h"
KisTransformProcessingVisitor::
KisTransformProcessingVisitor(qreal xscale, qreal yscale,
qreal xshear, qreal yshear,
const QPointF &shearOrigin,
qreal angle,
qint32 tx, qint32 ty,
KoUpdater *progress, KisFilterStrategy *filter,
bool scaleOnlyShapes)
: m_sx(xscale), m_sy(yscale)
, m_tx(tx), m_ty(ty)
, m_shearx(xshear), m_sheary(yshear)
, m_shearOrigin(shearOrigin)
, m_filter(filter)
, m_angle(angle)
, m_progress(progress)
, m_scaleOnlyShapes(scaleOnlyShapes)
{
}
void KisTransformProcessingVisitor::visit(KisNode *node, KisUndoAdapter *undoAdapter)
{
Q_UNUSED(node);
Q_UNUSED(undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisPaintLayer *layer, KisUndoAdapter *undoAdapter)
{
transformPaintDevice(layer->paintDevice(), undoAdapter);
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisGroupLayer *layer, KisUndoAdapter *undoAdapter)
{
Q_UNUSED(undoAdapter);
layer->resetCache();
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisAdjustmentLayer *layer, KisUndoAdapter *undoAdapter)
{
transformSelection(layer->selection(), undoAdapter);
layer->resetCache();
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisExternalLayer *layer, KisUndoAdapter *undoAdapter)
{
KUndo2Command* command =
layer->transform(m_sx, m_sy, m_shearx, m_sheary,
m_angle, m_tx, m_ty);
if(command) {
undoAdapter->addCommand(command);
}
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisGeneratorLayer *layer, KisUndoAdapter *undoAdapter)
{
transformSelection(layer->selection(), undoAdapter);
layer->resetCache();
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisCloneLayer *layer, KisUndoAdapter *undoAdapter)
{
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisFilterMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisTransparencyMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter);
}
void KisTransformProcessingVisitor::transformClones(KisLayer *layer, KisUndoAdapter *undoAdapter)
{
QList<KisCloneLayerWSP> clones = layer->registeredClones();
foreach(KisCloneLayerSP clone, clones) {
// we have just casted an object from a weak pointer,
// so check validity first
if(!clone) continue;
KisTransformWorker tw(clone->projection(), m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, m_progress, m_filter, true);
QTransform trans = tw.transform();
QTransform offsetTrans = QTransform::fromTranslate(clone->x(), clone->y());
QTransform newTrans = trans.inverted() * offsetTrans * trans;
QPoint oldPos(clone->x(), clone->y());
QPoint newPos(newTrans.dx(), newTrans.dy());
KUndo2Command *command = new KisNodeMoveCommand2(clone, oldPos, newPos, undoAdapter);
undoAdapter->addCommand(command);
}
}
void KisTransformProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *adapter)
{
// FIXME: check whether this is still used
if(m_scaleOnlyShapes) return;
KisTransaction transaction(i18n("Transform Node"), device);
KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, m_progress, m_filter, true);
tw.run();
transaction.commit(adapter);
}
void KisTransformProcessingVisitor::transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter)
{
if(selection->hasPixelSelection()) {
transformPaintDevice(selection->pixelSelection(), adapter);
}
if (selection->hasShapeSelection()) {
KUndo2Command* command =
selection->shapeSelection()->transform(m_sx, m_sy, m_shearx, m_sheary, m_angle, m_tx, m_ty);
if (command) {
adapter->addCommand(command);
}
}
selection->updateProjection();
}
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 __KIS_TRANSFORM_PROCESSING_VISITOR_H
#define __KIS_TRANSFORM_PROCESSING_VISITOR_H
#include "kis_processing_visitor.h"
class KoUpdater;
class KisFilterStrategy;
class KRITAIMAGE_EXPORT KisTransformProcessingVisitor : public KisProcessingVisitor
{
public:
KisTransformProcessingVisitor(qreal xscale, qreal yscale,
qreal xshear, qreal yshear, const QPointF &shearOrigin, qreal angle,
qint32 tx, qint32 ty,
KoUpdater *progress, KisFilterStrategy *filter,
bool scaleOnlyShapes = false);
void visit(KisNode *node, KisUndoAdapter *undoAdapter);
void visit(KisPaintLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisGroupLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisAdjustmentLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisExternalLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisGeneratorLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisCloneLayer *layer, KisUndoAdapter *undoAdapter);
void visit(KisFilterMask *mask, KisUndoAdapter *undoAdapter);
void visit(KisTransparencyMask *mask, KisUndoAdapter *undoAdapter);
void visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter);
private:
void transformClones(KisLayer *layer, KisUndoAdapter *undoAdapter);
void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *adapter);
void transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter);
private:
qreal m_sx, m_sy;
qint32 m_tx, m_ty;
qreal m_shearx, m_sheary;
QPointF m_shearOrigin;
KisFilterStrategy *m_filter;
qreal m_angle;
KoUpdater *m_progress;
bool m_scaleOnlyShapes;
};
#endif /* __KIS_TRANSFORM_PROCESSING_VISITOR_H */
......@@ -37,7 +37,12 @@
#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
#include "kis_transaction.h"
#include <KoProgressUpdater.h>
#include "testutil.h"
#include "kis_filter_strategy.h"
#include "kis_transform_worker.h"
#include "processing/kis_transform_processing_visitor.h"
class BaseProcessingTest
{
......@@ -73,7 +78,8 @@ private:
QRect imageRect = QRect(QPoint(0,0), sourceImage.size());
QRect transpRect(50,50,300,300);
QRect blurRect(100,100,300,300);
QRect blurRect(66,66,300,300);
QPoint blurShift(34,34);
QPoint cloneShift(75,75);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
......@@ -87,6 +93,8 @@ private:
KisAdjustmentLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
blur1->selection()->clear();
blur1->selection()->getOrCreatePixelSelection()->select(blurRect);
blur1->setX(blurShift.x());
blur1->setY(blurShift.y());
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
paintLayer1->paintDevice()->convertFromQImage(sourceImage, "", 0, 0);
......@@ -176,4 +184,23 @@ void KisProcessingsTest::testCropVisitor()
tester.test("crop", visitor);
}
void KisProcessingsTest::testTransformVisitorScale()
{
BaseProcessingTest tester;
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(0.5, 0.5,
0,0,QPointF(),
0,
0,0,
updater,filter);
tester.test("transform_scale", visitor);
}
QTEST_KDEMAIN(KisProcessingsTest, GUI)
......@@ -26,6 +26,7 @@ class KisProcessingsTest : public QObject
Q_OBJECT
private slots:
void testCropVisitor();
void testTransformVisitorScale();
};
#endif /* __KIS_PROCESSINGS_TEST_H */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment