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