File indexing completed on 2024-05-26 04:27:58

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_transaction_test.h"
0008 #include <simpletest.h>
0009 #include <KoColorSpace.h>
0010 #include <KoColorSpaceRegistry.h>
0011 
0012 #include <kundo2qstack.h>
0013 
0014 #include "kis_types.h"
0015 #include "kis_transform_worker.h"
0016 #include "kis_paint_device.h"
0017 #include "kis_transaction.h"
0018 #include "kis_surrogate_undo_adapter.h"
0019 #include "kis_image.h"
0020 #include "kis_paint_device_debug_utils.h"
0021 #include "kistest.h"
0022 
0023 void KisTransactionTest::testUndo()
0024 {
0025     KisSurrogateUndoAdapter undoAdapter;
0026     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0027     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0028 
0029     quint8* pixel = new quint8[cs->pixelSize()];
0030     cs->fromQColor(Qt::white, pixel);
0031     dev->fill(0, 0, 512, 512, pixel);
0032 
0033     cs->fromQColor(Qt::black, pixel);
0034     dev->fill(512, 0, 512, 512, pixel);
0035 
0036     QColor c1, c2;
0037     dev->pixel(5, 5, &c1);
0038     dev->pixel(517, 5, &c2);
0039 
0040     QVERIFY(c1 == Qt::white);
0041     QVERIFY(c2 == Qt::black);
0042 
0043     KisTransaction transaction(kundo2_noi18n("mirror"), dev, 0);
0044     KisTransformWorker::mirrorX(dev);
0045     transaction.commit(&undoAdapter);
0046 
0047     dev->pixel(5, 5, &c1);
0048     dev->pixel(517, 5, &c2);
0049 
0050     QVERIFY(c1 == Qt::black);
0051     QVERIFY(c2 == Qt::white);
0052 
0053     undoAdapter.undo();
0054 
0055     dev->pixel(5, 5, &c1);
0056     dev->pixel(517, 5, &c2);
0057 
0058     QVERIFY(c1 == Qt::white);
0059     QVERIFY(c2 == Qt::black);
0060 
0061 }
0062 
0063 void KisTransactionTest::testRedo()
0064 {
0065     KisSurrogateUndoAdapter undoAdapter;
0066 
0067     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0068     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0069 
0070     quint8* pixel = new quint8[cs->pixelSize()];
0071     cs->fromQColor(Qt::white, pixel);
0072     dev->fill(0, 0, 512, 512, pixel);
0073 
0074     cs->fromQColor(Qt::black, pixel);
0075     dev->fill(512, 0, 512, 512, pixel);
0076 
0077     QColor c1, c2;
0078     dev->pixel(5, 5, &c1);
0079     dev->pixel(517, 5, &c2);
0080 
0081     QVERIFY(c1 == Qt::white);
0082     QVERIFY(c2 == Qt::black);
0083 
0084     KisTransaction transaction(kundo2_noi18n("mirror"), dev, 0);
0085     KisTransformWorker::mirrorX(dev);
0086     transaction.commit(&undoAdapter);
0087 
0088     dev->pixel(5, 5, &c1);
0089     dev->pixel(517, 5, &c2);
0090 
0091     QVERIFY(c1 == Qt::black);
0092     QVERIFY(c2 == Qt::white);
0093 
0094 
0095     undoAdapter.undo();
0096 
0097     dev->pixel(5, 5, &c1);
0098     dev->pixel(517, 5, &c2);
0099 
0100     QVERIFY(c1 == Qt::white);
0101     QVERIFY(c2 == Qt::black);
0102 
0103     undoAdapter.redo();
0104 
0105     dev->pixel(5, 5, &c1);
0106     dev->pixel(517, 5, &c2);
0107 
0108     QVERIFY(c1 == Qt::black);
0109     QVERIFY(c2 == Qt::white);
0110 }
0111 
0112 void KisTransactionTest::testDeviceMove()
0113 {
0114     KisSurrogateUndoAdapter undoAdapter;
0115 
0116     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0117     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0118 
0119     QCOMPARE(dev->x(), 0);
0120     QCOMPARE(dev->y(), 0);
0121 
0122     KisTransaction t1(kundo2_noi18n("move1"), dev, 0);
0123     dev->moveTo(10,20);
0124     t1.commit(&undoAdapter);
0125 
0126     QCOMPARE(dev->x(), 10);
0127     QCOMPARE(dev->y(), 20);
0128 
0129     KisTransaction t2(kundo2_noi18n("move2"), dev, 0);
0130     dev->moveTo(7,11);
0131     t2.commit(&undoAdapter);
0132 
0133     QCOMPARE(dev->x(),  7);
0134     QCOMPARE(dev->y(), 11);
0135 
0136     undoAdapter.undo();
0137 
0138     QCOMPARE(dev->x(), 10);
0139     QCOMPARE(dev->y(), 20);
0140 
0141     undoAdapter.undo();
0142 
0143     QCOMPARE(dev->x(), 0);
0144     QCOMPARE(dev->y(), 0);
0145 
0146     undoAdapter.redo();
0147 
0148     QCOMPARE(dev->x(), 10);
0149     QCOMPARE(dev->y(), 20);
0150 
0151     undoAdapter.redo();
0152 
0153     QCOMPARE(dev->x(),  7);
0154     QCOMPARE(dev->y(), 11);
0155 }
0156 
0157 #include "kis_keyframe_channel.h"
0158 #include "kis_raster_keyframe_channel.h"
0159 #include "kis_paint_device_frames_interface.h"
0160 #include "testing_timed_default_bounds.h"
0161 
0162 #include <KoColor.h>
0163 
0164 
0165 void KisTransactionTest::testUndoWithUnswitchedFrames()
0166 {
0167     KisSurrogateUndoAdapter undoAdapter;
0168     const QRect imageRect(0,0,100,100);
0169 
0170 
0171     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0172     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0173 
0174     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
0175     dev->setDefaultBounds(bounds);
0176 
0177     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Raster);
0178     QVERIFY(channel);
0179 
0180     KisPaintDeviceFramesInterface *i = dev->framesInterface();
0181     QVERIFY(i);
0182 
0183     QCOMPARE(i->frames().size(), 1);
0184 
0185 
0186     dev->fill(QRect(10,10,20,20), KoColor(Qt::white, cs));
0187 
0188     KIS_DUMP_DEVICE_2(dev, imageRect, "00_f0_w20", "dd");
0189     QCOMPARE(dev->exactBounds(), QRect(10,10,20,20));
0190 
0191 
0192     // add keyframe at position 10
0193     channel->addKeyframe(10);
0194 
0195     // add keyframe at position 11
0196     channel->addKeyframe(11);
0197 
0198     // add keyframe at position 12
0199     channel->addKeyframe(12);
0200 
0201     KIS_DUMP_DEVICE_2(dev, imageRect, "01_f0_b20", "dd");
0202     QCOMPARE(dev->exactBounds(), QRect(10,10,20,20));
0203 
0204     {
0205         KisTransaction transaction(kundo2_noi18n("first_stroke"), dev, 0);
0206 
0207         dev->clear();
0208         dev->fill(QRect(40,40,21,21), KoColor(Qt::red, cs));
0209 
0210         transaction.commit(&undoAdapter);
0211 
0212         KIS_DUMP_DEVICE_2(dev, imageRect, "02_f0_b21_stroke", "dd");
0213         QCOMPARE(dev->exactBounds(), QRect(40,40,21,21));
0214     }
0215 
0216     // switch to frame 10
0217     bounds->testingSetTime(10);
0218 
0219     KIS_DUMP_DEVICE_2(dev, imageRect, "03_f10_b0_switched", "dd");
0220     QVERIFY(dev->exactBounds().isEmpty());
0221 
0222     {
0223         KisTransaction transaction(kundo2_noi18n("second_stroke"), dev, 0);
0224 
0225         dev->fill(QRect(60,60,22,22), KoColor(Qt::green, cs));
0226 
0227         transaction.commit(&undoAdapter);
0228 
0229         KIS_DUMP_DEVICE_2(dev, imageRect, "04_f10_b22_stroke", "dd");
0230         QCOMPARE(dev->exactBounds(), QRect(60,60,22,22));
0231     }
0232 
0233     undoAdapter.undo();
0234 
0235     KIS_DUMP_DEVICE_2(dev, imageRect, "05_f10_b0_undone", "dd");
0236     QVERIFY(dev->exactBounds().isEmpty());
0237 
0238     bounds->testingSetTime(0);
0239     KIS_DUMP_DEVICE_2(dev, imageRect, "06_f0_b21_undone", "dd");
0240     QCOMPARE(dev->exactBounds(), QRect(40,40,21,21));
0241 
0242     bounds->testingSetTime(10);
0243     QVERIFY(dev->exactBounds().isEmpty());
0244 
0245     undoAdapter.undo();
0246 
0247     KIS_DUMP_DEVICE_2(dev, imageRect, "07_f10_b0_undone_x2", "dd");
0248     QVERIFY(dev->exactBounds().isEmpty());
0249 
0250     bounds->testingSetTime(0);
0251     KIS_DUMP_DEVICE_2(dev, imageRect, "08_f0_b20_undone_x2", "dd");
0252     QCOMPARE(dev->exactBounds(), QRect(10,10,20,20));
0253 
0254     {
0255         KisTransaction transaction(kundo2_noi18n("third_move"), dev, 0);
0256 
0257         dev->moveTo(17,17);
0258 
0259         transaction.commit(&undoAdapter);
0260 
0261         KIS_DUMP_DEVICE_2(dev, imageRect, "09_f0_o27_move", "dd");
0262         QCOMPARE(dev->exactBounds(), QRect(27,27,20,20));
0263     }
0264 
0265     bounds->testingSetTime(10);
0266     QVERIFY(dev->exactBounds().isEmpty());
0267 
0268     undoAdapter.undo();
0269 
0270     KIS_DUMP_DEVICE_2(dev, imageRect, "10_f10_b0_undone_x3", "dd");
0271     QVERIFY(dev->exactBounds().isEmpty());
0272 
0273     bounds->testingSetTime(0);
0274     KIS_DUMP_DEVICE_2(dev, imageRect, "11_f0_b20_undone_x3", "dd");
0275     QCOMPARE(dev->exactBounds(), QRect(10,10,20,20));
0276 }
0277 
0278 #include "KisTransactionWrapperFactory.h"
0279 
0280 void KisTransactionTest::testTransactionWrapperFactory()
0281 {
0282     KisSurrogateUndoAdapter undoAdapter;
0283     const QRect imageRect(0,0,100,100);
0284 
0285 
0286     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0287     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0288 
0289     enum CommandState {
0290         Inexistent,
0291         Created,
0292         Redone,
0293         Undone,
0294         Destroyed
0295     };
0296     Q_ENUMS(CommandState)
0297 
0298     struct StateTrackingUndoCommand : public KUndo2Command
0299     {
0300         StateTrackingUndoCommand(CommandState &state)
0301             : m_state(state)
0302         {
0303             KIS_ASSERT(m_state == Inexistent);
0304             m_state = Created;
0305         }
0306 
0307         ~StateTrackingUndoCommand() override {
0308             m_state = Destroyed;
0309         }
0310 
0311         void redo() override {
0312             m_state = Redone;
0313         }
0314 
0315         void undo() override {
0316             m_state = Undone;
0317         }
0318 
0319         CommandState &m_state;
0320     };
0321 
0322     struct Factory : public KisTransactionWrapperFactory {
0323         Factory(CommandState &beginState, CommandState &endState)
0324             : m_beginState(beginState), m_endState(endState)
0325         {
0326         }
0327 
0328         KUndo2Command* createBeginTransactionCommand(KisPaintDeviceSP device) override {
0329             Q_UNUSED(device);
0330             return new StateTrackingUndoCommand(m_beginState);
0331         }
0332 
0333         KUndo2Command* createEndTransactionCommand() override {
0334             return new StateTrackingUndoCommand(m_endState);
0335         }
0336 
0337         CommandState &m_beginState;
0338         CommandState &m_endState;
0339     };
0340 
0341     CommandState beginState = Inexistent;
0342     CommandState endState = Inexistent;
0343 
0344     KisTransaction transaction(dev, 0, -1, new Factory(beginState, endState));
0345 
0346     QCOMPARE(beginState, Redone);
0347     QCOMPARE(endState, Inexistent);
0348 
0349     transaction.commit(&undoAdapter);
0350 
0351     QCOMPARE(beginState, Redone);
0352     QCOMPARE(endState, Redone);
0353 
0354     undoAdapter.undo();
0355 
0356     QCOMPARE(beginState, Undone);
0357     QCOMPARE(endState, Undone);
0358 
0359     undoAdapter.redo();
0360 
0361     QCOMPARE(beginState, Redone);
0362     QCOMPARE(endState, Redone);
0363 }
0364 
0365 #include "KisInterstrokeDataTransactionWrapperFactory.h"
0366 #include "KisInterstrokeDataFactory.h"
0367 #include "KisInterstrokeData.h"
0368 
0369 struct TestInterstrokeData : public KisInterstrokeData
0370 {
0371     TestInterstrokeData(KisPaintDeviceSP device,
0372                         int _typeId, int _value)
0373         : KisInterstrokeData(device),
0374           typeId(_typeId), value(_value)
0375     {
0376     }
0377 
0378     void beginTransaction() override {
0379         ENTER_FUNCTION() << ppVar(value);
0380         m_savedValue = value;
0381     }
0382 
0383     KUndo2Command* endTransaction() override {
0384 
0385         ENTER_FUNCTION() << ppVar(value) << ppVar(m_savedValue);
0386         struct SimpleIntCommand : public KUndo2Command
0387         {
0388             SimpleIntCommand(int *pointer, int newValue, int oldValue)
0389                 : m_pointer(pointer),
0390                   m_newValue(newValue),
0391                   m_oldValue(oldValue)
0392             {
0393             }
0394 
0395             void redo() override {
0396                 if (m_firstRedo) {
0397                     m_firstRedo = false;
0398                     return;
0399                 }
0400 
0401                 *m_pointer = m_newValue;
0402             }
0403 
0404             void undo() override {
0405                 *m_pointer = m_oldValue;
0406             }
0407 
0408         private:
0409             bool m_firstRedo {false};
0410             int *m_pointer {0};
0411             int m_newValue {0};
0412             int m_oldValue {0};
0413         };
0414 
0415         return new SimpleIntCommand(&value, value, m_savedValue);
0416     }
0417 
0418     int typeId {0};
0419     int value {0};
0420 
0421     int m_savedValue {0};
0422 };
0423 
0424 struct TestInterstrokeDataFactory : public KisInterstrokeDataFactory
0425 {
0426     TestInterstrokeDataFactory(int typeId)
0427         : m_typeId(typeId)
0428     {
0429     }
0430 
0431     bool isCompatible(KisInterstrokeData *_data) override {
0432         TestInterstrokeData *data = dynamic_cast<TestInterstrokeData*>(_data);
0433         return data && data->typeId == m_typeId;
0434     }
0435 
0436     KisInterstrokeData * create(KisPaintDeviceSP device) override {
0437         Q_UNUSED(device);
0438         return new TestInterstrokeData(device, m_typeId, 0);
0439     }
0440 
0441     int m_typeId {0};
0442 };
0443 
0444 TestInterstrokeData* resolveType(KisInterstrokeDataSP data) {
0445     TestInterstrokeData *result = dynamic_cast<TestInterstrokeData*>(data.data());
0446     KIS_ASSERT(result);
0447     return result;
0448 }
0449 
0450 void KisTransactionTest::testInterstrokeData()
0451 {
0452     KisSurrogateUndoAdapter undoAdapter;
0453     const QRect imageRect(0,0,100,100);
0454 
0455 
0456     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0457     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0458 
0459     KisTransaction transaction1(dev, 0, -1,
0460                                 new KisInterstrokeDataTransactionWrapperFactory(
0461                                     new TestInterstrokeDataFactory(13)));
0462 
0463     QVERIFY(dev->interstrokeData());
0464     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0465     QCOMPARE(resolveType(dev->interstrokeData())->value, 0);
0466 
0467     resolveType(dev->interstrokeData())->value = 10;
0468 
0469     transaction1.commit(&undoAdapter);
0470 
0471     QVERIFY(dev->interstrokeData());
0472     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0473     QCOMPARE(resolveType(dev->interstrokeData())->value, 10);
0474 
0475     KisInterstrokeDataSP firstData = dev->interstrokeData();
0476 
0477     KisTransaction transaction2(dev, 0, -1,
0478                                 new KisInterstrokeDataTransactionWrapperFactory(
0479                                     new TestInterstrokeDataFactory(13)));
0480 
0481     QVERIFY(dev->interstrokeData());
0482     QCOMPARE(dev->interstrokeData(), firstData);
0483     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0484     QCOMPARE(resolveType(dev->interstrokeData())->value, 10);
0485 
0486     resolveType(dev->interstrokeData())->value = 20;
0487 
0488     transaction2.commit(&undoAdapter);
0489 
0490     QVERIFY(dev->interstrokeData());
0491     QCOMPARE(dev->interstrokeData(), firstData);
0492     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0493     QCOMPARE(resolveType(dev->interstrokeData())->value, 20);
0494 
0495     KisTransaction transaction3(dev, 0, -1,
0496                                 new KisInterstrokeDataTransactionWrapperFactory(
0497                                     new TestInterstrokeDataFactory(17)));
0498 
0499     QVERIFY(dev->interstrokeData());
0500     QVERIFY(dev->interstrokeData() != firstData);
0501     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 17);
0502     QCOMPARE(resolveType(dev->interstrokeData())->value, 0);
0503 
0504     resolveType(dev->interstrokeData())->value = 30;
0505 
0506     transaction3.commit(&undoAdapter);
0507 
0508     QVERIFY(dev->interstrokeData());
0509     QVERIFY(dev->interstrokeData() != firstData);
0510     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 17);
0511     QCOMPARE(resolveType(dev->interstrokeData())->value, 30);
0512 
0513 
0514     KisTransaction transaction4(dev);
0515     QVERIFY(!dev->interstrokeData());
0516     transaction4.commit(&undoAdapter);
0517     QVERIFY(!dev->interstrokeData());
0518 
0519     undoAdapter.undo();
0520 
0521     QVERIFY(dev->interstrokeData());
0522     QVERIFY(dev->interstrokeData() != firstData);
0523     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 17);
0524     QCOMPARE(resolveType(dev->interstrokeData())->value, 30);
0525 
0526     undoAdapter.undo();
0527 
0528     QVERIFY(dev->interstrokeData());
0529     QCOMPARE(dev->interstrokeData(), firstData);
0530     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0531     QCOMPARE(resolveType(dev->interstrokeData())->value, 20);
0532 
0533     undoAdapter.undo();
0534 
0535     QVERIFY(dev->interstrokeData());
0536     QCOMPARE(dev->interstrokeData(), firstData);
0537     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 13);
0538     QCOMPARE(resolveType(dev->interstrokeData())->value, 10);
0539 
0540     undoAdapter.undo();
0541 
0542     QVERIFY(!dev->interstrokeData());
0543 }
0544 
0545 void KisTransactionTest::testInterstrokeDataWithUnswitchedFrames()
0546 {
0547     KisSurrogateUndoAdapter undoAdapter;
0548     const QRect imageRect(0,0,100,100);
0549 
0550 
0551     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0552     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0553 
0554     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
0555     dev->setDefaultBounds(bounds);
0556 
0557     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Raster);
0558     QVERIFY(channel);
0559 
0560     KisPaintDeviceFramesInterface *i = dev->framesInterface();
0561     QVERIFY(i);
0562 
0563     QCOMPARE(i->frames().size(), 1);
0564 
0565 
0566     dev->fill(QRect(10,10,20,20), KoColor(Qt::white, cs));
0567 
0568     KIS_DUMP_DEVICE_2(dev, imageRect, "00_f0_w20", "dd");
0569     QCOMPARE(dev->exactBounds(), QRect(10,10,20,20));
0570 
0571 
0572     // add keyframe at position 10
0573     channel->addKeyframe(10);
0574 
0575     // add keyframe at position 11
0576     channel->addKeyframe(11);
0577 
0578     // add keyframe at position 12
0579     channel->addKeyframe(12);
0580 
0581     QVERIFY(!dev->interstrokeData());
0582 
0583     {
0584         KisTransaction transaction(dev, 0, -1,
0585                 new KisInterstrokeDataTransactionWrapperFactory(
0586                     new TestInterstrokeDataFactory(17)));
0587 
0588         QVERIFY(dev->interstrokeData());
0589         QCOMPARE(resolveType(dev->interstrokeData())->typeId, 17);
0590         QCOMPARE(resolveType(dev->interstrokeData())->value, 0);
0591 
0592         resolveType(dev->interstrokeData())->value = 30;
0593         transaction.commit(&undoAdapter);
0594     }
0595 
0596     // switch to frame 10
0597     bounds->testingSetTime(10);
0598     QVERIFY(!dev->interstrokeData());
0599 
0600     {
0601         KisTransaction transaction(dev, 0, -1,
0602                 new KisInterstrokeDataTransactionWrapperFactory(
0603                     new TestInterstrokeDataFactory(18)));
0604 
0605         QVERIFY(dev->interstrokeData());
0606         QCOMPARE(resolveType(dev->interstrokeData())->typeId, 18);
0607         QCOMPARE(resolveType(dev->interstrokeData())->value, 0);
0608 
0609         resolveType(dev->interstrokeData())->value = 40;
0610         transaction.commit(&undoAdapter);
0611     }
0612 
0613     QVERIFY(dev->interstrokeData());
0614 
0615     undoAdapter.undo();
0616 
0617     QVERIFY(!dev->interstrokeData());
0618 
0619     // switch to frame 0
0620     bounds->testingSetTime(0);
0621 
0622     QVERIFY(dev->interstrokeData());
0623     QCOMPARE(resolveType(dev->interstrokeData())->typeId, 17);
0624     QCOMPARE(resolveType(dev->interstrokeData())->value, 30);
0625 
0626     // switch back to frame 10
0627     bounds->testingSetTime(10);
0628 
0629     QVERIFY(!dev->interstrokeData());
0630 
0631     undoAdapter.undo();
0632 
0633     QVERIFY(!dev->interstrokeData());
0634 
0635     // switch to frame 0
0636     bounds->testingSetTime(0);
0637 
0638     QVERIFY(!dev->interstrokeData());
0639 }
0640 
0641 SIMPLE_TEST_MAIN(KisTransactionTest)