1 /*
2 * Copyright (C) 2007 Boudewijn Rempt <boud@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_prescaled_projection_test.h"
20 #include <QTest>
21 #include <QCoreApplication>
22
23 #include <QSize>
24 #include <QImage>
25
26 #include <KoZoomHandler.h>
27 #include <KoColorSpaceRegistry.h>
28 #include <KoCompositeOp.h>
29 #include <KoColorSpaceConstants.h>
30
31 #include <kis_config.h>
32 #include <kis_types.h>
33 #include <kis_image.h>
34 #include <kis_paint_device.h>
35 #include <kis_paint_layer.h>
36 #include <kis_group_layer.h>
37 #include <kis_update_info.h>
38
39 #include "canvas/kis_coordinates_converter.h"
40 #include "canvas/kis_prescaled_projection.h"
41
42 #include "../../sdk/tests/testutil.h"
43
testProjectionScenario(KisPrescaledProjection & projection,KoZoomHandler * viewConverter,const QString & name)44 bool KisPrescaledProjectionTest::testProjectionScenario(KisPrescaledProjection & projection,
45 KoZoomHandler * viewConverter,
46 const QString & name)
47 {
48
49 projection.notifyCanvasSizeChanged(QSize(1000, 1000));
50 projection.prescaledQImage().save(name + "_prescaled_projection_01.png");
51
52 viewConverter->setZoom(0.5);
53 projection.preScale();
54 projection.prescaledQImage().save(name + "_prescaled_projection_021.png");
55
56 viewConverter->setZoom(0.6);
57 projection.preScale();
58 projection.prescaledQImage().save(name + "_prescaled_projection_022.png");
59
60 viewConverter->setZoom(0.71);
61 projection.preScale();
62 projection.prescaledQImage().save(name + "_prescaled_projection_023.png");
63
64 viewConverter->setZoom(0.84);
65 projection.preScale();
66 projection.prescaledQImage().save(name + "_prescaled_projection_024.png");
67
68 viewConverter->setZoom(0.9);
69 projection.preScale();
70 projection.prescaledQImage().save(name + "_prescaled_projection_025.png");
71
72 viewConverter->setZoom(1.9);
73 projection.preScale();
74 projection.prescaledQImage().save(name + "_prescaled_projection_03.png");
75
76 viewConverter->setZoom(2.0);
77 projection.preScale();
78 projection.prescaledQImage().save(name + "_prescaled_projection_04.png");
79
80 viewConverter->setZoom(2.5);
81 projection.preScale();
82 projection.prescaledQImage().save(name + "_prescaled_projection_05.png");
83
84 viewConverter->setZoom(16.0);
85 projection.preScale();
86 projection.prescaledQImage().save(name + "_prescaled_projection_06.png");
87
88 viewConverter->setZoom(1.0);
89 projection.preScale();
90 projection.prescaledQImage().save(name + "_prescaled_projection_07.png");
91
92 projection.viewportMoved(QPoint(50, 50));
93 projection.prescaledQImage().save(name + "_prescaled_projection_08.png");
94
95 projection.viewportMoved(QPoint(100, 100));
96 projection.prescaledQImage().save(name + "_prescaled_projection_081.png");
97
98 projection.viewportMoved(QPoint(200, 200));
99 projection.prescaledQImage().save(name + "_prescaled_projection_082.png");
100
101 projection.viewportMoved(QPoint(250, 250));
102 projection.prescaledQImage().save(name + "_prescaled_projection_083.png");
103
104 projection.viewportMoved(QPoint(150, 200));
105 projection.prescaledQImage().save(name + "_prescaled_projection_084.png");
106
107 projection.viewportMoved(QPoint(100, 200));
108 projection.prescaledQImage().save(name + "_prescaled_projection_085.png");
109
110 projection.viewportMoved(QPoint(50, 200));
111 projection.prescaledQImage().save(name + "_prescaled_projection_086.png");
112
113 projection.viewportMoved(QPoint(0, 200));
114 projection.prescaledQImage().save(name + "_prescaled_projection_087.png");
115
116 projection.notifyCanvasSizeChanged(QSize(750, 750));
117 projection.prescaledQImage().save(name + "_prescaled_projection_09.png");
118
119 viewConverter->setZoom(1.0);
120 projection.preScale();
121 projection.prescaledQImage().save(name + "_prescaled_projection_10.png");
122
123 projection.notifyCanvasSizeChanged(QSize(350, 350));
124 projection.prescaledQImage().save(name + "_prescaled_projection_11.png");
125
126 projection.viewportMoved(QPoint(100, 100));
127 projection.prescaledQImage().save(name + "_prescaled_projection_12.png");
128
129 viewConverter->setZoom(0.75);
130 projection.preScale();
131 projection.prescaledQImage().save(name + "_prescaled_projection_13.png");
132
133 projection.viewportMoved(QPoint(10, 10));
134 projection.prescaledQImage().save(name + "_prescaled_projection_14.png");
135
136 projection.viewportMoved(QPoint(0, 0));
137 projection.prescaledQImage().save(name + "_prescaled_projection_15.png");
138
139 projection.viewportMoved(QPoint(10, 10));
140 projection.prescaledQImage().save(name + "_prescaled_projection_16.png");
141
142 projection.viewportMoved(QPoint(30, 50));
143 projection.prescaledQImage().save(name + "_prescaled_projection_17.png");
144
145 return true;
146 }
147
testCreation()148 void KisPrescaledProjectionTest::testCreation()
149 {
150 KisPrescaledProjection * prescaledProjection = 0;
151 prescaledProjection = new KisPrescaledProjection();
152 QVERIFY(prescaledProjection != 0);
153 QVERIFY(prescaledProjection->prescaledQImage().isNull());
154 delete prescaledProjection;
155 }
156
testScalingUndeferredSmoothingPixelForPixel()157 void KisPrescaledProjectionTest::testScalingUndeferredSmoothingPixelForPixel()
158 {
159 // Set up a nice image
160 QImage qimage(QString(FILES_DATA_DIR) + '/' + "carrot.png");
161
162 // Undo adapter not necessary
163 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
164 KisImageSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
165
166 // 300 dpi recalculated to pixels per point (of which there are 72
167 // to the inch)
168 image->setResolution(100 / 72 , 100 / 72);
169
170 KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8, cs);
171 image->addNode(layer.data(), image->rootLayer(), 0);
172 layer->paintDevice()->convertFromQImage(qimage, 0);
173
174 KisPrescaledProjection projection;
175 KisCoordinatesConverter converter;
176
177 converter.setImage(image);
178 projection.setCoordinatesConverter(&converter);
179 projection.setMonitorProfile(0,
180 KoColorConversionTransformation::internalRenderingIntent(),
181 KoColorConversionTransformation::internalConversionFlags());
182 projection.setImage(image);
183
184 // pixel-for-pixel, at 100% zoom
185 converter.setResolution(image->xRes(), image->yRes());
186
187 testProjectionScenario(projection, &converter, "pixel_for_pixel");
188
189 }
190
191
testScalingUndeferredSmoothing()192 void KisPrescaledProjectionTest::testScalingUndeferredSmoothing()
193 {
194 // Set up a nice image
195 QImage qimage(QString(FILES_DATA_DIR) + '/' + "carrot.png");
196
197 // Undo adapter not necessary
198 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
199 KisImageSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
200
201 // 300 dpi recalculated to pixels per point (of which there are 72
202 // to the inch)
203 image->setResolution(100, 100);
204
205 KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8, cs);
206 image->addNode(layer.data(), image->rootLayer(), 0);
207 layer->paintDevice()->convertFromQImage(qimage, 0);
208
209 KisPrescaledProjection projection;
210 KisCoordinatesConverter converter;
211
212 converter.setImage(image);
213 projection.setCoordinatesConverter(&converter);
214 projection.setMonitorProfile(0,
215 KoColorConversionTransformation::internalRenderingIntent(),
216 KoColorConversionTransformation::internalConversionFlags());
217 projection.setImage(image);
218
219 testProjectionScenario(projection, &converter, "120dpi");
220
221 }
222
223 //#include <valgrind/callgrind.h>
224
benchmarkUpdate()225 void KisPrescaledProjectionTest::benchmarkUpdate()
226 {
227 QImage referenceImage(QString(FILES_DATA_DIR) + '/' + "carrot.png");
228 QRect imageRect = QRect(QPoint(0,0), referenceImage.size());
229
230 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
231 KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "projection test");
232
233 // set up 300dpi
234 image->setResolution(300 / 72 , 300 / 72);
235
236 KisPaintLayerSP layer = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, cs);
237 layer->paintDevice()->convertFromQImage(referenceImage, 0);
238
239 image->addNode(layer, image->rootLayer(), 0);
240
241 KisPrescaledProjection projection;
242
243 KisCoordinatesConverter converter;
244 converter.setImage(image);
245 projection.setCoordinatesConverter(&converter);
246 projection.setMonitorProfile(0,
247 KoColorConversionTransformation::internalRenderingIntent(),
248 KoColorConversionTransformation::internalConversionFlags());
249 projection.setImage(image);
250
251 // Emulate "Use same aspect as pixels"
252 converter.setResolution(image->xRes(), image->yRes());
253
254 converter.setZoom(1.0);
255
256 KisUpdateInfoSP info = projection.updateCache(image->bounds());
257 projection.recalculateCache(info);
258
259 QEXPECT_FAIL("", "We expected the image rect to be (0,0,512,512), but it is (0,0 308x245)", Continue);
260 QCOMPARE(imageRect, QRect(0,0,512,512));
261
262 QRect dirtyRect(0,0,20,20);
263 const qint32 numShifts = 25;
264 const QPoint offset(dirtyRect.width(),dirtyRect.height());
265
266 //CALLGRIND_START_INSTRUMENTATION;
267
268 QBENCHMARK {
269 for(qint32 i = 0; i < numShifts; i++) {
270 KisUpdateInfoSP tempInfo = projection.updateCache(dirtyRect);
271 projection.recalculateCache(tempInfo);
272
273 dirtyRect.translate(offset);
274 }
275 }
276
277 //CALLGRIND_STOP_INSTRUMENTATION;
278
279 }
280
281
282 class PrescaledProjectionTester
283 {
284 public:
PrescaledProjectionTester()285 PrescaledProjectionTester() {
286 sourceImage = QImage(QString(FILES_DATA_DIR) + '/' + "carrot.png");
287
288 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
289 image = new KisImage(0, sourceImage.width(), sourceImage.height(), cs, "projection test");
290 image->setResolution(100, 100);
291
292 layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8, cs);
293 layer->paintDevice()->convertFromQImage(sourceImage, 0);
294
295 image->addNode(layer, image->rootLayer(), 0);
296
297 converter.setResolution(100, 100);
298 converter.setZoom(1.);
299 converter.setImage(image);
300 converter.setCanvasWidgetSize(QSize(100,100));
301 converter.setDocumentOffset(QPoint(100,100));
302
303 projection.setCoordinatesConverter(&converter);
304 projection.setMonitorProfile(0,
305 KoColorConversionTransformation::internalRenderingIntent(),
306 KoColorConversionTransformation::internalConversionFlags());
307 projection.setImage(image);
308 projection.notifyCanvasSizeChanged(QSize(100,100));
309 projection.notifyZoomChanged();
310 }
311
312 QImage sourceImage;
313 KisImageSP image;
314 KisPaintLayerSP layer;
315 KisCoordinatesConverter converter;
316 KisPrescaledProjection projection;
317 };
318
testScrollingZoom100()319 void KisPrescaledProjectionTest::testScrollingZoom100()
320 {
321 PrescaledProjectionTester t;
322
323 QImage result = t.projection.prescaledQImage();
324 QImage reference = t.sourceImage.copy(QRect(100,100,100,100));
325
326 QPoint pt;
327 QVERIFY(TestUtil::compareQImages(pt, result, reference));
328
329 // Test actual scrolling
330 t.converter.setDocumentOffset(QPoint(150,150));
331 t.projection.viewportMoved(QPoint(-50,-50));
332
333 result = t.projection.prescaledQImage();
334 reference = t.sourceImage.copy(QRect(150,150,100,100));
335
336 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
337 QVERIFY(TestUtil::compareQImages(pt, result, reference));
338 }
339
testScrollingZoom50()340 void KisPrescaledProjectionTest::testScrollingZoom50()
341 {
342 PrescaledProjectionTester t;
343
344 t.converter.setDocumentOffset(QPoint(0,0));
345
346 t.converter.setCanvasWidgetSize(QSize(300,300));
347 t.projection.notifyCanvasSizeChanged(QSize(300,300));
348
349 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
350 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
351 "prescaled_projection_test",
352 "testScrollingZoom50",
353 "initial"));
354
355 t.converter.setZoom(0.5);
356 t.projection.notifyZoomChanged();
357
358 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
359 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
360 "prescaled_projection_test",
361 "testScrollingZoom50",
362 "zoom50"));
363
364 t.converter.setDocumentOffset(QPoint(50,50));
365 t.projection.viewportMoved(QPoint(-50,-50));
366
367 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
368 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
369 "prescaled_projection_test",
370 "testScrollingZoom50",
371 "zoom50_moved50"));
372 }
373
testUpdates()374 void KisPrescaledProjectionTest::testUpdates()
375 {
376 PrescaledProjectionTester t;
377
378 t.converter.setDocumentOffset(QPoint(10,10));
379
380 t.converter.setCanvasWidgetSize(2*QSize(300,300));
381 t.projection.notifyCanvasSizeChanged(2*QSize(300,300));
382
383
384 t.converter.setZoom(0.50);
385 t.projection.notifyZoomChanged();
386
387 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
388 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
389 "prescaled_projection_test",
390 "testUpdates",
391 "zoom50"));
392
393 t.layer->setVisible(false);
394 KisUpdateInfoSP info = t.projection.updateCache(t.image->bounds());
395 t.projection.recalculateCache(info);
396
397 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
398 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
399 "prescaled_projection_test",
400 "testUpdates",
401 "cleared"));
402
403 t.layer->setVisible(true);
404 t.image->refreshGraph();
405
406 // Update incrementally
407
408 const int step = 73;
409 const int patchOffset = -7;
410 const int patchSize = 93;
411
412 QList<KisUpdateInfoSP> infos;
413
414 for(int y = 0; y < t.image->height(); y+=step) {
415 for(int x = 0; x < t.image->width(); x+=step) {
416 QRect patchRect(x - patchOffset, y - patchOffset,
417 patchSize, patchSize);
418
419 infos.append(t.projection.updateCache(patchRect));
420 }
421 }
422
423 Q_FOREACH (KisUpdateInfoSP info, infos) {
424 t.projection.recalculateCache(info);
425 }
426
427 QEXPECT_FAIL("", "Testcase for bug: https://bugs.kde.org/show_bug.cgi?id=289915", Continue);
428 QVERIFY(TestUtil::checkQImage(t.projection.prescaledQImage(),
429 "prescaled_projection_test",
430 "testUpdates",
431 "zoom50", 1));
432 }
433
testQtScaling()434 void KisPrescaledProjectionTest::testQtScaling()
435 {
436 // See: https://bugreports.qt.nokia.com/browse/QTBUG-22827
437
438 /**
439 * Currently we rely on this behavior, so let's test for it.
440 */
441
442 // Create a canvas image
443 QImage canvas(6, 6, QImage::Format_ARGB32);
444 canvas.fill(0);
445
446 // Image we are going to scale down
447 QImage image(7, 7, QImage::Format_ARGB32);
448 QPainter imagePainter(&image);
449 imagePainter.fillRect(QRect(0,0,7,7),Qt::green);
450 imagePainter.end();
451
452
453 QPainter gc(&canvas);
454
455 // Scale down transformation
456 qreal scale = 3.49/7.0;
457 gc.setTransform(QTransform::fromScale(scale,scale));
458
459 // Draw a rect scale*(7x7)
460 gc.fillRect(QRectF(0,0,7,7), Qt::red);
461
462 // Draw an image scale*(7x7)
463 gc.drawImage(QPointF(), image, QRectF(0,0,7,7));
464
465 gc.end();
466
467 // Create an expected result
468 QImage expectedResult(6, 6, QImage::Format_ARGB32);
469 expectedResult.fill(0);
470 QPainter expectedPainter(&expectedResult);
471 expectedPainter.fillRect(QRect(0,0,4,4), Qt::red);
472 expectedPainter.fillRect(QRect(0,0,3,3), Qt::green);
473 expectedPainter.end();
474
475 QEXPECT_FAIL("", "Images should be the same, but aren't", Continue);
476 QCOMPARE(canvas, expectedResult);
477 }
478
479 QTEST_MAIN(KisPrescaledProjectionTest)
480
481
482