1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qpainter.h>
32#ifndef QT_NO_WIDGETS
33#include <qdrawutil.h>
34#include <qwidget.h>
35#endif
36#include <qguiapplication.h>
37#include <qfontmetrics.h>
38#include <qbitmap.h>
39#include <qimage.h>
40#include <qthread.h>
41#include <limits.h>
42#include <math.h>
43#include <qpaintengine.h>
44#include <qpixmap.h>
45#include <qrandom.h>
46
47#include <private/qdrawhelper_p.h>
48#include <qpainter.h>
49#include <qpainterpath.h>
50#include <qqueue.h>
51#include <qscreen.h>
52
53#ifndef QT_NO_WIDGETS
54#include <qgraphicsview.h>
55#include <qgraphicsscene.h>
56#include <qgraphicsproxywidget.h>
57#endif
58#include <qfontdatabase.h>
59
60Q_DECLARE_METATYPE(QGradientStops)
61Q_DECLARE_METATYPE(QPainterPath)
62Q_DECLARE_METATYPE(QImage::Format)
63Q_DECLARE_METATYPE(QPainter::CompositionMode)
64
65class tst_QPainter : public QObject
66{
67Q_OBJECT
68
69public:
70 tst_QPainter();
71
72private slots:
73 void cleanupTestCase();
74 void getSetCheck();
75#ifndef QT_NO_WIDGETS
76 void drawPixmap_comp_data();
77 void drawPixmap_comp();
78#endif
79 void saveAndRestore_data();
80 void saveAndRestore();
81
82#ifndef QT_NO_WIDGETS
83 void drawBorderPixmap();
84#endif
85 void drawPixmapFragments();
86 void drawPixmapNegativeScale();
87
88 void drawLine_data();
89 void drawLine();
90 void drawLine_clipped();
91 void drawLine_task121143();
92 void drawLine_task216948();
93
94 void drawLine_task190634();
95 void drawLine_task229459();
96 void drawLine_task234891();
97 void drawLineEndPoints();
98
99 void drawRect_data() { fillData(); }
100 void drawRect();
101 void drawRect2();
102
103 void fillRect_data();
104 void fillRect();
105 void fillRect2_data();
106 void fillRect2();
107 void fillRect3_data() { fillRect2_data(); }
108 void fillRect3();
109 void fillRect4_data() { fillRect2_data(); }
110 void fillRect4();
111 void fillRectNonPremul_data();
112 void fillRectNonPremul();
113
114 void fillRectRGB30_data();
115 void fillRectRGB30();
116
117 void drawEllipse_data();
118 void drawEllipse();
119 void drawClippedEllipse_data();
120 void drawClippedEllipse();
121
122 void drawPath_data();
123 void drawPath();
124 void drawPath2();
125 void drawPath3();
126
127#if QT_DEPRECATED_SINCE(5, 13)
128 void drawRoundRect_data() { fillData(); }
129 void drawRoundRect();
130#endif
131 void drawRoundedRect_data() { fillData(); }
132 void drawRoundedRect();
133
134 void qimageFormats_data();
135 void qimageFormats();
136 void textOnTransparentImage();
137
138#if !defined(QT_NO_WIDGETS) && QT_DEPRECATED_SINCE(5, 13)
139 void initFrom();
140#endif
141
142 void setWindow();
143
144 void combinedMatrix();
145 void renderHints();
146
147 void disableEnableClipping();
148 void setClipRect();
149 void clipRect();
150 void setEqualClipRegionAndPath_data();
151 void setEqualClipRegionAndPath();
152
153 void clipRectSaveRestore();
154 void clipStateSaveRestore();
155
156 void clippedFillPath_data();
157 void clippedFillPath();
158 void clippedLines_data();
159 void clippedLines();
160 void clippedPolygon_data();
161 void clippedPolygon();
162 void clippedText();
163
164 void clipBoundingRect();
165 void transformedClip();
166
167 void setOpacity_data();
168 void setOpacity();
169
170 void drawhelper_blend_untransformed_data();
171 void drawhelper_blend_untransformed();
172 void drawhelper_blend_tiled_untransformed_data();
173 void drawhelper_blend_tiled_untransformed();
174
175 void porterDuff_warning();
176
177 void drawhelper_blend_color();
178
179#ifndef QT_NO_WIDGETS
180 void childWidgetViewport();
181#endif
182
183 void fillRect_objectBoundingModeGradient();
184 void fillRect_stretchToDeviceMode();
185 void monoImages();
186
187 void linearGradientSymmetry_data();
188 void linearGradientSymmetry();
189 void gradientInterpolation();
190
191 void gradientPixelFormat_data();
192 void gradientPixelFormat();
193
194#if QT_CONFIG(raster_64bit)
195 void linearGradientRgb30_data();
196 void linearGradientRgb30();
197 void radialGradientRgb30_data();
198 void radialGradientRgb30();
199#endif
200
201 void fpe_pixmapTransform();
202 void fpe_zeroLengthLines();
203 void fpe_divByZero();
204
205 void fpe_steepSlopes_data();
206 void fpe_steepSlopes();
207 void fpe_rasterizeLine_task232012();
208
209 void fpe_radialGradients();
210
211 void rasterizer_asserts();
212 void rasterizer_negativeCoords();
213
214 void blendOverFlow_data();
215 void blendOverFlow();
216
217 void largeImagePainting_data();
218 void largeImagePainting();
219
220 void imageScaling_task206785();
221
222 void outlineFillConsistency();
223
224 void drawImage_task217400_data();
225 void drawImage_task217400();
226 void drawImage_1x1();
227 void drawImage_task258776();
228 void drawImage_QTBUG28324();
229 void drawRect_task215378();
230 void drawRect_task247505();
231
232#if defined(Q_OS_MAC)
233 void drawText_subPixelPositionsInRaster_qtbug5053();
234#endif
235
236 void drawImage_data();
237 void drawImage();
238
239 void clippedImage();
240
241 void stateResetBetweenQPainters();
242
243 void imageCoordinateLimit();
244 void imageBlending_data();
245 void imageBlending();
246 void imageBlending_clipped();
247
248 void paintOnNullPixmap();
249 void checkCompositionMode();
250
251 void drawPolygon();
252
253 void inactivePainter();
254
255 void extendedBlendModes();
256
257 void zeroOpacity();
258 void clippingBug();
259 void emptyClip();
260
261 void taskQT4444_dontOverflowDashOffset();
262
263 void painterBegin();
264 void setPenColorOnImage();
265 void setPenColorOnPixmap();
266
267#ifndef QT_NO_WIDGETS
268 void QTBUG5939_attachPainterPrivate();
269#endif
270
271 void drawPointScaled();
272
273 void QTBUG14614_gradientCacheRaceCondition();
274 void drawTextOpacity();
275
276 void QTBUG17053_zeroDashPattern();
277
278 void QTBUG38781_NoBrushAndQBitmap();
279
280 void drawTextOutsideGuiThread();
281
282 void drawTextWithComplexBrush();
283 void QTBUG26013_squareCapStroke();
284 void QTBUG25153_drawLine();
285
286 void cosmeticStrokerClipping_data();
287 void cosmeticStrokerClipping();
288
289 void blendARGBonRGB_data();
290 void blendARGBonRGB();
291
292 void RasterOp_NotDestination();
293 void drawTextNoHinting();
294
295 void drawPolyline_data();
296 void drawPolyline();
297
298 void QTBUG50153_drawImage_assert();
299
300 void rotateImage_data();
301 void rotateImage();
302
303 void QTBUG56252();
304
305 void blendNullRGB32();
306 void toRGB64();
307
308 void fillPolygon();
309
310 void drawImageAtPointF();
311 void scaledDashes();
312
313private:
314 void fillData();
315 void setPenColor(QPainter& p);
316 QColor baseColor( int k, int intensity=255 );
317 QImage getResImage( const QString &dir, const QString &addition, const QString &extension );
318 QBitmap getBitmap( const QString &dir, const QString &filename, bool mask );
319};
320
321// Testing get/set functions
322void tst_QPainter::getSetCheck()
323{
324 QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
325 QPainter obj1;
326 obj1.begin(&img);
327 // CompositionMode QPainter::compositionMode()
328 // void QPainter::setCompositionMode(CompositionMode)
329 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_SourceOver));
330 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_SourceOver), obj1.compositionMode());
331 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_DestinationOver));
332 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_DestinationOver), obj1.compositionMode());
333 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_Clear));
334 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_Clear), obj1.compositionMode());
335 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_Source));
336 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_Source), obj1.compositionMode());
337 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_Destination));
338 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_Destination), obj1.compositionMode());
339 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_SourceIn));
340 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_SourceIn), obj1.compositionMode());
341 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_DestinationIn));
342 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_DestinationIn), obj1.compositionMode());
343 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_SourceOut));
344 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_SourceOut), obj1.compositionMode());
345 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_DestinationOut));
346 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_DestinationOut), obj1.compositionMode());
347 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_SourceAtop));
348 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_SourceAtop), obj1.compositionMode());
349 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_DestinationAtop));
350 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_DestinationAtop), obj1.compositionMode());
351 obj1.setCompositionMode(QPainter::CompositionMode(QPainter::CompositionMode_Xor));
352 QCOMPARE(QPainter::CompositionMode(QPainter::CompositionMode_Xor), obj1.compositionMode());
353
354 // const QPen & QPainter::pen()
355 // void QPainter::setPen(const QPen &)
356 QPen var3(Qt::red);
357 obj1.setPen(var3);
358 QCOMPARE(var3, obj1.pen());
359 obj1.setPen(QPen());
360 QCOMPARE(QPen(), obj1.pen());
361
362 // const QBrush & QPainter::brush()
363 // void QPainter::setBrush(const QBrush &)
364 QBrush var4(Qt::red);
365 obj1.setBrush(var4);
366 QCOMPARE(var4, obj1.brush());
367 obj1.setBrush(QBrush());
368 QCOMPARE(QBrush(), obj1.brush());
369
370 // const QBrush & QPainter::background()
371 // void QPainter::setBackground(const QBrush &)
372 QBrush var5(Qt::yellow);
373 obj1.setBackground(var5);
374 QCOMPARE(var5, obj1.background());
375 obj1.setBackground(QBrush());
376 QCOMPARE(QBrush(), obj1.background());
377
378 // bool QPainter::matrixEnabled()
379 // void QPainter::setMatrixEnabled(bool)
380 obj1.setWorldMatrixEnabled(false);
381 QCOMPARE(false, obj1.worldMatrixEnabled());
382 obj1.setWorldMatrixEnabled(true);
383 QCOMPARE(true, obj1.worldMatrixEnabled());
384
385 // bool QPainter::viewTransformEnabled()
386 // void QPainter::setViewTransformEnabled(bool)
387 obj1.setViewTransformEnabled(false);
388 QCOMPARE(false, obj1.viewTransformEnabled());
389 obj1.setViewTransformEnabled(true);
390 QCOMPARE(true, obj1.viewTransformEnabled());
391}
392
393
394tst_QPainter::tst_QPainter()
395{
396 // QtTestCase sets this to false, but this turns off alpha pixmaps on Unix.
397 QGuiApplication::setDesktopSettingsAware(true);
398}
399
400void tst_QPainter::cleanupTestCase()
401{
402 QFile::remove(fileName: QLatin1String("dest.png"));
403 QFile::remove(fileName: QLatin1String("expected.png"));
404 QFile::remove(fileName: QLatin1String("foo.png"));
405}
406
407#ifndef QT_NO_WIDGETS
408void tst_QPainter::drawPixmap_comp_data()
409{
410 if (QGuiApplication::primaryScreen()->depth() < 24)
411 QSKIP("Test only works on 32 bit displays");
412
413 QTest::addColumn<uint>(name: "dest");
414 QTest::addColumn<uint>(name: "source");
415
416 QTest::newRow(dataTag: "0% on 0%, 1") << 0x00000000u<< 0x00000000u;
417 QTest::newRow(dataTag: "0% on 0%, 2") << 0x00007fffu << 0x00ff007fu;
418
419 QTest::newRow(dataTag: "50% on a=0%") << 0x00000000u << 0x7fff0000u;
420 QTest::newRow(dataTag: "50% on a=50%") << 0x7f000000u << 0x7fff0000u;
421 QTest::newRow(dataTag: "50% on deadbeef") << 0xdeafbeefu << 0x7fff0000u;
422 QTest::newRow(dataTag: "deadbeef on a=0%") << 0x00000000u << 0xdeadbeefu;
423 QTest::newRow(dataTag: "deadbeef on a=50%") << 0x7f000000u << 0xdeadbeefu;
424 QTest::newRow(dataTag: "50% blue on 50% red") << 0x7fff0000u << 0x7f0000ffu;
425 QTest::newRow(dataTag: "50% blue on 50% green") << 0x7f00ff00u << 0x7f0000ffu;
426 QTest::newRow(dataTag: "50% red on 50% green") << 0x7f00ff00u << 0x7fff0000u;
427 QTest::newRow(dataTag: "0% on 50%") << 0x7fff00ffu << 0x00ffffffu;
428 QTest::newRow(dataTag: "100% on deadbeef") << 0xdeafbeefu << 0xffabcdefu;
429 QTest::newRow(dataTag: "100% on a=0%") << 0x00000000u << 0xffabcdefu;
430}
431
432QRgb qt_compose_alpha(QRgb source, QRgb dest)
433{
434 int r1 = qRed(rgb: dest), g1 = qGreen(rgb: dest), b1 = qBlue(rgb: dest), a1 = qAlpha(rgb: dest);
435 int r2 = qRed(rgb: source), g2 = qGreen(rgb: source), b2 = qBlue(rgb: source), a2 = qAlpha(rgb: source);
436
437 int alpha = qMin(a: a2 + ((255 - a2) * a1 + 127) / 255, b: 255);
438 if (alpha == 0)
439 return qRgba(r: 0, g: 0, b: 0, a: 0);
440
441 return qRgba(
442 r: qMin(a: (r2 * a2 + (255 - a2) * r1 * a1 / 255) / alpha, b: 255),
443 g: qMin(a: (g2 * a2 + (255 - a2) * g1 * a1 / 255) / alpha, b: 255),
444 b: qMin(a: (b2 * a2 + (255 - a2) * b1 * a1 / 255) / alpha, b: 255),
445 a: alpha);
446}
447
448/* Tests that drawing masked pixmaps works
449*/
450void tst_QPainter::drawPixmap_comp()
451{
452#ifdef Q_OS_MAC
453 QSKIP("Mac has other ideas about alpha composition");
454#endif
455 QFETCH(uint, dest);
456 QFETCH(uint, source);
457
458 QRgb expected = qt_compose_alpha(source, dest);
459
460 QColor c1(qRed(rgb: dest), qGreen(rgb: dest), qBlue(rgb: dest), qAlpha(rgb: dest));
461 QColor c2(qRed(rgb: source), qGreen(rgb: source), qBlue(rgb: source), qAlpha(rgb: source));
462
463 QPixmap destPm(10, 10), srcPm(10, 10);
464 destPm.fill(fillColor: c1);
465 srcPm.fill(fillColor: c2);
466
467 QPainter p(&destPm);
468 p.drawPixmap(x: 0, y: 0, pm: srcPm);
469 p.end();
470
471 QImage result = destPm.toImage().convertToFormat(f: QImage::Format_ARGB32);
472 bool different = false;
473 for (int y=0; y<result.height(); ++y)
474 for (int x=0; x<result.width(); ++x) {
475 bool diff;
476 if (qAlpha(rgb: expected) == 0) {
477 diff = qAlpha(rgb: result.pixel(x, y)) != 0;
478 } else {
479 // Compensate for possible roundoff / platform fudge
480 int off = 1;
481 QRgb pix = result.pixel(x, y);
482 diff = (qAbs(t: qRed(rgb: pix) - qRed(rgb: expected)) > off)
483 || (qAbs(t: qGreen(rgb: pix) - qGreen(rgb: expected)) > off)
484 || (qAbs(t: qBlue(rgb: pix) - qBlue(rgb: expected)) > off)
485 || (qAbs(t: qAlpha(rgb: pix) - qAlpha(rgb: expected)) > off);
486 }
487 if (diff && !different)
488 qDebug( msg: "Different at %d,%d pixel [%d,%d,%d,%d] expected [%d,%d,%d,%d]", x, y,
489 qRed(rgb: result.pixel(x, y)), qGreen(rgb: result.pixel(x, y)),
490 qBlue(rgb: result.pixel(x, y)), qAlpha(rgb: result.pixel(x, y)),
491 qRed(rgb: expected), qGreen(rgb: expected), qBlue(rgb: expected), qAlpha(rgb: expected));
492 different |= diff;
493 }
494
495 QVERIFY(!different);
496}
497#endif
498
499void tst_QPainter::saveAndRestore_data()
500{
501 QTest::addColumn<QFont>(name: "font");
502 QTest::addColumn<QPen>(name: "pen");
503 QTest::addColumn<QBrush>(name: "brush");
504 QTest::addColumn<QColor>(name: "backgroundColor");
505 QTest::addColumn<int>(name: "backgroundMode");
506 QTest::addColumn<QPoint>(name: "brushOrigin");
507 QTest::addColumn<QRegion>(name: "clipRegion");
508 QTest::addColumn<QRect>(name: "window");
509 QTest::addColumn<QRect>(name: "viewport");
510
511 QPixmap pixmap(1, 1);
512 QPainter p(&pixmap);
513 QFont font = p.font();
514 QPen pen = p.pen();
515 QBrush brush = p.brush();
516 QColor backgroundColor = p.background().color();
517 Qt::BGMode backgroundMode = p.backgroundMode();
518 QPoint brushOrigin = p.brushOrigin();
519 QRegion clipRegion = p.clipRegion();
520 QRect window = p.window();
521 QRect viewport = p.viewport();
522
523 QTest::newRow(dataTag: "Original") << font << pen << brush << backgroundColor << int(backgroundMode)
524 << brushOrigin << clipRegion << window << viewport;
525
526 QFont font2 = font;
527 font2.setPointSize( 24 );
528 QTest::newRow(dataTag: "Modified font.pointSize, brush, backgroundColor, backgroundMode")
529 << font2 << pen << QBrush(Qt::red) << QColor(Qt::blue) << int(Qt::TransparentMode)
530 << brushOrigin << clipRegion << window << viewport;
531
532 font2 = font;
533 font2.setPixelSize( 20 );
534 QTest::newRow(dataTag: "Modified font.pixelSize, brushOrigin, pos")
535 << font2 << pen << brush << backgroundColor << int(backgroundMode)
536 << QPoint( 50, 32 ) << clipRegion << window << viewport;
537
538 QTest::newRow(dataTag: "Modified clipRegion, window, viewport")
539 << font << pen << brush << backgroundColor << int(backgroundMode)
540 << brushOrigin << clipRegion.subtracted(r: QRect(10,10,50,30))
541 << QRect(-500, -500, 500, 500 ) << QRect( 0, 0, 50, 50 );
542}
543
544void tst_QPainter::saveAndRestore()
545{
546 QFETCH( QFont, font );
547 QFETCH( QPen, pen );
548 QFETCH( QBrush, brush );
549 QFETCH( QColor, backgroundColor );
550 QFETCH( int, backgroundMode );
551 QFETCH( QPoint, brushOrigin );
552 QFETCH( QRegion, clipRegion );
553 QFETCH( QRect, window );
554 QFETCH( QRect, viewport );
555
556 QPixmap pixmap(1, 1);
557 QPainter painter(&pixmap);
558
559 QFont font_org = painter.font();
560 QPen pen_org = painter.pen();
561 QBrush brush_org = painter.brush();
562 QColor backgroundColor_org = painter.background().color();
563 Qt::BGMode backgroundMode_org = painter.backgroundMode();
564 QPoint brushOrigin_org = painter.brushOrigin();
565 QRegion clipRegion_org = painter.clipRegion();
566 QRect window_org = painter.window();
567 QRect viewport_org = painter.viewport();
568
569 painter.save();
570 painter.setFont( font );
571 painter.setPen( QPen(pen) );
572 painter.setBrush( brush );
573 painter.setBackground( backgroundColor );
574 painter.setBackgroundMode( (Qt::BGMode)backgroundMode );
575 painter.setBrushOrigin( brushOrigin );
576 painter.setClipRegion( clipRegion );
577 painter.setWindow( window );
578 painter.setViewport( viewport );
579 painter.restore();
580
581 QCOMPARE( painter.font(), font_org );
582 QCOMPARE( painter.font().pointSize(), font_org.pointSize() );
583 QCOMPARE( painter.font().pixelSize(), font_org.pixelSize() );
584 QCOMPARE( painter.pen(), pen_org );
585 QCOMPARE( painter.brush(), brush_org );
586 QCOMPARE( painter.background().color(), backgroundColor_org );
587 QCOMPARE( painter.backgroundMode(), backgroundMode_org );
588 QCOMPARE( painter.brushOrigin(), brushOrigin_org );
589 QCOMPARE( painter.clipRegion(), clipRegion_org );
590 QCOMPARE( painter.window(), window_org );
591 QCOMPARE( painter.viewport(), viewport_org );
592}
593
594/*
595 Helper functions
596*/
597
598QColor tst_QPainter::baseColor( int k, int intensity )
599{
600 int r = ( k & 1 ) * intensity;
601 int g = ( (k>>1) & 1 ) * intensity;
602 int b = ( (k>>2) & 1 ) * intensity;
603 return QColor( r, g, b );
604}
605
606QImage tst_QPainter::getResImage( const QString &dir, const QString &addition, const QString &extension )
607{
608 QImage res;
609 QString resFilename = dir + QLatin1String("/res_") + addition + QLatin1Char('.') + extension;
610 if ( !res.load( fileName: resFilename ) ) {
611 QWARN(QString("Could not load result data %s %1").arg(resFilename).toLatin1());
612 return QImage();
613 }
614 return res;
615}
616
617QBitmap tst_QPainter::getBitmap( const QString &dir, const QString &filename, bool mask )
618{
619 QBitmap bm;
620 QString bmFilename = dir + QLatin1Char('/') + filename + QLatin1String(".xbm");
621 if ( !bm.load( fileName: bmFilename ) ) {
622 QWARN(QString("Could not load bitmap '%1'").arg(bmFilename).toLatin1());
623 return QBitmap();
624 }
625 if ( mask ) {
626 QBitmap mask;
627 QString maskFilename = dir + QLatin1Char('/') + filename + QLatin1String("-mask.xbm");
628 if (!mask.load(fileName: maskFilename)) {
629 QWARN(QString("Could not load mask '%1'").arg(maskFilename).toLatin1());
630 return QBitmap();
631 }
632 bm.setMask( mask );
633 }
634 return bm;
635}
636
637static int getPaintedPixels(const QImage &image, const QColor &background)
638{
639 uint color = background.rgba();
640
641 int pixels = 0;
642
643 for (int y = 0; y < image.height(); ++y)
644 for (int x = 0; x < image.width(); ++x)
645 if (image.pixel(x, y) != color)
646 ++pixels;
647
648 return pixels;
649}
650
651static QRect getPaintedSize(const QImage &image, const QColor &background)
652{
653 // not the fastest but at least it works..
654 int xmin = image.width() + 1;
655 int xmax = -1;
656 int ymin = image.height() +1;
657 int ymax = -1;
658
659 uint color = background.rgba();
660
661 for ( int y = 0; y < image.height(); ++y ) {
662 for (int x = 0; x < image.width(); ++x) {
663 QRgb pixel = image.pixel( x, y );
664 if (pixel != color && x < xmin)
665 xmin = x;
666 if (pixel != color && x > xmax)
667 xmax = x;
668 if (pixel != color && y < ymin)
669 ymin = y;
670 if (pixel != color && y > ymax)
671 ymax = y;
672 }
673 }
674
675 return QRect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
676}
677
678static QRect getPaintedSize(const QPixmap &pm, const QColor &background)
679{
680 return getPaintedSize(image: pm.toImage(), background);
681}
682
683#ifndef QT_NO_WIDGETS
684
685#if QT_DEPRECATED_SINCE(5, 13)
686void tst_QPainter::initFrom()
687{
688 QWidget *widget = new QWidget();
689 QPalette pal = widget->palette();
690 pal.setColor(acr: QPalette::WindowText, acolor: QColor(255, 0, 0));
691 pal.setBrush(acr: QPalette::Window, abrush: QColor(0, 255, 0));
692 widget->setPalette(pal);
693 widget->show();
694
695 QFont font = widget->font();
696 font.setPointSize(26);
697 font.setItalic(true);
698 widget->setFont(font);
699
700 QPixmap pm(100, 100);
701 QPainter p(&pm);
702 p.initFrom(device: widget);
703
704 QCOMPARE(p.font(), font);
705 QCOMPARE(p.pen().color(), pal.color(QPalette::WindowText));
706 QCOMPARE(p.background(), pal.window());
707
708 delete widget;
709}
710#endif
711
712void tst_QPainter::drawBorderPixmap()
713{
714 QPixmap src(79,79);
715 src.fill(fillColor: Qt::transparent);
716
717 QImage pm(200,200,QImage::Format_RGB32);
718 QPainter p(&pm);
719 p.setTransform(transform: QTransform(-1,0,0,-1,173.5,153.5));
720 qDrawBorderPixmap(painter: &p, targetRect: QRect(0,0,75,105), targetMargins: QMargins(39,39,39,39), pixmap: src, sourceRect: QRect(0,0,79,79), sourceMargins: QMargins(39,39,39,39),
721 rules: QTileRules(Qt::StretchTile,Qt::StretchTile), hints: { });
722}
723#endif
724
725void tst_QPainter::drawPixmapFragments()
726{
727 QPixmap origPixmap(20, 20);
728 QPixmap resPixmap(20, 20);
729 QPainter::PixmapFragment fragments[4] = { {.x: 15, .y: 15, .sourceLeft: 0, .sourceTop: 0, .width: 10, .height: 10, .scaleX: 1, .scaleY: 1, .rotation: 0, .opacity: 1},
730 { .x: 5, .y: 15, .sourceLeft: 10, .sourceTop: 0, .width: 10, .height: 10, .scaleX: 1, .scaleY: 1, .rotation: 0, .opacity: 1},
731 {.x: 15, .y: 5, .sourceLeft: 0, .sourceTop: 10, .width: 10, .height: 10, .scaleX: 1, .scaleY: 1, .rotation: 0, .opacity: 1},
732 { .x: 5, .y: 5, .sourceLeft: 10, .sourceTop: 10, .width: 10, .height: 10, .scaleX: 1, .scaleY: 1, .rotation: 0, .opacity: 1} };
733 {
734 QPainter p(&origPixmap);
735 p.fillRect(x: 0, y: 0, w: 10, h: 10, c: Qt::red);
736 p.fillRect(x: 10, y: 0, w: 10, h: 10, c: Qt::green);
737 p.fillRect(x: 0, y: 10, w: 10, h: 10, c: Qt::blue);
738 p.fillRect(x: 10, y: 10, w: 10, h: 10, c: Qt::yellow);
739 }
740 {
741 QPainter p(&resPixmap);
742 p.drawPixmapFragments(fragments, fragmentCount: 4, pixmap: origPixmap);
743 }
744
745 QImage origImage = origPixmap.toImage().convertToFormat(f: QImage::Format_ARGB32);
746 QImage resImage = resPixmap.toImage().convertToFormat(f: QImage::Format_ARGB32);
747
748 QCOMPARE(resImage.size(), resPixmap.size());
749 QVERIFY(resImage.pixel(5, 5) == origImage.pixel(15, 15));
750 QVERIFY(resImage.pixel(5, 15) == origImage.pixel(15, 5));
751 QVERIFY(resImage.pixel(15, 5) == origImage.pixel(5, 15));
752 QVERIFY(resImage.pixel(15, 15) == origImage.pixel(5, 5));
753
754
755 QPainter::PixmapFragment fragment = QPainter::PixmapFragment::create(pos: QPointF(20, 20), sourceRect: QRectF(30, 30, 2, 2));
756 QCOMPARE(fragment.x, qreal(20));
757 QCOMPARE(fragment.y, qreal(20));
758 QCOMPARE(fragment.sourceLeft, qreal(30));
759 QCOMPARE(fragment.sourceTop, qreal(30));
760 QCOMPARE(fragment.width, qreal(2));
761 QCOMPARE(fragment.height, qreal(2));
762 QCOMPARE(fragment.scaleX, qreal(1));
763 QCOMPARE(fragment.scaleY, qreal(1));
764 QCOMPARE(fragment.rotation, qreal(0));
765 QCOMPARE(fragment.opacity, qreal(1));
766}
767
768void tst_QPainter::drawPixmapNegativeScale()
769{
770 // basePixmap is a 16x16 opaque white square ...
771 QPixmap basePixmap(16, 16);
772 basePixmap.fill(fillColor: QColor(255, 255, 255, 255));
773 // ... with an opaque black 8x16 left strip
774 QPainter p(&basePixmap);
775 p.setCompositionMode(QPainter::CompositionMode_Source);
776 p.fillRect(QRect(0, 0, 8, 16), color: QColor(0, 0, 0, 255));
777 p.end();
778
779 // verify one pixel value for each strip
780 QImage baseImage = basePixmap.toImage().convertToFormat(f: QImage::Format_ARGB32);
781 QVERIFY(baseImage.pixel(4, 8) == qRgba(0, 0, 0, 255));
782 QVERIFY(baseImage.pixel(12, 8) == qRgba(255, 255, 255, 255));
783
784 // resultPixmap is a 16x16 square
785 QPixmap resultPixmap(16, 16);
786
787 // draw basePixmap over resultPixmap using x=-1.0 y=-1.0
788 // scaling factors (i.e. 180° rotation)
789 QPainter p2(&resultPixmap);
790 p2.setCompositionMode(QPainter::CompositionMode_Source);
791 p2.scale(sx: qreal(-1.0), sy: qreal(-1.0));
792 p2.translate(dx: -resultPixmap.width(), dy: -resultPixmap.height());
793 p2.drawPixmap(r: resultPixmap.rect(), pm: basePixmap);
794 p2.end();
795
796 // check result
797 QImage resultImage = resultPixmap.toImage().convertToFormat(f: QImage::Format_ARGB32);
798 QVERIFY(resultImage.pixel(4, 8) == qRgba(255, 255, 255, 255)); // left strip is now white
799 QVERIFY(resultImage.pixel(12, 8) == qRgba(0, 0, 0, 255)); // and right strip is now black
800}
801
802void tst_QPainter::drawLine_data()
803{
804 QTest::addColumn<QLine>(name: "line");
805
806 QTest::newRow(dataTag: "0-45") << QLine(0, 20, 100, 0);
807 QTest::newRow(dataTag: "45-90") << QLine(0, 100, 20, 0);
808 QTest::newRow(dataTag: "90-135") << QLine(20, 100, 0, 0);
809 QTest::newRow(dataTag: "135-180") << QLine(100, 20, 0, 0);
810 QTest::newRow(dataTag: "180-225") << QLine(100, 0, 0, 20);
811 QTest::newRow(dataTag: "225-270") << QLine(20, 0, 0, 100);
812 QTest::newRow(dataTag: "270-315") << QLine(0, 0, 20, 100);
813 QTest::newRow(dataTag: "315-360") << QLine(0, 0, 100, 20);
814}
815
816void tst_QPainter::drawLine()
817{
818 const int offset = 5;
819 const int epsilon = 1; // allow for one pixel difference
820
821 QFETCH(QLine, line);
822
823 QPixmap pixmapUnclipped(qMin(a: line.x1(), b: line.x2())
824 + 2*offset + qAbs(t: line.dx()),
825 qMin(a: line.y1(), b: line.y2())
826 + 2*offset + qAbs(t: line.dy()));
827
828 { // unclipped
829 pixmapUnclipped.fill(fillColor: Qt::white);
830 QPainter p(&pixmapUnclipped);
831 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
832 p.translate(dx: offset, dy: offset);
833 p.setPen(QPen(Qt::black));
834 p.drawLine(line);
835 p.end();
836
837 const QRect painted = getPaintedSize(pm: pixmapUnclipped, background: Qt::white);
838
839 QLine l = line;
840 l.translate(adx: offset, ady: offset);
841 QVERIFY(qAbs(painted.width() - qAbs(l.dx())) <= epsilon);
842 QVERIFY(qAbs(painted.height() - qAbs(l.dy())) <= epsilon);
843 QVERIFY(qAbs(painted.top() - qMin(l.y1(), l.y2())) <= epsilon);
844 QVERIFY(qAbs(painted.left() - qMin(l.x1(), l.x2())) <= epsilon);
845 QVERIFY(qAbs(painted.bottom() - qMax(l.y1(), l.y2())) <= epsilon);
846 QVERIFY(qAbs(painted.right() - qMax(l.x1(), l.x2())) <= epsilon);
847 }
848
849 QPixmap pixmapClipped(qMin(a: line.x1(), b: line.x2())
850 + 2*offset + qAbs(t: line.dx()),
851 qMin(a: line.y1(), b: line.y2())
852 + 2*offset + qAbs(t: line.dy()));
853 { // clipped
854 const QRect clip = QRect(line.p1(), line.p2()).normalized();
855
856 pixmapClipped.fill(fillColor: Qt::white);
857 QPainter p(&pixmapClipped);
858 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
859 p.translate(dx: offset, dy: offset);
860 p.setClipRect(clip);
861 p.setPen(QPen(Qt::black));
862 p.drawLine(line);
863 p.end();
864 }
865
866 const QImage unclipped = pixmapUnclipped.toImage();
867 const QImage clipped = pixmapClipped.toImage();
868 QCOMPARE(unclipped, clipped);
869}
870
871void tst_QPainter::drawLine_clipped()
872{
873 QImage image(16, 1, QImage::Format_ARGB32_Premultiplied);
874 image.fill(pixel: 0x0);
875
876 QPainter p(&image);
877 p.setPen(QPen(Qt::black, 10));
878
879 // this should fill the whole image
880 p.drawLine(x1: -1, y1: -1, x2: 17, y2: 1);
881 p.end();
882
883 for (int x = 0; x < 16; ++x)
884 QCOMPARE(image.pixel(x, 0), 0xff000000);
885}
886
887void tst_QPainter::drawLine_task121143()
888{
889 QPen pen(Qt::black);
890
891 QImage image(5, 5, QImage::Format_ARGB32_Premultiplied);
892 image.fill(pixel: 0xffffffff);
893 QPainter p(&image);
894 p.setPen(pen);
895 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
896 p.drawLine(line: QLine(0, 0+4, 0+4, 0));
897 p.end();
898
899 QImage expected(5, 5, QImage::Format_ARGB32_Premultiplied);
900 expected.fill(pixel: 0xffffffff);
901 for (int x = 0; x < 5; ++x)
902 expected.setPixel(x, y: 5-x-1, index_or_rgb: pen.color().rgb());
903
904 QCOMPARE(image, expected);
905}
906
907void tst_QPainter::drawLine_task190634()
908{
909 QPen pen(Qt::black, 3);
910
911 QImage image(32, 32, QImage::Format_ARGB32_Premultiplied);
912 QPainter p(&image);
913 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), c: Qt::white);
914
915 p.setPen(pen);
916 p.drawLine(l: QLineF(2, -1.6, 10, -1.6));
917 p.end();
918
919 const uint *data = reinterpret_cast<uint *>(image.bits());
920
921 for (int i = 0; i < image.width() * image.height(); ++i)
922 QCOMPARE(data[i], 0xffffffff);
923
924 p.begin(&image);
925 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), c: Qt::white);
926
927 p.setPen(pen);
928 p.drawLine(l: QLineF(-1.6, 2, -1.6, 10));
929 p.end();
930
931 data = reinterpret_cast<uint *>(image.bits());
932
933 for (int i = 0; i < image.width() * image.height(); ++i)
934 QCOMPARE(data[i], 0xffffffff);
935
936 p.begin(&image);
937 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), c: Qt::white);
938
939 p.setPen(pen);
940 p.drawLine( p1: QPoint(2,-2), p2: QPoint(3,-5) );
941 p.end();
942
943 data = reinterpret_cast<uint *>(image.bits());
944
945 for (int i = 0; i < image.width() * image.height(); ++i)
946 QCOMPARE(data[i], 0xffffffff);
947}
948
949void tst_QPainter::drawLine_task229459()
950{
951 QImage image(32, 32, QImage::Format_ARGB32_Premultiplied);
952 image.fill(pixel: 0x0);
953 QPen pen(Qt::black, 64);
954
955 QPainter p(&image);
956 p.setPen(pen);
957 p.drawLine(x1: -8, y1: -8, x2: 10000000, y2: 10000000);
958 p.end();
959
960 QImage expected = image;
961 expected.fill(pixel: 0xff000000);
962
963 QCOMPARE(image, expected);
964}
965
966void tst_QPainter::drawLine_task234891()
967{
968 QImage img(100, 1000, QImage::Format_ARGB32_Premultiplied);
969 img.fill(pixel: 0x0);
970 QImage expected = img;
971
972 QPainter p(&img);
973 p.setPen(QPen(QBrush(QColor(255,0,0)), 6));
974 p.drawLine(p1: QPointF(25000,100),p2: QPointF(30000,105));
975
976 p.setPen(QPen(QBrush(QColor(0,255,0)), 6));
977 p.drawLine(p1: QPointF(30000,150),p2: QPointF(35000,155));
978
979 p.setPen(QPen(QBrush(QColor(0,0,255)), 6));
980 p.drawLine(p1: QPointF(65000,200),p2: QPointF(66000,205));
981
982 QCOMPARE(expected, img);
983}
984
985void tst_QPainter::drawLine_task216948()
986{
987 QImage img(1, 10, QImage::Format_ARGB32_Premultiplied);
988 img.fill(pixel: 0x0);
989
990 QPainter p(&img);
991 QLine line(10, 0, 10, 10);
992 p.translate(dx: -10, dy: 0);
993 p.drawLine(line);
994 p.end();
995
996 for (int i = 0; i < img.height(); ++i)
997 QCOMPARE(img.pixel(0, i), QColor(Qt::black).rgba());
998}
999
1000void tst_QPainter::drawLineEndPoints()
1001{
1002 QImage img(256, 256, QImage::Format_ARGB32_Premultiplied);
1003 img.fill(pixel: 0x0);
1004
1005 QPainter p;
1006 for (int x = 0; x < img.width(); ++x) {
1007 QRgb color = qRgb(r: x, g: 0, b: 0);
1008 p.begin(&img);
1009 p.setPen(QPen(color));
1010 p.drawLine(x1: x, y1: 0, x2: 255 - x, y2: 255);
1011 p.end();
1012 QCOMPARE(img.pixel(x, 0), color);
1013 QCOMPARE(img.pixel(255 - x, 255), color);
1014 }
1015 for (int y = 0; y < img.height(); ++y) {
1016 QRgb color = qRgb(r: 0, g: y, b: 0);
1017 p.begin(&img);
1018 p.setPen(QPen(color));
1019 p.drawLine(x1: 0, y1: y, x2: 255, y2: 255 - y);
1020 p.end();
1021 QCOMPARE(img.pixel(0, y), color);
1022 QCOMPARE(img.pixel(255, 255 - y), color);
1023 }
1024 for (int x = 0; x < img.width(); ++x) {
1025 QRgb color = qRgb(r: x, g: 0, b: x);
1026 p.begin(&img);
1027 p.setPen(QPen(color));
1028 p.drawLine(x1: x, y1: 255, x2: 255 - x, y2: 0);
1029 p.end();
1030 QCOMPARE(img.pixel(x, 255), color);
1031 QCOMPARE(img.pixel(255 - x, 0), color);
1032 }
1033 for (int y = 0; y < img.height(); ++y) {
1034 QRgb color = qRgb(r: 0, g: y, b: y);
1035 p.begin(&img);
1036 p.setPen(QPen(color));
1037 p.drawLine(x1: 255, y1: y, x2: 0, y2: 255 - y);
1038 p.end();
1039 QCOMPARE(img.pixel(255, y), color);
1040 QCOMPARE(img.pixel(0, 255 - y), color);
1041 }
1042}
1043
1044void tst_QPainter::drawRect()
1045{
1046 QFETCH(QRect, rect);
1047 QFETCH(bool, usePen);
1048
1049 QPixmap pixmap(rect.x() + rect.width() + 10,
1050 rect.y() + rect.height() + 10);
1051 {
1052 pixmap.fill(fillColor: Qt::white);
1053 QPainter p(&pixmap);
1054 p.setPen(usePen ? QPen(Qt::black) : QPen(Qt::NoPen));
1055 p.setBrush(Qt::black);
1056 p.drawRect(r: rect);
1057 p.end();
1058
1059 int increment = usePen ? 1 : 0;
1060
1061 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1062 QCOMPARE(painted.width(), rect.width() + increment);
1063 QCOMPARE(painted.height(), rect.height() + increment);
1064 }
1065}
1066
1067void tst_QPainter::drawRect2()
1068{
1069 QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);
1070 {
1071 image.fill(pixel: 0xffffffff);
1072
1073 QTransform transform(0.368567, 0, 0, 0, 0.368567, 0, 0.0289, 0.0289, 1);
1074
1075 QPainter p(&image);
1076 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1077 p.setTransform(transform);
1078 p.setBrush(Qt::red);
1079 p.setPen(Qt::NoPen);
1080 p.drawRect(r: QRect(14, 14, 39, 39));
1081 p.end();
1082
1083 QRect fill = getPaintedSize(image, background: Qt::white);
1084 image.fill(pixel: 0xffffffff);
1085
1086 p.begin(&image);
1087 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1088 p.setTransform(transform);
1089 p.drawRect(r: QRect(14, 14, 39, 39));
1090 p.end();
1091
1092 QRect stroke = getPaintedSize(image, background: Qt::white);
1093 QCOMPARE(stroke.adjusted(1, 1, 0, 0), fill.adjusted(0, 0, 1, 1));
1094 }
1095}
1096
1097void tst_QPainter::fillRect_data()
1098{
1099 QTest::addColumn<QImage::Format>(name: "format");
1100
1101 QTest::newRow(dataTag: "argb32pm") << QImage::Format_ARGB32_Premultiplied;
1102 QTest::newRow(dataTag: "rgba8888pm") << QImage::Format_RGBA8888_Premultiplied;
1103 QTest::newRow(dataTag: "rgba64pm") << QImage::Format_RGBA64_Premultiplied;
1104}
1105
1106void tst_QPainter::fillRect()
1107{
1108 QFETCH(QImage::Format, format);
1109
1110 QImage image(100, 100, format);
1111 image.fill(pixel: QColor(0, 0, 0, 0).rgba());
1112
1113 QPainter p(&image);
1114
1115 p.fillRect(x: 0, y: 0, w: 100, h: 100, b: QColor(255, 0, 0, 127));
1116
1117// pixmap.save("bla1.png", "PNG");
1118 QCOMPARE(getPaintedSize(image, QColor(0, 0, 0, 0)),
1119 QRect(0, 0, 100, 100));
1120 QCOMPARE(getPaintedSize(image, QColor(127, 0, 0, 127)).isValid(),
1121 QRect().isValid());
1122
1123 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
1124 p.fillRect(x: 50, y: 0, w: 50, h: 100, b: QColor(0, 0, 255, 255));
1125
1126 QCOMPARE(getPaintedSize(image, QColor(127, 0, 0, 127)),
1127 QRect(50, 0, 50, 100));
1128 QCOMPARE(getPaintedSize(image, QColor(0, 0, 127, 127)),
1129 QRect(0, 0, 50, 100));
1130}
1131
1132void tst_QPainter::fillRect2_data()
1133{
1134 QTest::addColumn<QImage::Format>(name: "format");
1135
1136 QTest::newRow(dataTag: "argb32") << QImage::Format_ARGB32;
1137 QTest::newRow(dataTag: "argb32pm") << QImage::Format_ARGB32_Premultiplied;
1138 QTest::newRow(dataTag: "rgba8888") << QImage::Format_RGBA8888;
1139 QTest::newRow(dataTag: "rgba8888pm") << QImage::Format_RGBA8888_Premultiplied;
1140 QTest::newRow(dataTag: "a2rgb30pm") << QImage::Format_A2RGB30_Premultiplied;
1141 QTest::newRow(dataTag: "a2bgr30pm") << QImage::Format_A2BGR30_Premultiplied;
1142}
1143
1144void tst_QPainter::fillRect2()
1145{
1146 QFETCH(QImage::Format, format);
1147
1148 QRgb background = 0x0;
1149
1150 QImage img(1, 20, format);
1151 img.fill(pixel: background);
1152
1153 QPainter p(&img);
1154
1155 QRectF rect(0, 1, 1.2, 18);
1156 p.fillRect(r: rect, c: Qt::yellow);
1157
1158 p.end();
1159
1160 QCOMPARE(img.pixel(0, 0), background);
1161 QCOMPARE(img.pixel(0, img.height() - 1), background);
1162
1163 QCOMPARE(img.pixel(0, 1), img.pixel(0, 2));
1164 QCOMPARE(img.pixel(0, img.height() - 2), img.pixel(0, img.height() - 3));
1165 QCOMPARE(img.pixel(0, 1), QColor(Qt::yellow).rgba());
1166}
1167
1168void tst_QPainter::fillRect3()
1169{
1170 QFETCH(QImage::Format, format);
1171
1172 QImage img(1, 1, format);
1173 img.fill(pixel: QColor(Qt::black).rgba());
1174
1175 QPainter p(&img);
1176 p.setCompositionMode(QPainter::CompositionMode_Source);
1177 p.fillRect(r: img.rect(), c: Qt::transparent);
1178 p.end();
1179
1180 QCOMPARE(img.pixel(0, 0), 0U);
1181}
1182
1183void tst_QPainter::fillRect4()
1184{
1185 QFETCH(QImage::Format, format);
1186
1187 QImage image(100, 1, format);
1188 image.fill(pixel: 0x0);
1189
1190 QImage expected = image;
1191 expected.fill(pixel: 0xffffffff);
1192
1193 QPainter p(&image);
1194 p.scale(sx: 1.1, sy: 1);
1195 p.setPen(Qt::NoPen);
1196
1197 for (int i = 0; i < 33; ++i)
1198 p.fillRect(r: QRectF(3 * i, 0, 3, 1), c: Qt::white);
1199
1200 p.end();
1201
1202 QCOMPARE(image, expected);
1203}
1204
1205void tst_QPainter::fillRectNonPremul_data()
1206{
1207 QTest::addColumn<QImage::Format>(name: "format");
1208 QTest::addColumn<uint>(name: "color");
1209
1210 QTest::newRow(dataTag: "argb32 7f1f3f7f") << QImage::Format_ARGB32 << qRgba(r: 31, g: 63, b: 127, a: 127);
1211 QTest::newRow(dataTag: "rgba8888 7f1f3f7f") << QImage::Format_RGBA8888 << qRgba(r: 31, g: 63, b: 127, a: 127);
1212
1213 QTest::newRow(dataTag: "argb32 3f1f3f7f") << QImage::Format_ARGB32 << qRgba(r: 31, g: 63, b: 127, a: 63);
1214 QTest::newRow(dataTag: "rgba8888 3f1f3f7f") << QImage::Format_RGBA8888 << qRgba(r: 31, g: 63, b: 127, a: 63);
1215
1216 QTest::newRow(dataTag: "argb32 070375f4") << QImage::Format_ARGB32 << qRgba(r: 3, g: 117, b: 244, a: 7);
1217 QTest::newRow(dataTag: "rgba8888 070375f4") << QImage::Format_RGBA8888 << qRgba(r: 3, g: 117, b: 244, a: 7);
1218
1219 QTest::newRow(dataTag: "argb32 0301fe0c") << QImage::Format_ARGB32 << qRgba(r: 1, g: 254, b: 12, a: 3);
1220 QTest::newRow(dataTag: "rgba8888 0301fe0c") << QImage::Format_RGBA8888 << qRgba(r: 1, g: 254, b: 12, a: 3);
1221
1222 QTest::newRow(dataTag: "argb32 01804010") << QImage::Format_ARGB32 << qRgba(r: 128, g: 64, b: 32, a: 1);
1223 QTest::newRow(dataTag: "rgba8888 01804010") << QImage::Format_RGBA8888 << qRgba(r: 128, g: 64, b: 32, a: 1);
1224}
1225
1226void tst_QPainter::fillRectNonPremul()
1227{
1228 QFETCH(QImage::Format, format);
1229 QFETCH(uint, color);
1230
1231 QImage image(1, 1, format);
1232 QRectF rect(0, 0, 1, 1);
1233
1234 // Fill with CompositionMode_SourceOver tests blend_color
1235 image.fill(color: Qt::transparent);
1236 QPainter painter(&image);
1237 painter.fillRect(rect, color: QColor::fromRgba(rgba: color));
1238 painter.end();
1239
1240 // Fill with CompositionMode_Source tests rectfill.
1241 painter.begin(&image);
1242 painter.setCompositionMode(QPainter::CompositionMode_Source);
1243 painter.fillRect(rect, color: QColor::fromRgba(rgba: color));
1244 painter.end();
1245
1246 QRgb p = image.pixel(x: 0, y: 0);
1247 QCOMPARE(qAlpha(p), qAlpha(color));
1248 QVERIFY(qAbs(qRed(p)-qRed(color)) <= 1);
1249 QVERIFY(qAbs(qGreen(p)-qGreen(color)) <= 1);
1250 QVERIFY(qAbs(qBlue(p)-qBlue(color)) <= 1);
1251}
1252
1253void tst_QPainter::fillRectRGB30_data()
1254{
1255 QTest::addColumn<uint>(name: "color");
1256
1257 QTest::newRow(dataTag: "17|43|259") << (0xc0000000 | (17 << 20) | (43 << 10) | 259);
1258 QTest::newRow(dataTag: "2|33|444") << (0xc0000000 | (2 << 20) | (33 << 10) | 444);
1259 QTest::newRow(dataTag: "1000|1000|1000") << (0xc0000000 | (1000 << 20) | (1000 << 10) | 1000);
1260}
1261
1262void tst_QPainter::fillRectRGB30()
1263{
1264 QFETCH(uint, color);
1265 QRectF rect(0, 0, 1, 1);
1266
1267 // Fill with CompositionMode_SourceOver tests blend_color
1268 QImage image1(1, 1, QImage::Format_A2BGR30_Premultiplied);
1269 image1.fill(color: Qt::transparent);
1270 QPainter painter(&image1);
1271 painter.fillRect(rect, color: QColor::fromRgba64(rgba: qConvertA2rgb30ToRgb64<PixelOrderBGR>(rgb: color)));
1272 painter.end();
1273
1274 uint pixel1 = ((const uint*)(image1.bits()))[0];
1275 QCOMPARE(pixel1, color);
1276
1277 // Fill with CompositionMode_Source tests rectfill.
1278 QImage image2(1, 1, QImage::Format_RGB30);
1279 painter.begin(&image2);
1280 painter.setCompositionMode(QPainter::CompositionMode_Source);
1281 painter.fillRect(rect, color: QColor::fromRgba64(rgba: qConvertA2rgb30ToRgb64<PixelOrderRGB>(rgb: color)));
1282 painter.end();
1283
1284 uint pixel2 = ((const uint*)(image2.bits()))[0];
1285 QCOMPARE(pixel2, color);
1286}
1287
1288void tst_QPainter::drawPath_data()
1289{
1290 QTest::addColumn<QPainterPath>(name: "path");
1291 QTest::addColumn<QRect>(name: "expectedBounds");
1292 QTest::addColumn<int>(name: "expectedPixels");
1293
1294 {
1295 QPainterPath p;
1296 p.addRect(x: 2, y: 2, w: 10, h: 10);
1297 QTest::newRow(dataTag: "int-aligned rect") << p << QRect(2, 2, 10, 10) << 10 * 10;
1298 }
1299
1300 {
1301 QPainterPath p;
1302 p.addRect(x: 2.25, y: 2.25, w: 10, h: 10);
1303 QTest::newRow(dataTag: "non-aligned rect") << p << QRect(3, 3, 10, 10) << 10 * 10;
1304 }
1305
1306 {
1307 QPainterPath p;
1308 p.addRect(x: 2.25, y: 2.25, w: 10.5, h: 10.5);
1309 QTest::newRow(dataTag: "non-aligned rect 2") << p << QRect(3, 3, 10, 10) << 10 * 10;
1310 }
1311
1312 {
1313 QPainterPath p;
1314 p.addRect(x: 2.5, y: 2.5, w: 10, h: 10);
1315 QTest::newRow(dataTag: "non-aligned rect 3") << p << QRect(3, 3, 10, 10) << 10 * 10;
1316 }
1317
1318 {
1319 QPainterPath p;
1320 p.addRect(x: 2, y: 2, w: 10, h: 10);
1321 p.addRect(x: 4, y: 4, w: 6, h: 6);
1322 QTest::newRow(dataTag: "rect-in-rect") << p << QRect(2, 2, 10, 10) << 10 * 10 - 6 * 6;
1323 }
1324
1325 {
1326 QPainterPath p;
1327 p.addRect(x: 2, y: 2, w: 10, h: 10);
1328 p.addRect(x: 4, y: 4, w: 6, h: 6);
1329 p.addRect(x: 6, y: 6, w: 2, h: 2);
1330 QTest::newRow(dataTag: "rect-in-rect-in-rect") << p << QRect(2, 2, 10, 10) << 10 * 10 - 6 * 6 + 2 * 2;
1331 }
1332}
1333
1334void tst_QPainter::drawPath()
1335{
1336 QFETCH(QPainterPath, path);
1337 QFETCH(QRect, expectedBounds);
1338 QFETCH(int, expectedPixels);
1339
1340 const int offset = 2;
1341
1342 QImage image(expectedBounds.width() + 2 * offset, expectedBounds.height() + 2 * offset,
1343 QImage::Format_ARGB32_Premultiplied);
1344 image.fill(pixel: QColor(Qt::white).rgb());
1345
1346 QPainter p(&image);
1347 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1348 p.setPen(Qt::NoPen);
1349 p.setBrush(Qt::black);
1350 p.translate(dx: offset - expectedBounds.left(), dy: offset - expectedBounds.top());
1351 p.drawPath(path);
1352 p.end();
1353
1354 const QRect paintedBounds = getPaintedSize(image, background: Qt::white);
1355
1356 QCOMPARE(paintedBounds.x(), offset);
1357 QCOMPARE(paintedBounds.y(), offset);
1358 QCOMPARE(paintedBounds.width(), expectedBounds.width());
1359 QCOMPARE(paintedBounds.height(), expectedBounds.height());
1360
1361 if (expectedPixels != -1) {
1362 int paintedPixels = getPaintedPixels(image, background: Qt::white);
1363 QCOMPARE(paintedPixels, expectedPixels);
1364 }
1365}
1366
1367void tst_QPainter::drawPath2()
1368{
1369 const int w = 50;
1370
1371 for (int h = 5; h < 200; ++h) {
1372 QPainterPath p1, p2;
1373 p1.lineTo(x: w, y: 0);
1374 p1.lineTo(x: w, y: h);
1375
1376 p2.lineTo(x: w, y: h);
1377 p2.lineTo(x: 0, y: h);
1378
1379 const int offset = 2;
1380
1381 QImage image(w + 2 * offset, h + 2 * offset,
1382 QImage::Format_ARGB32_Premultiplied);
1383 image.fill(pixel: QColor(Qt::white).rgb());
1384
1385 QPainter p(&image);
1386 p.setPen(Qt::NoPen);
1387 p.setBrush(Qt::black);
1388 p.translate(dx: offset, dy: offset);
1389 p.drawPath(path: p1);
1390 p.end();
1391
1392 const int p1Pixels = getPaintedPixels(image, background: Qt::white);
1393
1394 image.fill(pixel: QColor(Qt::white).rgb());
1395 p.begin(&image);
1396 p.setPen(Qt::NoPen);
1397 p.setBrush(Qt::black);
1398 p.translate(dx: offset, dy: offset);
1399 p.drawPath(path: p2);
1400 p.end();
1401
1402 const int p2Pixels = getPaintedPixels(image, background: Qt::white);
1403
1404 QCOMPARE(p1Pixels + p2Pixels, w * h);
1405 }
1406}
1407
1408void tst_QPainter::drawPath3()
1409{
1410 QImage imgA(100, 100, QImage::Format_RGB32);
1411 imgA.fill(pixel: 0xffffff);
1412 QImage imgB = imgA;
1413
1414 QPainterPath path;
1415 for (int y = 0; y < imgA.height(); ++y) {
1416 for (int x = 0; x < imgA.width(); ++x) {
1417 if ((x + y) & 1) {
1418 imgA.setPixel(x, y, index_or_rgb: 0);
1419 path.addRect(x, y, w: 1, h: 1);
1420 }
1421 }
1422 }
1423
1424 QPainter p(&imgB);
1425 p.setPen(Qt::NoPen);
1426 p.setBrush(Qt::black);
1427
1428 p.drawPath(path);
1429 p.end();
1430
1431 QCOMPARE(imgA, imgB);
1432
1433 imgA.invertPixels();
1434 imgB.fill(pixel: 0xffffff);
1435
1436 p.begin(&imgB);
1437 p.setPen(Qt::NoPen);
1438 p.setBrush(Qt::black);
1439
1440 QRectF rect(0, 0, imgA.width(), imgA.height());
1441 path.addRect(rect: rect.adjusted(xp1: -10, yp1: -10, xp2: 10, yp2: 10));
1442 p.drawPath(path);
1443 p.end();
1444
1445 QCOMPARE(imgA, imgB);
1446
1447 path.setFillRule(Qt::WindingFill);
1448 imgB.fill(pixel: 0xffffff);
1449
1450 p.begin(&imgB);
1451 p.setPen(Qt::NoPen);
1452 p.setBrush(Qt::black);
1453 QRect clip = rect.adjusted(xp1: 10, yp1: 10, xp2: -10, yp2: -10).toRect();
1454 p.setClipRect(clip);
1455 p.drawPath(path);
1456 p.end();
1457
1458 QCOMPARE(getPaintedPixels(imgB, Qt::white), clip.width() * clip.height());
1459}
1460
1461void tst_QPainter::drawEllipse_data()
1462{
1463 QTest::addColumn<QSize>(name: "size");
1464 QTest::addColumn<bool>(name: "usePen");
1465
1466 // The current drawEllipse algorithm (drawEllipse_midpoint_i in
1467 // qpaintengine_raster.cpp) draws ellipses that are too wide if the
1468 // ratio between width and hight is too large/small (task 114874). Those
1469 // ratios are therefore currently avoided.
1470 for (int w = 10; w < 128; w += 7) {
1471 const QByteArray wB = QByteArray::number(w);
1472 for (int h = w/2; h < qMin(a: 2*w, b: 128); h += 13) {
1473 const QByteArray sB = wB + 'x' + QByteArray::number(h);
1474 QTest::newRow(dataTag: (sB + " with pen").constData()) << QSize(w, h) << true;
1475 QTest::newRow(dataTag: (sB + " no pen").constData()) << QSize(w, h) << false;
1476 }
1477 }
1478}
1479
1480void tst_QPainter::drawEllipse()
1481{
1482 QFETCH(QSize, size);
1483 QFETCH(bool, usePen);
1484
1485 const int offset = 10;
1486 QRect rect(QPoint(offset, offset), size);
1487
1488 QImage image(size.width() + 2 * offset, size.height() + 2 * offset,
1489 QImage::Format_ARGB32_Premultiplied);
1490 image.fill(pixel: QColor(Qt::white).rgb());
1491
1492 QPainter p(&image);
1493 p.setPen(usePen ? QPen(Qt::black) : QPen(Qt::NoPen));
1494 p.setBrush(Qt::black);
1495 p.drawEllipse(r: rect);
1496 p.end();
1497
1498 QPixmap pixmap = QPixmap::fromImage(image);
1499
1500 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1501
1502 QCOMPARE(painted.x(), rect.x());
1503 QCOMPARE(painted.y(), rect.y() + (usePen ? 0 : 1));
1504 QCOMPARE(painted.width(), size.width() + (usePen ? 1 : 0));
1505 QCOMPARE(painted.height(), size.height() + (usePen ? 1 : -1));
1506}
1507
1508void tst_QPainter::drawClippedEllipse_data()
1509{
1510 QTest::addColumn<QRect>(name: "rect");
1511
1512 for (int w = 20; w < 128; w += 7) {
1513 const QByteArray wB = QByteArray::number(w);
1514 for (int h = w/2; h < qMin(a: 2*w, b: 128); h += 13) {
1515 const QByteArray sB = wB + 'x' + QByteArray::number(h);
1516 QTest::newRow(dataTag: (sB + " top").constData()) << QRect(0, -h/2, w, h);
1517 QTest::newRow(dataTag: (sB + " topright").constData()) << QRect(w/2, -h/2, w, h);
1518 QTest::newRow(dataTag: (sB + " right").constData()) << QRect(w/2, 0, w, h);
1519 QTest::newRow(dataTag: (sB + " bottomright").constData()) << QRect(w/2, h/2, w, h);
1520 QTest::newRow(dataTag: (sB + " bottom").constData()) << QRect(0, h/2, w, h);
1521 QTest::newRow(dataTag: (sB + " bottomleft").constData()) << QRect(-w/2, h/2, w, h);
1522 QTest::newRow(dataTag: (sB + " left").constData()) << QRect(-w/2, 0, w, h);
1523 QTest::newRow(dataTag: (sB + " topleft").constData()) << QRect(-w/2, -h/2, w, h);
1524 }
1525 }
1526}
1527
1528void tst_QPainter::drawClippedEllipse()
1529{
1530 QFETCH(QRect, rect);
1531 if (sizeof(qreal) != sizeof(double))
1532 QSKIP("Test only works for qreal==double");
1533 QImage image(rect.width() + 1, rect.height() + 1,
1534 QImage::Format_ARGB32_Premultiplied);
1535 QRect expected = QRect(rect.x(), rect.y(), rect.width()+1, rect.height()+1)
1536 & QRect(0, 0, image.width(), image.height());
1537
1538
1539 image.fill(pixel: QColor(Qt::white).rgb());
1540 QPainter p(&image);
1541 p.drawEllipse(r: rect);
1542 p.end();
1543
1544 QPixmap pixmap = QPixmap::fromImage(image);
1545 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1546
1547 QCOMPARE(painted.x(), expected.x());
1548 QCOMPARE(painted.y(), expected.y());
1549 QCOMPARE(painted.width(), expected.width());
1550 QCOMPARE(painted.height(), expected.height());
1551
1552}
1553
1554#if QT_DEPRECATED_SINCE(5, 13)
1555void tst_QPainter::drawRoundRect()
1556{
1557 QFETCH(QRect, rect);
1558 QFETCH(bool, usePen);
1559
1560#ifdef Q_OS_MAC
1561 if (QTest::currentDataTag() == QByteArray("rect(6, 12, 3, 14) with pen") ||
1562 QTest::currentDataTag() == QByteArray("rect(6, 17, 3, 25) with pen") ||
1563 QTest::currentDataTag() == QByteArray("rect(10, 6, 10, 3) with pen") ||
1564 QTest::currentDataTag() == QByteArray("rect(10, 12, 10, 14) with pen") ||
1565 QTest::currentDataTag() == QByteArray("rect(13, 45, 17, 80) with pen") ||
1566 QTest::currentDataTag() == QByteArray("rect(13, 50, 17, 91) with pen") ||
1567 QTest::currentDataTag() == QByteArray("rect(17, 6, 24, 3) with pen") ||
1568 QTest::currentDataTag() == QByteArray("rect(24, 12, 38, 14) with pen"))
1569 QSKIP("The Mac paint engine is off-by-one on certain rect sizes");
1570#endif
1571 QPixmap pixmap(rect.x() + rect.width() + 10,
1572 rect.y() + rect.height() + 10);
1573 {
1574 pixmap.fill(fillColor: Qt::white);
1575 QPainter p(&pixmap);
1576 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1577 p.setPen(usePen ? QPen(Qt::black) : QPen(Qt::NoPen));
1578 p.setBrush(Qt::black);
1579 p.drawRoundRect(r: rect);
1580 p.end();
1581
1582 int increment = usePen ? 1 : 0;
1583
1584 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1585 QCOMPARE(painted.width(), rect.width() + increment);
1586 QCOMPARE(painted.height(), rect.height() + increment);
1587 }
1588}
1589#endif
1590
1591void tst_QPainter::drawRoundedRect()
1592{
1593 QFETCH(QRect, rect);
1594 QFETCH(bool, usePen);
1595
1596#ifdef Q_OS_DARWIN
1597 if (QTest::currentDataTag() == QByteArray("rect(6, 12, 3, 14) with pen") ||
1598 QTest::currentDataTag() == QByteArray("rect(6, 17, 3, 25) with pen") ||
1599 QTest::currentDataTag() == QByteArray("rect(10, 6, 10, 3) with pen") ||
1600 QTest::currentDataTag() == QByteArray("rect(10, 12, 10, 14) with pen") ||
1601 QTest::currentDataTag() == QByteArray("rect(13, 45, 17, 80) with pen") ||
1602 QTest::currentDataTag() == QByteArray("rect(13, 50, 17, 91) with pen") ||
1603 QTest::currentDataTag() == QByteArray("rect(17, 6, 24, 3) with pen") ||
1604 QTest::currentDataTag() == QByteArray("rect(24, 12, 38, 14) with pen"))
1605 QSKIP("The Mac paint engine is off-by-one on certain rect sizes");
1606#endif
1607 QPixmap pixmap(rect.x() + rect.width() + 10,
1608 rect.y() + rect.height() + 10);
1609 {
1610 pixmap.fill(fillColor: Qt::white);
1611 QPainter p(&pixmap);
1612 p.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1613 p.setPen(usePen ? QPen(Qt::black) : QPen(Qt::NoPen));
1614 p.setBrush(Qt::black);
1615 p.drawRoundedRect(rect, xRadius: 25, yRadius: 25, mode: Qt::RelativeSize);
1616 p.end();
1617
1618 int increment = usePen ? 1 : 0;
1619
1620 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1621 QCOMPARE(painted.width(), rect.width() + increment);
1622 QCOMPARE(painted.height(), rect.height() + increment);
1623 }
1624}
1625
1626void tst_QPainter::qimageFormats_data()
1627{
1628 QTest::addColumn<QImage::Format>(name: "format");
1629 QTest::newRow(dataTag: "QImage::Format_RGB32") << QImage::Format_RGB32;
1630 QTest::newRow(dataTag: "QImage::Format_ARGB32") << QImage::Format_ARGB32;
1631 QTest::newRow(dataTag: "QImage::Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied;
1632 QTest::newRow(dataTag: "QImage::Format_RGB16") << QImage::Format_RGB16;
1633 QTest::newRow(dataTag: "Qimage::Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied;
1634 QTest::newRow(dataTag: "Qimage::Format_RGB666") << QImage::Format_RGB666;
1635 QTest::newRow(dataTag: "Qimage::Format_RGB555") << QImage::Format_RGB555;
1636 QTest::newRow(dataTag: "Qimage::Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied;
1637 QTest::newRow(dataTag: "Qimage::Format_RGB888") << QImage::Format_RGB888;
1638 QTest::newRow(dataTag: "Qimage::Format_BGR888") << QImage::Format_BGR888;
1639 QTest::newRow(dataTag: "Qimage::Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied;
1640 QTest::newRow(dataTag: "Qimage::Format_RGB30") << QImage::Format_RGB30;
1641}
1642
1643/*
1644 Tests that QPainter can paint on various QImage formats.
1645*/
1646void tst_QPainter::qimageFormats()
1647{
1648 QFETCH(QImage::Format, format);
1649
1650 const QSize size(100, 100);
1651 QImage image(size, format);
1652 image.fill(pixel: 0);
1653
1654 const QColor testColor(Qt::red);
1655 QPainter p(&image);
1656 QVERIFY(p.isActive());
1657 p.setBrush(QBrush(testColor));
1658 p.drawRect(r: QRect(QPoint(0,0), size));
1659 QCOMPARE(image.pixel(50, 50), testColor.rgb());
1660}
1661
1662void tst_QPainter::fillData()
1663{
1664 QTest::addColumn<QRect>(name: "rect");
1665 QTest::addColumn<bool>(name: "usePen");
1666
1667 for (int w = 3; w < 50; w += 7) {
1668 const QByteArray wB = QByteArray::number(w);
1669 for (int h = 3; h < 50; h += 11) {
1670 int x = w/2 + 5;
1671 int y = h/2 + 5;
1672 const QByteArray rB = "rect(" + QByteArray::number(x) + ", " + QByteArray::number(y)
1673 + ", " + QByteArray::number(w) + ", " + QByteArray::number(h) + ')';
1674 QTest::newRow(dataTag: (rB + " with pen").constData()) << QRect(x, y, w, h) << true;
1675 QTest::newRow(dataTag: (rB + " no pen").constData()) << QRect(x, y, w, h) << false;
1676 }
1677 }
1678}
1679
1680/*
1681 Test that drawline works properly after setWindow has been called.
1682*/
1683void tst_QPainter::setWindow()
1684{
1685 QPixmap pixmap(600, 600);
1686 pixmap.fill(fillColor: QColor(Qt::white));
1687
1688 QPainter painter(&pixmap);
1689 painter.setRenderHint(hint: QPainter::Qt4CompatiblePainting);
1690 painter.setWindow(x: 0, y: 0, w: 3, h: 3);
1691 painter.drawLine(x1: 1, y1: 1, x2: 2, y2: 2);
1692
1693 const QRect painted = getPaintedSize(pm: pixmap, background: Qt::white);
1694 QVERIFY(195 < painted.y() && painted.y() < 205); // correct value is around 200
1695 QVERIFY(195 < painted.height() && painted.height() < 205); // correct value is around 200
1696}
1697
1698void tst_QPainter::combinedMatrix()
1699{
1700 QPixmap pm(64, 64);
1701
1702 QPainter p(&pm);
1703 p.setWindow(x: 0, y: 0, w: 1, h: 1);
1704 p.setViewport(x: 32, y: 0, w: 32, h: 32);
1705
1706 p.translate(dx: 0.5, dy: 0.5);
1707
1708 QTransform ct = p.combinedTransform();
1709#if QT_DEPRECATED_SINCE(5, 13)
1710QT_WARNING_PUSH
1711QT_WARNING_DISABLE_DEPRECATED
1712 QMatrix cm = p.combinedMatrix();
1713 QCOMPARE(cm, ct.toAffine());
1714QT_WARNING_POP
1715#endif
1716
1717 QPointF pt = QPointF(0, 0) * ct.toAffine();
1718
1719 QCOMPARE(pt.x(), 48.0);
1720 QCOMPARE(pt.y(), 16.0);
1721}
1722
1723void tst_QPainter::textOnTransparentImage()
1724{
1725 bool foundPixel = false;
1726 QImage image(10, 10, QImage::Format_ARGB32_Premultiplied);
1727 image.fill(pixel: qRgba(r: 0, g: 0, b: 0, a: 0)); // transparent
1728 {
1729 QPainter painter(&image);
1730 painter.setPen(QColor(255, 255, 255));
1731 painter.drawText(x: 0, y: 10, s: "W");
1732 }
1733 for (int x = 0; x < image.width(); ++x)
1734 for (int y = 0; y < image.height(); ++y)
1735 if (image.pixel(x, y) != 0)
1736 foundPixel = true;
1737 QVERIFY(foundPixel);
1738}
1739
1740void tst_QPainter::renderHints()
1741{
1742 QImage img(1, 1, QImage::Format_RGB32);
1743
1744 QPainter p(&img);
1745
1746 // Turn off all...
1747 p.setRenderHints(hints: QPainter::RenderHints(0xffffffff), on: false);
1748 QCOMPARE(p.renderHints(), QPainter::RenderHints{});
1749
1750 // Single set/get
1751 p.setRenderHint(hint: QPainter::Antialiasing);
1752 QVERIFY(p.renderHints() & QPainter::Antialiasing);
1753
1754 p.setRenderHint(hint: QPainter::Antialiasing, on: false);
1755 QVERIFY(!(p.renderHints() & QPainter::Antialiasing));
1756
1757 // Multi set/get
1758 p.setRenderHints(hints: QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
1759 QVERIFY(p.renderHints() & (QPainter::Antialiasing | QPainter::SmoothPixmapTransform));
1760
1761 p.setRenderHints(hints: QPainter::Antialiasing | QPainter::SmoothPixmapTransform, on: false);
1762 QVERIFY(!(p.renderHints() & (QPainter::Antialiasing | QPainter::SmoothPixmapTransform)));
1763}
1764
1765int countPixels(const QImage &img, const QRgb &color)
1766{
1767 int count = 0;
1768 for (int y = 0; y < img.height(); ++y) {
1769 for (int x = 0; x < img.width(); ++x) {
1770 count += ((img.pixel(x, y) & 0xffffff) == color);
1771 }
1772 }
1773 return count;
1774}
1775
1776template <typename T>
1777void testClipping(QImage &img)
1778{
1779 QPainterPath a, b;
1780 a.addRect(rect: QRect(2, 2, 4, 4));
1781 b.addRect(rect: QRect(4, 4, 4, 4));
1782 QPainter p(&img);
1783
1784 p.end();
1785 img.fill(pixel: 0x0);
1786 p.begin(&img);
1787 p.setClipPath(path: a);
1788 p.setClipPath(path: b, op: Qt::IntersectClip);
1789
1790 p.setClipping(false);
1791 p.setPen(Qt::NoPen);
1792 p.setBrush(QColor(0xff0000));
1793 p.drawRect(T(0, 0, 10, 10));
1794
1795 p.setClipping(true);
1796 p.setBrush(QColor(0x00ff00));
1797 p.drawRect(T(0, 0, 10, 10));
1798
1799 QCOMPARE(countPixels(img, 0xff0000), 96);
1800 QCOMPARE(countPixels(img, 0x00ff00), 4);
1801}
1802
1803void tst_QPainter::disableEnableClipping()
1804{
1805 QImage img(10, 10, QImage::Format_RGB32);
1806
1807 testClipping<QRectF>(img);
1808 testClipping<QRect>(img);
1809}
1810
1811void tst_QPainter::setClipRect()
1812{
1813 QImage img(10, 10, QImage::Format_RGB32);
1814 // simple test to let valgrind check for buffer overflow
1815 {
1816 QPainter p(&img);
1817 p.setClipRect(x: -10, y: -10, w: 100, h: 100);
1818 p.fillRect(x: -10, y: -10, w: 100, h: 100, b: QBrush(QColor(Qt::red)));
1819 }
1820
1821 // rects with negative width/height
1822 {
1823 QPainter p(&img);
1824 p.setClipRect(QRect(10, 10, -10, 10));
1825 QVERIFY(p.clipRegion().isEmpty());
1826 p.setClipRect(QRect(10, 10, 10, -10));
1827 QVERIFY(p.clipRegion().isEmpty());
1828 p.setClipRect(QRectF(10.5, 10.5, -10.5, 10.5));
1829 QVERIFY(p.clipRegion().isEmpty());
1830 p.setClipRect(QRectF(10.5, 10.5, 10.5, -10.5));
1831 QVERIFY(p.clipRegion().isEmpty());
1832 }
1833}
1834
1835/*
1836 Verify that the clipping works correctly.
1837 The red outline should be covered by the blue rect on top and left,
1838 while it should be clipped on the right and bottom and thus the red outline be visible
1839
1840 See: QTBUG-83229
1841*/
1842void tst_QPainter::clipRect()
1843{
1844 int width = 654;
1845 int height = 480;
1846 QRect rect(0, 0, width, height);
1847
1848 QImage image(width, height, QImage::Format_ARGB32);
1849 QPainter p(&image);
1850 qreal halfWidth = width / 2.0;
1851 qreal halfHeight = height / 2.0;
1852
1853 QRectF clipRect = QRectF(halfWidth - halfWidth / 2.0, halfHeight - halfHeight / 2.0,
1854 halfWidth / 2.0, halfHeight / 2.0);
1855
1856 p.fillRect(r: rect, c: Qt::white);
1857 p.setPen(Qt::red);
1858 p.drawRect(rect: clipRect);
1859
1860 p.setClipRect(clipRect, op: Qt::ReplaceClip);
1861 p.fillRect(r: rect, c: Qt::blue);
1862
1863 p.end();
1864
1865 QCOMPARE(image.pixelColor(clipRect.left() + 1, clipRect.top()), QColor(Qt::blue));
1866 QCOMPARE(image.pixelColor(clipRect.left(), clipRect.top() + 1), QColor(Qt::blue));
1867 QCOMPARE(image.pixelColor(clipRect.left() + 1, clipRect.bottom()), QColor(Qt::red));
1868 QCOMPARE(image.pixelColor(clipRect.right(), clipRect.top() + 1), QColor(Qt::red));
1869}
1870
1871/*
1872 This tests the two different clipping approaches in QRasterPaintEngine,
1873 one when using a QRegion and one when using a QPainterPath. They should
1874 give equal results.
1875*/
1876void tst_QPainter::setEqualClipRegionAndPath_data()
1877{
1878 QTest::addColumn<QSize>(name: "deviceSize");
1879 QTest::addColumn<QRegion>(name: "region");
1880
1881 QTest::newRow(dataTag: "empty") << QSize(100, 100) << QRegion();
1882 QTest::newRow(dataTag: "simple rect") << QSize(100, 100)
1883 << QRegion(QRect(5, 5, 10, 10));
1884
1885 QVector<QRect> rects;
1886 QRegion region;
1887
1888 rects << QRect(5, 5, 10, 10) << QRect(20, 20, 10, 10);
1889 region.setRects(rect: rects.constData(), num: rects.size());
1890 QTest::newRow(dataTag: "two rects") << QSize(100, 100) << region;
1891
1892 rects.clear();
1893 rects << QRect(5, 5, 10, 10) << QRect(20, 5, 10, 10);
1894 region.setRects(rect: rects.constData(), num: rects.size());
1895 QTest::newRow(dataTag: "two x-adjacent rects") << QSize(100, 100) << region;
1896
1897 rects.clear();
1898 rects << QRect(0, 0, 10, 100) << QRect(12, 0, 10, 100);
1899 region.setRects(rect: rects.constData(), num: rects.size());
1900 QTest::newRow(dataTag: "two x-adjacent rects 2") << QSize(100, 100) << region;
1901
1902 rects.clear();
1903 rects << QRect(0, 0, 10, 100) << QRect(12, 0, 10, 100);
1904 region.setRects(rect: rects.constData(), num: rects.size());
1905 QTest::newRow(dataTag: "two x-adjacent rects 3") << QSize(50, 50) << region;
1906
1907 rects.clear();
1908 rects << QRect(0, 0, 10, 100) << QRect(12, 0, 10, 100);
1909 region.setRects(rect: rects.constData(), num: rects.size());
1910 QTest::newRow(dataTag: "two x-adjacent rects 4") << QSize(101, 101) << region;
1911
1912 region = QRegion(QRect(0, 0, 200, 200), QRegion::Ellipse);
1913
1914 QTest::newRow(dataTag: "ellipse") << QSize(190, 200) << region;
1915
1916 region ^= QRect(50, 50, 50, 50);
1917 QTest::newRow(dataTag: "ellipse 2") << QSize(200, 200) << region;
1918}
1919
1920void tst_QPainter::setEqualClipRegionAndPath()
1921{
1922 QFETCH(QSize, deviceSize);
1923 QFETCH(QRegion, region);
1924
1925 QPainterPath path;
1926 path.addRegion(region);
1927
1928 QImage img1(deviceSize.width(), deviceSize.height(),
1929 QImage::Format_ARGB32);
1930 QImage img2(deviceSize.width(), deviceSize.height(),
1931 QImage::Format_ARGB32);
1932 img1.fill(pixel: 0x12345678);
1933 img2.fill(pixel: 0x12345678);
1934
1935 {
1936 QPainter p(&img1);
1937 p.setClipRegion(region);
1938 p.fillRect(x: 0, y: 0, w: img1.width(), h: img1.height(), b: QColor(Qt::red));
1939 }
1940 {
1941 QPainter p(&img2);
1942 p.setClipPath(path);
1943 p.fillRect(x: 0, y: 0, w: img2.width(), h: img2.height(), b: QColor(Qt::red));
1944 }
1945
1946 QCOMPARE(img1, img2);
1947
1948 // rotated
1949 img1.fill(pixel: 0x12345678);
1950 img2.fill(pixel: 0x12345678);
1951
1952 {
1953 QPainter p(&img1);
1954 p.rotate(a: 25);
1955 p.setClipRegion(region);
1956 p.fillRect(x: 0, y: 0, w: img1.width(), h: img1.height(), b: QColor(Qt::red));
1957 }
1958 {
1959 QPainter p(&img2);
1960 p.rotate(a: 25);
1961 p.setClipPath(path);
1962 p.fillRect(x: 0, y: 0, w: img2.width(), h: img2.height(), b: QColor(Qt::red));
1963 }
1964
1965 QCOMPARE(img1, img2);
1966
1967 img1.fill(pixel: 0x12345678);
1968 img2.fill(pixel: 0x12345678);
1969
1970 // simple intersectclip
1971 img1.fill(pixel: 0x12345678);
1972 img2.fill(pixel: 0x12345678);
1973 {
1974 QPainter p(&img1);
1975 p.setClipRegion(region);
1976 p.setClipRegion(region, op: Qt::IntersectClip);
1977 p.fillRect(x: 0, y: 0, w: img1.width(), h: img1.height(), b: QColor(Qt::red));
1978 }
1979 {
1980 QPainter p(&img2);
1981 p.setClipPath(path);
1982 p.setClipPath(path, op: Qt::IntersectClip);
1983 p.fillRect(x: 0, y: 0, w: img2.width(), h: img2.height(), b: QColor(Qt::red));
1984 }
1985 QCOMPARE(img1, img2);
1986
1987 img1.fill(pixel: 0x12345678);
1988 img2.fill(pixel: 0x12345678);
1989 {
1990 QPainter p(&img1);
1991 p.setClipPath(path);
1992 p.setClipRegion(region, op: Qt::IntersectClip);
1993 p.fillRect(x: 0, y: 0, w: img1.width(), h: img1.height(), b: QColor(Qt::red));
1994 }
1995 {
1996 QPainter p(&img2);
1997 p.setClipRegion(region);
1998 p.setClipPath(path, op: Qt::IntersectClip);
1999 p.fillRect(x: 0, y: 0, w: img2.width(), h: img2.height(), b: QColor(Qt::red));
2000 }
2001 QCOMPARE(img1, img2);
2002
2003}
2004
2005void tst_QPainter::clippedFillPath_data()
2006{
2007 QTest::addColumn<QSize>(name: "imageSize");
2008 QTest::addColumn<QPainterPath>(name: "path");
2009 QTest::addColumn<QRect>(name: "clipRect");
2010 QTest::addColumn<QBrush>(name: "brush");
2011 QTest::addColumn<QPen>(name: "pen");
2012
2013 QLinearGradient gradient(QPoint(0, 0), QPoint(100, 100));
2014 gradient.setColorAt(pos: 0, color: Qt::red);
2015 gradient.setColorAt(pos: 1, color: Qt::blue);
2016
2017
2018 QPen pen2(QColor(223, 223, 0, 223));
2019 pen2.setWidth(2);
2020
2021 QPainterPath path;
2022 path.addRect(rect: QRect(15, 15, 50, 50));
2023 QTest::newRow(dataTag: "simple rect 0") << QSize(100, 100) << path
2024 << QRect(15, 15, 49, 49)
2025 << QBrush(Qt::NoBrush)
2026 << QPen(Qt::black);
2027 QTest::newRow(dataTag: "simple rect 1") << QSize(100, 100) << path
2028 << QRect(15, 15, 50, 50)
2029 << QBrush(Qt::NoBrush)
2030 << QPen(Qt::black);
2031 QTest::newRow(dataTag: "simple rect 2") << QSize(100, 100) << path
2032 << QRect(15, 15, 51, 51)
2033 << QBrush(Qt::NoBrush)
2034 << QPen(Qt::black);
2035 QTest::newRow(dataTag: "simple rect 3") << QSize(100, 100) << path
2036 << QRect(15, 15, 51, 51)
2037 << QBrush(QColor(Qt::blue))
2038 << QPen(Qt::NoPen);
2039 QTest::newRow(dataTag: "simple rect 4") << QSize(100, 100) << path
2040 << QRect(15, 15, 51, 51)
2041 << QBrush(gradient)
2042 << pen2;
2043
2044 path = QPainterPath();
2045 path.addEllipse(rect: QRect(15, 15, 50, 50));
2046 QTest::newRow(dataTag: "ellipse 0") << QSize(100, 100) << path
2047 << QRect(15, 15, 49, 49)
2048 << QBrush(Qt::NoBrush)
2049 << QPen(Qt::black);
2050 QTest::newRow(dataTag: "ellipse 1") << QSize(100, 100) << path
2051 << QRect(15, 15, 50, 50)
2052 << QBrush(Qt::NoBrush)
2053 << QPen(Qt::black);
2054 QTest::newRow(dataTag: "ellipse 2") << QSize(100, 100) << path
2055 << QRect(15, 15, 51, 51)
2056 << QBrush(Qt::NoBrush)
2057 << QPen(Qt::black);
2058 QTest::newRow(dataTag: "ellipse 3") << QSize(100, 100) << path
2059 << QRect(15, 15, 51, 51)
2060 << QBrush(QColor(Qt::blue))
2061 << QPen(Qt::NoPen);
2062 QTest::newRow(dataTag: "ellipse 4") << QSize(100, 100) << path
2063 << QRect(15, 15, 51, 51)
2064 << QBrush(gradient)
2065 << pen2;
2066
2067 path = QPainterPath();
2068 path.addRoundedRect(rect: QRect(15, 15, 50, 50), xRadius: 20, yRadius: Qt::RelativeSize);
2069 QTest::newRow(dataTag: "round rect 0") << QSize(100, 100) << path
2070 << QRect(15, 15, 49, 49)
2071 << QBrush(Qt::NoBrush)
2072 << QPen(Qt::black);
2073 QTest::newRow(dataTag: "round rect 1") << QSize(100, 100) << path
2074 << QRect(15, 15, 50, 50)
2075 << QBrush(Qt::NoBrush)
2076 << QPen(Qt::black);
2077 QTest::newRow(dataTag: "round rect 2") << QSize(100, 100) << path
2078 << QRect(15, 15, 51, 51)
2079 << QBrush(Qt::NoBrush)
2080 << QPen(Qt::black);
2081 QTest::newRow(dataTag: "round rect 3") << QSize(100, 100) << path
2082 << QRect(15, 15, 51, 51)
2083 << QBrush(QColor(Qt::blue))
2084 << QPen(Qt::NoPen);
2085 QTest::newRow(dataTag: "round rect 4") << QSize(100, 100) << path
2086 << QRect(15, 15, 51, 51)
2087 << QBrush(gradient)
2088 << pen2;
2089
2090 path = QPainterPath();
2091 path.moveTo(x: 15, y: 50);
2092 path.cubicTo(ctrlPt1x: 40, ctrlPt1y: 50, ctrlPt2x: 40, ctrlPt2y: 15, endPtx: 65, endPty: 50);
2093 path.lineTo(x: 15, y: 50);
2094 QTest::newRow(dataTag: "cubic 0") << QSize(100, 100) << path
2095 << QRect(15, 15, 49, 49)
2096 << QBrush(Qt::NoBrush)
2097 << QPen(Qt::black);
2098 QTest::newRow(dataTag: "cubic 1") << QSize(100, 100) << path
2099 << QRect(15, 15, 50, 50)
2100 << QBrush(Qt::NoBrush)
2101 << QPen(Qt::black);
2102 QTest::newRow(dataTag: "cubic 2") << QSize(100, 100) << path
2103 << QRect(15, 15, 51, 51)
2104 << QBrush(Qt::NoBrush)
2105 << QPen(Qt::black);
2106 QTest::newRow(dataTag: "cubic 3") << QSize(100, 100) << path
2107 << QRect(15, 15, 51, 51)
2108 << QBrush(QColor(Qt::blue))
2109 << QPen(Qt::NoPen);
2110 QTest::newRow(dataTag: "cubic 4") << QSize(100, 100) << path
2111 << QRect(15, 15, 51, 51)
2112 << QBrush(gradient)
2113 << pen2;
2114}
2115
2116void tst_QPainter::clippedFillPath()
2117{
2118 QFETCH(QSize, imageSize);
2119 QFETCH(QPainterPath, path);
2120 QFETCH(QRect, clipRect);
2121 QPainterPath clipPath;
2122 clipPath.addRect(rect: clipRect);
2123 QFETCH(QBrush, brush);
2124 QFETCH(QPen, pen);
2125
2126 const int width = imageSize.width();
2127 const int height = imageSize.height();
2128
2129 QImage clippedRect(width, height, QImage::Format_ARGB32_Premultiplied);
2130 clippedRect.fill(pixel: 0x12345678);
2131 {
2132 QPainter painter(&clippedRect);
2133 painter.setPen(pen);
2134 painter.setBrush(brush);
2135 painter.setClipRect(clipRect);
2136 painter.drawPath(path);
2137 }
2138
2139 QImage clippedPath(width, height, QImage::Format_ARGB32_Premultiplied);
2140 clippedPath.fill(pixel: 0x12345678);
2141 {
2142 QPainter painter(&clippedPath);
2143 painter.setPen(pen);
2144 painter.setBrush(brush);
2145 painter.setClipPath(path: clipPath);
2146 painter.drawPath(path);
2147 }
2148
2149 QCOMPARE(clippedRect, clippedPath);
2150
2151 // repeat with antialiasing
2152
2153 clippedRect.fill(pixel: 0x12345678);
2154 {
2155 QPainter painter(&clippedRect);
2156 painter.setRenderHint(hint: QPainter::Antialiasing);
2157 painter.setPen(pen);
2158 painter.setBrush(brush);
2159 painter.setClipRect(clipRect);
2160 painter.drawPath(path);
2161 }
2162
2163 clippedPath.fill(pixel: 0x12345678);
2164 {
2165 QPainter painter(&clippedPath);
2166 painter.setRenderHint(hint: QPainter::Antialiasing);
2167 painter.setPen(pen);
2168 painter.setBrush(brush);
2169 painter.setClipPath(path: clipPath);
2170 painter.drawPath(path);
2171 }
2172
2173 QCOMPARE(clippedRect, clippedPath);
2174}
2175
2176void tst_QPainter::clippedLines_data()
2177{
2178 QTest::addColumn<QSize>(name: "imageSize");
2179 QTest::addColumn<QLineF>(name: "line");
2180 QTest::addColumn<QRect>(name: "clipRect");
2181 QTest::addColumn<QPen>(name: "pen");
2182
2183 QPen pen2(QColor(223, 223, 0, 223));
2184 pen2.setWidth(2);
2185
2186 QVector<QLineF> lines;
2187 lines << QLineF(15, 15, 65, 65)
2188 << QLineF(14, 14, 66, 66)
2189 << QLineF(16, 16, 64, 64)
2190 << QLineF(65, 65, 15, 15)
2191 << QLineF(66, 66, 14, 14)
2192 << QLineF(64, 64, 14, 14)
2193 << QLineF(15, 50, 15, 64)
2194 << QLineF(15, 50, 15, 65)
2195 << QLineF(15, 50, 15, 66)
2196 << QLineF(15, 50, 64, 50)
2197 << QLineF(15, 50, 65, 50)
2198 << QLineF(15, 50, 66, 50);
2199
2200 foreach (QLineF line, lines) {
2201 const QByteArray desc = "line (" + QByteArray::number(line.x1())
2202 + ", " + QByteArray::number(line.y1()) + ", "
2203 + QByteArray::number(line.x2()) + ", " + QByteArray::number(line.y2())
2204 + ") ";
2205 QTest::newRow(dataTag: (desc + '0').constData()) << QSize(100, 100) << line
2206 << QRect(15, 15, 49, 49)
2207 << QPen(Qt::black);
2208 QTest::newRow(dataTag: (desc + '1').constData()) << QSize(100, 100) << line
2209 << QRect(15, 15, 50, 50)
2210 << QPen(Qt::black);
2211 QTest::newRow(dataTag: (desc + '2').constData()) << QSize(100, 100) << line
2212 << QRect(15, 15, 51, 51)
2213 << QPen(Qt::black);
2214 QTest::newRow(dataTag: (desc + '3').constData()) << QSize(100, 100) << line
2215 << QRect(15, 15, 51, 51)
2216 << QPen(Qt::NoPen);
2217 QTest::newRow(dataTag: (desc + '4').constData()) << QSize(100, 100) << line
2218 << QRect(15, 15, 51, 51)
2219 << pen2;
2220 }
2221}
2222
2223void tst_QPainter::clippedLines()
2224{
2225 QFETCH(QSize, imageSize);
2226 QFETCH(QLineF, line);
2227 QFETCH(QRect, clipRect);
2228 QPainterPath clipPath;
2229 clipPath.addRect(rect: clipRect);
2230 QFETCH(QPen, pen);
2231
2232 const int width = imageSize.width();
2233 const int height = imageSize.height();
2234
2235 QImage clippedRect(width, height, QImage::Format_ARGB32_Premultiplied);
2236 clippedRect.fill(pixel: 0x12345678);
2237 {
2238 QPainter painter(&clippedRect);
2239 painter.setPen(pen);
2240 painter.setClipRect(clipRect);
2241 painter.drawLine(l: line);
2242 painter.drawLine(line: line.toLine());
2243 }
2244
2245 QImage clippedPath(width, height, QImage::Format_ARGB32_Premultiplied);
2246 clippedPath.fill(pixel: 0x12345678);
2247 {
2248 QPainter painter(&clippedPath);
2249 painter.setPen(pen);
2250 painter.setClipPath(path: clipPath);
2251 painter.drawLine(l: line);
2252 painter.drawLine(line: line.toLine());
2253 }
2254
2255 QCOMPARE(clippedRect, clippedPath);
2256
2257 // repeat with antialiasing
2258 clippedRect.fill(pixel: 0x12345678);
2259 {
2260 QPainter painter(&clippedRect);
2261 painter.setRenderHint(hint: QPainter::Antialiasing);
2262 painter.setPen(pen);
2263 painter.setClipRect(clipRect);
2264 painter.drawLine(l: line);
2265 painter.drawLine(line: line.toLine());
2266 }
2267
2268 clippedPath.fill(pixel: 0x12345678);
2269 {
2270 QPainter painter(&clippedPath);
2271 painter.setRenderHint(hint: QPainter::Antialiasing);
2272 painter.setPen(pen);
2273 painter.setClipPath(path: clipPath);
2274 painter.drawLine(l: line);
2275 painter.drawLine(line: line.toLine());
2276 }
2277
2278 QCOMPARE(clippedRect, clippedPath);
2279}
2280
2281void tst_QPainter::clippedPolygon_data()
2282{
2283 clippedFillPath_data();
2284};
2285
2286void tst_QPainter::clippedPolygon()
2287{
2288 QFETCH(QSize, imageSize);
2289 QFETCH(QPainterPath, path);
2290 QPolygonF polygon = path.toFillPolygon(matrix: QTransform());
2291 QFETCH(QRect, clipRect);
2292 QPainterPath clipPath;
2293 clipPath.addRect(rect: clipRect);
2294 QFETCH(QPen, pen);
2295 QFETCH(QBrush, brush);
2296
2297 const int width = imageSize.width();
2298 const int height = imageSize.height();
2299
2300 QImage clippedRect(width, height, QImage::Format_ARGB32_Premultiplied);
2301 clippedRect.fill(pixel: 0x12345678);
2302 {
2303 QPainter painter(&clippedRect);
2304 painter.setPen(pen);
2305 painter.setBrush(brush);
2306 painter.setClipRect(clipRect);
2307 painter.drawPolygon(polygon);
2308 painter.drawPolygon(polygon: polygon.toPolygon());
2309 }
2310
2311 QImage clippedPath(width, height, QImage::Format_ARGB32_Premultiplied);
2312 clippedPath.fill(pixel: 0x12345678);
2313 {
2314 QPainter painter(&clippedPath);
2315 painter.setPen(pen);
2316 painter.setBrush(brush);
2317 painter.setClipRect(clipRect);
2318 painter.drawPolygon(polygon);
2319 painter.drawPolygon(polygon: polygon.toPolygon());
2320 }
2321
2322 QCOMPARE(clippedRect, clippedPath);
2323
2324 // repeat with antialiasing
2325
2326 clippedRect.fill(pixel: 0x12345678);
2327 {
2328 QPainter painter(&clippedRect);
2329 painter.setRenderHint(hint: QPainter::Antialiasing);
2330 painter.setPen(pen);
2331 painter.setBrush(brush);
2332 painter.setClipRect(clipRect);
2333 painter.drawPolygon(polygon);
2334 painter.drawPolygon(polygon: polygon.toPolygon());
2335 }
2336
2337 clippedPath.fill(pixel: 0x12345678);
2338 {
2339 QPainter painter(&clippedPath);
2340 painter.setRenderHint(hint: QPainter::Antialiasing);
2341 painter.setPen(pen);
2342 painter.setBrush(brush);
2343 painter.setClipRect(clipRect);
2344 painter.drawPolygon(polygon);
2345 painter.drawPolygon(polygon: polygon.toPolygon());
2346 }
2347
2348 QCOMPARE(clippedRect, clippedPath);
2349}
2350
2351// this just draws some text that should be clipped in the raster
2352// paint engine.
2353void tst_QPainter::clippedText()
2354{
2355 for (char ch = 'A'; ch < 'Z'; ++ch) {
2356 //qDebug() << ch;
2357 QFont f;
2358 f.setPixelSize(24);
2359 QFontMetrics metrics(f);
2360 QRect textRect = metrics.boundingRect(QChar(ch));
2361
2362 if (textRect.width() <= 8)
2363 continue;
2364 if (textRect.height() <= 8)
2365 continue;
2366
2367 QRect imageRect = textRect.adjusted(xp1: 4, yp1: 4, xp2: -4, yp2: -4);
2368
2369 QImage image(imageRect.size(), QImage::Format_ARGB32_Premultiplied);
2370
2371 image.fill(pixel: qRgba(r: 255, g: 255, b: 255, a: 255));
2372 {
2373 QPainter painter(&image);
2374 painter.setFont(f);
2375 painter.setPen(Qt::black);
2376
2377 painter.drawText(x: 0, y: 0, s: QChar(ch));
2378 }
2379
2380 image.fill(pixel: qRgba(r: 255, g: 255, b: 255, a: 255));
2381 {
2382 QPainter painter(&image);
2383 painter.setFont(f);
2384 painter.setPen(Qt::black);
2385
2386 painter.drawText(p: -imageRect.topLeft(), s: QChar(ch));
2387 }
2388
2389 bool foundPixel = false;
2390 for (int x = 0; x < image.width(); ++x)
2391 for (int y = 0; y < image.height(); ++y)
2392 if (image.pixel(x, y) != 0)
2393 foundPixel = true;
2394 // can't QVERIFY(foundPixel) as sometimes all pixels are clipped
2395 // away. For example for 'O'
2396 // just call /some/ function to prevent the compiler from optimizing
2397 // foundPixel away
2398 QString::number(foundPixel);
2399
2400 //image.save(QString("debug") + ch + ".xpm");
2401 }
2402
2403 QVERIFY(true); // reached, don't trigger any valgrind errors
2404}
2405
2406void tst_QPainter::setOpacity_data()
2407{
2408 QTest::addColumn<QImage::Format>(name: "destFormat");
2409 QTest::addColumn<QImage::Format>(name: "srcFormat");
2410
2411 QTest::newRow(dataTag: "ARGB32P on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2412 << QImage::Format_ARGB32_Premultiplied;
2413
2414 QTest::newRow(dataTag: "ARGB32 on ARGB32") << QImage::Format_ARGB32
2415 << QImage::Format_ARGB32;
2416
2417 QTest::newRow(dataTag: "RGB32 on RGB32") << QImage::Format_RGB32
2418 << QImage::Format_RGB32;
2419
2420 QTest::newRow(dataTag: "RGB16 on RGB16") << QImage::Format_RGB16
2421 << QImage::Format_RGB16;
2422
2423 QTest::newRow(dataTag: "ARGB8565_Premultiplied on ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied
2424 << QImage::Format_ARGB8565_Premultiplied;
2425
2426 QTest::newRow(dataTag: "RGB555 on RGB555") << QImage::Format_RGB555
2427 << QImage::Format_RGB555;
2428
2429 QTest::newRow(dataTag: "RGB666 on RGB666") << QImage::Format_RGB666
2430 << QImage::Format_RGB666;
2431
2432 QTest::newRow(dataTag: "ARGB8555_Premultiplied on ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied
2433 << QImage::Format_ARGB8555_Premultiplied;
2434
2435 QTest::newRow(dataTag: "RGB888 on RGB888") << QImage::Format_RGB888
2436 << QImage::Format_RGB888;
2437
2438 QTest::newRow(dataTag: "RGB32 on RGB16") << QImage::Format_RGB16
2439 << QImage::Format_RGB32;
2440
2441 QTest::newRow(dataTag: "RGB32 on ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied
2442 << QImage::Format_RGB32;
2443
2444 QTest::newRow(dataTag: "RGB32 on RGB666") << QImage::Format_RGB666
2445 << QImage::Format_RGB32;
2446
2447 QTest::newRow(dataTag: "RGB32 on RGB555") << QImage::Format_RGB555
2448 << QImage::Format_RGB32;
2449
2450 QTest::newRow(dataTag: "RGB32 on ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied
2451 << QImage::Format_RGB32;
2452
2453 QTest::newRow(dataTag: "RGB32 on RGB888") << QImage::Format_RGB888
2454 << QImage::Format_RGB32;
2455
2456 QTest::newRow(dataTag: "RGB16 on RGB32") << QImage::Format_RGB32
2457 << QImage::Format_RGB16;
2458
2459 QTest::newRow(dataTag: "ARGB8565_Premultiplied on RGB32") << QImage::Format_RGB32
2460 << QImage::Format_ARGB8565_Premultiplied;
2461
2462 QTest::newRow(dataTag: "RGB666 on RGB32") << QImage::Format_RGB32
2463 << QImage::Format_RGB666;
2464
2465 QTest::newRow(dataTag: "RGB555 on RGB32") << QImage::Format_RGB32
2466 << QImage::Format_RGB555;
2467
2468 QTest::newRow(dataTag: "ARGB8555_Premultiplied on RGB32") << QImage::Format_RGB32
2469 << QImage::Format_ARGB8555_Premultiplied;
2470
2471 QTest::newRow(dataTag: "RGB888 on RGB32") << QImage::Format_RGB32
2472 << QImage::Format_RGB888;
2473
2474 QTest::newRow(dataTag: "RGB555 on RGB888") << QImage::Format_RGB888
2475 << QImage::Format_RGB555;
2476
2477 QTest::newRow(dataTag: "RGB666 on RGB888") << QImage::Format_RGB888
2478 << QImage::Format_RGB666;
2479
2480 QTest::newRow(dataTag: "RGB444 on RGB444") << QImage::Format_RGB444
2481 << QImage::Format_RGB444;
2482
2483 QTest::newRow(dataTag: "RGBA8888P on RGBA8888P") << QImage::Format_RGBA8888_Premultiplied
2484 << QImage::Format_RGBA8888_Premultiplied;
2485
2486 QTest::newRow(dataTag: "RGBA8888 on RGBA8888") << QImage::Format_RGBA8888
2487 << QImage::Format_RGBA8888;
2488
2489 QTest::newRow(dataTag: "RGBx8888 on RGBx8888") << QImage::Format_RGBX8888
2490 << QImage::Format_RGBX8888;
2491
2492 QTest::newRow(dataTag: "RGBA8888P on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2493 << QImage::Format_RGBA8888_Premultiplied;
2494
2495 QTest::newRow(dataTag: "RGBx8888 on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2496 << QImage::Format_RGBX8888;
2497
2498 QTest::newRow(dataTag: "ARGB32P on RGBA8888P") << QImage::Format_RGBA8888_Premultiplied
2499 << QImage::Format_ARGB32_Premultiplied;
2500
2501 QTest::newRow(dataTag: "RGB32 on RGBx8888") << QImage::Format_RGBX8888
2502 << QImage::Format_RGB32;
2503
2504 QTest::newRow(dataTag: "RGB30 on RGB32") << QImage::Format_RGB32
2505 << QImage::Format_BGR30;
2506
2507 QTest::newRow(dataTag: "BGR30 on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2508 << QImage::Format_BGR30;
2509
2510 QTest::newRow(dataTag: "A2RGB30P on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2511 << QImage::Format_A2BGR30_Premultiplied;
2512
2513 QTest::newRow(dataTag: "A2RGB30P on A2RGB30P") << QImage::Format_A2RGB30_Premultiplied
2514 << QImage::Format_A2RGB30_Premultiplied;
2515
2516 QTest::newRow(dataTag: "ARGB32P on A2RGB30P") << QImage::Format_A2RGB30_Premultiplied
2517 << QImage::Format_ARGB32_Premultiplied;
2518
2519 QTest::newRow(dataTag: "RGB32 on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied
2520 << QImage::Format_RGB32;
2521
2522 QTest::newRow(dataTag: "RGB30 on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied
2523 << QImage::Format_RGB30;
2524
2525 QTest::newRow(dataTag: "A2RGB30P on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied
2526 << QImage::Format_A2RGB30_Premultiplied;
2527
2528 QTest::newRow(dataTag: "ARGB32P on BGR30") << QImage::Format_BGR30
2529 << QImage::Format_ARGB32_Premultiplied;
2530
2531 QTest::newRow(dataTag: "ARGB32P on RGB30") << QImage::Format_RGB30
2532 << QImage::Format_ARGB32_Premultiplied;
2533
2534 QTest::newRow(dataTag: "A2RGB30P on RGB30") << QImage::Format_RGB30
2535 << QImage::Format_A2RGB30_Premultiplied;
2536
2537 QTest::newRow(dataTag: "RGBA64P on RGBA64P") << QImage::Format_RGBA64_Premultiplied
2538 << QImage::Format_RGBA64_Premultiplied;
2539
2540 QTest::newRow(dataTag: "RGBA64 on RGBA64") << QImage::Format_RGBA64
2541 << QImage::Format_RGBA64;
2542
2543 QTest::newRow(dataTag: "RGBx64 on RGBx64") << QImage::Format_RGBX64
2544 << QImage::Format_RGBX64;
2545
2546 QTest::newRow(dataTag: "RGBA64P on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2547 << QImage::Format_RGBA64_Premultiplied;
2548
2549 QTest::newRow(dataTag: "RGBx64 on ARGB32P") << QImage::Format_ARGB32_Premultiplied
2550 << QImage::Format_RGBX64;
2551
2552 QTest::newRow(dataTag: "ARGB32P on RGBA64P") << QImage::Format_RGBA64_Premultiplied
2553 << QImage::Format_ARGB32_Premultiplied;
2554
2555}
2556
2557void tst_QPainter::setOpacity()
2558{
2559 QFETCH(QImage::Format, destFormat);
2560 QFETCH(QImage::Format, srcFormat);
2561
2562 const QSize imageSize(12, 12);
2563 const QRect imageRect(QPoint(0, 0), imageSize);
2564 QColor destColor = Qt::black;
2565 QColor srcColor = Qt::white;
2566
2567 QImage dest(imageSize, destFormat);
2568 QImage src(imageSize, srcFormat);
2569
2570 QPainter p;
2571 p.begin(&dest);
2572 p.fillRect(imageRect, color: destColor);
2573 p.end();
2574
2575 p.begin(&src);
2576 p.fillRect(imageRect, color: srcColor);
2577 p.end();
2578
2579 p.begin(&dest);
2580 p.setOpacity(0.5);
2581 p.drawImage(targetRect: imageRect, image: src, sourceRect: imageRect);
2582 p.end();
2583
2584 QImage actual = dest.convertToFormat(f: QImage::Format_RGB32);
2585
2586 for (int y = 0; y < actual.height(); ++y) {
2587 QRgb *p = (QRgb *)actual.scanLine(y);
2588 for (int x = 0; x < actual.width(); ++x) {
2589 QVERIFY(qAbs(qRed(p[x]) - 127) <= 0xf);
2590 QVERIFY(qAbs(qGreen(p[x]) - 127) <= 0xf);
2591 QVERIFY(qAbs(qBlue(p[x]) - 127) <= 0xf);
2592 }
2593 }
2594}
2595
2596void tst_QPainter::drawhelper_blend_untransformed_data()
2597{
2598 setOpacity_data();
2599}
2600
2601void tst_QPainter::drawhelper_blend_untransformed()
2602{
2603 QFETCH(QImage::Format, destFormat);
2604 QFETCH(QImage::Format, srcFormat);
2605
2606 const int size = 128;
2607 const QSize imageSize(size, size);
2608 const QRect paintRect(1, 0, size - 2, size); // needs alignment and tailing
2609
2610 QColor destColor(127, 127, 127);
2611 QColor srcColor(Qt::white);
2612
2613 QImage dest(imageSize, destFormat);
2614 QImage src(imageSize, srcFormat);
2615
2616 QPainter p;
2617 p.begin(&src);
2618 p.fillRect(paintRect, color: srcColor);
2619 p.end();
2620
2621 QList<qreal> opacities = (QList<qreal>() << 0.0 << 0.1 << 0.01 << 0.4
2622 << 0.5 << 0.6 << 0.9 << 1.0);
2623 foreach (qreal opacity, opacities) {
2624 p.begin(&dest);
2625 p.fillRect(paintRect, color: destColor);
2626
2627 p.setOpacity(opacity);
2628 p.drawImage(targetRect: paintRect, image: src, sourceRect: paintRect);
2629 p.end();
2630
2631 // sanity check: make sure all pixels are equal
2632 QImage expected(size - 2, size, destFormat);
2633 p.begin(&expected);
2634 p.fillRect(x: 0, y: 0, w: expected.width(), h: expected.height(),
2635 b: dest.pixelColor(x: 1, y: 0));
2636 p.end();
2637
2638 const QImage subDest(dest.bits() + dest.depth() / 8,
2639 dest.width() - 2, dest.height(),
2640 dest.bytesPerLine(), dest.format());
2641
2642 if (dest.format() == QImage::Format_ARGB8565_Premultiplied ||
2643 dest.format() == QImage::Format_ARGB8555_Premultiplied) {
2644 // Test skipped due to rounding errors...
2645 continue;
2646 }
2647 QCOMPARE(subDest, expected);
2648 }
2649}
2650
2651void tst_QPainter::drawhelper_blend_tiled_untransformed_data()
2652{
2653 setOpacity_data();
2654}
2655
2656void tst_QPainter::drawhelper_blend_tiled_untransformed()
2657{
2658 QFETCH(QImage::Format, destFormat);
2659 QFETCH(QImage::Format, srcFormat);
2660
2661 const int size = 128;
2662 const QSize imageSize(size, size);
2663 const QRect paintRect(1, 0, size - 2, size); // needs alignment and tailing
2664
2665 QColor destColor(127, 127, 127);
2666 QColor srcColor(Qt::white);
2667
2668 QImage dest(imageSize, destFormat);
2669 QImage src(imageSize / 2, srcFormat);
2670
2671 QPainter p;
2672 p.begin(&src);
2673 p.fillRect(QRect(QPoint(0, 0), imageSize/ 2), color: srcColor);
2674 p.end();
2675
2676 const QBrush brush(src);
2677
2678 QList<qreal> opacities = (QList<qreal>() << 0.0 << 0.1 << 0.01 << 0.4
2679 << 0.5 << 0.6 << 0.9 << 1.0);
2680 foreach (qreal opacity, opacities) {
2681 p.begin(&dest);
2682 p.fillRect(paintRect, color: destColor);
2683
2684 p.setOpacity(opacity);
2685 p.fillRect(paintRect, brush);
2686 p.end();
2687
2688 // sanity check: make sure all pixels are equal
2689 QImage expected(size - 2, size, destFormat);
2690 p.begin(&expected);
2691 p.fillRect(x: 0, y: 0, w: expected.width(), h: expected.height(),
2692 b: dest.pixelColor(x: 1, y: 0));
2693 p.end();
2694
2695 const QImage subDest(dest.bits() + dest.depth() / 8,
2696 dest.width() - 2, dest.height(),
2697 dest.bytesPerLine(), dest.format());
2698
2699 if (dest.format() == QImage::Format_ARGB8565_Premultiplied ||
2700 dest.format() == QImage::Format_ARGB8555_Premultiplied) {
2701 // Skipping test due to rounding errors. Test needs rewrite
2702 continue;
2703 }
2704 QCOMPARE(subDest, expected);
2705 }
2706}
2707
2708static QPaintEngine::PaintEngineFeatures no_porter_duff()
2709{
2710 QPaintEngine::PaintEngineFeatures features = QPaintEngine::AllFeatures;
2711 return features & ~QPaintEngine::PorterDuff;
2712}
2713
2714class DummyPaintEngine : public QPaintEngine, public QPaintDevice
2715{
2716public:
2717 DummyPaintEngine() : QPaintEngine(no_porter_duff()) {}
2718 virtual bool begin(QPaintDevice *) { return true; }
2719 virtual bool end() { return true; }
2720
2721 virtual void updateState(const QPaintEngineState &) {}
2722 virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
2723
2724 virtual Type type() const { return User; }
2725
2726 virtual QPaintEngine *paintEngine() const { return (QPaintEngine *)this; }
2727
2728 virtual int metric(PaintDeviceMetric metric) const { Q_UNUSED(metric); return 0; };
2729};
2730
2731static bool success;
2732
2733void porterDuff_warningChecker(QtMsgType type, const QMessageLogContext &, const QString &msg)
2734{
2735 if (type == QtWarningMsg && msg == QLatin1String("QPainter::setCompositionMode: PorterDuff modes not supported on device"))
2736 success = false;
2737}
2738
2739void tst_QPainter::porterDuff_warning()
2740{
2741 QtMessageHandler old = qInstallMessageHandler(porterDuff_warningChecker);
2742 DummyPaintEngine dummy;
2743 QPainter p(&dummy);
2744
2745 success = true;
2746 p.setCompositionMode(QPainter::CompositionMode_Source);
2747 QVERIFY(success);
2748
2749 success = true;
2750 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
2751 QVERIFY(success);
2752
2753 success = true;
2754 p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
2755 QVERIFY(!success);
2756
2757 QVERIFY(qInstallMessageHandler(old) == porterDuff_warningChecker);
2758}
2759
2760void tst_QPainter::drawhelper_blend_color()
2761{
2762 QImage dest(32, 32, QImage::Format_ARGB8555_Premultiplied);
2763 dest.fill(pixel: 0xff000000);
2764
2765 {
2766 QPainter p(&dest);
2767 p.fillRect(x: 0, y: 0, w: dest.width(), h: dest.height(), b: QColor(255, 0, 0, 127));
2768 }
2769
2770 QImage expected(32, 32, QImage::Format_ARGB8555_Premultiplied);
2771 expected.fill(pixel: 0xff3c007f);
2772
2773 QCOMPARE(dest.pixel(1, 1), expected.pixel(1, 1));
2774 QCOMPARE(dest, expected);
2775}
2776
2777#ifndef QT_NO_WIDGETS
2778class ViewportTestWidget : public QWidget
2779{
2780public:
2781 ViewportTestWidget(QWidget *parent = 0) : QWidget(parent), hasPainted(false) {}
2782 QSize sizeHint() const {
2783 return QSize(100, 100);
2784 }
2785
2786 QRect viewport;
2787 bool hasPainted;
2788
2789protected:
2790 void paintEvent(QPaintEvent *) {
2791 hasPainted = true;
2792 QPainter p(this);
2793 viewport = p.viewport();
2794 }
2795};
2796
2797void tst_QPainter::childWidgetViewport()
2798{
2799 QWidget parent;
2800 parent.setAutoFillBackground(true);
2801 parent.resize(w: 200, h: 200);
2802 ViewportTestWidget child(&parent);
2803 child.setAutoFillBackground(true);
2804 parent.show();
2805 parent.update();
2806 qApp->processEvents();
2807
2808 if (child.hasPainted) {
2809 QCOMPARE(child.viewport, QRect(QPoint(0, 0), child.sizeHint()));
2810 } else {
2811 qWarning(msg: "Failed to ensure that paintEvent has been run. Could not run test.");
2812 }
2813}
2814#endif
2815
2816void tst_QPainter::fillRect_objectBoundingModeGradient()
2817{
2818 QImage a(10, 10, QImage::Format_ARGB32_Premultiplied);
2819 a.fill(pixel: 0x0);
2820 QImage b = a;
2821
2822 QLinearGradient g(QPoint(0, 0), QPoint(0, 1));
2823 g.setColorAt(pos: 0, color: Qt::red);
2824 g.setColorAt(pos: 1, color: Qt::blue);
2825 g.setCoordinateMode(QGradient::ObjectBoundingMode);
2826
2827 QPainter p(&a);
2828 p.fillRect(QRect(0, 0, a.width(), a.height()), g);
2829 p.end();
2830
2831 QPainterPath path;
2832 path.addRect(x: 0, y: 0, w: a.width(), h: a.height());
2833
2834 p.begin(&b);
2835 p.fillPath(path, brush: g);
2836 p.end();
2837
2838 QCOMPARE(a, b);
2839}
2840
2841void tst_QPainter::fillRect_stretchToDeviceMode()
2842{
2843 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
2844
2845 QLinearGradient g(QPoint(0, 0), QPoint(0, 1));
2846 g.setCoordinateMode(QGradient::StretchToDeviceMode);
2847
2848 QPainter p(&img);
2849 p.fillRect(img.rect(), g);
2850 p.end();
2851
2852 for (int i = 1; i < img.height(); ++i)
2853 QVERIFY(img.pixel(0, i) != img.pixel(0, i-1));
2854}
2855
2856void tst_QPainter::monoImages()
2857{
2858 Qt::GlobalColor colorPairs[][2] = {
2859 { Qt::white, Qt::black },
2860 { Qt::color0, Qt::color1 },
2861 { Qt::red, Qt::blue }
2862 };
2863
2864 const int numColorPairs = sizeof(colorPairs) / sizeof(QRgb[2]);
2865
2866 QImage transparent(2, 2, QImage::Format_ARGB32_Premultiplied);
2867 transparent.fill(pixel: 0x0);
2868
2869 for (int i = 1; i < QImage::NImageFormats; ++i) {
2870 for (int j = 0; j < numColorPairs; ++j) {
2871 const QImage::Format format = QImage::Format(i);
2872 if (format == QImage::Format_Indexed8)
2873 continue;
2874
2875 QImage img(2, 2, format);
2876
2877 if (img.colorCount() > 0) {
2878 img.setColor(i: 0, c: QColor(colorPairs[j][0]).rgba());
2879 img.setColor(i: 1, c: QColor(colorPairs[j][1]).rgba());
2880 }
2881
2882 img.fill(pixel: 0x0);
2883 QPainter p(&img);
2884 p.fillRect(x: 0, y: 0, w: 2, h: 2, c: colorPairs[j][0]);
2885 p.fillRect(x: 0, y: 0, w: 1, h: 1, c: colorPairs[j][1]);
2886 p.fillRect(x: 1, y: 1, w: 1, h: 1, c: colorPairs[j][1]);
2887 p.end();
2888
2889 QImage original = img;
2890
2891 p.begin(&img);
2892 p.drawImage(x: 0, y: 0, image: transparent);
2893 p.end();
2894
2895 // drawing a transparent image on top of another image
2896 // should not change the image
2897 QCOMPARE(original, img);
2898
2899 if (img.colorCount() == 0)
2900 continue;
2901
2902 for (int k = 0; k < 2; ++k) {
2903 QPainter p(&img);
2904 p.fillRect(x: 0, y: 0, w: 2, h: 2, c: colorPairs[j][k]);
2905 p.end();
2906
2907 QImage argb32p(2, 2, QImage::Format_ARGB32_Premultiplied);
2908 p.begin(&argb32p);
2909 p.fillRect(x: 0, y: 0, w: 2, h: 2, c: colorPairs[j][k]);
2910 p.end();
2911
2912 QCOMPARE(argb32p, img.convertToFormat(argb32p.format()));
2913
2914 // drawing argb32p image on mono image
2915 p.begin(&img);
2916 p.drawImage(x: 0, y: 0, image: argb32p);
2917 p.end();
2918
2919 QCOMPARE(argb32p, img.convertToFormat(argb32p.format()));
2920
2921 // drawing mono image on argb32p image
2922 p.begin(&argb32p);
2923 p.drawImage(x: 0, y: 0, image: img);
2924 p.end();
2925
2926 QCOMPARE(argb32p, img.convertToFormat(argb32p.format()));
2927 }
2928 }
2929 }
2930}
2931
2932#if !defined(Q_OS_AIX) && !defined(Q_CC_MSVC) && !defined(Q_OS_SOLARIS) && !defined(__UCLIBC__)
2933#include <fenv.h>
2934
2935static const QString fpeExceptionString(int exception)
2936{
2937#ifdef FE_INEXACT
2938 if (exception & FE_INEXACT)
2939 return QLatin1String("Inexact result");
2940#endif
2941 if (exception & FE_UNDERFLOW)
2942 return QLatin1String("Underflow");
2943 if (exception & FE_OVERFLOW)
2944 return QLatin1String("Overflow");
2945 if (exception & FE_DIVBYZERO)
2946 return QLatin1String("Divide by zero");
2947 if (exception & FE_INVALID)
2948 return QLatin1String("Invalid operation");
2949 return QLatin1String("No exception");
2950}
2951
2952class FpExceptionChecker
2953{
2954public:
2955 FpExceptionChecker(int exceptionMask)
2956 : m_exceptionMask(exceptionMask)
2957 {
2958 feclearexcept(excepts: m_exceptionMask);
2959 }
2960
2961 ~FpExceptionChecker()
2962 {
2963 const int exceptions = fetestexcept(excepts: m_exceptionMask);
2964 QVERIFY2(!exceptions, qPrintable(QLatin1String("Floating point exception: ") + fpeExceptionString(exceptions)));
2965 }
2966
2967private:
2968 int m_exceptionMask;
2969};
2970
2971void fpe_rasterizeLine_task232012()
2972{
2973 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
2974 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
2975 img.fill(pixel: 0x0);
2976 QPainter p(&img);
2977
2978 p.setBrush(Qt::black);
2979 p.drawRect(rect: QRectF(0, 0, 5, 0));
2980 p.drawRect(rect: QRectF(0, 0, 0, 5));
2981}
2982
2983void fpe_pixmapTransform()
2984{
2985 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
2986
2987 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
2988
2989 QPainter p(&img);
2990
2991 const qreal scaleFactor = 0.001;
2992 const int translateDistance = 1000000;
2993
2994 p.setPen(Qt::red);
2995 p.setBrush(QBrush(Qt::red,Qt::Dense6Pattern));
2996
2997 for (int i = 0; i < 2; ++i) {
2998 p.setRenderHint(hint: QPainter::SmoothPixmapTransform, on: i);
2999
3000 p.resetTransform();
3001 p.scale(sx: 1.1, sy: 1.1);
3002 p.translate(dx: translateDistance, dy: 0);
3003 p.drawRect(x: -translateDistance, y: 0, w: 100, h: 100);
3004
3005 p.resetTransform();
3006 p.scale(sx: scaleFactor, sy: scaleFactor);
3007 p.drawRect(rect: QRectF(0, 0, 1 / scaleFactor, 1 / scaleFactor));
3008 }
3009}
3010
3011void fpe_zeroLengthLines()
3012{
3013 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
3014
3015 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
3016
3017 QPainter p(&img);
3018
3019 p.setPen(QPen(Qt::black, 3));
3020 p.drawLine(x1: 64, y1: 64, x2: 64, y2: 64);
3021}
3022
3023void fpe_divByZero()
3024{
3025 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
3026
3027 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
3028
3029 QPainter p(&img);
3030
3031 p.setRenderHint(hint: QPainter::Antialiasing);
3032
3033 p.drawRect(rect: QRectF(10, 10, 100, 0));
3034 p.drawRect(rect: QRectF(10, 10, 0, 100));
3035
3036 p.drawRect(r: QRect(10, 10, 100, 0));
3037 p.drawRect(r: QRect(10, 10, 0, 100));
3038
3039 p.fillRect(r: QRectF(10, 10, 100, 0), c: Qt::black);
3040 p.fillRect(r: QRectF(10, 10, 0, 100), c: Qt::black);
3041
3042 p.fillRect(r: QRect(10, 10, 100, 0), c: Qt::black);
3043 p.fillRect(r: QRect(10, 10, 0, 100), c: Qt::black);
3044}
3045
3046void fpe_steepSlopes()
3047{
3048 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
3049
3050 QImage img(1024, 1024, QImage::Format_ARGB32_Premultiplied);
3051
3052 QFETCH(QTransform, transform);
3053 QFETCH(QLineF, line);
3054 QFETCH(bool, antialiased);
3055
3056 QPainter p(&img);
3057
3058 p.setPen(QPen(Qt::black, 1));
3059 p.setRenderHint(hint: QPainter::Antialiasing, on: antialiased);
3060 p.setTransform(transform);
3061
3062 p.drawLine(l: line);
3063}
3064
3065void fpe_radialGradients()
3066{
3067 FpExceptionChecker checker(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
3068
3069 QImage img(21, 21, QImage::Format_ARGB32_Premultiplied);
3070 img.fill(pixel: 0);
3071
3072 double m = img.width() * 0.5;
3073
3074 QPainter p(&img);
3075 p.setRenderHints(hints: QPainter::Antialiasing);
3076 p.setPen(Qt::NoPen);
3077 p.setBrush(QRadialGradient(m, m, m));
3078 p.drawEllipse(r: img.rect());
3079}
3080
3081#define FPE_TEST(x) \
3082void tst_QPainter::x() \
3083{ \
3084 ::x(); \
3085}
3086#else
3087#define FPE_TEST(x) \
3088void tst_QPainter::x() \
3089{ \
3090 QSKIP("Floating point exception checking (fenv.h) not available"); \
3091}
3092#endif
3093
3094FPE_TEST(fpe_rasterizeLine_task232012)
3095FPE_TEST(fpe_pixmapTransform)
3096FPE_TEST(fpe_zeroLengthLines)
3097FPE_TEST(fpe_divByZero)
3098FPE_TEST(fpe_steepSlopes)
3099FPE_TEST(fpe_radialGradients)
3100
3101void tst_QPainter::fpe_steepSlopes_data()
3102{
3103 QTest::addColumn<QTransform>(name: "transform");
3104 QTest::addColumn<QLineF>(name: "line");
3105 QTest::addColumn<bool>(name: "antialiased");
3106
3107 {
3108 const qreal dsin = 0.000014946676875461832484392500630665523431162000633776187896728515625;
3109 const qreal dcos = 0.9999999998882984630910186751862056553363800048828125;
3110
3111 const QTransform transform = QTransform(dcos, dsin, -dsin, dcos, 64, 64);
3112 const QLineF line(2, 2, 2, 6);
3113
3114 QTest::newRow(dataTag: "task 207147 aa") << transform << line << true;
3115 QTest::newRow(dataTag: "task 207147 no aa") << transform << line << false;
3116 }
3117
3118 {
3119 QTransform transform;
3120 transform.rotate(a: 0.0000001);
3121 const QLineF line(5, 5, 10, 5);
3122
3123 QTest::newRow(dataTag: "task 166702 aa") << transform << line << true;
3124 QTest::newRow(dataTag: "task 166702 no aa") << transform << line << false;
3125 }
3126
3127 {
3128 const QTransform transform;
3129 const QLineF line(2.5, 2.5, 2.5 + 1/256., 60000.5);
3130
3131 QTest::newRow(dataTag: "steep line aa") << transform << line << true;
3132 QTest::newRow(dataTag: "steep line no aa") << transform << line << false;
3133 }
3134
3135 {
3136 const QTransform transform;
3137 const QLineF line(2.5, 2.5, 2.5 + 1/256., 1024);
3138
3139 QTest::newRow(dataTag: "steep line 2 aa") << transform << line << true;
3140 QTest::newRow(dataTag: "steep line 2 no aa") << transform << line << false;
3141 }
3142
3143 {
3144 const QTransform transform;
3145 const QLineF line(2.5, 2.5, 2.5 + 1/64., 1024);
3146
3147 QTest::newRow(dataTag: "steep line 3 aa") << transform << line << true;
3148 QTest::newRow(dataTag: "steep line 3 no aa") << transform << line << false;
3149 }
3150}
3151
3152qreal randf()
3153{
3154 return QRandomGenerator::global()->bounded(highest: 1.0);
3155}
3156
3157QPointF randInRect(const QRectF &rect)
3158{
3159 const qreal x = rect.left() + rect.width() * randf();
3160 const qreal y = rect.top() + rect.height() * randf();
3161
3162 return QPointF(x, y);
3163}
3164
3165void tst_QPainter::rasterizer_asserts()
3166{
3167 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
3168
3169 QRectF middle(QPointF(0, 0), img.size());
3170 QRectF left = middle.translated(dx: -middle.width(), dy: 0);
3171 QRectF right = middle.translated(dx: middle.width(), dy: 0);
3172
3173 QPainter p(&img);
3174 img.fill(color: Qt::white);
3175 p.setCompositionMode(QPainter::CompositionMode_Destination);
3176 for (int i = 0; i < 100000; ++i) {
3177 QPainterPath path;
3178 path.moveTo(p: randInRect(rect: middle));
3179 path.lineTo(p: randInRect(rect: left));
3180 path.lineTo(p: randInRect(rect: right));
3181
3182 p.fillPath(path, brush: Qt::black);
3183 }
3184}
3185
3186void tst_QPainter::rasterizer_negativeCoords()
3187{
3188 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
3189 img.fill(pixel: 0x0);
3190
3191 QImage original = img;
3192
3193 QPainter p(&img);
3194 p.rotate(a: 90);
3195 p.fillRect(x: 0, y: 0, w: 70, h: 50, c: Qt::black);
3196
3197 // image should not have changed
3198 QCOMPARE(img.pixel(0, 0), 0x0U);
3199 QCOMPARE(img, original);
3200}
3201
3202void tst_QPainter::blendOverFlow_data()
3203{
3204 QTest::addColumn<QImage::Format>(name: "format");
3205 QTest::addColumn<int>(name: "width");
3206 QTest::addColumn<int>(name: "height");
3207
3208 QImage::Format format = QImage::Format_ARGB8555_Premultiplied;
3209 QTest::newRow(dataTag: "555,1,1") << format << 1 << 1;
3210 QTest::newRow(dataTag: "555,2,2") << format << 2 << 2;
3211 QTest::newRow(dataTag: "555,10,10") << format << 10 << 10;
3212
3213 format = QImage::Format_ARGB8565_Premultiplied;
3214 QTest::newRow(dataTag: "565,1,1") << format << 1 << 1;
3215 QTest::newRow(dataTag: "565,2,2") << format << 2 << 2;
3216 QTest::newRow(dataTag: "565,10,10") << format << 10 << 10;
3217}
3218
3219void tst_QPainter::blendOverFlow()
3220{
3221 QFETCH(QImage::Format, format);
3222 QFETCH(int, width);
3223 QFETCH(int, height);
3224
3225 QImage dest(width, height, format);
3226 QImage src(width, height, format);
3227
3228 {
3229 QPainter p(&dest);
3230 p.fillRect(x: 0, y: 0, w: width, h: height, c: Qt::green);
3231 }
3232 QImage expected = dest;
3233
3234 {
3235 QPainter p(&src);
3236 p.setCompositionMode(QPainter::CompositionMode_Source);
3237 p.fillRect(x: 0, y: 0, w: width, h: height, b: QColor(0, 255, 0, 6));
3238 }
3239
3240 {
3241 QPainter p(&dest);
3242 p.drawImage(x: 0, y: 0, image: src);
3243 }
3244
3245 QCOMPARE(dest.pixel(0, 0), expected.pixel(0, 0));
3246 QCOMPARE(dest, expected);
3247}
3248
3249void tst_QPainter::largeImagePainting_data()
3250{
3251 QTest::addColumn<int>(name: "width");
3252 QTest::addColumn<int>(name: "height");
3253 QTest::addColumn<bool>(name: "antialiased");
3254
3255 QTest::newRow(dataTag: "tall") << 1 << 32767 << false;
3256 QTest::newRow(dataTag: "tall aa") << 1 << 32767 << true;
3257 QTest::newRow(dataTag: "wide") << 32767 << 1 << false;
3258 QTest::newRow(dataTag: "wide aa") << 32767 << 1 << true;
3259}
3260
3261void tst_QPainter::largeImagePainting()
3262{
3263 QPainterPath path;
3264 path.addRect(x: 0, y: 0, w: 1, h: 1);
3265 path.addRect(x: 2, y: 0, w: 1, h: 1);
3266 path.addRect(x: 0, y: 2, w: 1, h: 1);
3267
3268 QFETCH(int, width);
3269 QFETCH(int, height);
3270 QFETCH(bool, antialiased);
3271
3272 QImage img(width, height, QImage::Format_ARGB32_Premultiplied);
3273 img.fill(pixel: 0x0);
3274
3275 QPainter p(&img);
3276 p.setPen(Qt::NoPen);
3277 p.setBrush(Qt::white);
3278
3279 p.setRenderHint(hint: QPainter::Antialiasing, on: antialiased);
3280
3281 for (int i = 0; i < img.width(); i += 4) {
3282 p.drawPath(path);
3283 p.translate(dx: 4, dy: 0);
3284 }
3285
3286 p.resetTransform();
3287
3288 for (int i = 4; i < img.height(); i += 4) {
3289 p.translate(dx: 0, dy: 4);
3290 p.drawPath(path);
3291 }
3292
3293 for (int i = 0; i < img.width(); ++i) {
3294 if (i % 2)
3295 QCOMPARE(img.pixel(i, 0), 0x0U);
3296 else
3297 QCOMPARE(img.pixel(i, 0), 0xffffffffU);
3298 }
3299
3300 for (int i = 1; i < img.height(); ++i) {
3301 if (i % 2)
3302 QCOMPARE(img.pixel(0, i), 0x0U);
3303 else
3304 QCOMPARE(img.pixel(0, i), 0xffffffffU);
3305 }
3306}
3307
3308void tst_QPainter::imageScaling_task206785()
3309{
3310 QImage src(32, 2, QImage::Format_ARGB32_Premultiplied);
3311 src.fill(pixel: 0xffffffff);
3312
3313 QImage dst(128, 128, QImage::Format_ARGB32_Premultiplied);
3314
3315 QImage expected(128, 128, QImage::Format_ARGB32_Premultiplied);
3316 expected.fill(pixel: 0xffffffff);
3317
3318 for (int i = 1; i < 5; ++i) {
3319 qreal scale = i / qreal(5);
3320
3321 dst.fill(pixel: 0xff000000);
3322
3323 QPainter p(&dst);
3324 p.scale(sx: dst.width() / qreal(src.width()), sy: scale);
3325
3326 for (int y = 0; y * scale < dst.height(); ++y)
3327 p.drawImage(x: 0, y, image: src);
3328
3329 p.end();
3330
3331 QCOMPARE(dst, expected);
3332 }
3333}
3334
3335#define FOR_EACH_NEIGHBOR_8 for (int dx = -1; dx <= 1; ++dx) for (int dy = -1; dy <= 1; ++dy) if (dx != 0 || dy != 0)
3336#define FOR_EACH_NEIGHBOR_4 for (int dx = -1; dx <= 1; ++dx) for (int dy = -1; dy <= 1; ++dy) if ((dx == 0) != (dy == 0))
3337
3338uint qHash(const QPoint &point)
3339{
3340 return qHash(key: qMakePair(x: point.x(), y: point.y()));
3341}
3342
3343bool verifyOutlineFillConsistency(const QImage &img, QRgb outside, QRgb inside, QRgb outline)
3344{
3345 if (img.pixel(x: img.width() / 2, y: img.height() / 2) != inside)
3346 return false;
3347
3348 int x = img.width() / 2;
3349 int y = img.height() / 2;
3350
3351 while (img.pixel(x: ++x, y) == inside)
3352 ;
3353
3354 if (img.pixel(x, y) != outline)
3355 return false;
3356
3357 QQueue<QPoint> discovered;
3358 discovered.enqueue(t: QPoint(x, y));
3359
3360 QVector<bool> visited(img.width() * img.height());
3361 visited.fill(from: false);
3362
3363 while (!discovered.isEmpty()) {
3364 QPoint p = discovered.dequeue();
3365 QRgb pixel = img.pixel(x: p.x(), y: p.y());
3366
3367 bool &v = visited[p.y() * img.width() + p.x()];
3368 if (v)
3369 continue;
3370 v = true;
3371
3372 if (pixel == outline) {
3373 FOR_EACH_NEIGHBOR_8 {
3374 QPoint x(p.x() + dx, p.y() + dy);
3375 discovered.enqueue(t: x);
3376 }
3377 } else {
3378 FOR_EACH_NEIGHBOR_4 {
3379 if ((dx == 0) == (dy == 0))
3380 continue;
3381 QRgb neighbor = img.pixel(x: p.x() + dx, y: p.y() + dy);
3382 if ((pixel == inside && neighbor == outside) ||
3383 (pixel == outside && neighbor == inside))
3384 return false;
3385 }
3386 }
3387 }
3388
3389 return true;
3390}
3391
3392#undef FOR_EACH_NEIGHBOR_8
3393#undef FOR_EACH_NEIGHBOR_4
3394
3395void tst_QPainter::outlineFillConsistency()
3396{
3397 QImage dst(256, 256, QImage::Format_ARGB32_Premultiplied);
3398
3399 QPolygonF poly;
3400 poly << QPointF(5, -100) << QPointF(-70, 20) << QPointF(95, 25);
3401
3402 QPen pen(Qt::red);
3403 QBrush brush(Qt::black);
3404
3405 QRgb background = 0xffffffff;
3406 for (int i = 0; i < 360; ++i) {
3407 dst.fill(pixel: background);
3408
3409 QPainter p(&dst);
3410 p.translate(dx: dst.width() / 2, dy: dst.height() / 2);
3411
3412 QPolygonF copy = poly;
3413 for (int j = 0; j < copy.size(); ++j)
3414 copy[j] = QTransform().rotate(a: i).map(p: copy[j]);
3415
3416 p.setPen(pen);
3417 p.setBrush(brush);
3418 p.drawPolygon(polygon: copy);
3419 p.end();
3420
3421 QVERIFY(verifyOutlineFillConsistency(dst, background, brush.color().rgba(), pen.color().rgba()));
3422 }
3423}
3424
3425void tst_QPainter::drawImage_task217400_data()
3426{
3427 QTest::addColumn<QImage::Format>(name: "format");
3428
3429 QTest::newRow(dataTag: "444") << QImage::Format_ARGB4444_Premultiplied;
3430 QTest::newRow(dataTag: "555") << QImage::Format_ARGB8555_Premultiplied;
3431 QTest::newRow(dataTag: "565") << QImage::Format_ARGB8565_Premultiplied;
3432// QTest::newRow("666") << QImage::Format_ARGB6666_Premultiplied;
3433 QTest::newRow(dataTag: "888p") << QImage::Format_ARGB32_Premultiplied;
3434 QTest::newRow(dataTag: "888") << QImage::Format_ARGB32;
3435}
3436
3437void tst_QPainter::drawImage_task217400()
3438{
3439 QFETCH(QImage::Format, format);
3440
3441 const QImage src = QImage(QFINDTESTDATA("task217400.png"))
3442 .convertToFormat(f: format);
3443 QVERIFY(!src.isNull());
3444
3445 QImage expected(src.size(), format);
3446 {
3447 QPainter p(&expected);
3448 p.fillRect(x: 0, y: 0, w: expected.width(), h: expected.height(), c: Qt::white);
3449 p.drawImage(x: 0, y: 0, image: src);
3450 }
3451
3452 for (int i = 1; i <= 4; ++i) {
3453 QImage dest(src.width() + i, src.height(), format);
3454 {
3455 QPainter p(&dest);
3456 p.fillRect(x: 0, y: 0, w: dest.width(), h: dest.height(), c: Qt::white);
3457 p.drawImage(x: i, y: 0, image: src);
3458 }
3459
3460 const QImage result = dest.copy(x: i, y: 0, w: src.width(), h: src.height());
3461
3462 QCOMPARE(result, expected);
3463 }
3464}
3465
3466void tst_QPainter::drawImage_task258776()
3467{
3468 QImage src(16, 16, QImage::Format_RGB888);
3469 QImage dest(33, 33, QImage::Format_RGB888);
3470 src.fill(pixel: 0x00ff00);
3471 dest.fill(pixel: 0xff0000);
3472
3473 QPainter painter(&dest);
3474 painter.drawImage(targetRect: QRectF(0.499, 0.499, 32, 32), image: src, sourceRect: QRectF(0, 0, 16, 16));
3475 painter.end();
3476
3477 QImage expected(33, 33, QImage::Format_RGB32);
3478 expected.fill(pixel: 0xff0000);
3479
3480 painter.begin(&expected);
3481 painter.drawImage(r: QRectF(0, 0, 32, 32), image: src);
3482 painter.end();
3483
3484 dest = dest.convertToFormat(f: QImage::Format_RGB32);
3485
3486 dest.save(fileName: "dest.png");
3487 expected.save(fileName: "expected.png");
3488 QCOMPARE(dest, expected);
3489}
3490
3491void tst_QPainter::drawImage_QTBUG28324()
3492{
3493 QImage dest(512, 512, QImage::Format_ARGB32_Premultiplied);
3494 dest.fill(pixel: 0x0);
3495
3496 int x = 263; int y = 89; int w = 61; int h = 39;
3497
3498 QImage source(w, h, QImage::Format_ARGB32_Premultiplied);
3499 quint32 *b = (quint32 *)source.bits();
3500 for (int j = 0; j < w * h; ++j)
3501 b[j] = 0x7f7f7f7f;
3502
3503 // nothing to test here since the bug is about
3504 // an invalid memory read, which valgrind
3505 // would complain about
3506 QPainter p(&dest);
3507 p.drawImage(x, y, image: source);
3508}
3509
3510void tst_QPainter::clipRectSaveRestore()
3511{
3512 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
3513 img.fill(pixel: 0x0);
3514
3515 QPainter p(&img);
3516 p.setClipRect(QRect(0, 0, 10, 10));
3517 p.save();
3518 p.setClipRect(QRect(5, 5, 5, 5), op: Qt::IntersectClip);
3519 p.restore();
3520 p.fillRect(x: 0, y: 0, w: 64, h: 64, c: Qt::black);
3521 p.end();
3522
3523 QCOMPARE(img.pixel(0, 0), QColor(Qt::black).rgba());
3524}
3525
3526void tst_QPainter::clipStateSaveRestore()
3527{
3528 QImage img(16, 16, QImage::Format_RGB32);
3529 img.fill(color: Qt::blue);
3530 {
3531 QPainter p(&img);
3532 p.setClipRect(QRect(5, 5, 10, 10));
3533 p.save();
3534 p.setClipping(false);
3535 p.restore();
3536 p.fillRect(x: 0, y: 0, w: 16, h: 16, c: Qt::red);
3537 p.end();
3538 QCOMPARE(img.pixel(0, 0), QColor(Qt::blue).rgb());
3539 }
3540
3541 img.fill(color: Qt::blue);
3542 {
3543 QPainter p(&img);
3544 p.setClipRect(QRect(5, 5, 10, 10));
3545 p.setClipping(false);
3546 p.save();
3547 p.setClipping(true);
3548 p.restore();
3549 p.fillRect(x: 0, y: 0, w: 16, h: 16, c: Qt::red);
3550 p.end();
3551 QCOMPARE(img.pixel(0, 0), QColor(Qt::red).rgb());
3552 }
3553}
3554
3555void tst_QPainter::clippedImage()
3556{
3557 QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
3558 img.fill(pixel: 0x0);
3559
3560 QImage src(16, 16, QImage::Format_RGB32);
3561 src.fill(pixel: QColor(Qt::red).rgba());
3562
3563 QPainter p(&img);
3564 p.setClipRect(QRect(1, 1, 14, 14));
3565 p.drawImage(x: 0, y: 0, image: src);
3566 p.end();
3567
3568 QCOMPARE(img.pixel(0, 0), 0x0U);
3569 QCOMPARE(img.pixel(1, 1), src.pixel(1, 1));
3570}
3571
3572void tst_QPainter::stateResetBetweenQPainters()
3573{
3574 QImage img(16, 16, QImage::Format_ARGB32);
3575
3576 {
3577 QPainter p(&img);
3578 p.setCompositionMode(QPainter::CompositionMode_Source);
3579 p.fillRect(x: 0, y: 0, w: 16, h: 16, c: Qt::red);
3580 }
3581
3582 {
3583 QPainter p2(&img);
3584 p2.fillRect(x: 0, y: 0, w: 16, h: 16, b: QColor(0, 0, 255, 63));
3585 }
3586
3587 img.save(fileName: "foo.png");
3588
3589 QVERIFY(img.pixel(0, 0) != qRgba(0, 0, 255, 63));
3590 QVERIFY(qRed(img.pixel(0, 0)) > 0); // We didn't erase the red channel...
3591 QVERIFY(qBlue(img.pixel(0, 0)) < 255); // We blended the blue channel
3592}
3593
3594void tst_QPainter::drawRect_task215378()
3595{
3596 QImage img(11, 11, QImage::Format_ARGB32_Premultiplied);
3597 img.fill(pixel: QColor(Qt::white).rgba());
3598
3599 QPainter p(&img);
3600 p.setPen(QColor(127, 127, 127, 127));
3601 p.drawRect(x: 0, y: 0, w: 10, h: 10);
3602 p.end();
3603
3604 QCOMPARE(img.pixel(0, 0), img.pixel(1, 0));
3605 QCOMPARE(img.pixel(0, 0), img.pixel(0, 1));
3606 QVERIFY(img.pixel(0, 0) != img.pixel(1, 1));
3607}
3608
3609void tst_QPainter::drawRect_task247505()
3610{
3611 QImage a(10, 10, QImage::Format_ARGB32_Premultiplied);
3612 a.fill(pixel: 0);
3613 QImage b = a;
3614
3615 QPainter p(&a);
3616 p.setPen(Qt::NoPen);
3617 p.setBrush(Qt::black);
3618 p.drawRect(rect: QRectF(10, 0, -10, 10));
3619 p.end();
3620 p.begin(&b);
3621 p.setPen(Qt::NoPen);
3622 p.setBrush(Qt::black);
3623 p.drawRect(rect: QRectF(0, 0, 10, 10));
3624 p.end();
3625
3626 QCOMPARE(a, b);
3627}
3628
3629void tst_QPainter::drawImage_data()
3630{
3631 QTest::addColumn<int>(name: "x");
3632 QTest::addColumn<int>(name: "y");
3633 QTest::addColumn<int>(name: "w");
3634 QTest::addColumn<int>(name: "h");
3635 QTest::addColumn<QImage::Format>(name: "srcFormat");
3636 QTest::addColumn<QImage::Format>(name: "dstFormat");
3637
3638 for (int srcFormat = QImage::Format_Mono; srcFormat < QImage::NImageFormats; ++srcFormat) {
3639 for (int dstFormat = QImage::Format_Mono; dstFormat < QImage::NImageFormats; ++dstFormat) {
3640 // Indexed8 can't be painted to, and Alpha8 can't hold a color.
3641 if (dstFormat == QImage::Format_Indexed8 || dstFormat == QImage::Format_Alpha8)
3642 continue;
3643 for (int odd_x = 0; odd_x <= 1; ++odd_x) {
3644 for (int odd_width = 0; odd_width <= 1; ++odd_width) {
3645 QTest::addRow(format: "srcFormat %d, dstFormat %d, odd x: %d, odd width: %d",
3646 srcFormat, dstFormat, odd_x, odd_width)
3647 << (10 + odd_x) << 10 << (20 + odd_width) << 20
3648 << QImage::Format(srcFormat)
3649 << QImage::Format(dstFormat);
3650 }
3651 }
3652 }
3653 }
3654}
3655
3656bool verifyImage(const QImage &img, int x, int y, int w, int h, uint background)
3657{
3658 int imgWidth = img.width();
3659 int imgHeight = img.height();
3660 for (int i = 0; i < imgHeight; ++i) {
3661 for (int j = 0; j < imgWidth; ++j) {
3662 uint pixel = img.pixel(x: j, y: i);
3663 bool outside = j < x || j >= (x + w) || i < y || i >= (y + h);
3664 if (outside != (pixel == background)) {
3665 //printf("%d %d, expected %x, got %x, outside: %d\n", x, y, background, pixel, outside);
3666 return false;
3667 }
3668 }
3669 }
3670
3671 return true;
3672}
3673
3674void tst_QPainter::drawImage()
3675{
3676 QFETCH(int, x);
3677 QFETCH(int, y);
3678 QFETCH(int, w);
3679 QFETCH(int, h);
3680 QFETCH(QImage::Format, srcFormat);
3681 QFETCH(QImage::Format, dstFormat);
3682
3683 QImage dst(40, 40, QImage::Format_RGB32);
3684 dst.fill(pixel: 0xffffffff);
3685
3686 dst = dst.convertToFormat(f: dstFormat);
3687 uint background = dst.pixel(x: 0, y: 0);
3688
3689 QImage src(w, h, QImage::Format_RGB32);
3690 src.fill(pixel: 0xff000000);
3691 src = src.convertToFormat(f: srcFormat);
3692
3693 QPainter p(&dst);
3694 p.drawImage(x, y, image: src);
3695 p.end();
3696
3697 QVERIFY(verifyImage(dst, x, y, w, h, background));
3698}
3699
3700void tst_QPainter::imageCoordinateLimit()
3701{
3702 QImage img(64, 40000, QImage::Format_MonoLSB);
3703 QPainter p(&img);
3704 p.drawText(x: 10, y: 36000, s: QLatin1String("foo"));
3705 p.setPen(QPen(Qt::black, 2));
3706 p.drawLine(x1: 10, y1: 0, x2: 60, y2: 40000);
3707
3708 p.setRenderHint(hint: QPainter::Antialiasing);
3709 p.drawLine(x1: 10, y1: 0, x2: 60, y2: 40000);
3710}
3711
3712
3713void tst_QPainter::imageBlending_data()
3714{
3715 QTest::addColumn<QImage::Format>(name: "sourceFormat");
3716 QTest::addColumn<QImage::Format>(name: "destFormat");
3717 QTest::addColumn<int>(name: "error");
3718
3719 int error_rgb565 = ((1<<3) + (1<<2) + (1<<3));
3720 QTest::newRow(dataTag: "rgb565_on_rgb565") << QImage::Format_RGB16
3721 << QImage::Format_RGB16
3722 << 0;
3723 QTest::newRow(dataTag: "argb8565_on_rgb565") << QImage::Format_ARGB8565_Premultiplied
3724 << QImage::Format_RGB16
3725 << error_rgb565;
3726
3727 QTest::newRow(dataTag: "rgb32_on_rgb565") << QImage::Format_RGB32
3728 << QImage::Format_RGB16
3729 << error_rgb565;
3730
3731 QTest::newRow(dataTag: "argb32pm_on_rgb565") << QImage::Format_ARGB32_Premultiplied
3732 << QImage::Format_RGB16
3733 << error_rgb565;
3734}
3735
3736int diffColor(quint32 ap, quint32 bp)
3737{
3738 int a = qAlpha(rgb: ap) - qAlpha(rgb: bp);
3739 int r = qRed(rgb: ap) - qRed(rgb: bp);
3740 int b = qBlue(rgb: ap) - qBlue(rgb: bp);
3741 int g = qBlue(rgb: ap) - qBlue(rgb: bp);
3742
3743 return qAbs(t: a) + qAbs(t: r) + qAbs(t: g) + qAbs(t: b);
3744}
3745
3746// this test assumes premultiplied pixels...
3747
3748void tst_QPainter::imageBlending()
3749{
3750 QFETCH(QImage::Format, sourceFormat);
3751 QFETCH(QImage::Format, destFormat);
3752 QFETCH(int, error);
3753
3754 QImage dest;
3755 {
3756 QImage orig_dest(6, 6, QImage::Format_ARGB32_Premultiplied);
3757 orig_dest.fill(pixel: 0);
3758 QPainter p(&orig_dest);
3759 p.fillRect(x: 0, y: 0, w: 6, h: 3, b: QColor::fromRgbF(r: 1, g: 0, b: 0));
3760 p.fillRect(x: 3, y: 0, w: 3, h: 6, b: QColor::fromRgbF(r: 0, g: 0, b: 1, a: 0.5));
3761 p.end();
3762 dest = orig_dest.convertToFormat(f: destFormat);
3763
3764 // An image like this: (r = red, m = magenta, b = light alpha blue, 0 = transparent)
3765 // r r r m m m
3766 // r r r m m m
3767 // r r r m m m
3768 // 0 0 0 b b b
3769 // 0 0 0 b b b
3770 // 0 0 0 b b b
3771 }
3772
3773 QImage source;
3774 {
3775 QImage orig_source(6, 6, QImage::Format_ARGB32_Premultiplied);
3776 orig_source.fill(pixel: 0);
3777 QPainter p(&orig_source);
3778 p.fillRect(x: 1, y: 1, w: 4, h: 4, b: QColor::fromRgbF(r: 0, g: 1, b: 0, a: 0.5));
3779 p.fillRect(x: 2, y: 2, w: 2, h: 2, b: QColor::fromRgbF(r: 0, g: 1, b: 0));
3780 p.end();
3781 source = orig_source.convertToFormat(f: sourceFormat);
3782
3783 // An image like this: (0 = transparent, . = green at 0.5 alpha, g = opaque green.
3784 // 0 0 0 0 0 0
3785 // 0 . . . . 0
3786 // 0 . g g . 0
3787 // 0 . g g . 0
3788 // 0 . . . . 0
3789 // 0 0 0 0 0 0
3790 }
3791
3792 QPainter p(&dest);
3793 p.drawImage(x: 0, y: 0, image: source);
3794 p.end();
3795
3796 // resulting image:
3797 // r r r m m m
3798 // r r. r. m. m. m
3799 // r r. g g m. m
3800 // 0 . g g b. b
3801 // 0 . . b. b. b
3802 // 0 0 0 b b b
3803
3804 // the g pixels, always green..
3805 QVERIFY(diffColor(dest.pixel(2, 2), 0xff00ff00) <= error); // g
3806
3807 if (source.hasAlphaChannel()) {
3808 QVERIFY(diffColor(dest.pixel(0, 0), 0xffff0000) <= error); // r
3809 QVERIFY(diffColor(dest.pixel(5, 0), 0xff7f007f) <= error); // m
3810 QVERIFY(diffColor(dest.pixel(1, 1), 0xff7f7f00) <= error); // r.
3811 QVERIFY(diffColor(dest.pixel(4, 1), 0xff3f7f3f) <= error); // m.
3812 if (dest.hasAlphaChannel()) {
3813 QVERIFY(diffColor(dest.pixel(1, 3), 0x7f007f00) <= error); // .
3814 QVERIFY(diffColor(dest.pixel(4, 3), 0x7f007f3f) <= error); // b.
3815 QVERIFY(diffColor(dest.pixel(4, 3), 0x7f007f3f) <= error); // b.
3816 QVERIFY(diffColor(dest.pixel(4, 4), 0x7f00007f) <= error); // b
3817 QVERIFY(diffColor(dest.pixel(4, 0), 0) <= 0); // 0
3818 }
3819 } else {
3820 QVERIFY(diffColor(dest.pixel(0, 0), 0xff000000) <= 0);
3821 QVERIFY(diffColor(dest.pixel(1, 1), 0xff007f00) <= error);
3822 }
3823}
3824
3825void tst_QPainter::imageBlending_clipped()
3826{
3827 QImage src(20, 20, QImage::Format_RGB16);
3828 QPainter p(&src);
3829 p.fillRect(r: src.rect(), c: Qt::red);
3830 p.end();
3831
3832 QImage dst(40, 20, QImage::Format_RGB16);
3833 p.begin(&dst);
3834 p.fillRect(r: dst.rect(), c: Qt::white);
3835 p.end();
3836
3837 QImage expected = dst;
3838
3839 p.begin(&dst);
3840 p.setClipRect(QRect(23, 0, 20, 20));
3841
3842 // should be completely clipped
3843 p.drawImage(r: QRectF(3, 0, 20, 20), image: src);
3844 p.end();
3845
3846 // dst should be left unchanged
3847 QCOMPARE(dst, expected);
3848}
3849
3850void tst_QPainter::paintOnNullPixmap()
3851{
3852 QPixmap pix(16, 16);
3853
3854 QPixmap textPixmap;
3855 QPainter p(&textPixmap);
3856 p.drawPixmap(x: 10, y: 10, pm: pix);
3857 p.end();
3858
3859 QPixmap textPixmap2(16,16);
3860 p.begin(&textPixmap2);
3861 p.end();
3862}
3863
3864void tst_QPainter::checkCompositionMode()
3865{
3866 QImage refImage(50,50,QImage::Format_ARGB32);
3867 QPainter painter(&refImage);
3868 painter.fillRect(r: QRect(0,0,50,50),c: Qt::blue);
3869
3870 QImage testImage(50,50,QImage::Format_ARGB32);
3871 QPainter p(&testImage);
3872 p.fillRect(r: QRect(0,0,50,50),c: Qt::red);
3873 p.save();
3874 p.setCompositionMode(QPainter::CompositionMode_SourceOut);
3875 p.restore();
3876 p.fillRect(r: QRect(0,0,50,50),c: Qt::blue);
3877
3878 QCOMPARE(refImage.pixel(20,20),testImage.pixel(20,20));
3879}
3880
3881static QLinearGradient inverseGradient(QLinearGradient g)
3882{
3883 QLinearGradient g2 = g;
3884
3885 QGradientStops stops = g.stops();
3886
3887 QGradientStops inverse;
3888 foreach (QGradientStop stop, stops)
3889 inverse << QGradientStop(1 - stop.first, stop.second);
3890
3891 g2.setStops(inverse);
3892 return g2;
3893}
3894
3895void tst_QPainter::linearGradientSymmetry_data()
3896{
3897 QTest::addColumn<QGradientStops>(name: "stops");
3898
3899 if (sizeof(qreal) != sizeof(float)) {
3900 QGradientStops stops;
3901 stops << qMakePair(x: qreal(0.0), y: QColor(Qt::blue));
3902 stops << qMakePair(x: qreal(0.2), y: QColor(220, 220, 220, 0));
3903 stops << qMakePair(x: qreal(0.6), y: QColor(Qt::red));
3904 stops << qMakePair(x: qreal(0.9), y: QColor(220, 220, 220, 255));
3905 stops << qMakePair(x: qreal(1.0), y: QColor(Qt::black));
3906 QTest::newRow(dataTag: "multiple stops") << stops;
3907 }
3908
3909 {
3910 QGradientStops stops;
3911 stops << qMakePair(x: qreal(0.0), y: QColor(Qt::blue));
3912 stops << qMakePair(x: qreal(1.0), y: QColor(Qt::black));
3913 QTest::newRow(dataTag: "two stops") << stops;
3914 }
3915
3916 if (sizeof(qreal) != sizeof(float)) {
3917 QGradientStops stops;
3918 stops << qMakePair(x: qreal(0.3), y: QColor(Qt::blue));
3919 stops << qMakePair(x: qreal(0.6), y: QColor(Qt::black));
3920 QTest::newRow(dataTag: "two stops 2") << stops;
3921 }
3922}
3923
3924void tst_QPainter::linearGradientSymmetry()
3925{
3926 QFETCH(QGradientStops, stops);
3927
3928 QImage a(64, 8, QImage::Format_ARGB32_Premultiplied);
3929 QImage b(64, 8, QImage::Format_ARGB32_Premultiplied);
3930
3931 a.fill(pixel: 0);
3932 b.fill(pixel: 0);
3933
3934 QLinearGradient gradient(QRectF(b.rect()).topLeft(), QRectF(b.rect()).topRight());
3935 gradient.setStops(stops);
3936
3937 QPainter pa(&a);
3938 pa.fillRect(a.rect(), gradient);
3939 pa.end();
3940
3941 QPainter pb(&b);
3942 pb.fillRect(b.rect(), inverseGradient(g: gradient));
3943 pb.end();
3944
3945 b = b.mirrored(horizontally: true);
3946 QCOMPARE(a, b);
3947}
3948
3949void tst_QPainter::gradientPixelFormat_data()
3950{
3951 QTest::addColumn<QImage::Format>(name: "format");
3952
3953 QTest::newRow(dataTag: "argb32") << QImage::Format_ARGB32;
3954 QTest::newRow(dataTag: "rgb32") << QImage::Format_RGB32;
3955 QTest::newRow(dataTag: "rgb888") << QImage::Format_RGB888;
3956 QTest::newRow(dataTag: "rgbx8888") << QImage::Format_RGBX8888;
3957 QTest::newRow(dataTag: "rgba8888") << QImage::Format_RGBA8888;
3958 QTest::newRow(dataTag: "rgba8888_pm") << QImage::Format_RGBA8888_Premultiplied;
3959 QTest::newRow(dataTag: "rgbx64") << QImage::Format_RGBX64;
3960 QTest::newRow(dataTag: "rgba64_pm") << QImage::Format_RGBA64_Premultiplied;
3961}
3962
3963void tst_QPainter::gradientPixelFormat()
3964{
3965 QFETCH(QImage::Format, format);
3966
3967 QImage a(8, 64, QImage::Format_ARGB32_Premultiplied);
3968 QImage b(8, 64, format);
3969
3970
3971 QGradientStops stops;
3972 stops << qMakePair(x: qreal(0.0), y: QColor(Qt::blue));
3973 stops << qMakePair(x: qreal(0.3), y: QColor(Qt::red));
3974 stops << qMakePair(x: qreal(0.6), y: QColor(Qt::green));
3975 stops << qMakePair(x: qreal(1.0), y: QColor(Qt::black));
3976
3977 a.fill(pixel: 0);
3978 b.fill(pixel: 0);
3979
3980 QLinearGradient gradient(QRectF(b.rect()).topLeft(), QRectF(b.rect()).bottomLeft());
3981 gradient.setStops(stops);
3982
3983 QPainter pa(&a);
3984 pa.fillRect(a.rect(), gradient);
3985 pa.end();
3986
3987 QPainter pb(&b);
3988 pb.fillRect(b.rect(), gradient);
3989 pb.end();
3990
3991 QCOMPARE(a, b.convertToFormat(QImage::Format_ARGB32_Premultiplied));
3992}
3993
3994void tst_QPainter::gradientInterpolation()
3995{
3996 QImage image(256, 8, QImage::Format_ARGB32_Premultiplied);
3997 QPainter painter;
3998
3999 QLinearGradient gradient(QRectF(image.rect()).topLeft(), QRectF(image.rect()).topRight());
4000 gradient.setColorAt(pos: 0.0, color: QColor(255, 0, 0, 0));
4001 gradient.setColorAt(pos: 1.0, color: Qt::blue);
4002
4003 image.fill(pixel: 0);
4004 painter.begin(&image);
4005 painter.fillRect(image.rect(), gradient);
4006 painter.end();
4007
4008 const QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(3));
4009
4010 for (int i = 0; i < 256; ++i) {
4011 QCOMPARE(qAlpha(line[i]), qBlue(line[i])); // bright blue
4012 QVERIFY(qAbs(qAlpha(line[i]) - i) < 3); // linear alpha
4013 QCOMPARE(qRed(line[i]), 0); // no red component
4014 QCOMPARE(qGreen(line[i]), 0); // no green component
4015 }
4016
4017 gradient.setInterpolationMode(QGradient::ComponentInterpolation);
4018
4019 image.fill(pixel: 0);
4020 painter.begin(&image);
4021 painter.fillRect(image.rect(), gradient);
4022 painter.end();
4023
4024 for (int i = 1; i < 256; ++i) {
4025 if (i < 128) {
4026 QVERIFY(qRed(line[i]) >= qBlue(line[i])); // red is dominant
4027 } else {
4028 QVERIFY(qRed(line[i]) <= qBlue(line[i])); // blue is dominant
4029 }
4030 QVERIFY((qRed(line[i]) - 0.5) * (qAlpha(line[i - 1]) - 0.5) <= (qRed(line[i - 1]) + 0.5) * (qAlpha(line[i]) + 0.5)); // decreasing red
4031 QVERIFY((qBlue(line[i]) + 0.5) * (qAlpha(line[i - 1]) + 0.5) >= (qBlue(line[i - 1]) - 0.5) * (qAlpha(line[i]) - 0.5)); // increasing blue
4032 QVERIFY(qAbs(qAlpha(line[i]) - i) < 3); // linear alpha
4033 QCOMPARE(qGreen(line[i]), 0); // no green component
4034 }
4035}
4036
4037#if QT_CONFIG(raster_64bit)
4038void tst_QPainter::linearGradientRgb30_data()
4039{
4040 QTest::addColumn<QColor>(name: "stop0");
4041 QTest::addColumn<QColor>(name: "stop1");
4042
4043 QTest::newRow(dataTag: "white->black") << QColor(Qt::white) << QColor(Qt::black);
4044 QTest::newRow(dataTag: "blue->black") << QColor(Qt::blue) << QColor(Qt::black);
4045 QTest::newRow(dataTag: "white->red") << QColor(Qt::white) << QColor(Qt::red);
4046}
4047
4048void tst_QPainter::linearGradientRgb30()
4049{
4050 QFETCH(QColor, stop0);
4051 QFETCH(QColor, stop1);
4052
4053 QLinearGradient gradient(0, 0, 1000, 1);
4054 gradient.setColorAt(pos: 0.0, color: stop0);
4055 gradient.setColorAt(pos: 1.0, color: stop1);
4056
4057 QImage image(1000, 1, QImage::Format_RGB30);
4058 QPainter painter(&image);
4059 painter.fillRect(image.rect(), gradient);
4060 painter.end();
4061
4062 for (int i = 1; i < 1000; ++i) {
4063 QColor p1 = image.pixelColor(x: i - 1, y: 0);
4064 QColor p2 = image.pixelColor(x: i, y: 0);
4065 QVERIFY(p1 != p2);
4066 QVERIFY(qGray(p1.rgb()) >= qGray(p2.rgb()));
4067 }
4068}
4069
4070void tst_QPainter::radialGradientRgb30_data()
4071{
4072 linearGradientRgb30_data();
4073}
4074
4075void tst_QPainter::radialGradientRgb30()
4076{
4077 QFETCH(QColor, stop0);
4078 QFETCH(QColor, stop1);
4079
4080 QRadialGradient gradient(0, 0, 1000);
4081 gradient.setColorAt(pos: 0.0, color: stop0);
4082 gradient.setColorAt(pos: 1.0, color: stop1);
4083
4084 QImage image(1000, 1, QImage::Format_A2BGR30_Premultiplied);
4085 QPainter painter(&image);
4086 painter.fillRect(image.rect(), gradient);
4087 painter.end();
4088
4089 for (int i = 1; i < 1000; ++i) {
4090 QColor p1 = image.pixelColor(x: i - 1, y: 0);
4091 QColor p2 = image.pixelColor(x: i, y: 0);
4092 QVERIFY(p1 != p2);
4093 QVERIFY(qGray(p1.rgb()) >= qGray(p2.rgb()));
4094 }
4095}
4096#endif
4097
4098void tst_QPainter::drawPolygon()
4099{
4100 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
4101
4102 QPainterPathStroker stroker;
4103 stroker.setWidth(1.5);
4104
4105 QPainterPath path;
4106 path.moveTo(x: 2, y: 34);
4107 path.lineTo(x: 34, y: 2);
4108
4109 QPolygonF poly = stroker.createStroke(path).toFillPolygon(matrix: QTransform());
4110
4111 img.fill(pixel: 0xffffffff);
4112 QPainter p(&img);
4113 p.setRenderHint(hint: QPainter::Antialiasing);
4114 p.setBrush(Qt::red);
4115 p.setPen(Qt::NoPen);
4116 p.drawPolygon(polygon: poly);
4117 p.translate(dx: 64, dy: 64);
4118 p.drawPolygon(polygon: poly);
4119 p.end();
4120
4121 QImage a = img.copy();
4122
4123 img.fill(pixel: 0xffffffff);
4124 p.begin(&img);
4125 p.setRenderHint(hint: QPainter::Antialiasing);
4126 p.setBrush(Qt::red);
4127 p.setPen(Qt::NoPen);
4128 p.translate(dx: 64, dy: 64);
4129 p.drawPolygon(polygon: poly);
4130 p.resetTransform();
4131 p.drawPolygon(polygon: poly);
4132 p.end();
4133
4134 QCOMPARE(a, img);
4135}
4136
4137void tst_QPainter::inactivePainter()
4138{
4139 // This test succeeds if it doesn't segfault.
4140
4141 QPainter p;
4142 QPainterPath path;
4143 QRegion region(QRect(20, 20, 60, 40));
4144 QPolygonF polygon(QVector<QPointF>() << QPointF(0, 0) << QPointF(12, 0) << QPointF(8, 6));
4145 path.addPolygon(polygon);
4146
4147 p.save();
4148 p.restore();
4149
4150 p.background();
4151 p.setBackground(QBrush(Qt::blue));
4152
4153 p.brush();
4154 p.setBrush(Qt::red);
4155 p.setBrush(Qt::NoBrush);
4156 p.setBrush(QBrush(Qt::white, Qt::DiagCrossPattern));
4157
4158 p.backgroundMode();
4159 p.setBackgroundMode(Qt::OpaqueMode);
4160
4161 p.boundingRect(rect: QRectF(0, 0, 100, 20), flags: Qt::AlignCenter, text: QLatin1String("Hello, World!"));
4162 p.boundingRect(rect: QRect(0, 0, 100, 20), flags: Qt::AlignCenter, text: QLatin1String("Hello, World!"));
4163
4164 p.brushOrigin();
4165 p.setBrushOrigin(QPointF(12, 34));
4166 p.setBrushOrigin(QPoint(12, 34));
4167
4168 p.clipPath();
4169 p.clipRegion();
4170 p.hasClipping();
4171 p.setClipPath(path);
4172 p.setClipRect(QRectF(42, 42, 42, 42));
4173 p.setClipRect(QRect(42, 42, 42, 42));
4174 p.setClipRegion(region);
4175 p.setClipping(true);
4176
4177#if QT_DEPRECATED_SINCE(5, 13)
4178QT_WARNING_PUSH
4179QT_WARNING_DISABLE_DEPRECATED
4180 p.combinedMatrix();
4181QT_WARNING_POP
4182#endif
4183 p.combinedTransform();
4184
4185 p.compositionMode();
4186 p.setCompositionMode(QPainter::CompositionMode_Plus);
4187
4188 p.device();
4189#if QT_DEPRECATED_SINCE(5, 13)
4190QT_WARNING_PUSH
4191QT_WARNING_DISABLE_DEPRECATED
4192 p.deviceMatrix();
4193QT_WARNING_POP
4194#endif
4195 p.deviceTransform();
4196
4197 p.font();
4198 p.setFont(QFont(QLatin1String("Times"), 24));
4199
4200 p.fontInfo();
4201 p.fontMetrics();
4202
4203 p.layoutDirection();
4204 p.setLayoutDirection(Qt::RightToLeft);
4205
4206 p.opacity();
4207 p.setOpacity(0.75);
4208
4209 p.pen();
4210 p.setPen(QPen(Qt::red));
4211 p.setPen(Qt::green);
4212 p.setPen(Qt::NoPen);
4213
4214 p.renderHints();
4215 p.setRenderHint(hint: QPainter::Antialiasing, on: true);
4216 p.setRenderHints(hints: QPainter::Antialiasing | QPainter::SmoothPixmapTransform, on: false);
4217
4218#if QT_DEPRECATED_SINCE(5, 13)
4219QT_WARNING_PUSH
4220QT_WARNING_DISABLE_DEPRECATED
4221 p.resetMatrix();
4222QT_WARNING_POP
4223#endif
4224 p.resetTransform();
4225 p.rotate(a: 1);
4226 p.scale(sx: 2, sy: 2);
4227 p.shear(sh: -1, sv: 1);
4228 p.translate(dx: 3, dy: 14);
4229
4230 p.viewTransformEnabled();
4231 p.setViewTransformEnabled(true);
4232
4233 p.viewport();
4234 p.setViewport(QRect(10, 10, 620, 460));
4235
4236 p.window();
4237 p.setWindow(QRect(10, 10, 620, 460));
4238
4239#if QT_DEPRECATED_SINCE(5, 13)
4240QT_WARNING_PUSH
4241QT_WARNING_DISABLE_DEPRECATED
4242 p.worldMatrix();
4243 p.setWorldMatrix(matrix: QMatrix().translate(dx: 43, dy: 21), combine: true);
4244QT_WARNING_POP
4245#endif
4246 p.setWorldMatrixEnabled(true);
4247
4248 p.transform();
4249 p.setTransform(transform: QTransform().translate(dx: 12, dy: 34), combine: true);
4250
4251 p.worldTransform();
4252 p.setWorldTransform(matrix: QTransform().scale(sx: 0.5, sy: 0.5), combine: true);
4253}
4254
4255bool testCompositionMode(int src, int dst, int expected, QPainter::CompositionMode op, qreal opacity = 1.0)
4256{
4257 // The test image needs to be large enough to test SIMD code
4258 const QSize imageSize(100, 100);
4259
4260 QImage actual(imageSize, QImage::Format_ARGB32_Premultiplied);
4261 actual.fill(pixel: QColor(dst, dst, dst).rgb());
4262
4263 QPainter p(&actual);
4264 p.setCompositionMode(op);
4265 p.setOpacity(opacity);
4266 p.fillRect(QRect(QPoint(), imageSize), color: QColor(src, src, src));
4267 p.end();
4268
4269 if (qRed(rgb: actual.pixel(x: 0, y: 0)) != expected) {
4270 qDebug(msg: "Fail: mode %d, src[%d] dst [%d] actual [%d] expected [%d]", op,
4271 src, dst, qRed(rgb: actual.pixel(x: 0, y: 0)), expected);
4272 return false;
4273 } else {
4274 QImage refImage(imageSize, QImage::Format_ARGB32_Premultiplied);
4275 refImage.fill(pixel: QColor(expected, expected, expected).rgb());
4276 return actual == refImage;
4277 }
4278}
4279
4280void tst_QPainter::extendedBlendModes()
4281{
4282 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Plus));
4283 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Plus));
4284 QVERIFY(testCompositionMode(127, 128, 255, QPainter::CompositionMode_Plus));
4285 QVERIFY(testCompositionMode(127, 0, 127, QPainter::CompositionMode_Plus));
4286 QVERIFY(testCompositionMode( 0, 127, 127, QPainter::CompositionMode_Plus));
4287 QVERIFY(testCompositionMode(255, 0, 255, QPainter::CompositionMode_Plus));
4288 QVERIFY(testCompositionMode( 0, 255, 255, QPainter::CompositionMode_Plus));
4289 QVERIFY(testCompositionMode(128, 128, 255, QPainter::CompositionMode_Plus));
4290
4291 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Plus, 0.3));
4292 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Plus, 0.3));
4293 QVERIFY(testCompositionMode(126, 128, 165, QPainter::CompositionMode_Plus, 0.3));
4294 QVERIFY(testCompositionMode(127, 0, 38, QPainter::CompositionMode_Plus, 0.3));
4295 QVERIFY(testCompositionMode( 0, 127, 127, QPainter::CompositionMode_Plus, 0.3));
4296 QVERIFY(testCompositionMode(255, 0, 76, QPainter::CompositionMode_Plus, 0.3));
4297 QVERIFY(testCompositionMode( 0, 255, 255, QPainter::CompositionMode_Plus, 0.3));
4298 QVERIFY(testCompositionMode(128, 128, 166, QPainter::CompositionMode_Plus, 0.3));
4299 QVERIFY(testCompositionMode(186, 200, 255, QPainter::CompositionMode_Plus, 0.3));
4300
4301 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Multiply));
4302 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Multiply));
4303 QVERIFY(testCompositionMode(127, 255, 127, QPainter::CompositionMode_Multiply));
4304 QVERIFY(testCompositionMode(255, 127, 127, QPainter::CompositionMode_Multiply));
4305 QVERIFY(testCompositionMode( 63, 255, 63, QPainter::CompositionMode_Multiply));
4306 QVERIFY(testCompositionMode(255, 63, 63, QPainter::CompositionMode_Multiply));
4307 QVERIFY(testCompositionMode(127, 127, 63, QPainter::CompositionMode_Multiply));
4308
4309 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Screen));
4310 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Screen));
4311 QVERIFY(testCompositionMode( 63, 255, 255, QPainter::CompositionMode_Screen));
4312 QVERIFY(testCompositionMode(255, 63, 255, QPainter::CompositionMode_Screen));
4313 QVERIFY(testCompositionMode( 63, 0, 63, QPainter::CompositionMode_Screen));
4314 QVERIFY(testCompositionMode( 0, 63, 63, QPainter::CompositionMode_Screen));
4315 QVERIFY(testCompositionMode(127, 127, 191, QPainter::CompositionMode_Screen));
4316
4317 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Overlay));
4318 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Overlay));
4319 QVERIFY(testCompositionMode( 63, 63, 31, QPainter::CompositionMode_Overlay));
4320 QVERIFY(testCompositionMode( 63, 255, 255, QPainter::CompositionMode_Overlay));
4321
4322 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Darken));
4323 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Darken));
4324 QVERIFY(testCompositionMode( 63, 63, 63, QPainter::CompositionMode_Darken));
4325 QVERIFY(testCompositionMode( 63, 255, 63, QPainter::CompositionMode_Darken));
4326 QVERIFY(testCompositionMode(255, 63, 63, QPainter::CompositionMode_Darken));
4327 QVERIFY(testCompositionMode( 63, 127, 63, QPainter::CompositionMode_Darken));
4328 QVERIFY(testCompositionMode(127, 63, 63, QPainter::CompositionMode_Darken));
4329
4330 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_Lighten));
4331 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Lighten));
4332 QVERIFY(testCompositionMode( 63, 63, 63, QPainter::CompositionMode_Lighten));
4333 QVERIFY(testCompositionMode( 63, 255, 255, QPainter::CompositionMode_Lighten));
4334 QVERIFY(testCompositionMode(255, 63, 255, QPainter::CompositionMode_Lighten));
4335 QVERIFY(testCompositionMode( 63, 127, 127, QPainter::CompositionMode_Lighten));
4336 QVERIFY(testCompositionMode(127, 63, 127, QPainter::CompositionMode_Lighten));
4337
4338 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_ColorDodge));
4339 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_ColorDodge));
4340 QVERIFY(testCompositionMode( 63, 127, 169, QPainter::CompositionMode_ColorDodge));
4341 QVERIFY(testCompositionMode(191, 127, 255, QPainter::CompositionMode_ColorDodge));
4342 QVERIFY(testCompositionMode(127, 191, 255, QPainter::CompositionMode_ColorDodge));
4343
4344 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_ColorBurn));
4345 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_ColorBurn));
4346 QVERIFY(testCompositionMode(127, 127, 0, QPainter::CompositionMode_ColorBurn));
4347 QVERIFY(testCompositionMode(128, 128, 2, QPainter::CompositionMode_ColorBurn));
4348 QVERIFY(testCompositionMode(191, 127, 84, QPainter::CompositionMode_ColorBurn));
4349
4350 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_HardLight));
4351 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_HardLight));
4352 QVERIFY(testCompositionMode(127, 127, 127, QPainter::CompositionMode_HardLight));
4353 QVERIFY(testCompositionMode( 63, 63, 31, QPainter::CompositionMode_HardLight));
4354 QVERIFY(testCompositionMode(127, 63, 63, QPainter::CompositionMode_HardLight));
4355
4356 QVERIFY(testCompositionMode(255, 255, 255, QPainter::CompositionMode_SoftLight));
4357 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_SoftLight));
4358 QVERIFY(testCompositionMode(127, 127, 126, QPainter::CompositionMode_SoftLight));
4359 QVERIFY(testCompositionMode( 63, 63, 39, QPainter::CompositionMode_SoftLight));
4360 QVERIFY(testCompositionMode(127, 63, 62, QPainter::CompositionMode_SoftLight));
4361
4362 QVERIFY(testCompositionMode(255, 255, 0, QPainter::CompositionMode_Difference));
4363 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Difference));
4364 QVERIFY(testCompositionMode(255, 0, 255, QPainter::CompositionMode_Difference));
4365 QVERIFY(testCompositionMode(127, 127, 0, QPainter::CompositionMode_Difference));
4366 QVERIFY(testCompositionMode(127, 128, 1, QPainter::CompositionMode_Difference));
4367 QVERIFY(testCompositionMode(127, 63, 64, QPainter::CompositionMode_Difference));
4368 QVERIFY(testCompositionMode( 0, 127, 127, QPainter::CompositionMode_Difference));
4369
4370 QVERIFY(testCompositionMode(255, 255, 0, QPainter::CompositionMode_Exclusion));
4371 QVERIFY(testCompositionMode( 0, 0, 0, QPainter::CompositionMode_Exclusion));
4372 QVERIFY(testCompositionMode(255, 0, 255, QPainter::CompositionMode_Exclusion));
4373 QVERIFY(testCompositionMode(127, 127, 127, QPainter::CompositionMode_Exclusion));
4374 QVERIFY(testCompositionMode( 63, 127, 127, QPainter::CompositionMode_Exclusion));
4375 QVERIFY(testCompositionMode( 63, 63, 95, QPainter::CompositionMode_Exclusion));
4376 QVERIFY(testCompositionMode(191, 191, 96, QPainter::CompositionMode_Exclusion));
4377}
4378
4379void tst_QPainter::zeroOpacity()
4380{
4381 QImage source(1, 1, QImage::Format_ARGB32_Premultiplied);
4382 source.fill(pixel: 0xffffffff);
4383
4384 QImage target(1, 1, QImage::Format_RGB32);
4385 target.fill(pixel: 0xff000000);
4386
4387 QPainter p(&target);
4388 p.setOpacity(0.0);
4389 p.drawImage(x: 0, y: 0, image: source);
4390 p.end();
4391
4392 QCOMPARE(target.pixel(0, 0), 0xff000000);
4393}
4394
4395void tst_QPainter::clippingBug()
4396{
4397 QImage img(32, 32, QImage::Format_ARGB32_Premultiplied);
4398 img.fill(pixel: 0);
4399
4400 QImage expected = img;
4401 QPainter p(&expected);
4402 p.fillRect(x: 1, y: 1, w: 30, h: 30, c: Qt::red);
4403 p.end();
4404
4405 QPainterPath path;
4406 path.addRect(x: 1, y: 1, w: 30, h: 30);
4407 path.addRect(x: 1, y: 1, w: 30, h: 30);
4408 path.addRect(x: 1, y: 1, w: 30, h: 30);
4409
4410 p.begin(&img);
4411 p.setClipPath(path);
4412 p.fillRect(x: 0, y: 0, w: 32, h: 32, c: Qt::red);
4413 p.end();
4414
4415 QCOMPARE(img, expected);
4416}
4417
4418void tst_QPainter::emptyClip()
4419{
4420 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
4421 QPainter p(&img);
4422 p.setRenderHints(hints: QPainter::Antialiasing);
4423 p.setClipRect(x: 0, y: 32, w: 64, h: 0);
4424 p.fillRect(x: 0, y: 0, w: 64, h: 64, c: Qt::white);
4425
4426 QPainterPath path;
4427 path.lineTo(x: 64, y: 0);
4428 path.lineTo(x: 64, y: 64);
4429 path.lineTo(x: 40, y: 64);
4430 path.lineTo(x: 40, y: 80);
4431 path.lineTo(x: 0, y: 80);
4432
4433 p.fillPath(path, brush: Qt::green);
4434}
4435
4436void tst_QPainter::drawImage_1x1()
4437{
4438 QImage source(1, 1, QImage::Format_ARGB32_Premultiplied);
4439 source.fill(pixel: 0xffffffff);
4440
4441 QImage img(32, 32, QImage::Format_ARGB32_Premultiplied);
4442 img.fill(pixel: 0xff000000);
4443 QPainter p(&img);
4444 p.drawImage(r: QRectF(0.9, 0.9, 32, 32), image: source);
4445 p.end();
4446
4447 QImage expected = img;
4448 expected.fill(pixel: 0xff000000);
4449 p.begin(&expected);
4450 p.fillRect(x: 1, y: 1, w: 31, h: 31, c: Qt::white);
4451 p.end();
4452
4453 QCOMPARE(img, expected);
4454}
4455
4456void tst_QPainter::taskQT4444_dontOverflowDashOffset()
4457{
4458 QPainter p;
4459
4460 QPen pen;
4461 pen.setWidth(2);
4462 pen.setStyle(Qt::DashDotLine);
4463
4464 QPointF point[4];
4465 point[0] = QPointF(182.50868749707968,347.78457234212630);
4466 point[1] = QPointF(182.50868749707968,107.22501998401277);
4467 point[2] = QPointF(182.50868749707968,107.22501998401277);
4468 point[3] = QPointF(520.46600762283651,107.22501998401277);
4469
4470 QImage crashImage(QSize(1000, 120), QImage::Format_ARGB32_Premultiplied);
4471 p.begin(&crashImage);
4472 p.setPen(pen);
4473 p.drawLines(pointPairs: point, lineCount: 2);
4474 p.end();
4475
4476 QVERIFY(true); // Don't crash
4477}
4478
4479void tst_QPainter::painterBegin()
4480{
4481 QImage nullImage;
4482 QImage indexed8Image(16, 16, QImage::Format_Indexed8);
4483 QImage rgb32Image(16, 16, QImage::Format_RGB32);
4484 QImage argb32Image(16, 16, QImage::Format_ARGB32_Premultiplied);
4485
4486 QPainter p;
4487
4488 // Painting on null image should fail.
4489 QVERIFY(!p.begin(&nullImage));
4490
4491 // Check that the painter is not messed up by using it on another image.
4492 QVERIFY(p.begin(&rgb32Image));
4493 QVERIFY(p.end());
4494
4495 // If painting on indexed8 image fails, the painter state should still be OK.
4496 if (p.begin(&indexed8Image))
4497 QVERIFY(p.end());
4498 QVERIFY(p.begin(&rgb32Image));
4499 QVERIFY(p.end());
4500
4501 // Try opening a painter on the two different images.
4502 QVERIFY(p.begin(&rgb32Image));
4503 QVERIFY(!p.begin(&argb32Image));
4504 QVERIFY(p.end());
4505
4506 // Try opening two painters on the same image.
4507 QVERIFY(p.begin(&rgb32Image));
4508 QPainter q;
4509 QVERIFY(!q.begin(&rgb32Image));
4510 QVERIFY(!q.end());
4511 QVERIFY(p.end());
4512
4513 // Try ending an inactive painter.
4514 QVERIFY(!p.end());
4515}
4516
4517void tst_QPainter::setPenColor(QPainter& p)
4518{
4519 p.setPen(Qt::NoPen);
4520
4521 // Setting color, then style
4522 // Should work even though the pen is "NoPen with color", temporarily.
4523 QPen newPen(p.pen());
4524 newPen.setColor(Qt::red);
4525 QCOMPARE(p.pen().style(), newPen.style());
4526 QCOMPARE(p.pen().style(), Qt::NoPen);
4527 p.setPen(newPen);
4528
4529 QCOMPARE(p.pen().color().name(), QString("#ff0000"));
4530
4531 QPen newPen2(p.pen());
4532 newPen2.setStyle(Qt::SolidLine);
4533 p.setPen(newPen2);
4534
4535 QCOMPARE(p.pen().color().name(), QString("#ff0000"));
4536}
4537
4538void tst_QPainter::setPenColorOnImage()
4539{
4540 QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
4541 QPainter p(&img);
4542 setPenColor(p);
4543}
4544
4545void tst_QPainter::setPenColorOnPixmap()
4546{
4547 QPixmap pix(10, 10);
4548 QPainter p(&pix);
4549 setPenColor(p);
4550}
4551
4552#ifndef QT_NO_WIDGETS
4553class TestProxy : public QGraphicsProxyWidget
4554{
4555public:
4556 TestProxy() : QGraphicsProxyWidget() {}
4557 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
4558 {
4559 QGraphicsProxyWidget::paint(painter, option, widget);
4560 deviceTransform = painter->deviceTransform();
4561 }
4562 QTransform deviceTransform;
4563};
4564
4565class TestWidget : public QWidget
4566{
4567Q_OBJECT
4568public:
4569 TestWidget() : QWidget(), painted(false) {}
4570 void paintEvent(QPaintEvent *)
4571 {
4572 QPainter p(this);
4573 deviceTransform = p.deviceTransform();
4574 worldTransform = p.worldTransform();
4575 painted = true;
4576 }
4577 QTransform deviceTransform;
4578 QTransform worldTransform;
4579 bool painted;
4580};
4581
4582void tst_QPainter::QTBUG5939_attachPainterPrivate()
4583{
4584 QWidget *w = new QWidget();
4585 QGraphicsScene *scene = new QGraphicsScene();
4586 QGraphicsView *view = new QGraphicsView(scene, w);
4587 view->move(ax: 50 ,ay: 50);
4588 TestProxy *proxy = new TestProxy();
4589 TestWidget *widget = new TestWidget();
4590 proxy->setWidget(widget);
4591 scene->addItem(item: proxy);
4592 proxy->setTransform(matrix: QTransform().rotate(a: 45));
4593 w->resize(scene->sceneRect().size().toSize());
4594
4595 w->show();
4596 QTRY_VERIFY(widget->painted);
4597
4598 QVERIFY(widget->worldTransform.isIdentity());
4599 QCOMPARE(widget->deviceTransform, proxy->deviceTransform);
4600}
4601#endif
4602
4603void tst_QPainter::clipBoundingRect()
4604{
4605 QPixmap pix(500, 500);
4606
4607 QPainter p(&pix);
4608
4609 // Test a basic rectangle
4610 p.setClipRect(x: 100, y: 100, w: 200, h: 100);
4611 QVERIFY(p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4612 QVERIFY(!p.clipBoundingRect().contains(QRectF(50, 50, 300, 200)));
4613 p.setClipRect(x: 120, y: 120, w: 20, h: 20, op: Qt::IntersectClip);
4614 QVERIFY(p.clipBoundingRect().contains(QRect(120, 120, 20, 20)));
4615 QVERIFY(!p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4616
4617 // Test a basic float rectangle
4618 p.setClipRect(QRectF(100, 100, 200, 100));
4619 QVERIFY(p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4620 QVERIFY(!p.clipBoundingRect().contains(QRectF(50, 50, 300, 200)));
4621 p.setClipRect(QRectF(120, 120, 20, 20), op: Qt::IntersectClip);
4622 QVERIFY(p.clipBoundingRect().contains(QRect(120, 120, 20, 20)));
4623 QVERIFY(!p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4624
4625 // Test a basic path + region
4626 QPainterPath path;
4627 path.addRect(x: 100, y: 100, w: 200, h: 100);
4628 p.setClipPath(path);
4629 QVERIFY(p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4630 QVERIFY(!p.clipBoundingRect().contains(QRectF(50, 50, 300, 200)));
4631 p.setClipRegion(QRegion(120, 120, 20, 20), op: Qt::IntersectClip);
4632 QVERIFY(p.clipBoundingRect().contains(QRect(120, 120, 20, 20)));
4633 QVERIFY(!p.clipBoundingRect().contains(QRectF(100, 100, 200, 100)));
4634
4635 p.setClipRect(x: 0, y: 0, w: 500, h: 500);
4636 p.translate(dx: 250, dy: 250);
4637 for (int i=0; i<360; ++i) {
4638 p.rotate(a: 1);
4639 p.setClipRect(x: -100, y: -100, w: 200, h: 200, op: Qt::IntersectClip);
4640 }
4641 QVERIFY(p.clipBoundingRect().contains(QRectF(-100, -100, 200, 200)));
4642 QVERIFY(!p.clipBoundingRect().contains(QRectF(-250, -250, 500, 500)));
4643
4644}
4645
4646void tst_QPainter::transformedClip()
4647{
4648 QImage img(8, 4, QImage::Format_ARGB32_Premultiplied);
4649 QImage img2(img.size(), img.format());
4650 QRect clip(0, 0, 2, 1);
4651 QTransform xf;
4652 xf.translate(dx: 0.2, dy: 0);
4653 xf.scale(sx: 2.2, sy: 1);
4654 // setClipRect(QRectF)
4655 {
4656 img.fill(color: Qt::green);
4657 QPainter p(&img);
4658 p.setTransform(transform: xf);
4659 p.setClipRect(QRectF(clip));
4660 p.fillRect(r: img.rect(), c: Qt::white);
4661 }
4662 // setClipRect(QRect)
4663 {
4664 img2.fill(color: Qt::green);
4665 QPainter p(&img2);
4666 p.setTransform(transform: xf);
4667 p.setClipRect(clip);
4668 p.fillRect(r: img2.rect(), c: Qt::white);
4669 QCOMPARE(img, img2);
4670 }
4671 // setClipRegion
4672 {
4673 img2.fill(color: Qt::green);
4674 QPainter p(&img2);
4675 p.setTransform(transform: xf);
4676 p.setClipRegion(QRegion(clip) + QRect(0, 3, 1, 1)); // dummy extra rect to avoid single-rect codepath
4677 p.fillRect(r: img2.rect(), c: Qt::white);
4678 QCOMPARE(img.copy(0, 0, 8, 2), img2.copy(0, 0, 8, 2));
4679 }
4680 // setClipPath
4681 {
4682 img2.fill(color: Qt::green);
4683 QPainter p(&img2);
4684 p.setTransform(transform: xf);
4685 QPainterPath path;
4686 path.addRect(rect: clip);
4687 p.setClipPath(path);
4688 p.fillRect(r: img2.rect(), c: Qt::white);
4689 QCOMPARE(img, img2);
4690 }
4691}
4692
4693#if defined(Q_OS_MAC)
4694// Only Mac supports sub pixel positions in raster engine currently
4695void tst_QPainter::drawText_subPixelPositionsInRaster_qtbug5053()
4696{
4697 QFontMetricsF fm(qApp->font());
4698
4699 QImage baseLine(fm.horizontalAdvance(QChar::fromLatin1('e')), fm.height(), QImage::Format_RGB32);
4700 baseLine.fill(Qt::white);
4701 {
4702 QPainter p(&baseLine);
4703 p.setRenderHint(QPainter::Qt4CompatiblePainting);
4704 p.drawText(0, fm.ascent(), QString::fromLatin1("e"));
4705 }
4706
4707 bool foundDifferentRasterization = false;
4708 for (int i=1; i<12; ++i) {
4709 QImage comparison(baseLine.size(), QImage::Format_RGB32);
4710 comparison.fill(Qt::white);
4711
4712 {
4713 QPainter p(&comparison);
4714 p.setRenderHint(QPainter::Qt4CompatiblePainting);
4715 p.drawText(QPointF(i / 12.0, fm.ascent()), QString::fromLatin1("e"));
4716 }
4717
4718 if (comparison != baseLine) {
4719 foundDifferentRasterization = true;
4720 break;
4721 }
4722 }
4723
4724 QVERIFY(foundDifferentRasterization);
4725}
4726#endif
4727
4728void tst_QPainter::drawPointScaled()
4729{
4730 QImage image(32, 32, QImage::Format_RGB32);
4731 image.fill(pixel: 0xffffffff);
4732
4733 QPainter p(&image);
4734
4735 p.scale(sx: 0.1, sy: 0.1);
4736
4737 QPen pen;
4738 pen.setWidth(1000);
4739 pen.setColor(Qt::red);
4740
4741 p.setPen(pen);
4742 p.drawPoint(x: 0, y: 0);
4743 p.end();
4744
4745 QCOMPARE(image.pixel(16, 16), 0xffff0000);
4746}
4747
4748class GradientProducer : public QThread
4749{
4750protected:
4751 void run();
4752};
4753
4754void GradientProducer::run()
4755{
4756 QImage image(1, 1, QImage::Format_RGB32);
4757 QPainter p(&image);
4758
4759 for (int i = 0; i < 1000; ++i) {
4760 QLinearGradient g;
4761 g.setColorAt(pos: 0, color: QColor(i % 256, 0, 0));
4762 g.setColorAt(pos: 1, color: Qt::white);
4763
4764 p.fillRect(image.rect(), g);
4765 }
4766}
4767
4768void tst_QPainter::QTBUG14614_gradientCacheRaceCondition()
4769{
4770 const int threadCount = 16;
4771 GradientProducer producers[threadCount];
4772 for (int i = 0; i < threadCount; ++i)
4773 producers[i].start();
4774 for (int i = 0; i < threadCount; ++i)
4775 producers[i].wait();
4776}
4777
4778void tst_QPainter::drawTextOpacity()
4779{
4780 QImage image(32, 32, QImage::Format_RGB32);
4781 image.fill(pixel: 0xffffffff);
4782
4783 QPainter p(&image);
4784 p.setPen(QColor("#6F6F6F"));
4785 p.setOpacity(0.5);
4786 p.drawText(x: 5, y: 30, s: QLatin1String("Qt"));
4787 p.end();
4788
4789 QImage copy = image;
4790 image.fill(pixel: 0xffffffff);
4791
4792 p.begin(&image);
4793 p.setPen(QColor("#6F6F6F"));
4794 p.drawLine(x1: -10, y1: -10, x2: -1, y2: -1);
4795 p.setOpacity(0.5);
4796 p.drawText(x: 5, y: 30, s: QLatin1String("Qt"));
4797 p.end();
4798
4799 QCOMPARE(image, copy);
4800}
4801
4802void tst_QPainter::QTBUG17053_zeroDashPattern()
4803{
4804 QImage image(32, 32, QImage::Format_RGB32);
4805 image.fill(pixel: 0xffffffff);
4806
4807 QImage original = image;
4808
4809 QVector<qreal> pattern;
4810 pattern << qreal(0) << qreal(0);
4811
4812 QPainter p(&image);
4813 QPen pen(Qt::black, 2.0);
4814 pen.setDashPattern(pattern);
4815
4816 p.setPen(pen);
4817 p.drawLine(x1: 0, y1: 0, x2: image.width(), y2: image.height());
4818
4819 QCOMPARE(image, original);
4820}
4821
4822void tst_QPainter::QTBUG38781_NoBrushAndQBitmap()
4823{
4824 QBitmap bitmap(10, 10);
4825 bitmap.fill(fillColor: Qt::color0);
4826 QPainter p(&bitmap);
4827 p.setPen(Qt::color1);
4828 p.drawLine(x1: 0, y1: 1, x2: 9, y2: 1); // at horizontal line at y=1
4829 p.setBrush(Qt::NoBrush);
4830 p.drawRect(x: 0, y: 0, w: 9, h: 9); // a rect all around
4831
4832 QRgb white = qRgb(r: 0xff, g: 0xff, b: 0xff);
4833 QRgb black = qRgb(r: 0, g: 0, b: 0);
4834 QImage image = bitmap.toImage();
4835 QCOMPARE(image.pixel(0, 0), black);
4836 QCOMPARE(image.pixel(5, 5), white);
4837
4838 // Check that the rect didn't overwrite the line
4839 QCOMPARE(image.pixel(5, 1), black);
4840}
4841
4842class TextDrawerThread : public QThread
4843{
4844public:
4845 void run();
4846 QImage rendering;
4847};
4848
4849void TextDrawerThread::run()
4850{
4851 rendering = QImage(100, 100, QImage::Format_ARGB32_Premultiplied);
4852 rendering.fill(pixel: 0);
4853 QPainter p(&rendering);
4854 p.fillRect(x: 10, y: 10, w: 100, h: 100, c: Qt::blue);
4855 p.setPen(Qt::green);
4856 p.drawText(x: 20, y: 20, s: "some text");
4857 p.end();
4858}
4859
4860void tst_QPainter::drawTextOutsideGuiThread()
4861{
4862 QImage referenceRendering(100, 100, QImage::Format_ARGB32_Premultiplied);
4863 referenceRendering.fill(pixel: 0);
4864 QPainter p(&referenceRendering);
4865 p.fillRect(x: 10, y: 10, w: 100, h: 100, c: Qt::blue);
4866 p.setPen(Qt::green);
4867 p.drawText(x: 20, y: 20, s: "some text");
4868 p.end();
4869
4870 TextDrawerThread t;
4871 t.start();
4872 t.wait();
4873
4874 QCOMPARE(referenceRendering, t.rendering);
4875}
4876
4877void tst_QPainter::drawTextWithComplexBrush()
4878{
4879 QImage texture(10, 10, QImage::Format_ARGB32_Premultiplied);
4880 texture.fill(color: Qt::red);
4881
4882 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
4883 image.fill(color: Qt::white);
4884 QPainter p(&image);
4885 QFont f = p.font();
4886 f.setPixelSize(70);
4887 p.setFont(f);
4888
4889 QBrush brush(Qt::white);
4890 brush.setTextureImage(texture);
4891 p.setPen(QPen(brush, 2));
4892
4893 p.drawText(x: 10, y: 10, s: "Hello World");
4894
4895 int paintedPixels = getPaintedPixels(image, background: Qt::white);
4896 QVERIFY(paintedPixels > 0);
4897}
4898
4899void tst_QPainter::QTBUG26013_squareCapStroke()
4900{
4901 QImage image(4, 4, QImage::Format_RGB32);
4902
4903 QPainter p(&image);
4904 p.setPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
4905
4906 for (int i = 0; i < 3; ++i) {
4907 qreal d = i / 3.0;
4908
4909 image.fill(pixel: 0xffffffff);
4910
4911 p.drawLine(l: QLineF(0, d, 0, d + 2));
4912 p.drawLine(l: QLineF(1, d, 3, d));
4913
4914 // ensure that a horizontal line and a vertical line with square cap round up (downwards) at the same time
4915 QCOMPARE(image.pixel(0, 0), image.pixel(1, 0));
4916
4917 image.fill(pixel: 0xffffffff);
4918
4919 p.drawLine(l: QLineF(d, 0, d + 2, 0));
4920 p.drawLine(l: QLineF(d, 1, d, 3));
4921
4922 // ensure that a vertical line and a horizontal line with square cap round up (to the right) at the same time
4923 QCOMPARE(image.pixel(0, 0), image.pixel(0, 1));
4924 }
4925}
4926
4927void tst_QPainter::QTBUG25153_drawLine()
4928{
4929 QImage image(2, 2, QImage::Format_RGB32);
4930
4931 QVector<Qt::PenCapStyle> styles;
4932 styles << Qt::FlatCap << Qt::SquareCap << Qt::RoundCap;
4933
4934 foreach (Qt::PenCapStyle style, styles) {
4935 image.fill(pixel: 0xffffffff);
4936 QPainter p(&image);
4937 p.setPen(QPen(Qt::black, 0, Qt::SolidLine, style));
4938 p.drawLine(l: QLineF(0, 0, 0, 0));
4939 p.end();
4940
4941 QCOMPARE(image.pixel(0, 0), 0xff000000);
4942 QCOMPARE(image.pixel(0, 1), 0xffffffff);
4943 QCOMPARE(image.pixel(1, 0), 0xffffffff);
4944 }
4945}
4946
4947void tst_QPainter::blendARGBonRGB_data()
4948{
4949 QTest::addColumn<QImage::Format>(name: "dst_format");
4950 QTest::addColumn<QImage::Format>(name: "src_format");
4951 QTest::addColumn<QPainter::CompositionMode>(name: "compositionMode");
4952 QTest::addColumn<QRgb>(name: "color");
4953 QTest::addColumn<int>(name: "expected_red");
4954
4955 QTest::newRow(dataTag: "ARGB over ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32
4956 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 127 ;
4957 QTest::newRow(dataTag: "ARGB_PM over ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied
4958 << QPainter::CompositionMode_SourceOver<< qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4959 QTest::newRow(dataTag: "ARGB source ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32
4960 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 255;
4961 QTest::newRow(dataTag: "ARGB_PM source ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied
4962 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 255;
4963 QTest::newRow(dataTag: "ARGB source-in ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32
4964 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 255 ;
4965 QTest::newRow(dataTag: "ARGB_PM source-in ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied
4966 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 255;
4967 QTest::newRow(dataTag: "ARGB over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32
4968 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4969 QTest::newRow(dataTag: "ARGB_PM over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied
4970 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4971 QTest::newRow(dataTag: "ARGB source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32
4972 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 255;
4973 QTest::newRow(dataTag: "ARGB_PM source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied
4974 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 255;
4975 QTest::newRow(dataTag: "ARGB source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32
4976 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 255;
4977 QTest::newRow(dataTag: "ARGB_PM source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied
4978 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 255;
4979 // Only ARGB32 and RGBA8888 does inverse premultiply, on the rest over and source gives similar results:
4980 QTest::newRow(dataTag: "ARGB over RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32
4981 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4982 QTest::newRow(dataTag: "ARGB_PM over RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32_Premultiplied
4983 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4984 QTest::newRow(dataTag: "ARGB source RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32
4985 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4986 QTest::newRow(dataTag: "ARGB_PM source RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32_Premultiplied
4987 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4988 QTest::newRow(dataTag: "ARGB source-in RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32
4989 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4990 QTest::newRow(dataTag: "ARGB_PM source-in RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32_Premultiplied
4991 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4992 QTest::newRow(dataTag: "ARGB over RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32
4993 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4994 QTest::newRow(dataTag: "ARGB_PM over RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32_Premultiplied
4995 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
4996 QTest::newRow(dataTag: "ARGB source RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32
4997 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
4998 QTest::newRow(dataTag: "ARGB_PM source RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32_Premultiplied
4999 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
5000 QTest::newRow(dataTag: "ARGB source-in RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32
5001 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
5002 QTest::newRow(dataTag: "ARGB_PM source-in RGB888") << QImage::Format_RGB888 << QImage::Format_ARGB32_Premultiplied
5003 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
5004 QTest::newRow(dataTag: "ARGB over RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32
5005 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
5006 QTest::newRow(dataTag: "ARGB_PM over RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32_Premultiplied
5007 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
5008 QTest::newRow(dataTag: "ARGB source RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32
5009 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
5010 QTest::newRow(dataTag: "ARGB_PM source RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32_Premultiplied
5011 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
5012 QTest::newRow(dataTag: "ARGB source-in RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32
5013 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 127;
5014 QTest::newRow(dataTag: "ARGB_PM source-in RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32_Premultiplied
5015 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 127;
5016 QTest::newRow(dataTag: "ARGB over RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32
5017 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 123;
5018 QTest::newRow(dataTag: "ARGB_PM over RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32_Premultiplied
5019 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 123;
5020 QTest::newRow(dataTag: "ARGB source RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32
5021 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 123;
5022 QTest::newRow(dataTag: "ARGB_PM source RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32_Premultiplied
5023 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 123;
5024 QTest::newRow(dataTag: "ARGB source-in RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32
5025 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 123;
5026 QTest::newRow(dataTag: "ARGB_PM source-in RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32_Premultiplied
5027 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 123;
5028 QTest::newRow(dataTag: "ARGB over RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32
5029 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 127) << 125;
5030 QTest::newRow(dataTag: "ARGB_PM over RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32_Premultiplied
5031 << QPainter::CompositionMode_SourceOver << qRgba(r: 127, g: 0, b: 0, a: 127) << 125;
5032 QTest::newRow(dataTag: "ARGB source RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32
5033 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 127) << 125;
5034 QTest::newRow(dataTag: "ARGB_PM source RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32_Premultiplied
5035 << QPainter::CompositionMode_Source << qRgba(r: 127, g: 0, b: 0, a: 127) << 125;
5036 QTest::newRow(dataTag: "ARGB source-in RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32
5037 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 127) << 125;
5038 QTest::newRow(dataTag: "ARGB_PM source-in RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32_Premultiplied
5039 << QPainter::CompositionMode_SourceIn << qRgba(r: 127, g: 0, b: 0, a: 127) << 125;
5040 QTest::newRow(dataTag: "ARGB over RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32
5041 << QPainter::CompositionMode_SourceOver << qRgba(r: 255, g: 0, b: 0, a: 85) << 85;
5042 QTest::newRow(dataTag: "ARGB_PM over RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied
5043 << QPainter::CompositionMode_SourceOver << qRgba(r: 85, g: 0, b: 0, a: 85) << 85;
5044#if QT_CONFIG(raster_64bit)
5045 QTest::newRow(dataTag: "ARGB source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32
5046 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 85) << 85;
5047 QTest::newRow(dataTag: "ARGB source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32
5048 << QPainter::CompositionMode_Source << qRgba(r: 255, g: 0, b: 0, a: 120) << 85;
5049#endif
5050 QTest::newRow(dataTag: "ARGB_PM source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied
5051 << QPainter::CompositionMode_Source << qRgba(r: 85, g: 0, b: 0, a: 85) << 85;
5052#if QT_CONFIG(raster_64bit)
5053 QTest::newRow(dataTag: "ARGB_PM source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied
5054 << QPainter::CompositionMode_Source << qRgba(r: 180, g: 0, b: 0, a: 180) << 170;
5055#endif
5056 QTest::newRow(dataTag: "ARGB source-in RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32
5057 << QPainter::CompositionMode_SourceIn << qRgba(r: 255, g: 0, b: 0, a: 85) << 85;
5058 QTest::newRow(dataTag: "ARGB_PM source-in RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied
5059 << QPainter::CompositionMode_SourceIn << qRgba(r: 85, g: 0, b: 0, a: 85) << 85;
5060}
5061
5062void tst_QPainter::blendARGBonRGB()
5063{
5064 QFETCH(QImage::Format, dst_format);
5065 QFETCH(QImage::Format, src_format);
5066 QFETCH(QPainter::CompositionMode, compositionMode);
5067 QFETCH(QRgb, color);
5068 QFETCH(int, expected_red);
5069
5070 QImage imageRgb(16,16, dst_format);
5071 QImage imageArgb(16,16, src_format);
5072 QPainter painter;
5073
5074 imageArgb.fill(pixel: color);
5075
5076 imageRgb.fill(color: Qt::black);
5077 painter.begin(&imageRgb);
5078 painter.setCompositionMode(compositionMode);
5079 painter.drawImage(x: 0, y: 0, image: imageArgb);
5080 painter.end();
5081
5082 QCOMPARE(imageRgb.pixelColor(0,0).red(), expected_red);
5083}
5084
5085enum CosmeticStrokerPaint
5086{
5087 Antialiasing,
5088 Dashing
5089};
5090
5091static void paint_func(QPainter *p, CosmeticStrokerPaint type)
5092{
5093 p->save();
5094 switch (type) {
5095 case Antialiasing:
5096 p->setPen(Qt::black);
5097 p->setRenderHint(hint: QPainter::Antialiasing);
5098 p->drawLine(x1: 4, y1: 8, x2: 42, y2: 42);
5099 break;
5100 case Dashing:
5101 p->setPen(QPen(Qt::black, 1, Qt::DashLine, Qt::RoundCap, Qt::MiterJoin));
5102 p->drawLine(x1: 8, y1: 8, x2: 42, y2: 8);
5103 p->drawLine(x1: 42, y1: 8, x2: 42, y2: 42);
5104 p->drawLine(x1: 42, y1: 42, x2: 8, y2: 42);
5105 p->drawLine(x1: 8, y1: 42, x2: 8, y2: 8);
5106 break;
5107 default:
5108 Q_ASSERT(false);
5109 break;
5110 }
5111 p->restore();
5112}
5113
5114Q_DECLARE_METATYPE(CosmeticStrokerPaint)
5115
5116void tst_QPainter::cosmeticStrokerClipping_data()
5117{
5118 QTest::addColumn<CosmeticStrokerPaint>(name: "paint");
5119
5120 QTest::newRow(dataTag: "antialiasing_paint") << Antialiasing;
5121 QTest::newRow(dataTag: "dashing_paint") << Dashing;
5122}
5123
5124void tst_QPainter::cosmeticStrokerClipping()
5125{
5126 QFETCH(CosmeticStrokerPaint, paint);
5127
5128 QImage image(50, 50, QImage::Format_RGB32);
5129 image.fill(color: Qt::white);
5130
5131 QPainter p(&image);
5132 paint_func(p: &p, type: paint);
5133 p.end();
5134
5135 QImage old = image.copy();
5136
5137 image.paintEngine()->setSystemClip(QRect(10, 0, image.width() - 10, image.height()));
5138
5139 p.begin(&image);
5140 p.fillRect(r: image.rect(), c: Qt::white);
5141 paint_func(p: &p, type: paint);
5142
5143 // doing same paint operation again with different system clip should not change the image
5144 QCOMPARE(old, image);
5145
5146 old = image;
5147
5148 p.setClipRect(QRect(20, 20, 30, 30));
5149 p.fillRect(r: image.rect(), c: Qt::white);
5150 paint_func(p: &p, type: paint);
5151
5152 // ditto for regular clips
5153 QCOMPARE(old, image);
5154}
5155
5156void tst_QPainter::RasterOp_NotDestination()
5157{
5158 QImage image(3, 3, QImage::Format_RGB32);
5159 image.fill(color: Qt::red);
5160
5161 {
5162 QPainter p(&image);
5163 p.setCompositionMode(QPainter::RasterOp_NotDestination);
5164 p.fillRect(r: image.rect(), c: Qt::black);
5165 }
5166
5167 uint pixel = image.pixel(x: 1, y: 1);
5168 QCOMPARE(pixel, 0xff00ffff);
5169}
5170
5171void tst_QPainter::drawTextNoHinting()
5172{
5173 {
5174 QImage image(250, 250, QImage::Format_RGB32);
5175 QPainter p(&image);
5176 QFont font("Arial", 8);
5177 font.setHintingPreference(QFont::PreferNoHinting);
5178 font.setStyleStrategy(QFont::PreferAntialias);
5179 p.setFont(font);
5180 p.drawText(r: image.rect(), text: "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz");
5181 }
5182 // Testing for a crash when DirectWrite is used on Windows
5183 QVERIFY(true);
5184}
5185
5186void tst_QPainter::drawPolyline_data()
5187{
5188 QTest::addColumn< QVector<QPointF> >(name: "points");
5189
5190 QTest::newRow(dataTag: "basic") << (QVector<QPointF>() << QPointF(10, 10) << QPointF(20, 10) << QPointF(20, 20));
5191 QTest::newRow(dataTag: "clipped") << (QVector<QPointF>() << QPoint(-10, 100) << QPoint(-1, 100) << QPoint(-1, -2) << QPoint(100, -2) << QPoint(100, 40)); // QTBUG-31579
5192 QTest::newRow(dataTag: "shortsegment") << (QVector<QPointF>() << QPoint(20, 100) << QPoint(20, 99) << QPoint(21, 99) << QPoint(21, 104)); // QTBUG-42398
5193 QTest::newRow(dataTag: "edge") << (QVector<QPointF>() << QPointF(4.5, 121.6) << QPointF(9.4, 150.9) << QPointF(14.2, 184.8) << QPointF(19.1, 130.4));
5194}
5195
5196void tst_QPainter::drawPolyline()
5197{
5198 QFETCH(QVector<QPointF>, points);
5199 QImage images[2];
5200
5201 for (int r = 0; r < 2; r++) {
5202 images[r] = QImage(150, 150, QImage::Format_ARGB32);
5203 images[r].fill(color: Qt::white);
5204 QPainter p(images + r);
5205 QPen pen(Qt::red, 0, Qt::SolidLine, Qt::FlatCap);
5206 p.setPen(pen);
5207 QVERIFY(p.pen().isCosmetic());
5208 if (r) {
5209 for (int i = 0; i < points.count()-1; i++) {
5210 p.drawLine(p1: points.at(i), p2: points.at(i: i+1));
5211 }
5212 } else {
5213 p.drawPolyline(polyline: points);
5214 }
5215 }
5216
5217 QCOMPARE(images[0], images[1]);
5218}
5219
5220void tst_QPainter::QTBUG50153_drawImage_assert()
5221{
5222 QImage::Format formats[] = {
5223 QImage::Format_RGB32, // fetchTransformedBilinearARGB32PM
5224 QImage::Format_ARGB32 // fetchTransformedBilinear
5225 };
5226
5227 for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
5228 QImage image(3027, 2999, formats[i]);
5229
5230 QImage backingStore(image.size(), QImage::Format_ARGB32);
5231 QPainter backingStorePainter(&backingStore);
5232
5233 QTransform transform;
5234 transform.scale( sx: 0.999987, sy: 0.999987 );
5235
5236 backingStorePainter.setTransform(transform);
5237 backingStorePainter.setRenderHint(hint: QPainter::SmoothPixmapTransform, on: true);
5238 backingStorePainter.drawImage(x: 0, y: 0, image);
5239
5240 // No crash, all fine
5241 }
5242}
5243
5244void tst_QPainter::rotateImage_data()
5245{
5246 QTest::addColumn<QImage>(name: "image");
5247 QTest::addColumn<bool>(name: "smooth");
5248
5249 QImage image(128, 128, QImage::Format_RGB32);
5250 for (int y = 0; y < 128; ++y) {
5251 for (int x = 0; x < 128; ++x) {
5252 image.setPixel(x, y, index_or_rgb: qRgb(r: x + y, g: x + y, b: x + y));
5253 }
5254 }
5255
5256 QTest::newRow(dataTag: "fast") << image << false;
5257 QTest::newRow(dataTag: "smooth") << image << true;
5258}
5259
5260void tst_QPainter::rotateImage()
5261{
5262 QFETCH(QImage, image);
5263 QFETCH(bool, smooth);
5264
5265 QImage dest(184, 184, QImage::Format_ARGB32_Premultiplied);
5266 dest.fill(color: Qt::transparent);
5267
5268 QPainter painter(&dest);
5269 QTransform transform;
5270 transform.translate(dx: 92, dy: 0);
5271 transform.rotate(a: 45);
5272 painter.setTransform(transform);
5273 painter.setRenderHint(hint: QPainter::SmoothPixmapTransform, on: smooth);
5274 painter.drawImage(x: 0, y: 0, image);
5275 painter.end();
5276
5277 QRgb lastRow = qRgba(r: 0, g: 0, b: 0, a: 0);
5278 for (int y = 0; y < 184; ++y) {
5279 QRgb row = qRgba(r: 0, g: 0, b: 0, a: 0);
5280 for (int x = 0; x < 184; ++x) {
5281 QRgb pixel = dest.pixel(x, y);
5282 if (qAlpha(rgb: pixel) < 255)
5283 continue;
5284 if (qAlpha(rgb: row) == 0) {
5285 row = pixel;
5286 } else {
5287 QCOMPARE(qRed(pixel), qGreen(pixel));
5288 QCOMPARE(qGreen(pixel), qBlue(pixel));
5289 QVERIFY(qAbs(qRed(row) - qRed(pixel)) <= 2);
5290 QVERIFY(qAbs(qGreen(row) - qGreen(pixel)) <= 2);
5291 QVERIFY(qAbs(qBlue(row) - qBlue(pixel)) <= 2);
5292 }
5293
5294 }
5295 if (qAlpha(rgb: row) && qAlpha(rgb: lastRow))
5296 QVERIFY(qGray(lastRow) <= qGray(row));
5297 lastRow = row;
5298 }
5299
5300}
5301
5302void tst_QPainter::QTBUG56252()
5303{
5304 QImage sourceImage(1770, 1477, QImage::Format_RGB32);
5305 QImage rotatedImage(1478, 1771, QImage::Format_RGB32);
5306 QTransform transformCenter;
5307 transformCenter.translate(dx: 739.0, dy: 885.5);
5308 transformCenter.rotate(a: 270.0);
5309 transformCenter.translate(dx: -885.0, dy: -738.5);
5310 QPainter painter;
5311 painter.begin(&rotatedImage);
5312 painter.setTransform(transform: transformCenter);
5313 painter.drawImage(p: QPoint(0, 0),image: sourceImage);
5314 painter.end();
5315
5316 // If no crash or illegal memory read, all is fine
5317}
5318
5319void tst_QPainter::blendNullRGB32()
5320{
5321 quint32 data[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
5322
5323 QImage nullImage((const uchar*)data, 16, 1, QImage::Format_RGB32);
5324 QImage image(16, 1, QImage::Format_RGB32);
5325 image.fill(color: Qt::white);
5326
5327 QPainter paint(&image);
5328 paint.setCompositionMode(QPainter::CompositionMode_Source);
5329 paint.setOpacity(0.5);
5330 paint.drawImage(x: 0, y: 0, image: nullImage);
5331 paint.end();
5332
5333 for (int i=0; i < image.width(); ++i)
5334 QVERIFY(image.pixel(i,0) != 0xffffffff);
5335}
5336
5337void tst_QPainter::toRGB64()
5338{
5339 QImage dst(10, 1, QImage::Format_BGR30);
5340 QImage src(10, 1, QImage::Format_RGB16);
5341 src.fill(color: Qt::white);
5342
5343 QPainter paint(&dst);
5344 paint.drawImage(x: 0, y: 0, image: src);
5345 paint.end();
5346
5347 for (int i=0; i < dst.width(); ++i) {
5348 QVERIFY(dst.pixelColor(i,0) == QColor(Qt::white));
5349 }
5350}
5351
5352void tst_QPainter::fillPolygon()
5353{
5354 QImage image(50, 50, QImage::Format_RGB32);
5355 image.fill(color: Qt::white);
5356
5357 QPainter painter(&image);
5358 QBrush brush(Qt::black, Qt::SolidPattern);
5359 painter.setBrush(brush);
5360
5361 QPen pen(Qt::red, 0, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5362 painter.setPen(pen);
5363
5364 const QPoint diamondpoints[5] = {
5365 QPoint(-15, 0),
5366 QPoint(0, -15),
5367 QPoint(15, 0),
5368 QPoint(0, 15),
5369 QPoint(-15, 0)
5370 };
5371 enum { Outside1, Border1, Inside, Border2, Outside2 } state;
5372
5373 for (int i = 0; i < 16 ; i++)
5374 {
5375 for (int j = 0; j < 16 ; j++)
5376 {
5377 image.fill(color: Qt::white);
5378 painter.resetTransform();
5379 painter.translate(dx: 25 + i/16., dy: 25 + j/16.);
5380 painter.drawPolygon(points: diamondpoints, pointCount: 5);
5381
5382 for (int x = 0; x < 50; x++) {
5383 state = Outside1;
5384 for (int y = 0; y < 50; y++) {
5385 QRgb c = image.pixel(x, y);
5386 switch (state) {
5387 case Outside1:
5388 if (c == QColor(Qt::red).rgb())
5389 state = Border1;
5390 else
5391 QCOMPARE(c, QColor(Qt::white).rgb());
5392 break;
5393 case Border1:
5394 if (c == QColor(Qt::black).rgb())
5395 state = Inside;
5396 else if (c == QColor(Qt::white).rgb())
5397 state = Outside2;
5398 else
5399 QCOMPARE(c, QColor(Qt::red).rgb());
5400 break;
5401 case Inside:
5402 if (c == QColor(Qt::red).rgb())
5403 state = Border2;
5404 else
5405 QCOMPARE(c, QColor(Qt::black).rgb());
5406 break;
5407 case Border2:
5408 if (c == QColor(Qt::white).rgb())
5409 state = Outside2;
5410 else
5411 QCOMPARE(c, QColor(Qt::red).rgb());
5412 break;
5413 case Outside2:
5414 QCOMPARE(c, QColor(Qt::white).rgb());
5415 }
5416 }
5417 }
5418 for (int y = 0; y < 50; y++) {
5419 state = Outside1;
5420 for (int x = 0; x < 50; x++) {
5421 QRgb c = image.pixel(x, y);
5422 switch (state) {
5423 case Outside1:
5424 if (c == QColor(Qt::red).rgb())
5425 state = Border1;
5426 else
5427 QCOMPARE(c, QColor(Qt::white).rgb());
5428 break;
5429 case Border1:
5430 if (c == QColor(Qt::black).rgb())
5431 state = Inside;
5432 else if (c == QColor(Qt::white).rgb())
5433 state = Outside2;
5434 else
5435 QCOMPARE(c, QColor(Qt::red).rgb());
5436 break;
5437 case Inside:
5438 if (c == QColor(Qt::red).rgb())
5439 state = Border2;
5440 else
5441 QCOMPARE(c, QColor(Qt::black).rgb());
5442 break;
5443 case Border2:
5444 if (c == QColor(Qt::white).rgb())
5445 state = Outside2;
5446 else
5447 QCOMPARE(c, QColor(Qt::red).rgb());
5448 break;
5449 case Outside2:
5450 QCOMPARE(c, QColor(Qt::white).rgb());
5451 }
5452 }
5453 }
5454 }
5455 }
5456}
5457
5458void tst_QPainter::drawImageAtPointF()
5459{
5460 // Just test we do not crash
5461 QImage image1(10, 10, QImage::Format_RGB32);
5462 QImage image2(200, 200, QImage::Format_RGB32);
5463
5464 QPainter paint(&image2);
5465 paint.setClipRect(x: 97, y: 46, w: 14, h: 14);
5466 paint.setCompositionMode(QPainter::CompositionMode_Source);
5467 paint.drawImage(p: QPointF(96, std::numeric_limits<int>::max()), image: image1);
5468 paint.drawImage(p: QPointF(std::numeric_limits<int>::min(), 48), image: image1);
5469 paint.end();
5470}
5471
5472void tst_QPainter::scaledDashes()
5473{
5474 // Test that we do not hit the limit-huge-number-of-dashes path
5475 QRgb fore = qRgb(r: 0, g: 0, b: 0xff);
5476 QRgb back = qRgb(r: 0xff, g: 0xff, b: 0);
5477 QImage image(5, 32, QImage::Format_RGB32);
5478 image.fill(pixel: back);
5479 QPainter p(&image);
5480 QPen pen(QColor(fore), 3, Qt::DotLine);
5481 p.setPen(pen);
5482 p.scale(sx: 1, sy: 2);
5483 p.drawLine(x1: 2, y1: 0, x2: 2, y2: 16);
5484 p.end();
5485
5486 bool foreFound = false;
5487 bool backFound = false;
5488 int i = 0;
5489 while (i < 32 && (!foreFound || !backFound)) {
5490 QRgb pix = image.pixel(x: 3, y: i);
5491 if (pix == fore)
5492 foreFound = true;
5493 else if (pix == back)
5494 backFound = true;
5495 i++;
5496 }
5497
5498 QVERIFY(foreFound);
5499 QVERIFY(backFound);
5500}
5501
5502QTEST_MAIN(tst_QPainter)
5503
5504#include "tst_qpainter.moc"
5505

source code of qtbase/tests/auto/gui/painting/qpainter/tst_qpainter.cpp