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)