diff options
Diffstat (limited to 'tests/baseline/shared')
-rw-r--r-- | tests/baseline/shared/baselineprotocol.cpp | 112 | ||||
-rw-r--r-- | tests/baseline/shared/baselineprotocol.h | 55 | ||||
-rw-r--r-- | tests/baseline/shared/lookup3.cpp | 35 | ||||
-rw-r--r-- | tests/baseline/shared/paintcommands.cpp | 2955 | ||||
-rw-r--r-- | tests/baseline/shared/paintcommands.h | 325 | ||||
-rw-r--r-- | tests/baseline/shared/qbaselinetest.cpp | 128 | ||||
-rw-r--r-- | tests/baseline/shared/qbaselinetest.h | 58 | ||||
-rw-r--r-- | tests/baseline/shared/qwidgetbaselinetest.cpp | 137 | ||||
-rw-r--r-- | tests/baseline/shared/qwidgetbaselinetest.h | 31 |
9 files changed, 3498 insertions, 338 deletions
diff --git a/tests/baseline/shared/baselineprotocol.cpp b/tests/baseline/shared/baselineprotocol.cpp index 33620fbe5c..311b003c07 100644 --- a/tests/baseline/shared/baselineprotocol.cpp +++ b/tests/baseline/shared/baselineprotocol.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "baselineprotocol.h" #include <QLibraryInfo> #include <QImage> @@ -42,6 +17,7 @@ #include <QRegularExpression> const QString PI_Project(QLS("Project")); +const QString PI_ProjectImageKeys(QLS("ProjectImageKeys")); const QString PI_TestCase(QLS("TestCase")); const QString PI_HostName(QLS("HostName")); const QString PI_HostAddress(QLS("HostAddress")); @@ -52,11 +28,6 @@ const QString PI_QtBuildMode(QLS("QtBuildMode")); const QString PI_GitCommit(QLS("GitCommit")); const QString PI_GitBranch(QLS("GitBranch")); -PlatformInfo::PlatformInfo() - : QMap<QString, QString>(), adHoc(true) -{ -} - PlatformInfo PlatformInfo::localHostInfo() { PlatformInfo pi; @@ -74,24 +45,26 @@ PlatformInfo PlatformInfo::localHostInfo() #endif pi.insert(PI_OSVersion, QSysInfo::kernelVersion()); + QString gc = qEnvironmentVariable("BASELINE_GIT_COMMIT"); #if QT_CONFIG(process) - QProcess git; - QString cmd; - QStringList args; -#if defined(Q_OS_WIN) - cmd = QLS("cmd.exe"); - args << QLS("/c") << QLS("git"); -#else - cmd = QLS("git"); -#endif - args << QLS("log") << QLS("--max-count=1") << QLS("--pretty=%H [%an] [%ad] %s"); - git.start(cmd, args); - git.waitForFinished(3000); - if (!git.exitCode()) - pi.insert(PI_GitCommit, QString::fromLocal8Bit(git.readAllStandardOutput().constData()).simplified()); - else - pi.insert(PI_GitCommit, QLS("Unknown")); + if (gc.isEmpty()) { + QProcess git; + QString cmd; + QStringList args; + #if defined(Q_OS_WIN) + cmd = QLS("cmd.exe"); + args << QLS("/c") << QLS("git"); + #else + cmd = QLS("git"); + #endif + args << QLS("log") << QLS("--max-count=1") << QLS("--pretty=%H [%an] [%ad] %s"); + git.start(cmd, args); + git.waitForFinished(3000); + if (!git.exitCode()) + gc = QString::fromLocal8Bit(git.readAllStandardOutput().constData()).simplified(); + } #endif // QT_CONFIG(process) + pi.insert(PI_GitCommit, gc.isEmpty() ? QLS("Unknown") : gc); if (qEnvironmentVariableIsSet("JENKINS_HOME")) pi.setAdHocRun(false); @@ -104,23 +77,6 @@ PlatformInfo PlatformInfo::localHostInfo() } -PlatformInfo::PlatformInfo(const PlatformInfo &other) - : QMap<QString, QString>(other) -{ - orides = other.orides; - adHoc = other.adHoc; -} - - -PlatformInfo &PlatformInfo::operator=(const PlatformInfo &other) -{ - QMap<QString, QString>::operator=(other); - orides = other.orides; - adHoc = other.adHoc; - return *this; -} - - void PlatformInfo::addOverride(const QString& key, const QString& value) { orides.append(key); @@ -162,17 +118,6 @@ QDataStream & operator>> (QDataStream &stream, PlatformInfo &pi) } -ImageItem &ImageItem::operator=(const ImageItem &other) -{ - testFunction = other.testFunction; - itemName = other.itemName; - itemChecksum = other.itemChecksum; - status = other.status; - image = other.image; - imageChecksums = other.imageChecksums; - return *this; -} - // Defined in lookup3.c: void hashword2 ( const quint32 *k, /* the key, an array of quint32 values */ @@ -316,18 +261,21 @@ bool BaselineProtocol::disconnect() } -bool BaselineProtocol::connect(const QString &testCase, bool *dryrun, const PlatformInfo& clientInfo) +bool BaselineProtocol::connect(const QString &testCase, bool *dryrun, const PlatformInfo &clientInfo, const QString &server) { errMsg.clear(); - QByteArray serverName(qgetenv("QT_LANCELOT_SERVER")); - if (serverName.isNull()) - serverName = "lancelot.test.qt-project.org"; + QString serverName = server; + if (serverName.isEmpty()) { + serverName = qEnvironmentVariable("QT_LANCELOT_SERVER"); + if (serverName.isEmpty()) + serverName = QStringLiteral("lancelot.test.qt-project.org"); + } socket.connectToHost(serverName, ServerPort); if (!socket.waitForConnected(Timeout)) { - QThread::msleep(3000); // Wait a bit and try again, the server might just be restarting + QThread::sleep(std::chrono::seconds{3}); // Wait a bit and try again, the server might just be restarting if (!socket.waitForConnected(Timeout)) { - errMsg += QLS("TCP connectToHost failed. Host:") + QLS(serverName) + QLS(" port:") + QString::number(ServerPort); + errMsg += QLS("TCP connectToHost failed. Host:") + serverName + QLS(" port:") + QString::number(ServerPort); return false; } } diff --git a/tests/baseline/shared/baselineprotocol.h b/tests/baseline/shared/baselineprotocol.h index 4151d30c9c..93d416fcd8 100644 --- a/tests/baseline/shared/baselineprotocol.h +++ b/tests/baseline/shared/baselineprotocol.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASELINEPROTOCOL_H #define BASELINEPROTOCOL_H @@ -43,6 +18,7 @@ #define FileFormat "png" extern const QString PI_Project; +extern const QString PI_ProjectImageKeys; extern const QString PI_TestCase; extern const QString PI_HostName; extern const QString PI_HostAddress; @@ -56,12 +32,6 @@ extern const QString PI_GitBranch; class PlatformInfo : public QMap<QString, QString> { public: - PlatformInfo(); - PlatformInfo(const PlatformInfo &other); - ~PlatformInfo() - {} - PlatformInfo &operator=(const PlatformInfo &other); - static PlatformInfo localHostInfo(); void addOverride(const QString& key, const QString& value); @@ -71,7 +41,7 @@ public: private: QStringList orides; - bool adHoc; + bool adHoc = true; friend QDataStream & operator<< (QDataStream &stream, const PlatformInfo &pi); friend QDataStream & operator>> (QDataStream &stream, PlatformInfo& pi); }; @@ -81,16 +51,6 @@ QDataStream & operator>> (QDataStream &stream, PlatformInfo& pi); struct ImageItem { -public: - ImageItem() - : status(Ok), itemChecksum(0) - {} - ImageItem(const ImageItem &other) - { *this = other; } - ~ImageItem() - {} - ImageItem &operator=(const ImageItem &other); - static quint64 computeChecksum(const QImage& image); enum ItemStatus { @@ -104,10 +64,10 @@ public: QString testFunction; QString itemName; - ItemStatus status; + ItemStatus status = Ok; QImage image; QList<quint64> imageChecksums; - quint16 itemChecksum; + quint16 itemChecksum = 0; QByteArray misc; void writeImageToStream(QDataStream &stream) const; @@ -155,7 +115,8 @@ public: // For client: // For advanced client: - bool connect(const QString &testCase, bool *dryrun = nullptr, const PlatformInfo& clientInfo = PlatformInfo()); + bool connect(const QString &testCase, bool *dryrun = nullptr, + const PlatformInfo &clientInfo = PlatformInfo(), const QString &server = QString()); bool disconnect(); bool requestBaselineChecksums(const QString &testFunction, ImageItemList *itemList); bool submitMatch(const ImageItem &item, QByteArray *serverMsg); diff --git a/tests/baseline/shared/lookup3.cpp b/tests/baseline/shared/lookup3.cpp index 1fbe6b499e..7964a184ae 100644 --- a/tests/baseline/shared/lookup3.cpp +++ b/tests/baseline/shared/lookup3.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* @@ -328,7 +303,7 @@ quint32 hashlittle( const void *key, size_t length, quint32 initval) * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND @@ -536,7 +511,7 @@ void hashlittle2( * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND @@ -736,7 +711,7 @@ quint32 hashbig( const void *key, size_t length, quint32 initval) * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND diff --git a/tests/baseline/shared/paintcommands.cpp b/tests/baseline/shared/paintcommands.cpp new file mode 100644 index 0000000000..2cb3cd3bba --- /dev/null +++ b/tests/baseline/shared/paintcommands.cpp @@ -0,0 +1,2955 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include "paintcommands.h" + +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qpainter.h> +#include <qpainterpath.h> +#include <qbitmap.h> +#include <qtextstream.h> +#include <qtextlayout.h> +#include <qdebug.h> +#include <QStaticText> +#include <QTextDocument> +#include <private/qimage_p.h> + +#ifndef QT_NO_OPENGL +#include <QOpenGLFramebufferObjectFormat> +#include <QOpenGLContext> +#include <QOpenGLPaintDevice> +#endif + +/********************************************************************************* +** everything to populate static tables +**********************************************************************************/ +const char *PaintCommands::brushStyleTable[] = { + "NoBrush", + "SolidPattern", + "Dense1Pattern", + "Dense2Pattern", + "Dense3Pattern", + "Dense4Pattern", + "Dense5Pattern", + "Dense6Pattern", + "Dense7Pattern", + "HorPattern", + "VerPattern", + "CrossPattern", + "BDiagPattern", + "FDiagPattern", + "DiagCrossPattern", + "LinearGradientPattern" +}; + +const char *PaintCommands::penStyleTable[] = { + "NoPen", + "SolidLine", + "DashLine", + "DotLine", + "DashDotLine", + "DashDotDotLine" +}; + +const char *PaintCommands::fontWeightTable[] = { + "Light", + "Normal", + "DemiBold", + "Bold", + "Black" +}; + +const char *PaintCommands::fontHintingTable[] = { + "Default", + "None", + "Vertical", + "Full" +}; + +const char *PaintCommands::fontCapitalizationTable[] = { + "MixedCase", + "AllUppercase", + "AllLowercase", + "SmallCaps", + "Capitalize" +}; + +const char *PaintCommands::clipOperationTable[] = { + "NoClip", + "ReplaceClip", + "IntersectClip", + "UniteClip" +}; + +const char *PaintCommands::spreadMethodTable[] = { + "PadSpread", + "ReflectSpread", + "RepeatSpread" +}; + +const char *PaintCommands::coordinateMethodTable[] = { + "LogicalMode", + "StretchToDeviceMode", + "ObjectBoundingMode", + "ObjectMode" +}; + +const char *PaintCommands::sizeModeTable[] = { + "AbsoluteSize", + "RelativeSize" +}; + +const char *PaintCommands::compositionModeTable[] = { + "SourceOver", + "DestinationOver", + "Clear", + "Source", + "Destination", + "SourceIn", + "DestinationIn", + "SourceOut", + "DestinationOut", + "SourceAtop", + "DestinationAtop", + "Xor", + "Plus", + "Multiply", + "Screen", + "Overlay", + "Darken", + "Lighten", + "ColorDodge", + "ColorBurn", + "HardLight", + "SoftLight", + "Difference", + "Exclusion", + "SourceOrDestination", + "SourceAndDestination", + "SourceXorDestination", + "NotSourceAndNotDestination", + "NotSourceOrNotDestination", + "NotSourceXorDestination", + "NotSource", + "NotSourceAndDestination", + "SourceAndNotDestination" +}; + +const char *PaintCommands::imageFormatTable[] = { + "Invalid", + "Mono", + "MonoLSB", + "Indexed8", + "RGB32", + "ARGB32", + "ARGB32_Premultiplied", + "Format_RGB16", + "Format_ARGB8565_Premultiplied", + "Format_RGB666", + "Format_ARGB6666_Premultiplied", + "Format_RGB555", + "Format_ARGB8555_Premultiplied", + "Format_RGB888", + "Format_RGB444", + "Format_ARGB4444_Premultiplied", + "Format_RGBX8888", + "Format_RGBA8888", + "Format_RGBA8888_Premultiplied", + "Format_BGR30", + "Format_A2BGR30_Premultiplied", + "Format_RGB30", + "Format_A2RGB30_Premultiplied", + "Alpha8", + "Grayscale8", + "RGBx64", + "RGBA64", + "RGBA64_Premultiplied", + "Grayscale16", + "BGR888", + "RGBx16FPx4", + "RGBA16FPx4", + "RGBA16FPx4_Premultiplied", + "RGBx32FPx4", + "RGBA32FPx4", + "RGBA32FPx4_Premultiplied", + "CMYK32", +}; + +const char *PaintCommands::renderHintTable[] = { + "Antialiasing", + "SmoothPixmapTransform", + "NonCosmeticBrushPatterns" +}; + +int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit) +{ + QByteArray p = pattern.toLatin1().toLower(); + for (int i=0; i<limit; ++i) + if (p == QByteArray::fromRawData(table[i], qstrlen(table[i])).toLower()) + return i; + return -1; +} + +QList<PaintCommands::PaintCommandInfos> PaintCommands::s_commandInfoTable = QList<PaintCommands::PaintCommandInfos>(); +QList<QPair<QString,QStringList> > PaintCommands::s_enumsTable = QList<QPair<QString,QStringList> >(); +QMultiHash<QString, int> PaintCommands::s_commandHash; + +#define DECL_PAINTCOMMAND(identifier, method, regexp, syntax, sample) \ + s_commandInfoTable << PaintCommandInfos(QLatin1String(identifier), &PaintCommands::method, QRegularExpression(regexp), \ + QLatin1String(syntax), QLatin1String(sample) ); + +#define DECL_PAINTCOMMANDSECTION(title) \ + s_commandInfoTable << PaintCommandInfos(QLatin1String(title)); + +#define ADD_ENUMLIST(listCaption, cStrArray) { \ + QStringList list; \ + for (int i=0; i<int(sizeof(cStrArray)/sizeof(char*)); i++) \ + list << cStrArray[i]; \ + s_enumsTable << qMakePair(QString(listCaption), list); \ + } + +void PaintCommands::staticInit() +{ + // check if already done + if (!s_commandInfoTable.isEmpty()) return; + + // populate the command list + DECL_PAINTCOMMANDSECTION("misc"); + DECL_PAINTCOMMAND("comment", command_comment, + "^\\s*#", + "# this is some comments", + "# place your comments here"); + DECL_PAINTCOMMAND("import", command_import, + "^import\\s+\"(.*)\"$", + "import <qrcFilename>", + "import \"myfile.qrc\""); + DECL_PAINTCOMMAND("begin_block", command_begin_block, + "^begin_block\\s+(\\w*)$", + "begin_block <blockName>", + "begin_block blockName"); + DECL_PAINTCOMMAND("end_block", command_end_block, + "^end_block\\s*(\\w*)$", + "end_block [blockName]", + "end_block blockName"); + DECL_PAINTCOMMAND("repeat_block", command_repeat_block, + "^repeat_block\\s+(\\w*)$", + "repeat_block <blockName>", + "repeat_block blockName"); + DECL_PAINTCOMMAND("textlayout_draw", command_textlayout_draw, + "^textlayout_draw\\s+\"(.*)\"\\s+([0-9.]*)$", + "textlayout_draw <text> <width>", + "textlayout_draw \"your text\" 1.0"); + DECL_PAINTCOMMAND("abort", command_abort, + "^abort$", + "abort", + "abort"); + DECL_PAINTCOMMAND("noop", command_noop, + "^$", + "-", + "\n"); + + DECL_PAINTCOMMANDSECTION("setters"); + DECL_PAINTCOMMAND("setBackgroundMode", command_setBgMode, + "^(setBackgroundMode|setBgMode)\\s+(\\w*)$", + "setBackgroundMode <OpaqueMode|TransparentMode>", + "setBackgroundMode TransparentMode"); + DECL_PAINTCOMMAND("setBackground", command_setBackground, + "^setBackground\\s+#?(\\w*)\\s*(\\w*)?$", + "setBackground <color> [brush style enum]", + "setBackground black SolidPattern"); + DECL_PAINTCOMMAND("setOpacity", command_setOpacity, + "^setOpacity\\s+(-?\\d*\\.?\\d*)$", + "setOpacity <opacity>\n - opacity is in [0,1]", + "setOpacity 1.0"); + DECL_PAINTCOMMAND("path_setFillRule", command_path_setFillRule, + "^path_setFillRule\\s+(\\w*)\\s+(\\w*)$", + "path_setFillRule <pathName> [Winding|OddEven]", + "path_setFillRule pathName Winding"); + DECL_PAINTCOMMAND("setBrush", command_setBrush, + "^setBrush\\s+(#?[\\w.:\\/]*)\\s*(\\w*)?$", + "setBrush <imageFileName>\nsetBrush noBrush\nsetBrush <color> <brush style enum>", + "setBrush white SolidPattern"); + DECL_PAINTCOMMAND("setBrushOrigin", command_setBrushOrigin, + "^setBrushOrigin\\s*(-?\\w*)\\s+(-?\\w*)$", + "setBrushOrigin <dx> <dy>", + "setBrushOrigin 0 0"); + DECL_PAINTCOMMAND("brushTranslate", command_brushTranslate, + "^brushTranslate\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushTranslate <tx> <ty>", + "brushTranslate 0.0 0.0"); + DECL_PAINTCOMMAND("brushScale", command_brushScale, + "^brushScale\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushScale <kx> <ky>", + "brushScale 0.0 0.0"); + DECL_PAINTCOMMAND("brushRotate", command_brushRotate, + "^brushRotate\\s+(-?[\\w.]*)$", + "brushRotate <angle>\n - angle in degrees", + "brushRotate 0.0"); + DECL_PAINTCOMMAND("brushShear", command_brushShear, + "^brushShear\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "brushShear <sx> <sy>", + "brushShear 0.0 0.0"); + DECL_PAINTCOMMAND("setCompositionMode", command_setCompositionMode, + "^setCompositionMode\\s+([\\w_0-9]*)$", + "setCompositionMode <composition mode enum>", + "setCompositionMode SourceOver"); + DECL_PAINTCOMMAND("setFont", command_setFont, + "^setFont\\s+\"([\\w\\s]*)\"\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", + "setFont <fontFace> [size] [font weight|font weight enum] [italic] [hinting enum] [underline] [strikeout] [overline] [capitalization enum]\n - font weight is an integer between 0 and 99", + "setFont \"times\" 12"); + DECL_PAINTCOMMAND("setPen", command_setPen, + "^setPen\\s+#?(\\w*)$", + "setPen <color>\nsetPen <pen style enum>\nsetPen brush", + "setPen black"); + DECL_PAINTCOMMAND("setPen", command_setPen2, + "^setPen\\s+(#?\\w*)\\s+([\\w.]+)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", + "setPen brush|<color> [width] [pen style enum] [FlatCap|SquareCap|RoundCap] [MiterJoin|BevelJoin|RoundJoin]", + "setPen black 1 FlatCap MiterJoin"); + DECL_PAINTCOMMAND("pen_setDashOffset", command_pen_setDashOffset, + "^pen_setDashOffset\\s+(-?[\\w.]+)$", + "pen_setDashOffset <offset>\n", + "pen_setDashOffset 1.0"); + DECL_PAINTCOMMAND("pen_setDashPattern", command_pen_setDashPattern, + "^pen_setDashPattern\\s+\\[([\\w\\s.]*)\\]$", + "pen_setDashPattern <[ <dash_1> <space_1> ... <dash_n> <space_n> ]>", + "pen_setDashPattern [ 2 1 4 1 3 3 ]"); + DECL_PAINTCOMMAND("pen_setCosmetic", command_pen_setCosmetic, + "^pen_setCosmetic\\s+(\\w*)$", + "pen_setCosmetic <true|false>", + "pen_setCosmetic true"); + DECL_PAINTCOMMAND("setRenderHint", command_setRenderHint, + "^setRenderHint\\s+([\\w_0-9]*)\\s*(\\w*)$", + "setRenderHint <hint> <true|false>", + "setRenderHint Antialiasing true"); + DECL_PAINTCOMMAND("clearRenderHint", command_clearRenderHint, + "^clearRenderHint$", + "clearRenderHint", + "clearRenderHint"); + + DECL_PAINTCOMMANDSECTION("gradients"); + DECL_PAINTCOMMAND("gradient_appendStop", command_gradient_appendStop, + "^gradient_appendStop\\s+([\\w.]*)\\s+#?(\\w*)$", + "gradient_appendStop <pos> <color>", + "gradient_appendStop 1.0 red"); + DECL_PAINTCOMMAND("gradient_clearStops", command_gradient_clearStops, + "^gradient_clearStops$", + "gradient_clearStops", + "gradient_clearStops"); + DECL_PAINTCOMMAND("gradient_setConical", command_gradient_setConical, + "^gradient_setConical\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setConical <cx> <cy> <angle>\n - angle in degrees", + "gradient_setConical 5.0 5.0 45.0"); + DECL_PAINTCOMMAND("gradient_setLinear", command_gradient_setLinear, + "^gradient_setLinear\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setLinear <x1> <y1> <x2> <y2>", + "gradient_setLinear 1.0 1.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setRadial", command_gradient_setRadial, + "^gradient_setRadial\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$", + "gradient_setRadial <cx> <cy> <rad> <fx> <fy>\n - C is the center\n - rad is the radius\n - F is the focal point", + "gradient_setRadial 1.0 1.0 45.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setRadialExtended", command_gradient_setRadialExtended, + "^gradient_setRadialExtended\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$", + "gradient_setRadialExtended <cx> <cy> <rad> <fx> <fy> <frad>\n - C is the center\n - rad is the center radius\n - F is the focal point\n - frad is the focal radius", + "gradient_setRadialExtended 1.0 1.0 45.0 2.0 2.0 45.0"); + DECL_PAINTCOMMAND("gradient_setLinearPen", command_gradient_setLinearPen, + "^gradient_setLinearPen\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$", + "gradient_setLinearPen <x1> <y1> <x2> <y2>", + "gradient_setLinearPen 1.0 1.0 2.0 2.0"); + DECL_PAINTCOMMAND("gradient_setSpread", command_gradient_setSpread, + "^gradient_setSpread\\s+(\\w*)$", + "gradient_setSpread <spread method enum>", + "gradient_setSpread PadSpread"); + DECL_PAINTCOMMAND("gradient_setCoordinateMode", command_gradient_setCoordinateMode, + "^gradient_setCoordinateMode\\s+(\\w*)$", + "gradient_setCoordinateMode <coordinate method enum>", + "gradient_setCoordinateMode ObjectBoundingMode"); + + DECL_PAINTCOMMANDSECTION("drawing ops"); + DECL_PAINTCOMMAND("drawPoint", command_drawPoint, + "^drawPoint\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawPoint <x> <y>", + "drawPoint 10.0 10.0"); + DECL_PAINTCOMMAND("drawLine", command_drawLine, + "^drawLine\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawLine <x1> <y1> <x2> <y2>", + "drawLine 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawLines", command_drawLines, + "^drawLines\\s+\\[([\\w\\s\\-.]*)\\]$", + "drawLines <[ <l1x1> <l1y1> <l1x2> <l1y2> <l2x1> <l2y1> ... ]>", + "drawLines [ 10 10 50 10 50 20 10 20 ]"); + DECL_PAINTCOMMAND("drawRect", command_drawRect, + "^drawRect\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawRect <x> <y> <w> <h>", + "drawRect 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawRoundRect", command_drawRoundRect, + "^drawRoundRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(-?\\w*)?\\s*(-?\\w*)?$", + "drawRoundRect <x> <y> <w> <h> [rx] [ry]", + "drawRoundRect 10 10 20 20 3 3"); + DECL_PAINTCOMMAND("drawRoundedRect", command_drawRoundedRect, + "^drawRoundedRect\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(\\w*)?$", + "drawRoundedRect <x> <y> <w> <h> <rx> <ry> [SizeMode enum]", + "drawRoundedRect 10 10 20 20 4 4 AbsoluteSize"); + DECL_PAINTCOMMAND("drawArc", command_drawArc, + "^drawArc\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawArc <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawArc 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawChord", command_drawChord, + "^drawChord\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawChord <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawChord 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawEllipse", command_drawEllipse, + "^drawEllipse\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "drawEllipse <x> <y> <w> <h>", + "drawEllipse 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("drawPath", command_drawPath, + "^drawPath\\s+(\\w*)$", + "drawPath <pathName>", + "drawPath mypath"); + DECL_PAINTCOMMAND("drawPie", command_drawPie, + "^drawPie\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "drawPie <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in 1/16th of degree", + "drawPie 10 10 20 20 0 5760"); + DECL_PAINTCOMMAND("drawPixmap", command_drawPixmap, + "^drawPixmap\\s+([\\w.:\\-/]*)" + "\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?" // target rect + "\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?$", // source rect + "drawPixmap <filename> <tx> <ty> <tw> <th> <sx> <sy> <sw> <sh>" + "\n- where t means target and s means source" + "\n- a width or height of -1 means maximum space", + "drawPixmap :/images/face.png 0 0 -1 -1 0 0 -1 -1"); + DECL_PAINTCOMMAND("drawImage", command_drawImage, + "^drawImage\\s+([\\w.:\\/]*)" + "\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?" // target rect + "\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?\\s*(-?[\\w.]*)?$", // source rect + "drawImage <filename> <tx> <ty> <tw> <th> <sx> <sy> <sw> <sh>" + "\n- where t means target and s means source" + "\n- a width or height of -1 means maximum space", + "drawImage :/images/face.png 0 0 -1 -1 0 0 -1 -1"); + DECL_PAINTCOMMAND("drawPolygon", command_drawPolygon, + "^drawPolygon\\s+\\[([\\w\\s\\-.]*)\\]\\s*(\\w*)$", + "drawPolygon <[ <x1> <y1> ... <xn> <yn> ]> <Winding|OddEven>", + "drawPolygon [ 1 4 6 8 5 3 ] Winding"); + DECL_PAINTCOMMAND("drawConvexPolygon", command_drawConvexPolygon, + "^drawConvexPolygon\\s+\\[([\\w\\s-.]*)\\]$", + "drawConvexPolygon <[ <x1> <y1> ... <xn> <yn> ]>", + "drawConvexPolygon [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("drawPolyline", command_drawPolyline, + "^drawPolyline\\s+\\[([\\w\\s\\-.]*)\\]$", + "drawPolyline <[ <x1> <y1> ... <xn> <yn> ]>", + "drawPolyline [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("drawText", command_drawText, + "^drawText\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawText <x> <y> <text>", + "drawText 10 10 \"my text\""); + DECL_PAINTCOMMAND("drawStaticText", command_drawStaticText, + "^drawStaticText\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawStaticText <x> <y> <text>", + "drawStaticText 10 10 \"my text\""); + DECL_PAINTCOMMAND("drawGlyphRun", command_drawGlyphRun, + "^drawGlyphRun\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawGlyphRun <x> <y> <text> - Will create glyph run using QTextLayout and draw this", + "drawGlyphRun 10 10 \"my text\""); +#ifndef QT_NO_TEXTHTMLPARSER + DECL_PAINTCOMMAND("drawTextDocument", command_drawTextDocument, + "^drawTextDocument\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawTextDocument <x> <y> <html>", + "drawTextDocument 10 10 \"html\""); +#endif + DECL_PAINTCOMMAND("drawTiledPixmap", command_drawTiledPixmap, + "^drawTiledPixmap\\s+([\\w.:\\/]*)" + "\\s+(-?\\w*)\\s+(-?\\w*)\\s*(-?\\w*)\\s*(-?\\w*)" + "\\s*(-?\\w*)\\s*(-?\\w*)$", + "drawTiledPixmap <tile image filename> <tx> <ty> <tx> <ty> <sx> <sy>" + "\n - where t means tile" + "\n - and s is an offset in the tile", + "drawTiledPixmap :/images/alpha.png "); + DECL_PAINTCOMMAND("fillRect", command_fillRect, + "^fillRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(\\w*)?$", + "fillRect <x> <y> <w> <h> [color]\n - Uses current brush if no color given", + "fillRect 10 10 20 20 blue"); + DECL_PAINTCOMMAND("fillRectF", command_fillRectF, + "^fillRectF\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s*(\\w*)?$", + "fillRectF <x> <y> <w> <h> [color]\n - Uses current brush if no color given", + "fillRectF 10.5 10.5 20.2 20.2 blue"); + DECL_PAINTCOMMAND("drawPixmapFragments", command_drawPixmapFragments, + "^drawPixmapFragments\\s+([\\w.:\\/]*)" + "\\s+(-?\\w*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?$", + "drawPixmapFragments <image filename> <count>" + " <centerx0> <centery0> <x0> <y0> <w0> <h0> <sx0> <sy0> <r0> <o0>" + " <centerx1> <centery1> <x1> <y1> <w1> ..." + "\n - where count is 1 or 2, and followed by centerPos, sourceRect, scaleX, scaleY, rotation, opacity <count> times", + "drawPixmapFragments :/images/sign.png 1 50 50 10 10 60 60 10 10 30 1"); + + DECL_PAINTCOMMANDSECTION("painterPaths"); + DECL_PAINTCOMMAND("path_moveTo", command_path_moveTo, + "^path_moveTo\\s+([.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_moveTo <pathName> <x> <y>", + "path_moveTo mypath 1.0 1.0"); + DECL_PAINTCOMMAND("path_lineTo", command_path_lineTo, + "^path_lineTo\\s+([.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_lineTo <pathName> <x> <y>", + "path_lineTo mypath 1.0 1.0"); + DECL_PAINTCOMMAND("path_addEllipse", command_path_addEllipse, + "^path_addEllipse\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_addEllipse <pathName> <x1> <y1> <x2> <y2>", + "path_addEllipse mypath 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_addPolygon", command_path_addPolygon, + "^path_addPolygon\\s+(\\w*)\\s+\\[([\\w\\s]*)\\]\\s*(\\w*)$", + "path_addPolygon <pathName> <[ <x1> <y1> ... <xn> <yn> ]>", + "path_addPolygon mypath [ 1 4 6 8 5 3 ]"); + DECL_PAINTCOMMAND("path_addRect", command_path_addRect, + "^path_addRect\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_addRect <pathName> <x1> <y1> <x2> <y2>", + "path_addRect mypath 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_addText", command_path_addText, + "^path_addText\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+\"(.*)\"$", + "path_addText <pathName> <x> <y> <text>", + "path_addText mypath 10.0 20.0 \"some text\""); + DECL_PAINTCOMMAND("path_arcTo", command_path_arcTo, + "^path_arcTo\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_arcTo <pathName> <x> <y> <w> <h> <angleStart> <angleArc>\n - angles are expressed in degrees", + "path_arcTo mypath 0.0 0.0 10.0 10.0 0.0 360.0"); + DECL_PAINTCOMMAND("path_cubicTo", command_path_cubicTo, + "^path_cubicTo\\s+(\\w*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "path_cubicTo <pathName> <x1> <y1> <x2> <y2> <x3> <y3>", + "path_cubicTo mypath 0.0 0.0 10.0 10.0 20.0 20.0"); + DECL_PAINTCOMMAND("path_closeSubpath", command_path_closeSubpath, + "^path_closeSubpath\\s+(\\w*)$", + "path_closeSubpath <pathName>", + "path_closeSubpath mypath"); + DECL_PAINTCOMMAND("path_createOutline", command_path_createOutline, + "^path_createOutline\\s+(\\w*)\\s+(\\w*)$", + "path_createOutline <pathName> <newName>", + "path_createOutline mypath myoutline"); + DECL_PAINTCOMMAND("path_debugPrint", command_path_debugPrint, + "^path_debugPrint\\s+(\\w*)$", + "path_debugPrint <pathName>", + "path_debugPrint mypath"); + + DECL_PAINTCOMMANDSECTION("regions"); + DECL_PAINTCOMMAND("region_addRect", command_region_addRect, + "^region_addRect\\s+(\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "region_addRect <regionName> <x1> <y1> <x2> <y2>", + "region_addRect myregion 0.0 0.0 10.0 10.0"); + DECL_PAINTCOMMAND("region_addEllipse", command_region_addEllipse, + "^region_addEllipse\\s+(\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)$", + "region_addEllipse <regionName> <x1> <y1> <x2> <y2>", + "region_addEllipse myregion 0.0 0.0 10.0 10.0"); + + DECL_PAINTCOMMANDSECTION("clipping"); + DECL_PAINTCOMMAND("region_getClipRegion", command_region_getClipRegion, + "^region_getClipRegion\\s+(\\w*)$", + "region_getClipRegion <regionName>", + "region_getClipRegion myregion"); + DECL_PAINTCOMMAND("setClipRegion", command_setClipRegion, + "^setClipRegion\\s+(\\w*)\\s*(\\w*)$", + "setClipRegion <regionName> <clip operation enum>", + "setClipRegion myregion ReplaceClip"); + DECL_PAINTCOMMAND("path_getClipPath", command_path_getClipPath, + "^path_getClipPath\\s+([\\w0-9]*)$", + "path_getClipPath <pathName>", + "path_getClipPath mypath"); + DECL_PAINTCOMMAND("setClipPath", command_setClipPath, + "^setClipPath\\s+(\\w*)\\s*(\\w*)$", + "setClipPath <pathName> <clip operation enum>", + "setClipPath mypath ReplaceClip"); + DECL_PAINTCOMMAND("setClipRect", command_setClipRect, + "^setClipRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(\\w*)$", + "setClipRect <x> <y> <w> <h> <clip operation enum>", + "setClipRect 0 0 10 10 ReplaceClip"); + DECL_PAINTCOMMAND("setClipRectF", command_setClipRectF, + "^setClipRectF\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s*(\\w.*)$", + "setClipRectF <x> <y> <w> <h> <clip operation enum>", + "setClipRectF 0.1 0.2 10.3 10.4 ReplaceClip"); + DECL_PAINTCOMMAND("setClipping", command_setClipping, + "^setClipping\\s+(\\w*)$", + "setClipping <true|false>", + "setClipping true"); + + DECL_PAINTCOMMANDSECTION("surface"); + DECL_PAINTCOMMAND("surface_begin", command_surface_begin, + "^surface_begin\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "surface_begin <x> <y> <w> <h>", + "surface_begin 0.0 0.0 10.0 10.0"); + DECL_PAINTCOMMAND("surface_end", command_surface_end, + "^surface_end$", + "surface_end", + "surface_end"); + + DECL_PAINTCOMMANDSECTION("painter states"); + DECL_PAINTCOMMAND("restore", command_restore, + "^restore$", + "restore", + "restore"); + DECL_PAINTCOMMAND("save", command_save, + "^save$", + "save", + "save"); + + DECL_PAINTCOMMANDSECTION("pixmaps'n'images"); + DECL_PAINTCOMMAND("pixmap_load", command_pixmap_load, + "^pixmap_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "pixmap_load <image filename> <pixmapName>", + "pixmap_load :/images/face.png myPixmap"); + DECL_PAINTCOMMAND("pixmap_setMask", command_pixmap_setMask, + "^pixmap_setMask\\s+([\\w.:\\/]*)\\s+([\\w.:\\/]*)$", + "pixmap_setMask <pixmapName> <bitmap filename>", + "pixmap_setMask myPixmap :/images/bitmap.png"); + DECL_PAINTCOMMAND("bitmap_load", command_bitmap_load, + "^bitmap_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "bitmap_load <bitmap filename> <bitmapName>\n - note that the image is stored as a pixmap", + "bitmap_load :/images/bitmap.png myBitmap"); + DECL_PAINTCOMMAND("pixmap_setDevicePixelRatio", command_pixmap_setDevicePixelRatio, + "^pixmap_setDevicePixelRatio\\s+([\\w.:\\/]*)\\s+([.0-9]*)$", + "pixmap_setDevicePixelRatio <pixmapName> <dpr>", + "pixmap_setDevicePixelRatio myPixmap 2.0"); + DECL_PAINTCOMMAND("image_convertToFormat", command_image_convertToFormat, + "^image_convertToFormat\\s+([\\w.:\\/]*)\\s+([\\w.:\\/]+)\\s+([\\w0-9_]*)$", + "image_convertToFormat <sourceImageName> <destImageName> <image format enum>", + "image_convertToFormat myImage myNewImage Indexed8"); + DECL_PAINTCOMMAND("image_load", command_image_load, + "^image_load\\s+([\\w.:\\/]*)\\s*([\\w.:\\/]*)$", + "image_load <filename> <imageName>", + "image_load :/images/face.png myImage"); + DECL_PAINTCOMMAND("image_setColor", command_image_setColor, + "^image_setColor\\s+([\\w.:\\/]*)\\s+([0-9]*)\\s+#([0-9]*)$", + "image_setColor <imageName> <index> <color>", + "image_setColor myImage 0 black"); + DECL_PAINTCOMMAND("image_setColorCount", command_image_setColorCount, + "^image_setColorCount\\s+([\\w.:\\/]*)\\s+([0-9]*)$", + "image_setColorCount <imageName> <nbColors>", + "image_setColorCount myImage 128"); + DECL_PAINTCOMMAND("image_setDevicePixelRatio", command_image_setDevicePixelRatio, + "^image_setDevicePixelRatio\\s+([\\w.:\\/]*)\\s+([.0-9]*)$", + "image_setDevicePixelRatio <imageName> <dpr>", + "image_setDevicePixelRatio myImage 2.0"); + + DECL_PAINTCOMMANDSECTION("transformations"); + DECL_PAINTCOMMAND("resetMatrix", command_resetMatrix, + "^resetMatrix$", + "resetMatrix", + "resetMatrix"); + DECL_PAINTCOMMAND("setMatrix", command_setMatrix, + "^setMatrix\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "setMatrix <m11> <m12> <m13> <m21> <m22> <m23> <m31> <m32> <m33>", + "setMatrix 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0"); + DECL_PAINTCOMMAND("translate", command_translate, + "^translate\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "translate <tx> <ty>", + "translate 10.0 10.0"); + DECL_PAINTCOMMAND("rotate", command_rotate, + "^rotate\\s+(-?[\\w.]*)$", + "rotate <angle>\n - with angle in degrees", + "rotate 30.0"); + DECL_PAINTCOMMAND("rotate_x", command_rotate_x, + "^rotate_x\\s+(-?[\\w.]*)$", + "rotate_x <angle>\n - with angle in degrees", + "rotate_x 30.0"); + DECL_PAINTCOMMAND("rotate_y", command_rotate_y, + "^rotate_y\\s+(-?[\\w.]*)$", + "rotate_y <angle>\n - with angle in degrees", + "rotate_y 30.0"); + DECL_PAINTCOMMAND("scale", command_scale, + "^scale\\s+(-?[\\w.]*)\\s+(-?[\\w.]*)$", + "scale <sx> <sy>", + "scale 2.0 1.0"); + DECL_PAINTCOMMAND("mapQuadToQuad", command_mapQuadToQuad, + "^mapQuadToQuad\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)$", + "mapQuadToQuad <x1> <y1> <x2> <y2> <x3> <y3> <x4> <y4> <x5> <y5> <x6> <y6> <x7> <y7> <x8> <y8>" + "\n - where vertices 1 to 4 defines the source quad and 5 to 8 the destination quad", + "mapQuadToQuad 0.0 0.0 1.0 1.0 0.0 0.0 -1.0 -1.0"); + + // populate the command lookup hash + for (int i=0; i<s_commandInfoTable.size(); i++) { + // and pre-optimize the regexps. + s_commandInfoTable.at(i).regExp.optimize(); + if (s_commandInfoTable.at(i).isSectionHeader() || + s_commandInfoTable.at(i).identifier == QLatin1String("comment") || + s_commandInfoTable.at(i).identifier == QLatin1String("noop")) + continue; + s_commandHash.insert(s_commandInfoTable.at(i).identifier, i); + } + + // populate the enums list + ADD_ENUMLIST("brush styles", brushStyleTable); + ADD_ENUMLIST("pen styles", penStyleTable); + ADD_ENUMLIST("font weights", fontWeightTable); + ADD_ENUMLIST("font hintings", fontHintingTable); + ADD_ENUMLIST("clip operations", clipOperationTable); + ADD_ENUMLIST("spread methods", spreadMethodTable); + ADD_ENUMLIST("composition modes", compositionModeTable); + ADD_ENUMLIST("image formats", imageFormatTable); + ADD_ENUMLIST("coordinate modes", coordinateMethodTable); + ADD_ENUMLIST("size modes", sizeModeTable); + ADD_ENUMLIST("render hints", renderHintTable); +} + +#undef DECL_PAINTCOMMAND +#undef ADD_ENUMLIST +/********************************************************************************* +** utility +**********************************************************************************/ +template <typename T> T PaintCommands::image_load(const QString &filepath) +{ + T t(filepath); + + if (t.isNull()) + t = T(":images/" + filepath); + + if (t.isNull()) + t = T("images/" + filepath); + + if (t.isNull()) { + QFileInfo fi(filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + fi.fileName(); + t = T(fileName); + if (t.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + t = T(fileName); + } + } + + return t; +} + +/********************************************************************************* +** setters +**********************************************************************************/ +void PaintCommands::insertAt(int commandIndex, const QStringList &newCommands) +{ + int index = 0; + int left = newCommands.size(); + while (left--) + m_commands.insert(++commandIndex, newCommands.at(index++)); +} + +/********************************************************************************* +** run +**********************************************************************************/ +void PaintCommands::runCommand(const QString &scriptLine) +{ + static QRegularExpression separators("\\s"); + if (scriptLine.isEmpty()) { + command_noop(QRegularExpressionMatch()); + return; + } + if (scriptLine.startsWith('#')) { + command_comment(QRegularExpressionMatch()); + return; + } + QString firstWord = scriptLine.section(separators, 0, 0); + const QList<int> indices = s_commandHash.values(firstWord); + for (int idx : indices) { + PaintCommandInfos command = s_commandInfoTable.at(idx); + Q_ASSERT(command.regExp.isValid()); + QRegularExpressionMatch match = command.regExp.match(scriptLine); + if (match.hasMatch()) { + (this->*(command.paintMethod))(match); + return; + } + } + qWarning("ERROR: unknown command or argument syntax error in \"%s\"", qPrintable(scriptLine)); +} + +void PaintCommands::runCommands() +{ + staticInit(); + int width = m_painter->window().width(); + int height = m_painter->window().height(); + + if (width <= 0) + width = 800; + if (height <= 0) + height = 800; + + m_pathMap.clear(); + m_imageMap.clear(); + m_pixmapMap.clear(); + m_regionMap.clear(); + m_gradientStops.clear(); + m_blockMap.clear(); + + // paint background + if (m_checkers_background) { + QPixmap pm(20, 20); + pm.fill(Qt::white); + QPainter pt(&pm); + pt.fillRect(0, 0, 10, 10, QColor::fromRgba(0xffdfdfdf)); + pt.fillRect(10, 10, 10, 10, QColor::fromRgba(0xffdfdfdf)); + pt.end(); + m_painter->drawTiledPixmap(0, 0, width, height, pm); + } else { + m_painter->fillRect(0, 0, width, height, Qt::white); + } + + // run each command + m_abort = false; + for (int i=0; i<m_commands.size() && !m_abort; ++i) { + const QString &commandNow = m_commands.at(i); + m_currentCommand = commandNow; + m_currentCommandIndex = i; + runCommand(commandNow.trimmed()); + } +} + +/********************************************************************************* +** conversions +**********************************************************************************/ +int PaintCommands::convertToInt(const QString &str) +{ + return qRound(convertToDouble(str)); +} + +float PaintCommands::convertToFloat(const QString &str) +{ + return float(convertToDouble(str)); +} + +double PaintCommands::convertToDouble(const QString &str) +{ + static QRegularExpression re("cp([0-9])([xy])"); + if (str.toLower() == "width") { + if (m_painter->device()->devType() == Qt::Widget) + return m_painter->window().width(); + else + return 800; + } + if (str.toLower() == "height") { + if (m_painter->device()->devType() == Qt::Widget) + return m_painter->window().height(); + else + return 800; + } + QRegularExpressionMatch match = re.match(str); + if (match.hasMatch()) { + int index = match.captured(1).toInt(); + bool is_it_x = match.captured(2) == "x"; + if (index < 0 || index >= m_controlPoints.size()) { + qWarning("ERROR: control point index=%d is out of bounds", index); + return 0; + } + return is_it_x ? m_controlPoints.at(index).x() : m_controlPoints.at(index).y(); + } + return str.toDouble(); +} + +QColor PaintCommands::convertToColor(const QString &str) +{ + static QRegularExpression alphaColorRe("#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})"); + static QRegularExpression opaqueColorRe("#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})"); + + Q_ASSERT(alphaColorRe.isValid()); + Q_ASSERT(opaqueColorRe.isValid()); + + QRegularExpressionMatch alphaColor = alphaColorRe.match(str); + QRegularExpressionMatch opaqueColor = opaqueColorRe.match(str); + if (alphaColor.hasMatch()) { + return QColor(alphaColor.captured(2).toInt(0, 16), + alphaColor.captured(3).toInt(0, 16), + alphaColor.captured(4).toInt(0, 16), + alphaColor.captured(1).toInt(0, 16)); + } else if (opaqueColor.hasMatch()) { + return QColor(opaqueColor.captured(1).toInt(0, 16), + opaqueColor.captured(2).toInt(0, 16), + opaqueColor.captured(3).toInt(0, 16)); + } + return QColor(str); +} + +/********************************************************************************* +** command implementations +**********************************************************************************/ +void PaintCommands::command_comment(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) comment: %s\n", qPrintable(m_currentCommand)); +} + +/***************************************************************************************************/ +void PaintCommands::command_import(QRegularExpressionMatch re) +{ + QString importFile(re.captured(1)); + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + QFile *file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + + if (importFile.isEmpty() || !file->exists()) { + dir.cdUp(); + dir.cd("data"); + dir.cd("qps"); + delete file; + file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + } + + if (importFile.isEmpty() || !file->exists()) { + dir.cdUp(); + dir.cd("images"); + delete file; + file = new QFile(dir.absolutePath() + QDir::separator() + importFile); + } + + if (importFile.isEmpty() || !file->exists()) { + printf(" - importing non-existing file at line %d (%s)\n", m_currentCommandIndex, + qPrintable(file->fileName())); + delete file; + return; + } + + if (!file->open(QIODevice::ReadOnly)) { + printf(" - failed to read file: '%s'\n", qPrintable(file->fileName())); + delete file; + return; + } + if (m_verboseMode) + printf(" -(lance) importing file at line %d (%s)\n", m_currentCommandIndex, + qPrintable(fi.fileName())); + + QFileInfo fileinfo(*file); + m_commands[m_currentCommandIndex] = QLatin1String("# import file (") + fileinfo.fileName() + + QLatin1String(") start"); + QString rawContent = QString::fromUtf8(file->readAll()); + QStringList importedData = rawContent.split('\n', Qt::SkipEmptyParts); + importedData.append(QLatin1String("# import file (") + fileinfo.fileName() + QLatin1String(") end ---")); + insertAt(m_currentCommandIndex, importedData); + + if (m_verboseMode) { + printf(" -(lance) Command buffer now looks like:\n"); + for (int i = 0; i < m_commands.size(); ++i) + printf(" ---> {%s}\n", qPrintable(m_commands.at(i))); + } + delete file; +} + +/***************************************************************************************************/ +void PaintCommands::command_begin_block(QRegularExpressionMatch re) +{ + const QString &blockName = re.captured(1); + if (m_verboseMode) + printf(" -(lance) begin_block (%s)\n", qPrintable(blockName)); + if (m_blockMap.contains(blockName)) + qFatal("Two blocks named (%s)", qPrintable(blockName)); + + m_commands[m_currentCommandIndex] = QLatin1String("# begin block (") + blockName + QLatin1Char(')'); + QStringList newBlock; + int i = m_currentCommandIndex + 1; + for (; i < m_commands.size(); ++i) { + const QString &nextCmd = m_commands.at(i); + if (nextCmd.startsWith("end_block")) { + m_commands[i] = QLatin1String("# end block (") + blockName + QLatin1Char(')'); + break; + } + newBlock += nextCmd; + } + + if (m_verboseMode) + for (int j = 0; j < newBlock.size(); ++j) + printf(" %d: %s\n", j, qPrintable(newBlock.at(j))); + + if (i >= m_commands.size()) + printf(" - Warning! Block doesn't have an 'end_block' marker!\n"); + + m_blockMap.insert(blockName, newBlock); +} + +/***************************************************************************************************/ +void PaintCommands::command_end_block(QRegularExpressionMatch) +{ + printf(" - end_block should be consumed by begin_block command.\n"); + printf(" You will never see this if your block markers are in sync\n"); + printf(" (noop)\n"); +} + +/***************************************************************************************************/ +void PaintCommands::command_repeat_block(QRegularExpressionMatch re) +{ + QString blockName = re.captured(1); + if (m_verboseMode) + printf(" -(lance) repeating block (%s)\n", qPrintable(blockName)); + + QStringList block = m_blockMap.value(blockName); + if (block.isEmpty()) { + printf(" - repeated block (%s) is empty!\n", qPrintable(blockName)); + return; + } + + m_commands[m_currentCommandIndex] = QLatin1String("# repeated block (") + blockName + QLatin1Char(')'); + insertAt(m_currentCommandIndex, block); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawLine(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawLine((%.2f, %.2f), (%.2f, %.2f))\n", x1, y1, x2, y2); + + m_painter->drawLine(QLineF(x1, y1, x2, y2)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawLines(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList numbers = re.captured(1).split(separators, Qt::SkipEmptyParts); + + QList<QLineF> array; + for (int i = 0; i + 3 < numbers.size(); i += 4) { + QPointF pt1(numbers.at(i).toFloat(), numbers.at(i + 1).toFloat()); + QPointF pt2(numbers.at(i + 2).toFloat(), numbers.at(i + 3).toFloat()); + array.append(QLineF(pt1, pt2)); + } + + if (m_verboseMode) + printf(" -(lance) drawLines(size=%zd)\n", size_t(array.size())); + + m_painter->drawLines(array); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPath(QRegularExpressionMatch re) +{ + if (m_verboseMode) + printf(" -(lance) drawPath(name=%s)\n", qPrintable(re.captured(1))); + + QPainterPath &path = m_pathMap[re.captured(1)]; + m_painter->drawPath(path); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPixmap(QRegularExpressionMatch re) +{ + QPixmap pm; + pm = m_pixmapMap[re.captured(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.captured(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + re.captured(1); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawPixmap): failed to load pixmap: '%s'\n", + qPrintable(re.captured(1))); + return; + } + + qreal tx = convertToFloat(re.captured(2)); + qreal ty = convertToFloat(re.captured(3)); + qreal tw = convertToFloat(re.captured(4)); + qreal th = convertToFloat(re.captured(5)); + + qreal sx = convertToFloat(re.captured(6)); + qreal sy = convertToFloat(re.captured(7)); + qreal sw = convertToFloat(re.captured(8)); + qreal sh = convertToFloat(re.captured(9)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + if (sw == 0) sw = -1; + if (sh == 0) sh = -1; + + if (m_verboseMode) + printf(" -(lance) drawPixmap('%s' dim=(%d, %d), depth=%d, (%f, %f, %f, %f), (%f, %f, %f, %f)\n", + qPrintable(re.captured(1)), pm.width(), pm.height(), pm.depth(), + tx, ty, tw, th, sx, sy, sw, sh); + + if (!re.capturedLength(4)) // at most two coordinates specified + m_painter->drawPixmap(QPointF(tx, ty), pm); + else + m_painter->drawPixmap(QRectF(tx, ty, tw, th), pm, QRectF(sx, sy, sw, sh)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawImage(QRegularExpressionMatch re) +{ + QImage im; + im = m_imageMap[re.captured(1)]; // try cache first + if (im.isNull()) + im = image_load<QImage>(re.captured(1)); + + if (im.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + re.captured(1); + im = QImage(fileName); + if (im.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + im = QImage(fileName); + } + } + if (im.isNull()) { + fprintf(stderr, "ERROR(drawImage): failed to load image: '%s'\n", qPrintable(re.captured(1))); + return; + } + + qreal tx = convertToFloat(re.captured(2)); + qreal ty = convertToFloat(re.captured(3)); + qreal tw = convertToFloat(re.captured(4)); + qreal th = convertToFloat(re.captured(5)); + + qreal sx = convertToFloat(re.captured(6)); + qreal sy = convertToFloat(re.captured(7)); + qreal sw = convertToFloat(re.captured(8)); + qreal sh = convertToFloat(re.captured(9)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + if (sw == 0) sw = -1; + if (sh == 0) sh = -1; + + if (m_verboseMode) + printf(" -(lance) drawImage('%s' dim=(%d, %d), (%f, %f, %f, %f), (%f, %f, %f, %f)\n", + qPrintable(re.captured(1)), im.width(), im.height(), tx, ty, tw, th, sx, sy, sw, sh); + + if (!re.capturedLength(4)) // at most two coordinates specified + m_painter->drawImage(QPointF(tx, ty), im); + else + m_painter->drawImage(QRectF(tx, ty, tw, th), im, QRectF(sx, sy, sw, sh)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawTiledPixmap(QRegularExpressionMatch re) +{ + QPixmap pm; + pm = m_pixmapMap[re.captured(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.captured(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + re.captured(1); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawTiledPixmap): failed to load pixmap: '%s'\n", + qPrintable(re.captured(1))); + return; + } + + int tx = convertToInt(re.captured(2)); + int ty = convertToInt(re.captured(3)); + int tw = convertToInt(re.captured(4)); + int th = convertToInt(re.captured(5)); + + int sx = convertToInt(re.captured(6)); + int sy = convertToInt(re.captured(7)); + + if (tw == 0) tw = -1; + if (th == 0) th = -1; + + if (m_verboseMode) + printf(" -(lance) drawTiledPixmap('%s' dim=(%d, %d), (%d, %d, %d, %d), (%d, %d)\n", + qPrintable(re.captured(1)), pm.width(), pm.height(), tx, ty, tw, th, sx, sy); + + m_painter->drawTiledPixmap(tx, ty, tw, th, pm, sx, sy); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPoint(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) drawPoint(%.2f, %.2f)\n", x, y); + + m_painter->drawPoint(QPointF(x, y)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPolygon(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, Qt::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(convertToDouble(numbers.at(i)), convertToDouble(numbers.at(i+1)))); + + if (m_verboseMode) + printf(" -(lance) drawPolygon(size=%zd)\n", size_t(array.size())); + + m_painter->drawPolygon(array, caps.at(2).toLower() == "winding" ? Qt::WindingFill : Qt::OddEvenFill); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPolyline(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList numbers = re.captured(1).split(separators, Qt::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(numbers.at(i).toFloat(),numbers.at(i+1).toFloat())); + + if (m_verboseMode) + printf(" -(lance) drawPolyline(size=%zd)\n", size_t(array.size())); + + m_painter->drawPolyline(array.toPolygon()); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawRect(%.2f, %.2f, %.2f, %.2f)\n", x, y, w, h); + + m_painter->drawRect(QRectF(x, y, w, h)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRoundedRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + float xr = convertToFloat(caps.at(5)); + float yr = convertToFloat(caps.at(6)); + + int mode = translateEnum(sizeModeTable, caps.at(7), sizeof(sizeModeTable)/sizeof(char *)); + if (mode < 0) + mode = Qt::AbsoluteSize; + + if (m_verboseMode) + printf(" -(lance) drawRoundRect(%f, %f, %f, %f, %f, %f, %s)\n", x, y, w, h, xr, yr, mode ? "RelativeSize" : "AbsoluteSize"); + + m_painter->drawRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::SizeMode(mode)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawRoundRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int xs = caps.at(5).isEmpty() ? 50 : convertToInt(caps.at(5)); + int ys = caps.at(6).isEmpty() ? 50 : convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawRoundRect(%d, %d, %d, %d, [%d, %d])\n", x, y, w, h, xs, ys); + + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED + m_painter->drawRoundedRect(x, y, w, h, xs, ys, Qt::RelativeSize); + QT_WARNING_POP +} + +/***************************************************************************************************/ +void PaintCommands::command_drawEllipse(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + float x = convertToFloat(caps.at(1)); + float y = convertToFloat(caps.at(2)); + float w = convertToFloat(caps.at(3)); + float h = convertToFloat(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) drawEllipse(%.2f, %.2f, %.2f, %.2f)\n", x, y, w, h); + + m_painter->drawEllipse(QRectF(x, y, w, h)); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawPie(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawPie(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawPie(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawChord(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawChord(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawChord(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawArc(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + int angle = convertToInt(caps.at(5)); + int sweep = convertToInt(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) drawArc(%d, %d, %d, %d, %d, %d)\n", x, y, w, h, angle, sweep); + + m_painter->drawArc(x, y, w, h, angle, sweep); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawText(QRegularExpressionMatch re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawText(%d, %d, %s)\n", x, y, qPrintable(txt)); + + m_painter->drawText(x, y, txt); +} + +void PaintCommands::command_drawStaticText(QRegularExpressionMatch re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawStaticText(%d, %d, %s)\n", x, y, qPrintable(txt)); + + m_painter->drawStaticText(x, y, QStaticText(txt)); +} + +void PaintCommands::command_drawGlyphRun(QRegularExpressionMatch re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawGlyphRun(%d, %d, %s)\n", x, y, qPrintable(txt)); + + QTextLayout layout; + layout.setFont(m_painter->font()); + layout.setText(txt); + layout.beginLayout(); + qreal lineY = 0.0; + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + line.setPosition(QPointF(0.0, lineY)); + lineY += line.height(); + } + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + + for (const QGlyphRun &glyphRun : glyphRuns) + m_painter->drawGlyphRun(QPointF(x, y), glyphRun); +} + +#ifndef QT_NO_TEXTHTMLPARSER +void PaintCommands::command_drawTextDocument(QRegularExpressionMatch re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawTextDocument(%d, %d, %s)\n", x, y, qPrintable(txt)); + + QTextDocument doc; + doc.setBaseUrl(QUrl::fromLocalFile(QDir::currentPath() + QLatin1String("/"))); + doc.setHtml(txt); + + m_painter->save(); + m_painter->translate(x, y); + doc.drawContents(m_painter); + m_painter->restore(); +} +#endif + +/***************************************************************************************************/ +void PaintCommands::command_fillRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + + if (!caps.at(5).isEmpty()) { + QColor color = convertToColor(caps.at(5)); + if (m_verboseMode) + printf(" -(lance) fillRect(%d, %d, %d, %d, %s)\n", x, y, w, h, qPrintable(color.name())); + m_painter->fillRect(x, y, w, h, color); + } else { + if (m_verboseMode) + printf(" -(lance) fillRect(%d, %d, %d, %d)\n", x, y, w, h); + m_painter->fillRect(x, y, w, h, m_painter->brush()); + } +} + +void PaintCommands::command_fillRectF(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x = convertToDouble(caps.at(1)); + double y = convertToDouble(caps.at(2)); + double w = convertToDouble(caps.at(3)); + double h = convertToDouble(caps.at(4)); + + if (!caps.at(5).isEmpty()) { + QColor color = convertToColor(caps.at(5)); + if (m_verboseMode) + printf(" -(lance) fillRectF(%.2f, %.2f, %.2f, %.2f, %s)\n", x, y, w, h, qPrintable(color.name())); + m_painter->fillRect(QRectF(x, y, w, h), color); + } else { + if (m_verboseMode) + printf(" -(lance) fillRectF(%.2f, %.2f, %.2f, %.2f)\n", x, y, w, h); + m_painter->fillRect(QRectF(x, y, w, h), m_painter->brush()); + } +} + +void PaintCommands::command_drawPixmapFragments(QRegularExpressionMatch re) +{ + QPixmap pm; + pm = m_pixmapMap[re.captured(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.captured(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + re.captured(1); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawPixmapFragments): failed to load pixmap: '%s'\n", + qPrintable(re.captured(1))); + return; + } + + int count = convertToInt(re.captured(2)); + + struct Fragment { + double posx; + double posy; + double srcx; + double srcy; + double srcw; + double srch; + double sx; + double sy; + double rotation; + double opacity; + }; + + QList<Fragment> fragments; + for (int i = 0; i < count; ++i) { + int captureIndexStart = 3 + i * 10; + if (re.hasCaptured(captureIndexStart)) { + Fragment f; + f.posx = convertToDouble(re.captured(captureIndexStart)); + f.posy = convertToDouble(re.captured(captureIndexStart + 1)); + f.srcx = convertToDouble(re.captured(captureIndexStart + 2)); + f.srcy = convertToDouble(re.captured(captureIndexStart + 3)); + f.srcw = convertToDouble(re.captured(captureIndexStart + 4)); + f.srch = convertToDouble(re.captured(captureIndexStart + 5)); + f.sx = convertToDouble(re.captured(captureIndexStart + 6)); + f.sy = convertToDouble(re.captured(captureIndexStart + 7)); + f.rotation = convertToDouble(re.captured(captureIndexStart + 8)); + f.opacity = convertToDouble(re.captured(captureIndexStart + 9)); + fragments.append(f); + } else { + break; + } + } + + if (m_verboseMode) { + printf(" -(lance) drawPixmapFragments('%s' count=%d ", + qPrintable(re.captured(1)), int(fragments.count())); + for (int i = 0; i < fragments.count(); ++i) { + printf("pos=(%.2f, %.2f) srcrect=(%.2f %.2f %.2f %.2f) scale=(%.2f %.2f) rotation=%.2f opacity=%.2f ", + fragments[i].posx, fragments[i].posy, + fragments[i].srcx, fragments[i].srcy, fragments[i].srcw, fragments[i].srch, + fragments[i].sx, fragments[i].sy, + fragments[i].rotation, + fragments[i].opacity); + } + printf("\n"); + } + + QList<QPainter::PixmapFragment> pixmapFragments; + for (int i = 0; i < fragments.count(); ++i) { + pixmapFragments.append( + QPainter::PixmapFragment::create(QPointF(fragments[i].posx, fragments[i].posy), + QRectF(fragments[i].srcx, fragments[i].srcy, fragments[i].srcw, fragments[i].srch), + fragments[i].sx, fragments[i].sy, + fragments[i].rotation, + fragments[i].opacity)); + } + + m_painter->drawPixmapFragments(pixmapFragments.constData(), pixmapFragments.count(), pm); +} + +/***************************************************************************************************/ +void PaintCommands::command_noop(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) noop: %s\n", qPrintable(m_currentCommand)); + + if (!m_currentCommand.trimmed().isEmpty()) { + fprintf(stderr, "unknown command: '%s'\n", qPrintable(m_currentCommand.trimmed())); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addText(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + QString text = caps.at(4); + + if (m_verboseMode) + printf(" -(lance) path_addText(%s, %.2f, %.2f, text=%s\n", qPrintable(name), x, y, qPrintable(text)); + + m_pathMap[name].addText(x, y, m_painter->font(), text); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addEllipse(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) path_addEllipse(%s, %.2f, %.2f, %.2f, %.2f)\n", qPrintable(name), x, y, w, h); + + m_pathMap[name].addEllipse(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) path_addRect(%s, %.2f, %.2f, %.2f, %.2f)\n", qPrintable(name), x, y, w, h); + + m_pathMap[name].addRect(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_addPolygon(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QString cap = caps.at(2); + QStringList numbers = cap.split(separators, Qt::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(numbers.at(i).toFloat(),numbers.at(i+1).toFloat())); + + if (m_verboseMode) + printf(" -(lance) path_addPolygon(name=%s, size=%zd)\n", qPrintable(name), size_t(array.size())); + + m_pathMap[name].addPolygon(array); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_arcTo(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x = convertToDouble(caps.at(2)); + double y = convertToDouble(caps.at(3)); + double w = convertToDouble(caps.at(4)); + double h = convertToDouble(caps.at(5)); + double angle = convertToDouble(caps.at(6)); + double length = convertToDouble(caps.at(7)); + + if (m_verboseMode) + printf(" -(lance) path_arcTo(%s, %.2f, %.2f, %.2f, %.2f, angle=%.2f, len=%.2f)\n", qPrintable(name), x, y, w, h, angle, length); + + m_pathMap[name].arcTo(x, y, w, h, angle, length); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_createOutline(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QString newName = caps.at(2); + QPen pen = m_painter->pen(); + + if (m_verboseMode) + printf(" -(lance) path_createOutline(%s, name=%s, width=%d)\n", + qPrintable(name), qPrintable(newName), pen.width()); + + if (!m_pathMap.contains(name)) { + fprintf(stderr, "createOutline(), unknown path: %s\n", qPrintable(name)); + return; + } + QPainterPathStroker stroker; + stroker.setWidth(pen.widthF()); + stroker.setDashPattern(pen.style()); + stroker.setCapStyle(pen.capStyle()); + stroker.setJoinStyle(pen.joinStyle()); + m_pathMap[newName] = stroker.createStroke(m_pathMap[name]); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_cubicTo(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + double x2 = convertToDouble(caps.at(4)); + double y2 = convertToDouble(caps.at(5)); + double x3 = convertToDouble(caps.at(6)); + double y3 = convertToDouble(caps.at(7)); + + if (m_verboseMode) + printf(" -(lance) path_cubicTo(%s, (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f))\n", qPrintable(name), x1, y1, x2, y2, x3, y3); + + m_pathMap[name].cubicTo(x1, y1, x2, y2, x3, y3); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_moveTo(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) path_moveTo(%s, (%.2f, %.2f))\n", qPrintable(name), x1, y1); + + m_pathMap[name].moveTo(x1, y1); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_lineTo(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + double x1 = convertToDouble(caps.at(2)); + double y1 = convertToDouble(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) path_lineTo(%s, (%.2f, %.2f))\n", qPrintable(name), x1, y1); + + m_pathMap[name].lineTo(x1, y1); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_setFillRule(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + bool winding = caps.at(2).toLower() == "winding"; + + if (m_verboseMode) + printf(" -(lance) path_setFillRule(name=%s, winding=%d)\n", qPrintable(name), winding); + + m_pathMap[name].setFillRule(winding ? Qt::WindingFill : Qt::OddEvenFill); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_closeSubpath(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + + if (m_verboseMode) + printf(" -(lance) path_closeSubpath(name=%s)\n", qPrintable(name)); + + m_pathMap[name].closeSubpath(); +} + +/***************************************************************************************************/ +void PaintCommands::command_path_getClipPath(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + + if (m_verboseMode) + printf(" -(lance) path_closeSubpath(name=%s)\n", qPrintable(name)); + + m_pathMap[name] = m_painter->clipPath(); +} + +/***************************************************************************************************/ +static void qt_debug_path(const QPainterPath &path, const QString &name) +{ + const char *names[] = { + "MoveTo ", + "LineTo ", + "CurveTo ", + "CurveToData" + }; + + printf("\nQPainterPath (%s): elementCount=%d\n", qPrintable(name), path.elementCount()); + for (int i=0; i<path.elementCount(); ++i) { + const QPainterPath::Element &e = path.elementAt(i); + Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement); + printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_path_debugPrint(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + qt_debug_path(m_pathMap[name], name); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_addRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + int x = convertToInt(caps.at(2)); + int y = convertToInt(caps.at(3)); + int w = convertToInt(caps.at(4)); + int h = convertToInt(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) region_addRect(%s, %d, %d, %d, %d)\n", qPrintable(name), x, y, w, h); + + m_regionMap[name] += QRect(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_addEllipse(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + int x = convertToInt(caps.at(2)); + int y = convertToInt(caps.at(3)); + int w = convertToInt(caps.at(4)); + int h = convertToInt(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) region_addEllipse(%s, %d, %d, %d, %d)\n", qPrintable(name), x, y, w, h); + + m_regionMap[name] += QRegion(x, y, w, h, QRegion::Ellipse); +} + +/***************************************************************************************************/ +void PaintCommands::command_region_getClipRegion(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString name = caps.at(1); + QRegion region = m_painter->clipRegion(); + + if (m_verboseMode) + printf(" -(lance) region_getClipRegion(name=%s), bounds=[%d, %d, %d, %d]\n", qPrintable(name), + region.boundingRect().x(), + region.boundingRect().y(), + region.boundingRect().width(), + region.boundingRect().height()); + + m_regionMap[name] = region; +} + +/***************************************************************************************************/ +void PaintCommands::command_resetMatrix(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) resetMatrix()\n"); + + m_painter->resetTransform(); +} + +/***************************************************************************************************/ +void PaintCommands::command_restore(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) restore()\n"); + + m_painter->restore(); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate(%.2f)\n", angle); + + m_painter->rotate(angle); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate_x(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate_x(%.2f)\n", angle); + + QTransform transform; + transform.rotate(angle, Qt::XAxis); + m_painter->setTransform(transform, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_rotate_y(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double angle = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) rotate_y(%.2f)\n", angle); + + QTransform transform; + transform.rotate(angle, Qt::YAxis); + m_painter->setTransform(transform, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_save(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) save()\n"); + + m_painter->save(); +} + +/***************************************************************************************************/ +void PaintCommands::command_mapQuadToQuad(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + double x3 = convertToDouble(caps.at(5)); + double y3 = convertToDouble(caps.at(6)); + double x4 = convertToDouble(caps.at(7)); + double y4 = convertToDouble(caps.at(8)); + QPolygonF poly1(4); + poly1[0] = QPointF(x1, y1); + poly1[1] = QPointF(x2, y2); + poly1[2] = QPointF(x3, y3); + poly1[3] = QPointF(x4, y4); + + double x5 = convertToDouble(caps.at(9)); + double y5 = convertToDouble(caps.at(10)); + double x6 = convertToDouble(caps.at(11)); + double y6 = convertToDouble(caps.at(12)); + double x7 = convertToDouble(caps.at(13)); + double y7 = convertToDouble(caps.at(14)); + double x8 = convertToDouble(caps.at(15)); + double y8 = convertToDouble(caps.at(16)); + QPolygonF poly2(4); + poly2[0] = QPointF(x5, y5); + poly2[1] = QPointF(x6, y6); + poly2[2] = QPointF(x7, y7); + poly2[3] = QPointF(x8, y8); + + if (m_verboseMode) + printf(" -(lance) mapQuadToQuad(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f ->\n\t" + ",%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", + x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, x7, y7, x8, y8); + + QTransform trans; + + if (!QTransform::quadToQuad(poly1, poly2, trans)) { + qWarning("Couldn't perform quad to quad transformation!"); + } + + m_painter->setTransform(trans, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_setMatrix(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double m11 = convertToDouble(caps.at(1)); + double m12 = convertToDouble(caps.at(2)); + double m13 = convertToDouble(caps.at(3)); + double m21 = convertToDouble(caps.at(4)); + double m22 = convertToDouble(caps.at(5)); + double m23 = convertToDouble(caps.at(6)); + double m31 = convertToDouble(caps.at(7)); + double m32 = convertToDouble(caps.at(8)); + double m33 = convertToDouble(caps.at(9)); + + if (m_verboseMode) + printf(" -(lance) setMatrix(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", + m11, m12, m13, m21, m22, m23, m31, m32, m33); + + QTransform trans; + trans.setMatrix(m11, m12, m13, + m21, m22, m23, + m31, m32, m33); + + m_painter->setTransform(trans, true); +} + +/***************************************************************************************************/ +void PaintCommands::command_scale(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) scale(%.2f, %.2f)\n", sx, sy); + + + m_painter->scale(sx, sy); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBackground(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QColor color = convertToColor(caps.at(1)); + QString pattern = caps.at(2); + + int style = translateEnum(brushStyleTable, pattern, Qt::LinearGradientPattern); + if (style < 0) + style = Qt::SolidPattern; + + if (m_verboseMode) + printf(" -(lance) setBackground(%s, %s)\n", qPrintable(color.name()), qPrintable(pattern)); + + m_painter->setBackground(QBrush(color, Qt::BrushStyle(style))); +} + +/***************************************************************************************************/ +void PaintCommands::command_setOpacity(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double opacity = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) setOpacity(%lf)\n", opacity); + + m_painter->setOpacity(opacity); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBgMode(QRegularExpressionMatch re) +{ + QString cap = re.captured(2); + Qt::BGMode mode = Qt::TransparentMode; + if (cap.toLower() == QLatin1String("opaquemode") || cap.toLower() == QLatin1String("opaque")) + mode = Qt::OpaqueMode; + + if (m_verboseMode) + printf(" -(lance) setBackgroundMode(%s)\n", mode == Qt::OpaqueMode ? "OpaqueMode" : "TransparentMode"); + + m_painter->setBackgroundMode(mode); +} + +/***************************************************************************************************/ +void PaintCommands::command_setBrush(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QImage img = m_imageMap[caps.at(1)]; // try cache first + if (img.isNull()) + img = image_load<QImage>(caps.at(1)); + if (!img.isNull()) { // Assume image brush + if (m_verboseMode) + printf(" -(lance) setBrush(image=%s, width=%d, height=%d)\n", + qPrintable(caps.at(1)), img.width(), img.height()); + + m_painter->setBrush(QBrush(img)); + } else if (caps.at(1).toLower() == "nobrush") { + m_painter->setBrush(Qt::NoBrush); + if (m_verboseMode) + printf(" -(lance) setBrush(Qt::NoBrush)\n"); + } else { + QColor color = convertToColor(caps.at(1)); + QString pattern = caps.at(2); + + int style = translateEnum(brushStyleTable, pattern, Qt::LinearGradientPattern); + if (style < 0) + style = Qt::SolidPattern; + + if (m_verboseMode) + printf(" -(lance) setBrush(%s, %s (%d))\n", qPrintable(color.name()), qPrintable(pattern), style); + + m_painter->setBrush(QBrush(color, Qt::BrushStyle(style))); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_setBrushOrigin(QRegularExpressionMatch re) +{ + int x = convertToInt(re.captured(1)); + int y = convertToInt(re.captured(2)); + + if (m_verboseMode) + printf(" -(lance) setBrushOrigin(%d, %d)\n", x, y); + + m_painter->setBrushOrigin(x, y); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushTranslate(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double dx = convertToDouble(caps.at(1)); + double dy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushTranslate(%f, %f)\n", dx, dy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.translate(dx, dy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushScale(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushScale(%f, %f)\n", sx, sy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.scale(sx, sy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushRotate(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double rot = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) brushScale(%f)\n", rot); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.rotate(rot); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_brushShear(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double sx = convertToDouble(caps.at(1)); + double sy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) brushShear(%f, %f)\n", sx, sy); + + QBrush new_brush = m_painter->brush(); + QTransform brush_matrix = new_brush.transform(); + brush_matrix.shear(sx, sy); + new_brush.setTransform(brush_matrix); + m_painter->setBrush(new_brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipping(QRegularExpressionMatch re) +{ + bool clipping = re.captured(1).toLower() == "true"; + + if (m_verboseMode) + printf(" -(lance) setClipping(%d)\n", clipping); + + m_painter->setClipping(clipping); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipRect(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + int w = convertToInt(caps.at(3)); + int h = convertToInt(caps.at(4)); + + int combine = translateEnum(clipOperationTable, caps.at(5), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipRect(%d, %d, %d, %d), %s\n", x, y, w, h, clipOperationTable[combine]); + + m_painter->setClipRect(x, y, w, h, Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipRectF(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x = convertToDouble(caps.at(1)); + double y = convertToDouble(caps.at(2)); + double w = convertToDouble(caps.at(3)); + double h = convertToDouble(caps.at(4)); + + int combine = translateEnum(clipOperationTable, caps.at(5), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipRectF(%f, %f, %f, %f), %s\n", x, y, w, h, clipOperationTable[combine]); + + m_painter->setClipRect(QRectF(x, y, w, h), Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipPath(QRegularExpressionMatch re) +{ + int combine = translateEnum(clipOperationTable, re.captured(2), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipPath(name=%s), %s\n", qPrintable(re.captured(1)), clipOperationTable[combine]); + + if (!m_pathMap.contains(re.captured(1))) + fprintf(stderr, " - setClipPath, no such path"); + m_painter->setClipPath(m_pathMap[re.captured(1)], Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setClipRegion(QRegularExpressionMatch re) +{ + int combine = translateEnum(clipOperationTable, re.captured(2), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + QRegion r = m_regionMap[re.captured(1)]; + + if (m_verboseMode) + printf(" -(lance) setClipRegion(name=%s), bounds=[%d, %d, %d, %d], %s\n", + qPrintable(re.captured(1)), + r.boundingRect().x(), + r.boundingRect().y(), + r.boundingRect().width(), + r.boundingRect().height(), + clipOperationTable[combine]); + + m_painter->setClipRegion(m_regionMap[re.captured(1)], Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setFont(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QString family = caps.at(1); + + int size = -1; // Default + QString sizeArg = caps.at(2); + if (!sizeArg.isEmpty()) + size = convertToInt(caps.at(2)); + + int weight = -1; // Default + QString weightArg = caps.at(3); + if (!weightArg.isEmpty()) { + weight = translateEnum(fontWeightTable, weightArg.toLower(), 5); + if (weight != -1) { + switch (weight) { + case 0: weight = QFont::Light; break; + case 1: weight = QFont::Normal; break; + case 2: weight = QFont::DemiBold; break; + case 3: weight = QFont::Bold; break; + case 4: weight = QFont::Black; break; + } + } else { + weight = convertToInt(weightArg); + } + } + + bool italic = caps.at(4).toLower() == "true" || caps.at(4).toLower() == "italic"; + + QFont font(family, size, weight, italic); + + int hinting = translateEnum(fontHintingTable, caps.at(5), 4); + if (hinting == -1) + hinting = 0; + else + font.setHintingPreference(QFont::HintingPreference(hinting)); + + bool underline = caps.at(6).toLower() == "true" || caps.at(6).toLower() == "underline"; + bool strikeOut = caps.at(7).toLower() == "true" || caps.at(7).toLower() == "strikeout"; + bool overline = caps.at(8).toLower() == "true" || caps.at(8).toLower() == "overline"; + font.setUnderline(underline); + font.setStrikeOut(strikeOut); + font.setOverline(overline); + + int capitalization = translateEnum(fontCapitalizationTable, caps.at(9), 5); + if (capitalization == -1) + capitalization = 0; + else + font.setCapitalization(QFont::Capitalization(capitalization)); + + if (m_verboseMode) + printf(" -(lance) setFont(family=%s, size=%d, weight=%d, italic=%d hinting=%s\n", + qPrintable(family), size, weight, italic, fontHintingTable[hinting]); + + m_painter->setFont(font); +} + +/***************************************************************************************************/ +void PaintCommands::command_setPen(QRegularExpressionMatch re) +{ + QString cap = re.captured(1); + int style = translateEnum(penStyleTable, cap, Qt::DashDotDotLine + 1); + if (style >= 0) { + if (m_verboseMode) + printf(" -(lance) setPen(%s)\n", qPrintable(cap)); + + m_painter->setPen(Qt::PenStyle(style)); + } else if (cap.toLower() == "brush") { + QPen pen(m_painter->brush(), 0); + if (m_verboseMode) { + printf(" -(lance) setPen(brush), style=%d, color=%08x\n", + pen.brush().style(), pen.color().rgba()); + } + m_painter->setPen(pen); + } else { + QColor color = convertToColor(cap); + if (m_verboseMode) + printf(" -(lance) setPen(%s)\n", qPrintable(color.name())); + + m_painter->setPen(color); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_setPen2(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QBrush brush; + + if (caps.at(1).toLower() == "brush") + brush = m_painter->brush(); + else + brush = convertToColor(caps.at(1)); + + double width = convertToDouble(caps.at(2)); + int penStyle = translateEnum(penStyleTable, caps.at(3), Qt::DashDotDotLine + 1); + if (penStyle < 0) + penStyle = Qt::SolidLine; + + Qt::PenCapStyle capStyle = Qt::SquareCap; + if (caps.at(4).toLower() == "flatcap") capStyle = Qt::FlatCap; + else if (caps.at(4).toLower() == "squarecap") capStyle = Qt::SquareCap; + else if (caps.at(4).toLower() == "roundcap") capStyle = Qt::RoundCap; + else if (!caps.at(4).isEmpty()) + fprintf(stderr, "ERROR: setPen, unknown capStyle: %s\n", qPrintable(caps.at(4))); + + Qt::PenJoinStyle joinStyle = Qt::BevelJoin; + if (caps.at(5).toLower() == "miterjoin") joinStyle = Qt::MiterJoin; + else if (caps.at(5).toLower() == "beveljoin") joinStyle = Qt::BevelJoin; + else if (caps.at(5).toLower() == "roundjoin") joinStyle = Qt::RoundJoin; + else if (!caps.at(5).isEmpty()) + fprintf(stderr, "ERROR: setPen, unknown joinStyle: %s\n", qPrintable(caps.at(5))); + + if (m_verboseMode) + printf(" -(lance) setPen(%s, width=%f, style=%d, cap=%d, join=%d)\n", + qPrintable(brush.color().name()), width, penStyle, capStyle, joinStyle); + + m_painter->setPen(QPen(brush, width, Qt::PenStyle(penStyle), capStyle, joinStyle)); +} + +/***************************************************************************************************/ +void PaintCommands::command_setRenderHint(QRegularExpressionMatch re) +{ + QString hintString = re.captured(1).toLower(); + QString setting = re.captured(2).toLower(); + + bool on = setting.isEmpty() || setting == "true" || setting == "on"; + QPainter::RenderHint hint; + int hintIdx = -1; + if (hintString.contains("antialiasing")) { + hintIdx = 0; + hint = QPainter::Antialiasing; + } else if (hintString.contains("smoothpixmaptransform")) { + hintIdx = 1; + hint = QPainter::SmoothPixmapTransform; + } else if (hintString.contains("noncosmeticbrushpatterns")) { + hintIdx = 2; + hint = QPainter::NonCosmeticBrushPatterns; + } + if (hintIdx >= 0) { + if (m_verboseMode) + printf(" -(lance) setRenderHint %s %s\n", renderHintTable[hintIdx], on ? "true" : "false"); + m_painter->setRenderHint(hint, on); + } else { + fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(re.captured(1))); + } +} + +/***************************************************************************************************/ +void PaintCommands::command_clearRenderHint(QRegularExpressionMatch /*re*/) +{ + m_painter->setRenderHint(QPainter::Antialiasing, false); + m_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + m_painter->setRenderHint(QPainter::NonCosmeticBrushPatterns, false); + if (m_verboseMode) + printf(" -(lance) clearRenderHint\n"); +} + +/***************************************************************************************************/ +void PaintCommands::command_setCompositionMode(QRegularExpressionMatch re) +{ + QString modeString = re.captured(1).toLower(); + int mode = translateEnum(compositionModeTable, modeString, 33); + + if (mode < 0 || mode > QPainter::RasterOp_SourceAndNotDestination) { + fprintf(stderr, "ERROR: invalid mode: %s\n", qPrintable(modeString)); + return; + } + + if (m_verboseMode) + printf(" -(lance) setCompositionMode: %d: %s\n", mode, qPrintable(modeString)); + + m_painter->setCompositionMode(QPainter::CompositionMode(mode)); +} + +/***************************************************************************************************/ +void PaintCommands::command_translate(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double dx = convertToDouble(caps.at(1)); + double dy = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) translate(%f, %f)\n", dx, dy); + + m_painter->translate(dx, dy); +} + +/***************************************************************************************************/ +void PaintCommands::command_pixmap_load(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QImage im = image_load<QImage>(fileName); + QPixmap px = QPixmap::fromImage(im, Qt::OrderedDither | Qt::OrderedAlphaDither); + + if (m_verboseMode) + printf(" -(lance) pixmap_load(%s as %s), size=[%d, %d], depth=%d\n", + qPrintable(fileName), qPrintable(name), + px.width(), px.height(), px.depth()); + + m_pixmapMap[name] = px; +} + +/***************************************************************************************************/ +void PaintCommands::command_bitmap_load(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QBitmap bm = image_load<QBitmap>(fileName); + + if (m_verboseMode) + printf(" -(lance) bitmap_load(%s as %s), size=[%d, %d], depth=%d\n", + qPrintable(fileName), qPrintable(name), + bm.width(), bm.height(), bm.depth()); + + m_pixmapMap[name] = bm; +} + +void PaintCommands::command_pixmap_setDevicePixelRatio(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + double dpr = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) pixmap_setDevicePixelRatio(%s), %.1f -> %.1f\n", + qPrintable(name), m_pixmapMap[name].devicePixelRatio(), dpr); + + m_pixmapMap[name].setDevicePixelRatio(dpr); +} + +/***************************************************************************************************/ +void PaintCommands::command_pixmap_setMask(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + QBitmap mask = image_load<QBitmap>(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) pixmap_setMask(%s, %s)\n", qPrintable(caps.at(1)), qPrintable(caps.at(2))); + + if (!m_pixmapMap[caps.at(1)].isNull()) + m_pixmapMap[caps.at(1)].setMask(mask); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_load(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString fileName = caps.at(1); + QString name = caps.at(2); + + if (name.isEmpty()) + name = fileName; + + QImage image = image_load<QImage>(fileName); + + if (m_verboseMode) + printf(" -(lance) image_load(%s as %s), size=[%d, %d], format=%d\n", + qPrintable(fileName), qPrintable(name), + image.width(), image.height(), image.format()); + + m_imageMap[name] = image; +} + +/***************************************************************************************************/ +void PaintCommands::command_image_setColorCount(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + int count = convertToInt(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) image_setColorCount(%s), %d -> %d\n", + qPrintable(name), m_imageMap[name].colorCount(), count); + + m_imageMap[name].setColorCount(count); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_setColor(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + int index = convertToInt(caps.at(2)); + QColor color = convertToColor(caps.at(3)); + + if (m_verboseMode) + printf(" -(lance) image_setColor(%s), %d = %08x\n", qPrintable(name), index, color.rgba()); + + m_imageMap[name].setColor(index, color.rgba()); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_setDevicePixelRatio(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString name = caps.at(1); + double dpr = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) image_setDevicePixelRatio(%s), %.1f -> %.1f\n", + qPrintable(name), m_imageMap[name].devicePixelRatio(), dpr); + + m_imageMap[name].setDevicePixelRatio(dpr); +} + +/***************************************************************************************************/ +void PaintCommands::command_abort(QRegularExpressionMatch) +{ + m_abort = true; +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_clearStops(QRegularExpressionMatch) +{ + if (m_verboseMode) + printf(" -(lance) gradient_clearStops\n"); + m_gradientStops.clear(); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_appendStop(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double pos = convertToDouble(caps.at(1)); + QColor color = convertToColor(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) gradient_appendStop(%.2f, %x)\n", pos, color.rgba()); + + m_gradientStops << QGradientStop(pos, color); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setLinear(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) gradient_setLinear (%.2f, %.2f), (%.2f, %.2f), spread=%d\n", + x1, y1, x2, y2, m_gradientSpread); + + QLinearGradient lg(QPointF(x1, y1), QPointF(x2, y2)); + lg.setStops(m_gradientStops); + lg.setSpread(m_gradientSpread); + lg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(lg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setLinearPen(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x1 = convertToDouble(caps.at(1)); + double y1 = convertToDouble(caps.at(2)); + double x2 = convertToDouble(caps.at(3)); + double y2 = convertToDouble(caps.at(4)); + + if (m_verboseMode) + printf(" -(lance) gradient_setLinear (%.2f, %.2f), (%.2f, %.2f), spread=%d\n", + x1, y1, x2, y2, m_gradientSpread); + + QLinearGradient lg(QPointF(x1, y1), QPointF(x2, y2)); + lg.setStops(m_gradientStops); + lg.setSpread(m_gradientSpread); + lg.setCoordinateMode(m_gradientCoordinate); + QPen pen = m_painter->pen(); + pen.setBrush(lg); + m_painter->setPen(pen); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setRadial(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double rad = convertToDouble(caps.at(3)); + double fx = convertToDouble(caps.at(4)); + double fy = convertToDouble(caps.at(5)); + + if (m_verboseMode) + printf(" -(lance) gradient_setRadial center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), " + "spread=%d\n", + cx, cy, rad, fx, fy, m_gradientSpread); + + QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy)); + rg.setStops(m_gradientStops); + rg.setSpread(m_gradientSpread); + rg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(rg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setRadialExtended(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double rad = convertToDouble(caps.at(3)); + double fx = convertToDouble(caps.at(4)); + double fy = convertToDouble(caps.at(5)); + double frad = convertToDouble(caps.at(6)); + + if (m_verboseMode) + printf(" -(lance) gradient_setRadialExtended center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), " + "focal radius=%.2f, spread=%d\n", + cx, cy, rad, fx, fy, frad, m_gradientSpread); + + QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy), frad); + rg.setStops(m_gradientStops); + rg.setSpread(m_gradientSpread); + rg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(rg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setConical(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double cx = convertToDouble(caps.at(1)); + double cy = convertToDouble(caps.at(2)); + double angle = convertToDouble(caps.at(3)); + + if (m_verboseMode) { + printf(" -(lance) gradient_setConical center=(%.2f, %.2f), angle=%.2f\n, spread=%d", + cx, cy, angle, m_gradientSpread); + } + + QConicalGradient cg(QPointF(cx, cy), angle); + cg.setStops(m_gradientStops); + cg.setSpread(m_gradientSpread); + cg.setCoordinateMode(m_gradientCoordinate); + QBrush brush(cg); + QTransform brush_matrix = m_painter->brush().transform(); + brush.setTransform(brush_matrix); + m_painter->setBrush(brush); +} + +/***************************************************************************************************/ +void PaintCommands::command_gradient_setSpread(QRegularExpressionMatch re) +{ + int spreadMethod = translateEnum(spreadMethodTable, re.captured(1), 3); + + if (m_verboseMode) + printf(" -(lance) gradient_setSpread %d=[%s]\n", spreadMethod, spreadMethodTable[spreadMethod]); + + m_gradientSpread = QGradient::Spread(spreadMethod); +} + +void PaintCommands::command_gradient_setCoordinateMode(QRegularExpressionMatch re) +{ + int coord = translateEnum(coordinateMethodTable, re.captured(1), 4); + + if (m_verboseMode) + printf(" -(lance) gradient_setCoordinateMode %d=[%s]\n", coord, + coordinateMethodTable[coord]); + + m_gradientCoordinate = QGradient::CoordinateMode(coord); +} + +/***************************************************************************************************/ +void PaintCommands::command_surface_begin(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x = convertToDouble(caps.at(1)); + double y = convertToDouble(caps.at(2)); + double w = convertToDouble(caps.at(3)); + double h = convertToDouble(caps.at(4)); + + if (m_surface_painter) { + fprintf(stderr, "ERROR: surface already active"); + return; + } + + if (m_verboseMode) + printf(" -(lance) surface_begin, pos=[%.2f, %.2f], size=[%.2f, %.2f]\n", x, y, w, h); + + m_surface_painter = m_painter; + + if (m_type == OpenGLType || m_type == OpenGLBufferType) { +#ifndef QT_NO_OPENGL + m_default_glcontext = QOpenGLContext::currentContext(); + m_surface_glcontext = new QOpenGLContext(); + // Pick up the format from the current context; this is especially + // important in order to pick the right version/profile to test. + m_surface_glcontext->setFormat(m_default_glcontext->format()); + m_surface_glcontext->create(); + m_surface_glcontext->makeCurrent(m_default_glcontext->surface()); + QOpenGLFramebufferObjectFormat fmt; // ###TBD: get format from caller + fmt.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + fmt.setSamples(4); + m_surface_glbuffer = new QOpenGLFramebufferObject(qRound(w), qRound(h), fmt); + m_surface_glbuffer->bind(); + m_surface_glpaintdevice = new QOpenGLPaintDevice(qRound(w), qRound(h)); + m_painter = new QPainter(m_surface_glpaintdevice); + m_painter->save(); + m_painter->setCompositionMode(QPainter::CompositionMode_Clear); + m_painter->fillRect(QRect(0, 0, qRound(w), qRound(h)), Qt::transparent); + m_painter->restore(); +#endif + } else { + QImage::Format surface_format; + if (QImage::toPixelFormat(m_format).alphaUsage() != QPixelFormat::UsesAlpha) + surface_format = qt_alphaVersion(m_format); + else + surface_format = m_format; + + m_surface_image = QImage(qRound(w), qRound(h), surface_format); + m_surface_image.fill(0); + m_painter = new QPainter(&m_surface_image); + } + m_surface_rect = QRectF(x, y, w, h); +} + +/***************************************************************************************************/ +void PaintCommands::command_surface_end(QRegularExpressionMatch) +{ + if (!m_surface_painter) { + fprintf(stderr, "ERROR: surface not active"); + return; + } + + if (m_verboseMode) + printf(" -(lance) surface_end, pos=[%.2f, %.2f], size=[%.2f, %.2f]\n", + m_surface_rect.x(), + m_surface_rect.y(), + m_surface_rect.width(), + m_surface_rect.height()); + m_painter->end(); + + delete m_painter; + m_painter = m_surface_painter; + m_surface_painter = 0; + + if (m_type == OpenGLType || m_type == OpenGLBufferType) { +#ifndef QT_NO_OPENGL + QImage new_image = m_surface_glbuffer->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + + delete m_surface_glpaintdevice; + m_surface_glpaintdevice = 0; + delete m_surface_glbuffer; + m_surface_glbuffer = 0; + delete m_surface_glcontext; + m_surface_glcontext = 0; + + m_default_glcontext->makeCurrent(m_default_glcontext->surface()); + m_painter->drawImage(m_surface_rect, new_image); + // Flush the pipeline: + m_painter->beginNativePainting(); + m_painter->endNativePainting(); +#endif + } else { + m_painter->drawImage(m_surface_rect, m_surface_image); + m_surface_image = QImage(); + } + m_surface_rect = QRectF(); +} + +/***************************************************************************************************/ +void PaintCommands::command_image_convertToFormat(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString srcName = caps.at(1); + QString destName = caps.at(2); + + if (!m_imageMap.contains(srcName)) { + fprintf(stderr, "ERROR(convertToFormat): no such image '%s'\n", qPrintable(srcName)); + return; + } + + int format = translateEnum(imageFormatTable, caps.at(3), QImage::NImageFormats); + if (format < 0 || format >= QImage::NImageFormats) { + fprintf(stderr, "ERROR(convertToFormat): invalid format %d = '%s'\n", + format, qPrintable(caps.at(3))); + return; + } + + QImage src = m_imageMap[srcName]; + QImage dest = src.convertToFormat(QImage::Format(format), + Qt::OrderedAlphaDither | Qt::OrderedDither); + + if (m_verboseMode) { + printf(" -(lance) convertToFormat %s:%d -> %s:%d\n", + qPrintable(srcName), src.format(), + qPrintable(destName), dest.format()); + } + + m_imageMap[destName] = dest; +} + +/***************************************************************************************************/ +void PaintCommands::command_textlayout_draw(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + + QString text = caps.at(1); + double width = convertToDouble(caps.at(2)); + + if (m_verboseMode) + printf(" -(lance) textlayout_draw text='%s', width=%f\n", + qPrintable(text), width); + + QFont copy = m_painter->font(); + copy.setPointSize(10); + + QTextLayout layout(text, copy, m_painter->device()); + layout.beginLayout(); + + double y_offset = 0; + + while (true) { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(width); + line.setPosition(QPointF(0, y_offset)); + + y_offset += line.height(); + } + + layout.draw(m_painter, QPointF(0, 0)); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setDashOffset(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double offset = convertToDouble(caps.at(1)); + + if (m_verboseMode) + printf(" -(lance) setDashOffset(%lf)\n", offset); + + QPen p = m_painter->pen(); + p.setDashOffset(offset); + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setDashPattern(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, Qt::SkipEmptyParts); + + QList<qreal> pattern; + for (int i=0; i<numbers.size(); ++i) + pattern.append(convertToDouble(numbers.at(i))); + + if (m_verboseMode) + printf(" -(lance) pen_setDashPattern(size=%zd)\n", size_t(pattern.size())); + + QPen p = m_painter->pen(); + p.setDashPattern(pattern); + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_pen_setCosmetic(QRegularExpressionMatch re) +{ + QString hm = re.capturedTexts().at(1); + bool on = hm == "true" || hm == "yes" || hm == "on"; + + if (m_verboseMode) { + printf(" -(lance) pen_setCosmetic(%s)\n", on ? "true" : "false"); + } + + QPen p = m_painter->pen(); + p.setCosmetic(on); + + m_painter->setPen(p); +} + +/***************************************************************************************************/ +void PaintCommands::command_drawConvexPolygon(QRegularExpressionMatch re) +{ + static QRegularExpression separators("\\s"); + QStringList caps = re.capturedTexts(); + QString cap = caps.at(1); + QStringList numbers = cap.split(separators, Qt::SkipEmptyParts); + + QPolygonF array; + for (int i=0; i + 1<numbers.size(); i+=2) + array.append(QPointF(convertToDouble(numbers.at(i)), convertToDouble(numbers.at(i+1)))); + + if (m_verboseMode) + printf(" -(lance) drawConvexPolygon(size=%zd)\n", size_t(array.size())); + + + m_painter->drawConvexPolygon(array); +} diff --git a/tests/baseline/shared/paintcommands.h b/tests/baseline/shared/paintcommands.h new file mode 100644 index 0000000000..de412e246e --- /dev/null +++ b/tests/baseline/shared/paintcommands.h @@ -0,0 +1,325 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#ifndef PAINTCOMMANDS_H +#define PAINTCOMMANDS_H + +#include <qcolor.h> +#include <qmap.h> +#include <qpainterpath.h> +#include <qregion.h> +#include <qregularexpression.h> +#include <qstringlist.h> +#include <qpixmap.h> +#include <qbrush.h> +#include <qhash.h> + +QT_FORWARD_DECLARE_CLASS(QPainter) +#ifndef QT_NO_OPENGL +QT_FORWARD_DECLARE_CLASS(QOpenGLFramebufferObject) +QT_FORWARD_DECLARE_CLASS(QOpenGLPaintDevice) +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +#endif + +enum DeviceType { + WidgetType, + BitmapType, + PixmapType, + ImageType, + ImageMonoType, + OpenGLType, + OpenGLBufferType, + PictureType, + PrinterType, + PdfType, + PsType, + GrabType, + CustomDeviceType, + CustomWidgetType, + ImageWidgetType +}; + +/************************************************************************/ +class PaintCommands +{ +public: + // construction / initialization + PaintCommands(const QStringList &cmds, int /*w*/, int /*h*/, QImage::Format format) + : m_painter(0) + , m_surface_painter(0) + , m_format(format) + , m_commands(cmds) + , m_gradientSpread(QGradient::PadSpread) + , m_gradientCoordinate(QGradient::LogicalMode) + , m_verboseMode(false) + , m_type(WidgetType) + , m_checkers_background(true) + , m_shouldDrawText(true) +#ifndef QT_NO_OPENGL + , m_default_glcontext(0) + , m_surface_glcontext(0) + , m_surface_glbuffer(0) + , m_surface_glpaintdevice(0) +#endif + { + staticInit(); + } + +public: + void setCheckersBackground(bool b) { staticInit(); m_checkers_background = b; } + void setContents(const QStringList &cmds) { + staticInit(); + m_blockMap.clear(); + m_pathMap.clear(); + m_pixmapMap.clear(); + m_imageMap.clear(); + m_regionMap.clear(); + m_gradientStops.clear(); + m_controlPoints.clear(); + m_gradientSpread = QGradient::PadSpread; + m_gradientCoordinate = QGradient::LogicalMode; + m_commands = cmds; + + + } + void setPainter(QPainter *pt) { staticInit(); m_painter = pt; } + void setType(DeviceType t) { staticInit(); m_type = t; } + void setFilePath(const QString &path) { staticInit(); m_filepath = path; } + void setControlPoints(const QList<QPointF> &points) + { + staticInit(); + m_controlPoints = points; + } + void setVerboseMode(bool v) { staticInit(); m_verboseMode = v; } + void insertAt(int commandIndex, const QStringList &newCommands); + void setShouldDrawText(bool drawText) { m_shouldDrawText = drawText; } + + // run + void runCommands(); + +private: + // run + void runCommand(const QString &scriptLine); + + // conversion methods + int convertToInt(const QString &str); + double convertToDouble(const QString &str); + float convertToFloat(const QString &str); + QColor convertToColor(const QString &str); + + // commands: comments + void command_comment(QRegularExpressionMatch re); + + // commands: importer + void command_import(QRegularExpressionMatch re); + + // commands: blocks + void command_begin_block(QRegularExpressionMatch re); + void command_end_block(QRegularExpressionMatch re); + void command_repeat_block(QRegularExpressionMatch re); + + // commands: misc + void command_textlayout_draw(QRegularExpressionMatch re); + void command_abort(QRegularExpressionMatch re); + + // commands: noops + void command_noop(QRegularExpressionMatch re); + + // commands: setters + void command_setBgMode(QRegularExpressionMatch re); + void command_setBackground(QRegularExpressionMatch re); + void command_setOpacity(QRegularExpressionMatch re); + void command_path_setFillRule(QRegularExpressionMatch re); + void command_setBrush(QRegularExpressionMatch re); + void command_setBrushOrigin(QRegularExpressionMatch re); + void command_brushTranslate(QRegularExpressionMatch re); + void command_brushRotate(QRegularExpressionMatch re); + void command_brushScale(QRegularExpressionMatch re); + void command_brushShear(QRegularExpressionMatch re); + void command_setClipPath(QRegularExpressionMatch re); + void command_setClipRect(QRegularExpressionMatch re); + void command_setClipRectF(QRegularExpressionMatch re); + void command_setClipRegion(QRegularExpressionMatch re); + void command_setClipping(QRegularExpressionMatch re); + void command_setCompositionMode(QRegularExpressionMatch re); + void command_setFont(QRegularExpressionMatch re); + void command_setPen(QRegularExpressionMatch re); + void command_setPen2(QRegularExpressionMatch re); + void command_pen_setDashOffset(QRegularExpressionMatch re); + void command_pen_setDashPattern(QRegularExpressionMatch re); + void command_pen_setCosmetic(QRegularExpressionMatch re); + void command_setRenderHint(QRegularExpressionMatch re); + void command_clearRenderHint(QRegularExpressionMatch re); + void command_gradient_appendStop(QRegularExpressionMatch re); + void command_gradient_clearStops(QRegularExpressionMatch re); + void command_gradient_setConical(QRegularExpressionMatch re); + void command_gradient_setLinear(QRegularExpressionMatch re); + void command_gradient_setRadial(QRegularExpressionMatch re); + void command_gradient_setRadialExtended(QRegularExpressionMatch re); + void command_gradient_setLinearPen(QRegularExpressionMatch re); + void command_gradient_setSpread(QRegularExpressionMatch re); + void command_gradient_setCoordinateMode(QRegularExpressionMatch re); + + // commands: drawing ops + void command_drawArc(QRegularExpressionMatch re); + void command_drawChord(QRegularExpressionMatch re); + void command_drawConvexPolygon(QRegularExpressionMatch re); + void command_drawEllipse(QRegularExpressionMatch re); + void command_drawImage(QRegularExpressionMatch re); + void command_drawLine(QRegularExpressionMatch re); + void command_drawLines(QRegularExpressionMatch re); + void command_drawPath(QRegularExpressionMatch re); + void command_drawPie(QRegularExpressionMatch re); + void command_drawPixmap(QRegularExpressionMatch re); + void command_drawPoint(QRegularExpressionMatch re); + void command_drawPolygon(QRegularExpressionMatch re); + void command_drawPolyline(QRegularExpressionMatch re); + void command_drawRect(QRegularExpressionMatch re); + void command_drawRoundedRect(QRegularExpressionMatch re); + void command_drawRoundRect(QRegularExpressionMatch re); + void command_drawText(QRegularExpressionMatch re); + void command_drawStaticText(QRegularExpressionMatch re); + void command_drawGlyphRun(QRegularExpressionMatch re); +#ifndef QT_NO_TEXTHTMLPARSER + void command_drawTextDocument(QRegularExpressionMatch re); +#endif + void command_drawTiledPixmap(QRegularExpressionMatch re); + void command_fillRect(QRegularExpressionMatch re); + void command_fillRectF(QRegularExpressionMatch re); + void command_drawPixmapFragments(QRegularExpressionMatch re); + + // paths + void command_path_addEllipse(QRegularExpressionMatch re); + void command_path_addPolygon(QRegularExpressionMatch re); + void command_path_addRect(QRegularExpressionMatch re); + void command_path_addText(QRegularExpressionMatch re); + void command_path_arcTo(QRegularExpressionMatch re); + void command_path_closeSubpath(QRegularExpressionMatch re); + void command_path_createOutline(QRegularExpressionMatch re); + void command_path_cubicTo(QRegularExpressionMatch re); + void command_path_debugPrint(QRegularExpressionMatch re); + void command_path_lineTo(QRegularExpressionMatch re); + void command_path_moveTo(QRegularExpressionMatch re); + void command_region_addEllipse(QRegularExpressionMatch re); + void command_region_addRect(QRegularExpressionMatch re); + + // getters + void command_region_getClipRegion(QRegularExpressionMatch re); + void command_path_getClipPath(QRegularExpressionMatch re); + + // commands: surface begin/end + void command_surface_begin(QRegularExpressionMatch re); + void command_surface_end(QRegularExpressionMatch re); + + // commands: save/restore painter state + void command_restore(QRegularExpressionMatch re); + void command_save(QRegularExpressionMatch re); + + // commands: pixmap/image + void command_pixmap_load(QRegularExpressionMatch re); + void command_pixmap_setMask(QRegularExpressionMatch re); + void command_bitmap_load(QRegularExpressionMatch re); + void command_pixmap_setDevicePixelRatio(QRegularExpressionMatch re); + void command_image_convertToFormat(QRegularExpressionMatch re); + void command_image_load(QRegularExpressionMatch re); + void command_image_setColor(QRegularExpressionMatch re); + void command_image_setColorCount(QRegularExpressionMatch re); + void command_image_setDevicePixelRatio(QRegularExpressionMatch re); + + // commands: transformation + void command_resetMatrix(QRegularExpressionMatch re); + void command_translate(QRegularExpressionMatch re); + void command_rotate(QRegularExpressionMatch re); + void command_rotate_x(QRegularExpressionMatch re); + void command_rotate_y(QRegularExpressionMatch re); + void command_scale(QRegularExpressionMatch re); + void command_mapQuadToQuad(QRegularExpressionMatch re); + void command_setMatrix(QRegularExpressionMatch re); + + // attributes + QPainter *m_painter; + QPainter *m_surface_painter; + QImage::Format m_format; + QImage m_surface_image; + QRectF m_surface_rect; + QStringList m_commands; + QString m_currentCommand; + int m_currentCommandIndex; + QString m_filepath; + QMap<QString, QStringList> m_blockMap; + QMap<QString, QPainterPath> m_pathMap; + QMap<QString, QPixmap> m_pixmapMap; + QMap<QString, QImage> m_imageMap; + QMap<QString, QRegion> m_regionMap; + QGradientStops m_gradientStops; + QGradient::Spread m_gradientSpread; + QGradient::CoordinateMode m_gradientCoordinate; + bool m_abort; + + bool m_verboseMode; + DeviceType m_type; + bool m_checkers_background; + bool m_shouldDrawText; + + QList<QPointF> m_controlPoints; + +#ifndef QT_NO_OPENGL + QOpenGLContext *m_default_glcontext; + QOpenGLContext *m_surface_glcontext; + QOpenGLFramebufferObject *m_surface_glbuffer; + QOpenGLPaintDevice *m_surface_glpaintdevice; +#endif + + // painter functionalities string tables + static const char *brushStyleTable[]; + static const char *penStyleTable[]; + static const char *fontWeightTable[]; + static const char *fontHintingTable[]; + static const char *fontCapitalizationTable[]; + static const char *clipOperationTable[]; + static const char *spreadMethodTable[]; + static const char *coordinateMethodTable[]; + static const char *compositionModeTable[]; + static const char *imageFormatTable[]; + static const char *sizeModeTable[]; + static const char *renderHintTable[]; + static int translateEnum(const char *table[], const QString &pattern, int limit); + + // utility + template <typename T> T image_load(const QString &filepath); + + // commands dictionary management + static void staticInit(); + +public: + struct PaintCommandInfos + { + PaintCommandInfos(QString id, void (PaintCommands::*p)(QRegularExpressionMatch), QRegularExpression r, QString sy, QString sa) + : identifier(id) + , regExp(r) + , syntax(sy) + , sample(sa) + , paintMethod(p) + {} + PaintCommandInfos(QString title) + : identifier(title), paintMethod(0) {} + bool isSectionHeader() const { return paintMethod == 0; } + QString identifier; + QRegularExpression regExp; + QString syntax; + QString sample; + void (PaintCommands::*paintMethod)(QRegularExpressionMatch); + }; + + static PaintCommandInfos *findCommandById(const QString &identifier) { + for (int i=0; i<s_commandInfoTable.size(); i++) + if (s_commandInfoTable[i].identifier == identifier) + return &s_commandInfoTable[i]; + return 0; + } + + static QList<PaintCommandInfos> s_commandInfoTable; + static QList<QPair<QString,QStringList> > s_enumsTable; + static QMultiHash<QString, int> s_commandHash; +}; + +#endif // PAINTCOMMANDS_H diff --git a/tests/baseline/shared/qbaselinetest.cpp b/tests/baseline/shared/qbaselinetest.cpp index 004d8013f1..964266f1b4 100644 --- a/tests/baseline/shared/qbaselinetest.cpp +++ b/tests/baseline/shared/qbaselinetest.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qbaselinetest.h" #include "baselineprotocol.h" @@ -36,6 +11,7 @@ namespace QBaselineTest { static char *fargv[MAXCMDLINEARGS]; +static QString server; static bool simfail = false; static PlatformInfo customInfo; static bool customAutoModeSet = false; @@ -51,9 +27,6 @@ static QByteArray curFunction; static ImageItemList itemList; static bool gotBaselines; -static QString definedTestProject; -static QString definedTestCase; - void handleCmdLineArgs(int *argcp, char ***argvp) { @@ -61,6 +34,7 @@ void handleCmdLineArgs(int *argcp, char ***argvp) return; bool showHelp = false; + bool abortOnHelp = true; int fargc = 0; int numArgs = *argcp; @@ -69,7 +43,16 @@ void handleCmdLineArgs(int *argcp, char ***argvp) QByteArray arg = (*argvp)[i]; QByteArray nextArg = (i+1 < numArgs) ? (*argvp)[i+1] : nullptr; - if (arg == "-simfail") { + if (arg == "-server") { + i++; + if (!nextArg.isEmpty()) { + server = QString::fromLocal8Bit(nextArg); + } else { + qWarning() << "-server requires parameter"; + showHelp = true; + break; + } + } else if (arg == "-simfail") { simfail = true; } else if (arg == "-fuzzlevel") { i++; @@ -105,10 +88,13 @@ void handleCmdLineArgs(int *argcp, char ***argvp) } customInfo.addOverride(key, value); } else { - if ( (arg == "-help") || (arg == "--help") ) + if ( (arg == "-help") || (arg == "--help") ) { showHelp = true; + abortOnHelp = false; + } if (fargc >= MAXCMDLINEARGS) { qWarning() << "Too many command line arguments!"; + showHelp = true; break; } fargv[fargc++] = (*argvp)[i]; @@ -121,7 +107,9 @@ void handleCmdLineArgs(int *argcp, char ***argvp) // TBD: arrange for this to be printed *after* QTest's help QTextStream out(stdout); out << "\n Baseline testing (lancelot) options:\n"; - out << " -simfail : Force an image comparison mismatch. For testing purposes.\n"; + out << " -server <host> : Set the network baseline server to connect to.\n"; + out << " The default is taken from the environment variable QT_LANCELOT_SERVER.\n"; + out << " -simfail : Force an image comparison mismatch. For development purposes.\n"; out << " -fuzzlevel <int> : Specify the percentage of fuzziness in comparison. Overrides server default. 0 means exact match.\n"; out << " -auto : Inform server that this run is done by a daemon, CI system or similar.\n"; out << " -adhoc (default) : The inverse of -auto; this run is done by human, e.g. for testing.\n"; @@ -132,6 +120,9 @@ void handleCmdLineArgs(int *argcp, char ***argvp) out << " for example: -compareto QtVersion=4.8.0\n"; out << " Multiple -compareto client specifications may be given.\n"; out << "\n"; + out.flush(); + if (abortOnHelp) + std::exit(1); } } @@ -167,7 +158,7 @@ void fetchCustomClientProperties() key = line.left(colonPos).simplified().replace(' ', '_'); val = line.mid(colonPos+1).trimmed(); } - if (!key.isEmpty() && key.length() < 64 && val.length() < 256) // ###TBD: maximum 256 chars in value? + if (!key.isEmpty() && key.size() < 64 && val.size() < 256) // ###TBD: maximum 256 chars in value? addClientProperty(key, val); else qDebug() << "Unparseable script output ignored:" << line; @@ -200,10 +191,7 @@ bool connect(QByteArray *msg, bool *error) if (!customAutoModeSet) clientInfo.setAdHocRun(defaultInfo.isAdHocRun()); - if (!definedTestProject.isEmpty()) - clientInfo.insert(PI_Project, definedTestProject); - - QString testCase = definedTestCase; + QString testCase = clientInfo.value(PI_TestCase); if (testCase.isEmpty() && QTest::testObject() && QTest::testObject()->metaObject()) { //qDebug() << "Trying to Read TestCaseName from Testlib!"; testCase = QTest::testObject()->metaObject()->className(); @@ -213,7 +201,7 @@ bool connect(QByteArray *msg, bool *error) return false; } - if (!proto.connect(testCase, &dryRunMode, clientInfo)) { + if (!proto.connect(testCase, &dryRunMode, clientInfo, server)) { *msg += "Failed to connect to baseline server: " + proto.errorMessage().toLatin1(); *error = true; return false; @@ -233,14 +221,10 @@ bool disconnectFromBaselineServer() return false; } -bool connectToBaselineServer(QByteArray *msg, const QString &testProject, const QString &testCase) +bool connectToBaselineServer(QByteArray *msg) { bool dummy; QByteArray dummyMsg; - - definedTestProject = testProject; - definedTestCase = testCase; - return connect(msg ? msg : &dummyMsg, &dummy); } @@ -255,6 +239,16 @@ void setSimFail(bool fail) simfail = fail; } +void setProject(const QString &projectName) +{ + addClientProperty(PI_Project, projectName); +} + +void setProjectImageKeys(const QStringList &keys) +{ + QString keyList = keys.join(QLC(',')); + addClientProperty(PI_ProjectImageKeys, keyList); +} void modifyImage(QImage *img) { @@ -274,6 +268,7 @@ void modifyImage(QImage *img) bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, bool *error) { + *error = false; ImageItem item = baseline; if (simfail) { // Simulate test failure by forcing image mismatch; for testing purposes @@ -284,6 +279,7 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, } else { item.image = img; } + bool isNewItem = false; item.imageChecksums.clear(); item.imageChecksums.prepend(ImageItem::computeChecksum(item.image)); QByteArray srvMsg; @@ -295,9 +291,11 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, return true; break; case ImageItem::BaselineNotFound: - if (!customInfo.overrides().isEmpty() || baselinePolicy == UploadNone) { - qWarning() << "Cannot compare to baseline: No such baseline found on server."; + if (!customInfo.overrides().isEmpty()) return true; + if (baselinePolicy == UploadNone) { + isNewItem = true; + break; } if (proto.submitNewBaseline(item, &srvMsg)) qDebug() << msg->constData() << "Baseline not found on server. New baseline uploaded."; @@ -310,7 +308,6 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, return true; break; } - *error = false; // The actual comparison of the given image with the baseline: if (baseline.imageChecksums.contains(item.imageChecksums.at(0))) { if (!proto.submitMatch(item, &srvMsg)) @@ -331,7 +328,11 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, qInfo() << "Baseline server reports:" << srvMsg; return true; // The server decides: a fuzzy match means no mismatch } - *msg += "Mismatch. See report:\n " + srvMsg; + if (isNewItem) + *msg += "No baseline on server, so cannot compare."; + else + *msg += "Mismatch."; + *msg += " See report:\n " + srvMsg; if (dryRunMode) { qDebug() << "Dryrun, so ignoring" << *msg; return true; @@ -397,22 +398,21 @@ QTestData &newRow(const char *dataTag, quint16 checksum) return QTest::newRow(dataTag); } - -bool testImage(const QImage& img, QByteArray *msg, bool *error) +const ImageItem *findCurrentItem(QByteArray *msg, bool *error) { if (!connected && !connect(msg, error)) - return true; + return nullptr; if (QTest::currentTestFunction() != curFunction || itemList.isEmpty()) { - qWarning() << "Usage error: QBASELINE_TEST used without corresponding QBaselineTest::newRow()"; - return true; + qWarning() << "Usage error: QBASELINE_ macro used without corresponding QBaselineTest::newRow()"; + return nullptr; } if (!gotBaselines) { if (!proto.requestBaselineChecksums(QString::fromLatin1(QTest::currentTestFunction()), &itemList) || itemList.isEmpty()) { *msg = "Communication with baseline server failed: " + proto.errorMessage().toLatin1(); *error = true; - return true; + return nullptr; } gotBaselines = true; } @@ -422,10 +422,24 @@ bool testImage(const QImage& img, QByteArray *msg, bool *error) while (it != itemList.constEnd() && it->itemName != curTag) ++it; if (it == itemList.constEnd()) { - qWarning() << "Usage error: QBASELINE_TEST used without corresponding QBaselineTest::newRow() for row" << curTag; - return true; + qWarning() << "Usage error: QBASELINE_ macro used without corresponding QBaselineTest::newRow() for row" << curTag; + return nullptr; } - return compareItem(*it, img, msg, error); + return &(*it); +} + +bool testImage(const QImage &img, QByteArray *msg, bool *error) +{ + const ImageItem *item = findCurrentItem(msg, error); + return item ? compareItem(*item, img, msg, error) : true; +} + +bool isCurrentItemBlacklisted() +{ + QByteArray msg; + bool error = false; + const ImageItem *item = findCurrentItem(&msg, &error); + return item ? (item->status == ImageItem::IgnoreItem) : false; } } diff --git a/tests/baseline/shared/qbaselinetest.h b/tests/baseline/shared/qbaselinetest.h index c525fc8466..73bb5dfda9 100644 --- a/tests/baseline/shared/qbaselinetest.h +++ b/tests/baseline/shared/qbaselinetest.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASELINETEST_H #define BASELINETEST_H @@ -36,11 +11,14 @@ namespace QBaselineTest { void setAutoMode(bool mode); void setSimFail(bool fail); void handleCmdLineArgs(int *argcp, char ***argvp); +void setProject(const QString &projectName); // Selects server config settings and top level dir +void setProjectImageKeys(const QStringList &keys); // Overrides the ItemPathKeys config setting void addClientProperty(const QString& key, const QString& value); -bool connectToBaselineServer(QByteArray *msg = nullptr, const QString &testProject = QString(), const QString &testCase = QString()); +bool connectToBaselineServer(QByteArray *msg = nullptr); bool checkImage(const QImage& img, const char *name, quint16 checksum, QByteArray *msg, bool *error, int manualdatatag = 0); bool testImage(const QImage& img, QByteArray *msg, bool *error); QTestData &newRow(const char *dataTag, quint16 checksum = 0); +bool isCurrentItemBlacklisted(); bool disconnectFromBaselineServer(); bool shouldAbortIfUnstable(); } @@ -56,8 +34,21 @@ do {\ }\ } while (0) +#define QBASELINE_CHECK_SUM_DEFERRED(image, name, checksum)\ +do {\ + QByteArray _msg;\ + bool _err = false;\ + if (!QBaselineTest::checkImage((image), (name), (checksum), &_msg, &_err)) {\ + QTest::qFail(_msg.constData(), __FILE__, __LINE__);\ + } else if (_err) {\ + QSKIP(_msg.constData());\ + }\ +} while (0) + #define QBASELINE_CHECK(image, name) QBASELINE_CHECK_SUM(image, name, 0) +#define QBASELINE_CHECK_DEFERRED(image, name) QBASELINE_CHECK_SUM_DEFERRED(image, name, 0) + #define QBASELINE_TEST(image)\ do {\ QByteArray _msg;\ @@ -69,4 +60,15 @@ do {\ }\ } while (0) +#define QBASELINE_SKIP_IF_BLACKLISTED \ +do {\ + if (QBaselineTest::isCurrentItemBlacklisted())\ + QSKIP("Blacklisted on baseline server.");\ +} while (0) + +#define QBASELINETEST_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject, \ + QHashSeed::setDeterministicGlobalSeed(); \ + QBaselineTest::handleCmdLineArgs(&argc, &argv); \ + QTEST_MAIN_SETUP()) + #endif // BASELINETEST_H diff --git a/tests/baseline/shared/qwidgetbaselinetest.cpp b/tests/baseline/shared/qwidgetbaselinetest.cpp index 9aafe152db..72a074e268 100644 --- a/tests/baseline/shared/qwidgetbaselinetest.cpp +++ b/tests/baseline/shared/qwidgetbaselinetest.cpp @@ -1,43 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwidgetbaselinetest.h" #include <qbaselinetest.h> #include <QApplication> #include <QStyle> +#include <QStyleHints> #include <QScreen> +#include <QtWidgets/private/qapplication_p.h> + QT_BEGIN_NAMESPACE QWidgetBaselineTest::QWidgetBaselineTest() { - QBaselineTest::addClientProperty("Project", "Widgets"); + QBaselineTest::setProject("Widgets"); // Set key platform properties that are relevant for the appearance of widgets const QString platformName = QGuiApplication::platformName() + "-" + QSysInfo::productType(); @@ -47,18 +25,25 @@ QWidgetBaselineTest::QWidgetBaselineTest() // Encode a number of parameters that impact the UI QPalette palette; QFont font; - QByteArray appearanceBytes; - { - QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly); - appearanceStream << palette << font << + const QString styleName = #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::style()->metaObject()->className(); #else QApplication::style()->name(); #endif + // turn off animations and make the cursor flash time really long to avoid blinking + QApplication::style()->setProperty("_qt_animation_time", QTime()); + QGuiApplication::styleHints()->setCursorFlashTime(50000); + + QByteArray appearanceBytes; + { + QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly); + appearanceStream << palette << font; const qreal screenDpr = QApplication::primaryScreen()->devicePixelRatio(); - if (screenDpr != 1.0) - qWarning() << "DPR is" << screenDpr << "- images will be scaled"; + if (screenDpr != 1.0) { + qWarning() << "DPR is" << screenDpr << "- images will not be compared to 1.0 baseline!"; + appearanceStream << screenDpr; + } } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const quint16 appearanceId = qChecksum(appearanceBytes, appearanceBytes.size()); @@ -72,8 +57,8 @@ QWidgetBaselineTest::QWidgetBaselineTest() const QColor windowColor = palette.window().color(); const QColor textColor = palette.text().color(); const QString appearanceIdString = (windowColor.value() > textColor.value() - ? QString("light-%1") : QString("dark-%1")) - .arg(appearanceId, 0, 16); + ? QString("light-%1-%2") : QString("dark-%1-%2")) + .arg(styleName).arg(appearanceId, 0, 16); QBaselineTest::addClientProperty("AppearanceID", appearanceIdString); // let users know where they can find the results @@ -92,11 +77,15 @@ void QWidgetBaselineTest::initTestCase() void QWidgetBaselineTest::init() { QVERIFY(!window); - window = new QWidget; + background = new QWidget(nullptr, Qt::FramelessWindowHint); + window = new QWidget(background, Qt::Window); window->setWindowTitle(QTest::currentDataTag()); + window->setFocusPolicy(Qt::StrongFocus); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + background->setScreen(QGuiApplication::primaryScreen()); window->setScreen(QGuiApplication::primaryScreen()); #endif + background->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); window->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); doInit(); @@ -106,33 +95,46 @@ void QWidgetBaselineTest::cleanup() { doCleanup(); - delete window; + delete background; + background = nullptr; window = nullptr; } void QWidgetBaselineTest::makeVisible() { Q_ASSERT(window); + background->showMaximized(); window->show(); - QApplication::setActiveWindow(window); + QApplicationPrivate::setActiveWindow(window); QVERIFY(QTest::qWaitForWindowActive(window)); - // explicitly unset focus, the test needs to control when focus is shown - if (window->focusWidget()) - window->focusWidget()->clearFocus(); + // explicitly set focus on the window so that the test widget doesn't have it + window->setFocus(Qt::OtherFocusReason); + QTRY_COMPARE(window->focusWidget(), window); } /* - Always return images scaled to a DPR of 1.0. - - This might produce some fuzzy differences, but lets us - compare those. + Grabs the test window and returns the resulting QImage, without + compensating for DPR differences. */ QImage QWidgetBaselineTest::takeSnapshot() { - QGuiApplication::processEvents(); - QPixmap pm = window->grab(); - QTransform scaleTransform = QTransform::fromScale(1.0 / pm.devicePixelRatioF(), 1.0 / pm.devicePixelRatioF()); - return pm.toImage().transformed(scaleTransform, Qt::SmoothTransformation); + // make sure all effects are done + QTest::qWait(250); + return window->grab().toImage(); +} + +/* + Grabs the test window screen and returns the resulting QImage, without + compensating for DPR differences. + This can be used for popup windows. +*/ +QImage QWidgetBaselineTest::takeScreenSnapshot(const QRect& windowRect) +{ + // make sure all effects are done - wait longer here because entire + // windows might be fading in and out. + QTest::qWait(750); + return window->screen()->grabWindow(0, windowRect.x(), windowRect.y(), + windowRect.width(), windowRect.height()).toImage(); } /*! @@ -145,27 +147,30 @@ QImage QWidgetBaselineTest::takeSnapshot() void QWidgetBaselineTest::takeStandardSnapshots() { makeVisible(); - struct PublicWidget : QWidget { - bool focusNextPrevChild(bool next) override { return QWidget::focusNextPrevChild(next); } - }; - QBASELINE_CHECK(takeSnapshot(), "default"); + QWidget *oldFocusWidget = testWindow()->focusWidget(); + QCOMPARE(oldFocusWidget, testWindow()); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "default"); // try hard to set focus - static_cast<PublicWidget*>(window)->focusNextPrevChild(true); - if (!window->focusWidget()) { - QWidget *firstChild = window->findChild<QWidget*>(); - if (firstChild) - firstChild->setFocus(); - } - if (testWindow()->focusWidget()) { - QBASELINE_CHECK(takeSnapshot(), "focused"); - testWindow()->focusWidget()->clearFocus(); + QWidget *testWidget = window->nextInFocusChain(); + if (!testWidget) + testWidget = window->findChild<QWidget*>(); + QVERIFY(testWidget); + // use TabFocusReason, some widgets handle that specifically to e.g. select + testWidget->setFocus(Qt::TabFocusReason); + + if (testWindow()->focusWidget() != oldFocusWidget) { + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "focused"); + // set focus back + oldFocusWidget->setFocus(Qt::OtherFocusReason); + } else { + qWarning() << "Couldn't set focus on tested widget" << testWidget; } // this disables all children window->setEnabled(false); - QBASELINE_CHECK(takeSnapshot(), "disabled"); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "disabled"); window->setEnabled(true); // show and activate another window so that our test window becomes inactive @@ -176,7 +181,7 @@ void QWidgetBaselineTest::takeStandardSnapshots() otherWindow.show(); otherWindow.windowHandle()->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(&otherWindow)); - QBASELINE_CHECK(takeSnapshot(), "inactive"); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "inactive"); window->windowHandle()->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); @@ -184,6 +189,4 @@ void QWidgetBaselineTest::takeStandardSnapshots() window->focusWidget()->clearFocus(); } -#include "qwidgetbaselinetest.moc" - QT_END_NAMESPACE diff --git a/tests/baseline/shared/qwidgetbaselinetest.h b/tests/baseline/shared/qwidgetbaselinetest.h index db73dd0540..2142217c09 100644 --- a/tests/baseline/shared/qwidgetbaselinetest.h +++ b/tests/baseline/shared/qwidgetbaselinetest.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #pragma once @@ -57,8 +32,10 @@ private slots: protected: void makeVisible(); QImage takeSnapshot(); + QImage takeScreenSnapshot(const QRect& rect = QRect()); private: + QWidget *background = nullptr; QWidget *window = nullptr; }; |