| 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 | |
| 32 | #include <qpicture.h> |
| 33 | #include <qpainter.h> |
| 34 | #include <qimage.h> |
| 35 | #include <qpaintengine.h> |
| 36 | #include <qguiapplication.h> |
| 37 | #include <qscreen.h> |
| 38 | #include <limits.h> |
| 39 | |
| 40 | class tst_QPicture : public QObject |
| 41 | { |
| 42 | Q_OBJECT |
| 43 | |
| 44 | public: |
| 45 | tst_QPicture(); |
| 46 | |
| 47 | private slots: |
| 48 | void getSetCheck(); |
| 49 | void devType(); |
| 50 | void paintingActive(); |
| 51 | void boundingRect(); |
| 52 | void swap(); |
| 53 | void serialization(); |
| 54 | void save_restore(); |
| 55 | void boundaryValues_data(); |
| 56 | void boundaryValues(); |
| 57 | }; |
| 58 | |
| 59 | // Testing get/set functions |
| 60 | void tst_QPicture::getSetCheck() |
| 61 | { |
| 62 | QPictureIO obj1; |
| 63 | // const QPicture & QPictureIO::picture() |
| 64 | // void QPictureIO::setPicture(const QPicture &) |
| 65 | // const char * QPictureIO::format() |
| 66 | // void QPictureIO::setFormat(const char *) |
| 67 | const char var2[] = "PNG" ; |
| 68 | obj1.setFormat(var2); |
| 69 | QCOMPARE(var2, obj1.format()); |
| 70 | obj1.setFormat((char *)0); |
| 71 | // The format is stored internally in a QString, so return is always a valid char * |
| 72 | QVERIFY(QString(obj1.format()).isEmpty()); |
| 73 | |
| 74 | // const char * QPictureIO::parameters() |
| 75 | // void QPictureIO::setParameters(const char *) |
| 76 | const char var3[] = "Bogus data" ; |
| 77 | obj1.setParameters(var3); |
| 78 | QCOMPARE(var3, obj1.parameters()); |
| 79 | obj1.setParameters((char *)0); |
| 80 | // The format is stored internally in a QString, so return is always a valid char * |
| 81 | QVERIFY(QString(obj1.parameters()).isEmpty()); |
| 82 | } |
| 83 | |
| 84 | tst_QPicture::tst_QPicture() |
| 85 | { |
| 86 | } |
| 87 | |
| 88 | void tst_QPicture::devType() |
| 89 | { |
| 90 | QPicture p; |
| 91 | QCOMPARE( p.devType(), (int)QInternal::Picture ); |
| 92 | } |
| 93 | |
| 94 | void tst_QPicture::paintingActive() |
| 95 | { |
| 96 | // actually implemented in QPainter but QPicture is a good |
| 97 | // example of an external paint device |
| 98 | QPicture p; |
| 99 | QVERIFY( !p.paintingActive() ); |
| 100 | QPainter pt( &p ); |
| 101 | QVERIFY( p.paintingActive() ); |
| 102 | pt.end(); |
| 103 | QVERIFY( !p.paintingActive() ); |
| 104 | } |
| 105 | |
| 106 | void tst_QPicture::boundingRect() |
| 107 | { |
| 108 | QPicture p1; |
| 109 | // default value |
| 110 | QVERIFY( !p1.boundingRect().isValid() ); |
| 111 | |
| 112 | QRect r1( 20, 30, 5, 15 ); |
| 113 | p1.setBoundingRect( r1 ); |
| 114 | QCOMPARE( p1.boundingRect(), r1 ); |
| 115 | p1.setBoundingRect(QRect()); |
| 116 | |
| 117 | QPainter pt( &p1 ); |
| 118 | pt.drawLine( x1: 10, y1: 20, x2: 110, y2: 80 ); |
| 119 | pt.end(); |
| 120 | |
| 121 | // assignment and copy constructor |
| 122 | QRect r2( 10, 20, 100, 60 ); |
| 123 | QCOMPARE( p1.boundingRect(), r2 ); |
| 124 | QPicture p2( p1 ); |
| 125 | QCOMPARE( p2.boundingRect(), r2 ); |
| 126 | QPicture p3; |
| 127 | p3 = p1; |
| 128 | QCOMPARE( p3.boundingRect(), r2 ); |
| 129 | |
| 130 | { |
| 131 | QPicture p4; |
| 132 | QPainter p(&p4); |
| 133 | p.drawLine(x1: 0, y1: 0, x2: 5, y2: 0); |
| 134 | p.drawLine(x1: 0, y1: 0, x2: 0, y2: 5); |
| 135 | p.end(); |
| 136 | |
| 137 | QRect r3(0, 0, 5, 5); |
| 138 | QCOMPARE(p4.boundingRect(), r3); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | void tst_QPicture::swap() |
| 143 | { |
| 144 | QPicture p1, p2; |
| 145 | QPainter(&p1).drawLine(x1: 0, y1: 0, x2: 5, y2: 5); |
| 146 | QPainter(&p2).drawLine(x1: 0, y1: 3, x2: 3, y2: 0); |
| 147 | QCOMPARE(p1.boundingRect(), QRect(0,0,5,5)); |
| 148 | QCOMPARE(p2.boundingRect(), QRect(0,0,3,3)); |
| 149 | p1.swap(other&: p2); |
| 150 | QCOMPARE(p1.boundingRect(), QRect(0,0,3,3)); |
| 151 | QCOMPARE(p2.boundingRect(), QRect(0,0,5,5)); |
| 152 | } |
| 153 | |
| 154 | Q_DECLARE_METATYPE(QDataStream::Version) |
| 155 | Q_DECLARE_METATYPE(QPicture) |
| 156 | |
| 157 | void ensureSerializesCorrectly(const QPicture &picture, QDataStream::Version version) |
| 158 | { |
| 159 | QDataStream stream; |
| 160 | |
| 161 | QBuffer buffer; |
| 162 | buffer.open(openMode: QIODevice::WriteOnly); |
| 163 | stream.setDevice(&buffer); |
| 164 | stream.setVersion(version); |
| 165 | stream << picture; |
| 166 | buffer.close(); |
| 167 | |
| 168 | buffer.open(openMode: QIODevice::ReadOnly); |
| 169 | QPicture readpicture; |
| 170 | stream >> readpicture; |
| 171 | QVERIFY2(memcmp(picture.data(), readpicture.data(), picture.size()) == 0, |
| 172 | qPrintable(QString::fromLatin1("Picture data does not compare equal for QDataStream version %1" ).arg(version))); |
| 173 | } |
| 174 | |
| 175 | class PaintEngine : public QPaintEngine |
| 176 | { |
| 177 | public: |
| 178 | PaintEngine() : QPaintEngine() {} |
| 179 | bool begin(QPaintDevice *) { return true; } |
| 180 | bool end() { return true; } |
| 181 | void updateState(const QPaintEngineState &) {} |
| 182 | void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} |
| 183 | Type type() const { return Raster; } |
| 184 | |
| 185 | QFont font() { return state->font(); } |
| 186 | }; |
| 187 | |
| 188 | class Picture : public QPicture |
| 189 | { |
| 190 | public: |
| 191 | Picture() : QPicture() {} |
| 192 | QPaintEngine *paintEngine() const { return (QPaintEngine*)&mPaintEngine; } |
| 193 | private: |
| 194 | PaintEngine mPaintEngine; |
| 195 | }; |
| 196 | |
| 197 | void tst_QPicture::serialization() |
| 198 | { |
| 199 | QDataStream stream; |
| 200 | const int thisVersion = stream.version(); |
| 201 | |
| 202 | for (int version = QDataStream::Qt_1_0; version <= thisVersion; ++version) { |
| 203 | const QDataStream::Version versionEnum = static_cast<QDataStream::Version>(version); |
| 204 | |
| 205 | { |
| 206 | // streaming of null pictures |
| 207 | ensureSerializesCorrectly(picture: QPicture(), version: versionEnum); |
| 208 | } |
| 209 | { |
| 210 | // picture with a simple line, checking bitwise equality |
| 211 | QPicture picture; |
| 212 | QPainter painter(&picture); |
| 213 | painter.drawLine(x1: 10, y1: 20, x2: 30, y2: 40); |
| 214 | ensureSerializesCorrectly(picture, version: versionEnum); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | { |
| 219 | // Test features that were added after Qt 4.5, as that was hard-coded as the major |
| 220 | // version for a while, which was incorrect. In this case, we'll test font hints. |
| 221 | QPicture picture; |
| 222 | QPainter painter; |
| 223 | QFont font; |
| 224 | font.setStyleName("Blah" ); |
| 225 | font.setHintingPreference(QFont::PreferFullHinting); |
| 226 | painter.begin(&picture); |
| 227 | painter.setFont(font); |
| 228 | painter.drawText(x: 20, y: 20, s: "Hello" ); |
| 229 | painter.end(); |
| 230 | |
| 231 | Picture customPicture; |
| 232 | painter.begin(&customPicture); |
| 233 | picture.play(p: &painter); |
| 234 | const QFont actualFont = ((PaintEngine*)customPicture.paintEngine())->font(); |
| 235 | painter.end(); |
| 236 | QCOMPARE(actualFont.styleName(), QStringLiteral("Blah" )); |
| 237 | QCOMPARE(actualFont.hintingPreference(), QFont::PreferFullHinting); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | static QRectF scaleRect(const QRectF &rect, qreal xf, qreal yf) |
| 242 | { |
| 243 | return QRectF(rect.left() * xf, rect.top() * yf, rect.width() * xf, rect.height() * yf); |
| 244 | } |
| 245 | |
| 246 | static void paintStuff(QPainter *p) |
| 247 | { |
| 248 | const QScreen *screen = QGuiApplication::primaryScreen(); |
| 249 | // Calculate factors from the screen resolution against QPicture's 96DPI |
| 250 | // (enforced by Qt::AA_Use96Dpi as set by QTEST_MAIN). |
| 251 | const qreal xf = qreal(p->device()->logicalDpiX()) / screen->logicalDotsPerInchX(); |
| 252 | const qreal yf = qreal(p->device()->logicalDpiY()) / screen->logicalDotsPerInchY(); |
| 253 | p->drawRect(rect: scaleRect(rect: QRectF(100, 100, 100, 100), xf, yf)); |
| 254 | p->save(); |
| 255 | p->translate(dx: 10 * xf, dy: 10 * yf); |
| 256 | p->restore(); |
| 257 | p->drawRect(rect: scaleRect(rect: QRectF(100, 100, 100, 100), xf, yf)); |
| 258 | } |
| 259 | |
| 260 | /* See task: 41469 |
| 261 | Problem is that the state is not properly restored if the basestate of |
| 262 | the painter is different when the picture data was created compared to |
| 263 | the base state of the painter when it is played back. |
| 264 | */ |
| 265 | void tst_QPicture::save_restore() |
| 266 | { |
| 267 | QPicture pic; |
| 268 | QPainter p; |
| 269 | p.begin(&pic); |
| 270 | paintStuff(p: &p); |
| 271 | p.end(); |
| 272 | |
| 273 | QPixmap pix1(300, 300); |
| 274 | pix1.fill(fillColor: Qt::white); |
| 275 | p.begin(&pix1); |
| 276 | p.drawPicture(x: 50, y: 50, p: pic); |
| 277 | p.end(); |
| 278 | |
| 279 | QPixmap pix2(300, 300); |
| 280 | pix2.fill(fillColor: Qt::white); |
| 281 | p.begin(&pix2); |
| 282 | p.translate(dx: 50, dy: 50); |
| 283 | paintStuff(p: &p); |
| 284 | p.end(); |
| 285 | |
| 286 | QVERIFY( pix1.toImage() == pix2.toImage() ); |
| 287 | } |
| 288 | |
| 289 | void tst_QPicture::boundaryValues_data() |
| 290 | { |
| 291 | QTest::addColumn<int>(name: "x" ); |
| 292 | QTest::addColumn<int>(name: "y" ); |
| 293 | QTest::newRow(dataTag: "max x" ) << INT_MAX << 50; |
| 294 | QTest::newRow(dataTag: "max y" ) << 50 << INT_MAX; |
| 295 | QTest::newRow(dataTag: "max x and y" ) << INT_MAX << INT_MAX; |
| 296 | |
| 297 | QTest::newRow(dataTag: "min x" ) << INT_MIN << 50; |
| 298 | QTest::newRow(dataTag: "min y" ) << 50 << INT_MIN; |
| 299 | QTest::newRow(dataTag: "min x and y" ) << INT_MIN << INT_MIN; |
| 300 | |
| 301 | QTest::newRow(dataTag: "min x, max y" ) << INT_MIN << INT_MAX; |
| 302 | QTest::newRow(dataTag: "max x, min y" ) << INT_MAX << INT_MIN; |
| 303 | } |
| 304 | |
| 305 | void tst_QPicture::boundaryValues() |
| 306 | { |
| 307 | QPicture picture; |
| 308 | |
| 309 | QPainter painter; |
| 310 | painter.begin(&picture); |
| 311 | |
| 312 | QFETCH(int, x); |
| 313 | QFETCH(int, y); |
| 314 | painter.drawPoint(p: QPoint(x, y)); |
| 315 | |
| 316 | painter.end(); |
| 317 | } |
| 318 | |
| 319 | |
| 320 | QTEST_MAIN(tst_QPicture) |
| 321 | #include "tst_qpicture.moc" |
| 322 | |