Commit eaa023c1 authored by Dmitry Kazakov's avatar Dmitry Kazakov Committed by Inge Wallin
Browse files

Fixed several bugs of transforming Shape/Paint layers

This patch fixes several problems:
1) Fixes the updates of the clone layer, when changing
   its source's mask
2) Fixes updates for undo of a transformation of a layer
   having both clone and a mask
3) Fixes Mirroring of a paint layer with odd size
4) Fixes Undo for transformations on Shape Layers
5) Adds a Unit Test for all this.

BUG:251168,312734,312742,312750
parent 9311bda5
......@@ -366,8 +366,14 @@ protected:
QRect changeRect = currentNode->changeRect(m_resultChangeRect);
m_changeRectVaries |= changeRect != m_resultChangeRect;
m_resultChangeRect = changeRect;
m_resultUncroppedChangeRect = changeRect;
}
}
KisNodeSP parentLayer = firstMask->parent();
Q_ASSERT(parentLayer);
registerCloneNotification(parentLayer, N_FILTHY_PROJECTION);
}
static qint32 calculateChecksum(KisNodeSP node, const QRect &requestedRect) {
......
......@@ -125,6 +125,7 @@ public:
vKisAnnotationSP annotations;
QAtomicInt disableUIUpdateSignals;
QAtomicInt disableDirtyRequests;
KisImageSignalRouter *signalRouter;
KisUpdateScheduler *scheduler;
......@@ -1434,6 +1435,16 @@ void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &c
}
}
void KisImage::disableDirtyRequests()
{
m_d->disableDirtyRequests.ref();
}
void KisImage::enableDirtyRequests()
{
m_d->disableDirtyRequests.deref();
}
void KisImage::disableUIUpdates()
{
m_d->disableUIUpdateSignals.ref();
......@@ -1453,6 +1464,8 @@ void KisImage::notifyProjectionUpdated(const QRect &rc)
void KisImage::requestProjectionUpdate(KisNode *node, const QRect& rect)
{
if (m_d->disableDirtyRequests) return;
KisNodeGraphListener::requestProjectionUpdate(node, rect);
if (m_d->scheduler) {
......
......@@ -617,9 +617,38 @@ public slots:
void blockUpdates();
void unblockUpdates();
/**
* Disables notification of the UI about the changes in the image.
* This feature is used by KisProcessingApplicator. It is needed
* when we change the size of the image. In this case, the whole
* image will be reloaded into UI by sigSizeChanged(), so there is
* no need to inform the UI about individual dirty rects.
*/
void disableUIUpdates();
/**
* \see disableUIUpdates
*/
void enableUIUpdates();
/**
* Disables the processing of all the setDirty() requests that
* come to the image. The incoming requests are effectively
* *dropped*.
*
* This feature is used by KisProcessingApplicator. For many cases
* it provides its own updates interface, which recalculates the
* whole subtree of nodes. But while we change any particular
* node, it can ask for an update itself. This method is a way of
* blocking such intermediate (and excessive) requests.
*/
void disableDirtyRequests();
/**
* \see disableDirtyRequests()
*/
void enableDirtyRequests();
void refreshGraphAsync(KisNodeSP root = 0);
void refreshGraphAsync(KisNodeSP root, const QRect &rc);
void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect);
......
......@@ -47,6 +47,9 @@ public:
virtual void disableUIUpdates() = 0;
virtual void enableUIUpdates() = 0;
virtual void disableDirtyRequests() = 0;
virtual void enableDirtyRequests() = 0;
virtual void refreshGraphAsync(KisNodeSP root = 0) = 0;
virtual void refreshGraphAsync(KisNodeSP root, const QRect &rc) = 0;
virtual void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect) = 0;
......
......@@ -284,6 +284,7 @@ void KisMask::testingInitSelection(const QRect &rect)
m_d->selection = new KisSelection();
m_d->selection->getOrCreatePixelSelection()->select(rect, OPACITY_OPAQUE_U8);
m_d->selection->updateProjection(rect);
m_d->selection->setParentNode(this);
}
#include "kis_mask.moc"
......@@ -21,6 +21,7 @@
#include "kis_image.h"
#include "kis_image_signal_router.h"
#include "kis_node.h"
#include "kis_clone_layer.h"
#include "kis_processing_visitor.h"
#include "commands_new/kis_processing_command.h"
#include "kis_stroke_strategy_undo_command_based.h"
......@@ -66,20 +67,61 @@ public:
}
void redo() {
if(m_finalUpdate) finalize();
if(!m_finalUpdate) initialize();
else finalize();
}
void undo() {
if(!m_finalUpdate) finalize();
if(m_finalUpdate) initialize();
else finalize();
}
private:
void initialize() {
/**
* We disable all non-centralized updates here. Everything
* should be done by this command's explicit updates.
*
* If you still need third-party updates work, please add a
* flag to the applicator.
*/
m_image->disableDirtyRequests();
}
void finalize() {
m_image->enableDirtyRequests();
if(m_flags.testFlag(KisProcessingApplicator::RECURSIVE)) {
m_image->refreshGraphAsync(m_node);
}
m_node->setDirty(m_image->bounds());
updateClones(m_node);
}
void updateClones(KisNodeSP node) {
// simple tail-recursive iteration
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
updateClones(prevNode);
prevNode = prevNode->prevSibling();
}
KisLayer *layer = dynamic_cast<KisLayer*>(m_node.data());
if(layer && layer->hasClones()) {
foreach(KisCloneLayerSP clone, layer->registeredClones()) {
if(!clone) continue;
QPoint offset(clone->x(), clone->y());
QRegion dirtyRegion(m_image->bounds());
dirtyRegion -= m_image->bounds().translated(offset);
clone->setDirty(dirtyRegion);
}
}
}
private:
......
......@@ -446,7 +446,7 @@ QRect KisTransformWorker::mirrorX(KisPaintDeviceSP dev, qreal axis, const KisSel
// Extend rect so it has the same width on both sides of the axis
qreal distanceFromAxis = qMax(fabs((qreal)r.left() - axis), fabs((qreal)r.right() - axis));
QRect newRect(floor(axis - distanceFromAxis), r.y(), ceil(2*distanceFromAxis), r.height());
r = newRect.adjusted(-1, 0, 2, 0);
r = newRect;
}
}
......@@ -516,7 +516,7 @@ QRect KisTransformWorker::mirrorY(KisPaintDeviceSP dev, qreal axis, const KisSel
// Extend rect so it has the same height on both sides of the axis
qreal distanceFromAxis = qMax(fabs((qreal)r.top() - axis), fabs((qreal)r.bottom() - axis));
QRect newRect(r.x(), floor(axis - distanceFromAxis), r.width(), ceil(2*distanceFromAxis));
r = newRect.adjusted(0, -1, 0, 2);
r = newRect;
}
}
{
......
......@@ -25,6 +25,11 @@
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoShapeRegistry.h>
#include "kis_doc2.h"
#include "kis_shape_layer.h"
#include "kis_undo_stores.h"
#include "kis_image.h"
#include "kis_selection.h"
......@@ -112,6 +117,29 @@ protected:
image->undoAdapter()->addCommand(cmd);
}
void addShapeLayer(KisDoc2 *doc, KisImageSP image) {
KoShapeContainer *parentContainer =
dynamic_cast<KoShapeContainer*>(doc->shapeForNode(image->root()));
Q_ASSERT(parentContainer);
KisShapeLayerSP shapeLayer = new KisShapeLayer(parentContainer, doc->shapeController(), image.data(), "shape", OPACITY_OPAQUE_U8);
image->addNode(shapeLayer);
KoShapeFactoryBase *f1 = KoShapeRegistry::instance()->get("StarShape");
KoShapeFactoryBase *f2 = KoShapeRegistry::instance()->get("RectangleShape");
KoShape *shape1 = f1->createDefaultShape();
KoShape *shape2 = f2->createDefaultShape();
shape1->setPosition(QPointF(100,100));
shape2->setPosition(QPointF(200,200));
shapeLayer->addShape(shape1);
shapeLayer->addShape(shape2);
QApplication::processEvents();
}
/**
* Checks the content of image's layers against the set of
* QImages stored in @p prefix subfolder
......
/*
* Copyright (c) 2012 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 __UI_MANAGER_TEST_H
#define __UI_MANAGER_TEST_H
#include "testutil.h"
#include "qimage_based_test.h"
#include "kis_pattern.h"
#include "kis_resource_server_provider.h"
#include "kis_canvas_resource_provider.h"
#include "kis_filter_strategy.h"
#include "kis_selection_manager.h"
#include "kis_node_manager.h"
#include "kis_view2.h"
#include "kis_part2.h"
#include "KoMainWindow.h"
namespace TestUtil
{
class UiManagerTest : public TestUtil::QImageBasedTest
{
public:
UiManagerTest(bool useSelection, bool useShapeLayer, const QString &testName)
: QImageBasedTest(testName) // "selection_manager_test"
{
undoStore = new KisSurrogateUndoStore();
image = createImage(undoStore);
part = new KisPart2(0);
doc = new KisDoc2(part);
part->setDocument(doc);
doc->setCurrentImage(image);
if(useSelection) addGlobalSelection(image);
if(useShapeLayer) addShapeLayer(doc, image);
image->initialRefreshGraph();
QVERIFY(checkLayers("initial"));
shell = new KoMainWindow(part->componentData());
view = new KisView2(part, doc, shell);
KisPattern *newPattern = new KisPattern(QString(FILES_DATA_DIR) + QDir::separator() + "HR_SketchPaper_01.pat");
newPattern->load();
Q_ASSERT(newPattern->valid());
view->resourceProvider()->slotPatternActivated(newPattern);
KoColor fgColor(Qt::black, image->colorSpace());
KoColor bgColor(Qt::white, image->colorSpace());
view->resourceProvider()->setBGColor(bgColor);
view->resourceProvider()->setFGColor(fgColor);
KisNodeSP paint1 = findNode(image->root(), "paint1");
Q_ASSERT(paint1);
view->nodeManager()->slotNonUiActivatedNode(paint1);
selectionManager = view->selectionManager();
}
~UiManagerTest() {
/**
* Here is a weird way of precessing pending events.
* This is needed for the dummies facade could process
* all the queued events telling it some nodes were
* added/deleted
*/
QApplication::processEvents();
QTest::qSleep(500);
QApplication::processEvents();
delete shell;
delete doc;
delete part;
/**
* The event queue may have up to 200k events
* by the time all the tests are finished. Removing
* all of them may last forever, so clear them after
* every single test is finished
*/
QApplication::removePostedEvents(0);
}
void checkUndo() {
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayers("initial"));
}
void checkDoubleUndo() {
undoStore->undo();
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayers("initial"));
}
void startConcurrentTask() {
KisFilterStrategy * filter = new KisBoxFilterStrategy();
QSize initialSize = image->size();
image->scaleImage(2 * initialSize, image->xRes(), image->yRes(), filter);
image->waitForDone();
image->scaleImage(initialSize, image->xRes(), image->yRes(), filter);
}
using QImageBasedTest::checkLayers;
bool checkLayers(const QString &name) {
return checkLayers(image, name);
}
bool checkLayersFuzzy(const QString &name) {
return checkLayers(image, name, 1);
}
bool checkSelectionOnly(const QString &name) {
KisNodeSP mask = findNode(image->root(), "selection");
return checkOneLayer(image, mask, name);
}
bool checkNoSelection() {
KisNodeSP mask = findNode(image->root(), "selection");
return !mask && !image->globalSelection();
}
KisImageSP image;
KisSelectionManager *selectionManager;
KisSurrogateUndoStore *undoStore;
protected:
KisView2 *view;
KisDoc2 *doc;
KisPart2 *part;
KoMainWindow *shell;
};
}
#endif /* __UI_MANAGER_TEST_H */
......@@ -18,8 +18,8 @@
#include "kis_shape_layer_canvas.h"
#include <QTimer>
#include <QPainter>
#include <QMutexLocker>
#include <KoShapeManager.h>
#include <KoViewConverter.h>
......@@ -44,10 +44,10 @@ KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KoViewConverter
, m_shapeManager(new KoShapeManager(this))
, m_projection(0)
, m_parentLayer(parent)
, m_repaintTriggered(false)
, m_antialias(false)
{
m_shapeManager->selection()->setActiveLayer(parent);
connect(this, SIGNAL(forwardRepaint()), SLOT(repaint()), Qt::QueuedConnection);
}
KisShapeLayerCanvas::~KisShapeLayerCanvas()
......@@ -84,7 +84,7 @@ KoShapeManager *KisShapeLayerCanvas::shapeManager() const
void KisShapeLayerCanvas::updateCanvas(const QRectF& rc)
{
dbgImage << "KisShapeLayerCanvas::updateCanvas()" << rc;
dbgUI << "KisShapeLayerCanvas::updateCanvas()" << rc;
//image is 0, if parentLayer is being deleted so don't update
if (!m_parentLayer->image()) {
return;
......@@ -92,27 +92,39 @@ void KisShapeLayerCanvas::updateCanvas(const QRectF& rc)
QRect r = m_viewConverter->documentToView(rc).toRect();
r.adjust(-2, -2, 2, 2); // for antialias
m_dirty += r;
if (! m_repaintTriggered) {
{
QMutexLocker locker(&m_dirtyRegionMutex);
m_dirtyRegion += r;
qreal x, y;
m_viewConverter->zoom(&x, &y);
m_antialias = x < 3 || y < 3;
QTimer::singleShot(0, this, SLOT(repaint()));
m_repaintTriggered = true;
}
emit forwardRepaint();
}
void KisShapeLayerCanvas::repaint()
{
QRect r = m_dirty.boundingRect();
QRect r;
bool antialias;
{
QMutexLocker locker(&m_dirtyRegionMutex);
antialias = m_antialias;
r = m_dirtyRegion.boundingRect();
m_dirtyRegion = QRegion();
}
if (r.isEmpty()) return;
r.intersect(m_parentLayer->image()->bounds());
QImage image(r.width(), r.height(), QImage::Format_ARGB32);
image.fill(0);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing, m_antialias);
p.setRenderHint(QPainter::TextAntialiasing, m_antialias);
p.setRenderHint(QPainter::Antialiasing, antialias);
p.setRenderHint(QPainter::TextAntialiasing, antialias);
p.translate(-r.x(), -r.y());
p.setClipRect(r);
#ifdef DEBUG_REPAINT
......@@ -130,8 +142,6 @@ void KisShapeLayerCanvas::repaint()
kp.bitBlt(r.x(), r.y(), dev, 0, 0, r.width(), r.height());
kp.end();
m_parentLayer->setDirty(r);
m_dirty = QRegion();
m_repaintTriggered = false;
}
KoToolProxy * KisShapeLayerCanvas::toolProxy() const
......
......@@ -18,6 +18,7 @@
#ifndef KIS_SHAPE_LAYER_CANVAS_H
#define KIS_SHAPE_LAYER_CANVAS_H
#include <QMutex>
#include <KoCanvasBase.h>
#include <kis_types.h>
......@@ -64,7 +65,8 @@ public:
private slots:
void repaint();
signals:
void forwardRepaint();
private:
KoViewConverter * m_viewConverter;
......@@ -72,8 +74,9 @@ private:
KisPaintDeviceSP m_projection;
KisShapeLayer *m_parentLayer;
QRegion m_dirty;
bool m_repaintTriggered, m_antialias;
bool m_antialias;
QRegion m_dirtyRegion;
QMutex m_dirtyRegionMutex;
};
#endif
......@@ -113,6 +113,12 @@ target_link_libraries(KisSelectionManagerTest ${KDE4_KDEUI_LIBS} kritaui krita
########### next target ###############
set(kis_node_manager_test_SRCS kis_node_manager_test.cpp)
kde4_add_unit_test(KisNodeManagerTest TESTNAME krita-ui-KisNodeManagerTest ${kis_node_manager_test_SRCS})
target_link_libraries(KisNodeManagerTest ${KDE4_KDEUI_LIBS} kritaui kritaimage ${QT_QTTEST_LIBRARY})
########### next target ###############
set(kis_node_dummies_graph_test_SRCS kis_node_dummies_graph_test.cpp ../../sdk/tests/testutil.cpp)
kde4_add_unit_test(KisNodeDummiesGraphTest TESTNAME krita-ui-KisNodeDummiesGraphTest ${kis_node_dummies_graph_test_SRCS})
target_link_libraries(KisNodeDummiesGraphTest ${KDE4_KDEUI_LIBS} kritaui kritaimage ${QT_QTTEST_LIBRARY})
......
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