summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/accessible/qaccessible.cpp44
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp14
-rw-r--r--src/gui/accessible/qaccessiblecache_p.h1
-rw-r--r--src/gui/configure.json155
-rw-r--r--src/gui/configure.pri3
-rw-r--r--src/gui/doc/qtgui.qdocconf3
-rw-r--r--src/gui/doc/snippets/code/src_gui_accessible_qaccessible.cpp18
-rw-r--r--src/gui/doc/snippets/code/src_gui_math3d_qquaternion.cpp46
-rw-r--r--src/gui/doc/snippets/code/src_gui_opengl_qopenglbuffer.cpp49
-rw-r--r--src/gui/doc/snippets/code/src_gui_opengl_qopengldebug.cpp89
-rw-r--r--src/gui/doc/snippets/code/src_gui_opengl_qopenglfunctions.cpp101
-rw-r--r--src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp28
-rw-r--r--src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp59
-rw-r--r--src/gui/doc/snippets/code/src_gui_vulkan_qvulkaninstance.cpp135
-rw-r--r--src/gui/doc/snippets/code/src_gui_vulkan_qvulkanwindow.cpp121
-rw-r--r--src/gui/image/image.pri5
-rw-r--r--src/gui/image/qbitmap.cpp52
-rw-r--r--src/gui/image/qbitmap.h1
-rw-r--r--src/gui/image/qicon.cpp33
-rw-r--r--src/gui/image/qicon.h3
-rw-r--r--src/gui/image/qiconloader.cpp20
-rw-r--r--src/gui/image/qiconloader_p.h3
-rw-r--r--src/gui/image/qimage.cpp384
-rw-r--r--src/gui/image/qimage.h7
-rw-r--r--src/gui/image/qimage_conversions.cpp884
-rw-r--r--src/gui/image/qimage_p.h44
-rw-r--r--src/gui/image/qimage_sse2.cpp125
-rw-r--r--src/gui/image/qimagereader.cpp131
-rw-r--r--src/gui/image/qimagereader.h1
-rw-r--r--src/gui/image/qimagereaderwriterhelpers.cpp180
-rw-r--r--src/gui/image/qimagereaderwriterhelpers_p.h139
-rw-r--r--src/gui/image/qimagewriter.cpp123
-rw-r--r--src/gui/image/qimagewriter.h1
-rw-r--r--src/gui/image/qmovie.cpp4
-rw-r--r--src/gui/image/qpixmap.cpp10
-rw-r--r--src/gui/image/qpixmap_blitter.cpp8
-rw-r--r--src/gui/image/qpixmap_raster.cpp11
-rw-r--r--src/gui/image/qpixmap_win.cpp423
-rw-r--r--src/gui/image/qpixmapcache.cpp25
-rw-r--r--src/gui/image/qpnghandler.cpp204
-rw-r--r--src/gui/image/qxbmhandler.cpp2
-rw-r--r--src/gui/image/qxpmhandler.cpp4
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.cpp45
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.h3
-rw-r--r--src/gui/kernel/kernel.pri7
-rw-r--r--src/gui/kernel/qdnd_p.h4
-rw-r--r--src/gui/kernel/qevent.cpp58
-rw-r--r--src/gui/kernel/qevent.h13
-rw-r--r--src/gui/kernel/qguiapplication.cpp146
-rw-r--r--src/gui/kernel/qguiapplication_p.h11
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp3
-rw-r--r--src/gui/kernel/qinputmethod.cpp18
-rw-r--r--src/gui/kernel/qinputmethod_p.h2
-rw-r--r--src/gui/kernel/qkeysequence.cpp5
-rw-r--r--src/gui/kernel/qopenglcontext.cpp100
-rw-r--r--src/gui/kernel/qopenglwindow.cpp6
-rw-r--r--src/gui/kernel/qpalette.cpp43
-rw-r--r--src/gui/kernel/qpalette.h4
-rw-r--r--src/gui/kernel/qplatformdialoghelper.cpp37
-rw-r--r--src/gui/kernel/qplatformdialoghelper.h23
-rw-r--r--src/gui/kernel/qplatformgraphicsbufferhelper.cpp41
-rw-r--r--src/gui/kernel/qplatformintegration.cpp4
-rw-r--r--src/gui/kernel/qplatformmenu.h1
-rw-r--r--src/gui/kernel/qplatformscreen.cpp2
-rw-r--r--src/gui/kernel/qplatformsurface.cpp25
-rw-r--r--src/gui/kernel/qplatformsurface.h9
-rw-r--r--src/gui/kernel/qplatformwindow.cpp84
-rw-r--r--src/gui/kernel/qplatformwindow.h5
-rw-r--r--src/gui/kernel/qplatformwindow_p.h2
-rw-r--r--src/gui/kernel/qshortcutmap.cpp13
-rw-r--r--src/gui/kernel/qsimpledrag.cpp132
-rw-r--r--src/gui/kernel/qsimpledrag_p.h32
-rw-r--r--src/gui/kernel/qsurface.cpp4
-rw-r--r--src/gui/kernel/qsurface.h6
-rw-r--r--src/gui/kernel/qtestsupport_gui.cpp83
-rw-r--r--src/gui/kernel/qtestsupport_gui.h56
-rw-r--r--src/gui/kernel/qwindow.cpp44
-rw-r--r--src/gui/kernel/qwindow_p.h6
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp71
-rw-r--r--src/gui/kernel/qwindowsysteminterface.h19
-rw-r--r--src/gui/kernel/qwindowsysteminterface_p.h3
-rw-r--r--src/gui/math3d/qquaternion.cpp8
-rw-r--r--src/gui/math3d/qvector2d.cpp67
-rw-r--r--src/gui/math3d/qvector2d.h74
-rw-r--r--src/gui/math3d/qvector3d.cpp97
-rw-r--r--src/gui/math3d/qvector3d.h94
-rw-r--r--src/gui/math3d/qvector4d.cpp124
-rw-r--r--src/gui/math3d/qvector4d.h112
-rw-r--r--src/gui/opengl/opengl.pri2
-rw-r--r--src/gui/opengl/qopengl.h2
-rw-r--r--src/gui/opengl/qopenglbuffer.cpp24
-rw-r--r--src/gui/opengl/qopengldebug.cpp67
-rw-r--r--src/gui/opengl/qopenglengineshadermanager.cpp75
-rw-r--r--src/gui/opengl/qopenglengineshadermanager_p.h4
-rw-r--r--src/gui/opengl/qopenglengineshadersource_p.h173
-rw-r--r--src/gui/opengl/qopenglextensions_p.h3
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp86
-rw-r--r--src/gui/opengl/qopenglfunctions.cpp106
-rw-r--r--src/gui/opengl/qopenglfunctions.h3
-rw-r--r--src/gui/opengl/qopenglpaintengine.cpp102
-rw-r--r--src/gui/opengl/qopenglprogrambinarycache.cpp27
-rw-r--r--src/gui/opengl/qopenglshaderprogram.cpp88
-rw-r--r--src/gui/opengl/qopengltexturecache.cpp199
-rw-r--r--src/gui/opengl/qopengltexturecache_p.h24
-rw-r--r--src/gui/opengl/qopengltextureglyphcache.cpp10
-rw-r--r--src/gui/opengl/qopengltextureuploader.cpp328
-rw-r--r--src/gui/opengl/qopengltextureuploader_p.h84
-rw-r--r--src/gui/painting/WEBGRADIENTS_LICENSE.txt21
-rw-r--r--src/gui/painting/painting.pri9
-rw-r--r--src/gui/painting/qblendfunctions.cpp16
-rw-r--r--src/gui/painting/qbrush.cpp88
-rw-r--r--src/gui/painting/qbrush.h176
-rw-r--r--src/gui/painting/qcolor.cpp5
-rw-r--r--src/gui/painting/qcolorprofile_p.h156
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp1094
-rw-r--r--src/gui/painting/qcosmeticstroker_p.h3
-rw-r--r--src/gui/painting/qdrawhelper.cpp3519
-rw-r--r--src/gui/painting/qdrawhelper_avx2.cpp69
-rw-r--r--src/gui/painting/qdrawhelper_neon.cpp181
-rw-r--r--src/gui/painting/qdrawhelper_p.h136
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp266
-rw-r--r--src/gui/painting/qdrawhelper_ssse3.cpp59
-rw-r--r--src/gui/painting/qdrawingprimitive_sse2_p.h69
-rw-r--r--src/gui/painting/qemulationpaintengine.cpp27
-rw-r--r--src/gui/painting/qimagescale.cpp239
-rw-r--r--src/gui/painting/qimagescale_p.h9
-rw-r--r--src/gui/painting/qmemrotate.cpp33
-rw-r--r--src/gui/painting/qmemrotate_p.h1
-rw-r--r--src/gui/painting/qpagedpaintdevice.cpp68
-rw-r--r--src/gui/painting/qpagedpaintdevice.h8
-rw-r--r--src/gui/painting/qpagedpaintdevice_p.h40
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp5
-rw-r--r--src/gui/painting/qpaintengineex.cpp2
-rw-r--r--src/gui/painting/qpainter.cpp71
-rw-r--r--src/gui/painting/qpainter.h18
-rw-r--r--src/gui/painting/qpainterpath.cpp2
-rw-r--r--src/gui/painting/qpdf.cpp70
-rw-r--r--src/gui/painting/qpdf_p.h4
-rw-r--r--src/gui/painting/qpdfwriter.cpp35
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp9
-rw-r--r--src/gui/painting/qplatformbackingstore.h2
-rw-r--r--src/gui/painting/qrgba64.h4
-rw-r--r--src/gui/painting/qrgba64.qdoc2
-rw-r--r--src/gui/painting/qrgba64_p.h39
-rw-r--r--src/gui/painting/qt_attribution.json14
-rw-r--r--src/gui/painting/qtriangulator.cpp30
-rw-r--r--src/gui/painting/webgradients.binaryjsonbin0 -> 50792 bytes
-rw-r--r--src/gui/painting/webgradients.css909
-rw-r--r--src/gui/qtgui.tracepoints21
-rw-r--r--src/gui/text/qcssparser.cpp11
-rw-r--r--src/gui/text/qfont.cpp13
-rw-r--r--src/gui/text/qfontdatabase.cpp9
-rw-r--r--src/gui/text/qfontengine.cpp2
-rw-r--r--src/gui/text/qstatictext.cpp9
-rw-r--r--src/gui/text/qtextcursor.cpp3
-rw-r--r--src/gui/text/qtextdocument.cpp52
-rw-r--r--src/gui/text/qtextdocument.h4
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp40
-rw-r--r--src/gui/text/qtextdocumentfragment_p.h1
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp42
-rw-r--r--src/gui/text/qtextengine.cpp93
-rw-r--r--src/gui/text/qtextengine_p.h8
-rw-r--r--src/gui/text/qtextformat.cpp57
-rw-r--r--src/gui/text/qtextformat.h14
-rw-r--r--src/gui/text/qtexthtmlparser.cpp7
-rw-r--r--src/gui/text/qtextlayout.cpp28
-rw-r--r--src/gui/text/qtextodfwriter.cpp261
-rw-r--r--src/gui/text/qtextodfwriter_p.h18
-rw-r--r--src/gui/text/qtextoption.cpp3
-rw-r--r--src/gui/text/qzip.cpp23
-rw-r--r--src/gui/util/qdesktopservices.cpp28
-rw-r--r--src/gui/util/qktxhandler.cpp199
-rw-r--r--src/gui/util/qktxhandler_p.h (renamed from src/gui/image/qimage_avx2.cpp)51
-rw-r--r--src/gui/util/qpkmhandler.cpp126
-rw-r--r--src/gui/util/qpkmhandler_p.h70
-rw-r--r--src/gui/util/qshadergraphloader.cpp15
-rw-r--r--src/gui/util/qshadernodesloader.cpp8
-rw-r--r--src/gui/util/qshadernodesloader_p.h1
-rw-r--r--src/gui/util/qtexturefiledata.cpp280
-rw-r--r--src/gui/util/qtexturefiledata_p.h115
-rw-r--r--src/gui/util/qtexturefilehandler_p.h79
-rw-r--r--src/gui/util/qtexturefilereader.cpp102
-rw-r--r--src/gui/util/qtexturefilereader_p.h (renamed from src/gui/image/qimage_sse4.cpp)68
-rw-r--r--src/gui/util/qvalidator.cpp38
-rw-r--r--src/gui/util/util.pri13
-rw-r--r--src/gui/vulkan/qvulkanfunctions.cpp21
-rw-r--r--src/gui/vulkan/qvulkaninstance.cpp97
-rw-r--r--src/gui/vulkan/qvulkanwindow.cpp94
188 files changed, 11245 insertions, 5233 deletions
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index d60a21606b..a3f53e149b 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -202,7 +202,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
This enum type defines accessible event types.
- \omitvalue InvalidEvent Internal: Used when creating subclasses of QAccessibleEvent.
+ \omitvalue InvalidEvent \omit Internal: Used when creating subclasses of QAccessibleEvent. \endomit
\value AcceleratorChanged The keyboard accelerator for an action has been changed.
\value ActionChanged An action has been changed.
\value ActiveDescendantChanged
@@ -273,27 +273,27 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value SelectionWithin Several changes to a selection has occurred in an item
view.
\value SoundPlayed A sound has been played by an object
- \omitvalue StateChanged The QAccessible::State of an object has changed.
- This value is used internally for the QAccessibleStateChangeEvent.
+ \omitvalue StateChanged \omit The QAccessible::State of an object has changed.
+ This value is used internally for the QAccessibleStateChangeEvent. \endomit
\value TableCaptionChanged A table caption has been changed.
\value TableColumnDescriptionChanged The description of a table column, typically found in
the column's header, has been changed.
\value TableColumnHeaderChanged A table column header has been changed.
- \omitvalue TableModelChanged The model providing data for a table has been changed.
+ \omitvalue TableModelChanged \omit The model providing data for a table has been changed. \endomit
\value TableRowDescriptionChanged The description of a table row, typically found in the
row's header, has been changed.
\value TableRowHeaderChanged A table row header has been changed.
\value TableSummaryChanged The summary of a table has been changed.
\omitvalue TextAttributeChanged
- \omitvalue TextCaretMoved The caret has moved in an editable widget.
+ \omitvalue TextCaretMoved \omit The caret has moved in an editable widget.
The caret represents the cursor position in an editable
- widget with the input focus.
+ widget with the input focus. \endomit
\value TextColumnChanged A text column has been changed.
- \omitvalue TextInserted Text has been inserted into an editable widget.
- \omitvalue TextRemoved Text has been removed from an editable widget.
- \omitvalue TextSelectionChanged The selected text has changed in an editable widget.
- \omitvalue TextUpdated The text has been update in an editable widget.
- \omitvalue ValueChanged The QAccessible::Value of an object has changed.
+ \omitvalue TextInserted \omit Text has been inserted into an editable widget. \endomit
+ \omitvalue TextRemoved \omit Text has been removed from an editable widget. \endomit
+ \omitvalue TextSelectionChanged \omit The selected text has changed in an editable widget. \endomit
+ \omitvalue TextUpdated \omit The text has been update in an editable widget. \endomit
+ \omitvalue ValueChanged \omit The QAccessible::Value of an object has changed. \endomit
\value VisibleDataChanged
The values for this enum are defined to be the same as those defined in the
@@ -441,10 +441,10 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\note When subclassing one of these interfaces, \l QAccessibleInterface::interface_cast() needs to be implemented.
\value TextInterface For text that supports selections or is more than one line. Simple labels do not need to implement this interface.
- \omitvalue EditableTextInterface For text that can be edited by the user.
+ \omitvalue EditableTextInterface \omit For text that can be edited by the user. \endomit
\value ValueInterface For objects that are used to manipulate a value, for example slider or scroll bar.
\value ActionInterface For interactive objects that allow the user to trigger an action. Basically everything that allows for example mouse interaction.
- \omitvalue ImageInterface For objects that represent an image. This interface is generally less important.
+ \omitvalue ImageInterface \omit For objects that represent an image. This interface is generally less important. \endomit
\value TableInterface For lists, tables and trees.
\value TableCellInterface For cells in a TableInterface object.
@@ -1309,14 +1309,7 @@ QColor QAccessibleInterface::backgroundColor() const
For example to notify about a focus change when re-implementing QWidget::setFocus,
the event could be used as follows:
- \code
- void MyWidget::setFocus(Qt::FocusReason reason)
- {
- // handle custom focus setting...
- QAccessibleEvent event(f, QAccessible::Focus);
- QAccessible::updateAccessibility(&event);
- }
- \endcode
+ \snippet code/src_gui_accessible_qaccessible.cpp 2
To enable in process screen readers, all events must be sent after the change has happened.
*/
@@ -1826,14 +1819,7 @@ void QAccessibleInterface::virtual_hook(int /*id*/, void * /*data*/)
Qt's QLineEdit for example has its accessibility support
implemented in QAccessibleLineEdit.
- \code
-void *QAccessibleLineEdit::interface_cast(QAccessible::InterfaceType t)
-{
- if (t == QAccessible::TextInterface)
- return static_cast<QAccessibleTextInterface*>(this);
- return QAccessibleWidget::interface_cast(t);
-}
- \endcode
+ \snippet code/src_gui_accessible_qaccessible.cpp 3
\sa QAccessible::InterfaceType, QAccessibleTextInterface,
QAccessibleValueInterface, QAccessibleActionInterface,
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp
index f4242036ce..20376a54c4 100644
--- a/src/gui/accessible/qaccessiblecache.cpp
+++ b/src/gui/accessible/qaccessiblecache.cpp
@@ -38,11 +38,15 @@
****************************************************************************/
#include "qaccessiblecache_p.h"
+#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
#ifndef QT_NO_ACCESSIBILITY
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcAccessibilityCache, "qt.accessibility.cache");
+
/*!
\class QAccessibleCache
\internal
@@ -57,6 +61,12 @@ static void cleanupAccessibleCache()
accessibleCache = nullptr;
}
+QAccessibleCache::~QAccessibleCache()
+{
+ for (QAccessible::Id id: idToInterface.keys())
+ deleteInterface(id);
+}
+
QAccessibleCache *QAccessibleCache::instance()
{
if (!accessibleCache) {
@@ -116,6 +126,7 @@ QAccessible::Id QAccessibleCache::insert(QObject *object, QAccessibleInterface *
}
idToInterface.insert(id, iface);
interfaceToId.insert(iface, id);
+ qCDebug(lcAccessibilityCache) << "insert - id:" << id << " iface:" << iface;
return id;
}
@@ -131,6 +142,9 @@ void QAccessibleCache::objectDestroyed(QObject* obj)
void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj)
{
QAccessibleInterface *iface = idToInterface.take(id);
+ qCDebug(lcAccessibilityCache) << "delete - id:" << id << " iface:" << iface;
+ if (!iface) // the interface may be deleted already
+ return;
interfaceToId.take(iface);
if (!obj)
obj = iface->object();
diff --git a/src/gui/accessible/qaccessiblecache_p.h b/src/gui/accessible/qaccessiblecache_p.h
index f054ee9678..a976277c1d 100644
--- a/src/gui/accessible/qaccessiblecache_p.h
+++ b/src/gui/accessible/qaccessiblecache_p.h
@@ -68,6 +68,7 @@ class Q_GUI_EXPORT QAccessibleCache :public QObject
Q_OBJECT
public:
+ ~QAccessibleCache() override;
static QAccessibleCache *instance();
QAccessibleInterface *interfaceForId(QAccessible::Id id) const;
QAccessible::Id idForInterface(QAccessibleInterface *iface) const;
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 219385a108..0332631ec8 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -44,7 +44,7 @@
"xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] },
"xcb-native-painting": "boolean",
"xcb-xlib": "boolean",
- "xinput2": "boolean",
+ "xcb-xinput": "boolean",
"xkb": "boolean",
"xkbcommon": { "type": "enum", "values": [ "no", "qt", "system" ] },
"xkbcommon-evdev": "boolean",
@@ -114,11 +114,16 @@
"drm": {
"label": "KMS",
"test": {
- "include": [ "stdlib.h", "stdint.h" ],
+ "head": [
+ "#include <stdlib.h>",
+ "#include <stdint.h>",
+ "extern \"C\" {"
+ ],
+ "include": [
+ "xf86drmMode.h",
+ "xf86drm.h"
+ ],
"tail": [
- "extern \"C\" {",
- "#include <xf86drmMode.h>",
- "#include <xf86drm.h>",
"}"
],
"main": "(void) drmModeGetCrtc(0, 0);"
@@ -146,8 +151,8 @@
"freetype": {
"label": "FreeType",
"test": {
- "head": [
- "#include <ft2build.h>",
+ "include": "ft2build.h",
+ "tail": [
"#include FT_FREETYPE_H",
"#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) < 20200)",
"# error This version of freetype is too old.",
@@ -159,14 +164,15 @@
},
"sources": [
{ "type": "pkgConfig", "args": "freetype2" },
- { "type": "freetype", "libs": "-lfreetype" }
+ { "type": "freetype", "libs": "-lfreetype", "condition": "!config.wasm" },
+ { "type": "freetype", "libs": "-s USE_FREETYPE=1", "condition": "config.wasm" }
]
},
"fontconfig": {
"label": "Fontconfig",
"test": {
- "head": [
- "#include <fontconfig/fontconfig.h>",
+ "include": "fontconfig/fontconfig.h",
+ "tail": [
"#ifndef FC_RGBA_UNKNOWN",
"# error This version of fontconfig is tool old, it is missing the FC_RGBA_UNKNOWN define",
"#endif"
@@ -184,10 +190,13 @@
"gbm": {
"label": "GBM",
"test": {
- "include": [ "stdlib.h", "stdint.h" ],
+ "head": [
+ "#include <stdlib.h>",
+ "#include <stdint.h>",
+ "extern \"C\" {"
+ ],
+ "include": "gbm.h",
"tail": [
- "extern \"C\" {",
- "#include <gbm.h>",
"}"
],
"main": "gbm_surface *surface = 0;"
@@ -252,7 +261,11 @@
"integrityhid": {
"label": "integrityhid",
"test": {
- "include": [ "stdlib.h", "stdint.h", "device/hiddriver.h" ],
+ "head": [
+ "#include <stdlib.h>",
+ "#include <stdint.h>"
+ ],
+ "include": "device/hiddriver.h",
"main": [
"HIDDriver *driver;",
"uintptr_t devicecontext;",
@@ -267,10 +280,13 @@
"libjpeg": {
"label": "libjpeg",
"test": {
- "include": [ "sys/types.h", "stdio.h" ],
+ "head": [
+ "#include <sys/types.h>",
+ "#include <stdio.h>",
+ "extern \"C\" {"
+ ],
+ "include": "jpeglib.h",
"tail": [
- "extern \"C\" {",
- "#include <jpeglib.h>",
"}",
"",
"j_compress_ptr cinfo;"
@@ -279,7 +295,7 @@
},
"sources": [
{ "libs": "-llibjpeg", "condition": "config.msvc" },
- { "libs": "-ljpeg", "condition": "!config.msvc" }
+ "-ljpeg"
]
},
"libpng": {
@@ -290,8 +306,11 @@
},
"sources": [
{ "type": "pkgConfig", "args": "libpng" },
+ { "libs": "-llibpng16", "condition": "config.msvc" },
{ "libs": "-llibpng", "condition": "config.msvc" },
- { "libs": "-lpng", "condition": "!config.msvc" }
+ { "libs": "-lpng16", "condition": "!config.msvc" },
+ { "libs": "-lpng", "condition": "!config.msvc" },
+ { "libs": "-s USE_LIBPNG=1", "condition": "config.wasm" }
],
"use": [
{ "lib": "zlib", "condition": "features.system-zlib" }
@@ -393,13 +412,15 @@
"v4l2": {
"label": "V4L2",
"test": {
+ "head": [
+ "#include <cstddef>",
+ "extern \"C\" {"
+ ],
"include": [
- "cstddef"
+ "mediactl/mediactl.h",
+ "mediactl/v4l2subdev.h"
],
"tail": [
- "extern \"C\" {",
- "#include <mediactl/mediactl.h>",
- "#include <mediactl/v4l2subdev.h>",
"}"
],
"main": [
@@ -439,18 +460,18 @@
]
},
"xcb": {
- "label": "XCB >= 1.5 (core)",
+ "label": "XCB >= 1.9 (core)",
"test": {
"include": "xcb/xcb.h",
"main": [
"int primaryScreen = 0;",
"(void)xcb_connect(\"\", &primaryScreen);",
- "// This won't compile unless libxcb >= 1.5 which defines XCB_ATOM_PRIMARY.",
- "int xcbAtomPrimary = XCB_ATOM_PRIMARY;"
+ "// This won't compile unless libxcb >= 1.9 which defines XCB_CONN_CLOSED_INVALID_SCREEN.",
+ "int xcbScreenError = XCB_CONN_CLOSED_INVALID_SCREEN;"
]
},
"sources": [
- { "type": "pkgConfig", "args": "xcb >= 1.5" },
+ { "type": "pkgConfig", "args": "xcb >= 1.9" },
"-lxcb"
]
},
@@ -566,33 +587,22 @@
"-lxcb-glx -lxcb"
]
},
- "xinput2": {
- "label": "Xinput2",
+ "xcb_xinput": {
+ "label": "XCB XInput",
"test": {
- "include": [ "X11/Xlib.h", "X11/extensions/XInput2.h", "X11/extensions/Xge.h" ],
- "tail": [
- "#ifndef XInput_2_0",
- "# error Missing XInput_2_0 #define",
- "#endif"
- ],
+ "include": [ "xcb/xcb.h", "xcb/xinput.h" ],
"main": [
- "// need XGenericEventCookie for XInput2 to work",
- "Display *dpy = 0;",
- "XEvent xevent;",
- "XIEvent *xievent = 0;",
- "XIDeviceEvent *xideviceevent = 0;",
- "XIHierarchyEvent *xihierarchyevent = 0;",
- "int deviceid = 0;",
- "int len = 0;",
- "(void) XGetEventData(dpy, &xevent.xcookie);",
- "XFreeEventData(dpy, &xevent.xcookie);",
- "(void) XIListProperties(dpy, deviceid, &len);"
- ],
- "qmake": "CONFIG += x11"
+ "int primaryScreen = 0;",
+ "xcb_connection_t *connection = xcb_connect(\"\", &primaryScreen);",
+ "xcb_generic_error_t *error = 0;",
+ "xcb_input_xi_query_version_cookie_t xinput_query_cookie = xcb_input_xi_query_version(",
+ " connection, XCB_INPUT_MAJOR_VERSION, XCB_INPUT_MINOR_VERSION);",
+ "xcb_input_xi_query_version_reply(connection, xinput_query_cookie, &error);"
+ ]
},
"sources": [
- { "type": "pkgConfig", "args": "xi" },
- "-lXi"
+ { "type": "pkgConfig", "args": "xcb-xinput >= 1.12 xcb" },
+ "-lxcb-xinput -lxcb"
]
},
"xkbcommon": {
@@ -661,6 +671,26 @@
"fxc.exe"
]
},
+ "drm_atomic": {
+ "label": "DRM Atomic API",
+ "type": "compile",
+ "test": {
+ "head": [
+ "#include <stdlib.h>",
+ "#include <stdint.h>",
+ "extern \"C\" {"
+ ],
+ "include": [
+ "xf86drmMode.h",
+ "xf86drm.h"
+ ],
+ "tail": [
+ "}"
+ ],
+ "main": "drmModeAtomicReq *request;"
+ },
+ "use": "drm"
+ },
"egl-x11": {
"label": "EGL on X11",
"type": "compile",
@@ -953,7 +983,7 @@
},
"evdev": {
"label": "evdev",
- "condition": "tests.evdev",
+ "condition": "features.thread && tests.evdev",
"output": [ "privateFeature" ]
},
"freetype": {
@@ -1010,6 +1040,11 @@
"condition": "libs.drm",
"output": [ "publicQtConfig", "privateFeature" ]
},
+ "drm_atomic": {
+ "label": "DRM Atomic API",
+ "condition": "libs.drm && tests.drm_atomic",
+ "output": [ "privateFeature" ]
+ },
"libinput": {
"label": "libinput",
"condition": "features.libudev && libs.libinput",
@@ -1078,7 +1113,7 @@
},
"opengles3": {
"label": "OpenGL ES 3.0",
- "condition": "features.opengles2 && !features.angle && tests.opengles3",
+ "condition": "features.opengles2 && !features.angle && tests.opengles3 && !config.wasm",
"output": [
"publicFeature",
{ "type": "define", "name": "QT_OPENGL_ES_3" }
@@ -1105,7 +1140,7 @@
"enable": "input.opengl == 'desktop'",
"disable": "input.opengl == 'es2' || input.opengl == 'dynamic' || input.opengl == 'no'",
"condition": "(config.win32 && !config.winrt && !features.opengles2 && (config.msvc || libs.opengl))
- || (!config.watchos && !config.win32 && libs.opengl)"
+ || (!config.watchos && !config.win32 && !config.wasm && libs.opengl)"
},
"opengl-dynamic": {
"label": "Dynamic OpenGL",
@@ -1139,7 +1174,7 @@
},
"egl_x11": {
"label": "EGL on X11",
- "condition": "features.egl && tests.egl-x11",
+ "condition": "features.thread && features.egl && tests.egl-x11",
"output": [ "privateFeature" ]
},
"eglfs": {
@@ -1269,7 +1304,7 @@
"section": "Platform plugins",
"autoDetect": "!config.darwin",
"enable": "input.xcb == 'system' || input.xcb == 'qt' || input.xcb == 'yes'",
- "condition": "libs.xcb",
+ "condition": "features.thread && libs.xcb",
"output": [ "privateFeature" ]
},
"system-xcb": {
@@ -1327,10 +1362,10 @@
"condition": "features.sessionmanager && libs.x11sm",
"output": [ "privateFeature" ]
},
- "xinput2": {
- "label": "Xinput2",
+ "xcb-xinput": {
+ "label": "XCB XInput",
"emitIf": "features.xcb",
- "condition": "features.xcb-xlib && libs.xinput2",
+ "condition": "!features.system-xcb || libs.xcb_xinput",
"output": [ "privateFeature" ]
},
"xkbcommon-evdev": {
@@ -1542,7 +1577,7 @@
},
"multiprocess": {
"label": "Multi process",
- "description": "Provides support for detecting the desktop environment, launching external processes and opening URLs.",
+ "purpose": "Provides support for detecting the desktop environment, launching external processes and opening URLs.",
"section": "Utilities",
"condition": "!config.integrity",
"output": [ "privateFeature" ]
@@ -1701,7 +1736,7 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla
"section": "X11",
"condition": "features.xcb",
"entries": [
- "system-xcb", "egl_x11", "xinput2", "xkb", "xlib", "xcb-render", "xcb-glx", "xcb-xlib", "xkbcommon-system", "xcb-native-painting"
+ "system-xcb", "egl_x11", "xkb", "xlib", "xcb-render", "xcb-glx", "xcb-xinput", "xcb-xlib", "xkbcommon-system", "xcb-native-painting"
]
},
{
diff --git a/src/gui/configure.pri b/src/gui/configure.pri
index fcd2d1f73e..2971fd136e 100644
--- a/src/gui/configure.pri
+++ b/src/gui/configure.pri
@@ -7,7 +7,7 @@ defineTest(qtConfLibrary_freetype) {
for (p, TRY_INCLUDEPATHS) {
includedir = $$p/freetype2
exists($$includedir) {
- $${1}.includedir = "$$val_escape(includedir)"
+ $${1}.includedir = $$includedir
export($${1}.includedir)
return(true)
}
@@ -55,6 +55,7 @@ defineTest(qtConfTest_qpaDefaultPlatform) {
else: qnx: name = qnx
else: integrity: name = integrityfb
else: haiku: name = haiku
+ else: wasm: name = webassembly
else: name = xcb
$${1}.value = $$name
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index e1afa426ed..e546c817a7 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -60,3 +60,6 @@ manifestmeta.highlighted.names = "QtGui/Analog Clock Window Example"
navigation.landingpage = "Qt GUI"
navigation.cppclassespage = "Qt GUI C++ Classes"
+
+# Ignore warnings about undocumented enum values for the QGradient presets
+spurious += "Undocumented enum item '.*' in QGradient::Preset"
diff --git a/src/gui/doc/snippets/code/src_gui_accessible_qaccessible.cpp b/src/gui/doc/snippets/code/src_gui_accessible_qaccessible.cpp
index 98d40c94f9..2fd76d08c7 100644
--- a/src/gui/doc/snippets/code/src_gui_accessible_qaccessible.cpp
+++ b/src/gui/doc/snippets/code/src_gui_accessible_qaccessible.cpp
@@ -51,3 +51,21 @@
//! [1]
typedef QAccessibleInterface* myFactoryFunction(const QString &key, QObject *);
//! [1]
+
+//! [2]
+void MyWidget::setFocus(Qt::FocusReason reason)
+{
+ // handle custom focus setting...
+ QAccessibleEvent event(f, QAccessible::Focus);
+ QAccessible::updateAccessibility(&event);
+}
+//! [2]
+
+//! [3]
+void *QAccessibleLineEdit::interface_cast(QAccessible::InterfaceType t)
+{
+ if (t == QAccessible::TextInterface)
+ return static_cast<QAccessibleTextInterface*>(this);
+ return QAccessibleWidget::interface_cast(t);
+}
+//! [3]
diff --git a/src/gui/doc/snippets/code/src_gui_math3d_qquaternion.cpp b/src/gui/doc/snippets/code/src_gui_math3d_qquaternion.cpp
new file mode 100644
index 0000000000..b5cd00d5aa
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_math3d_qquaternion.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ QVector3D result = q.rotatedVector(vector);
+//! [0]
+
+//! [1]
+ QVector3D result = (q * QQuaternion(0, vector) * q.conjugated()).vector();
+//! [1]
diff --git a/src/gui/doc/snippets/code/src_gui_opengl_qopenglbuffer.cpp b/src/gui/doc/snippets/code/src_gui_opengl_qopenglbuffer.cpp
new file mode 100644
index 0000000000..4a4a6fe16d
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_opengl_qopenglbuffer.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ QOpenGLBuffer buffer1(QOpenGLBuffer::IndexBuffer);
+ buffer1.create();
+
+ QOpenGLBuffer buffer2 = buffer1;
+//! [0]
+
+//! [1]
+ QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
+//! [1]
diff --git a/src/gui/doc/snippets/code/src_gui_opengl_qopengldebug.cpp b/src/gui/doc/snippets/code/src_gui_opengl_qopengldebug.cpp
new file mode 100644
index 0000000000..4ab84deb3e
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_opengl_qopengldebug.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ GLenum error = GL_NO_ERROR;
+ do {
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ // handle the error
+ } while (error != GL_NO_ERROR);
+//! [0]
+
+//! [1]
+ QSurfaceFormat format;
+ // asks for a OpenGL 3.2 debug context using the Core profile
+ format.setMajorVersion(3);
+ format.setMinorVersion(2);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setOption(QSurfaceFormat::DebugContext);
+
+ QOpenGLContext *context = new QOpenGLContext;
+ context->setFormat(format);
+ context->create();
+//! [1]
+
+//! [2]
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
+
+ logger->initialize(); // initializes in the current context, i.e. ctx
+//! [2]
+
+//! [3]
+ ctx->hasExtension(QByteArrayLiteral("GL_KHR_debug"))
+//! [3]
+
+//! [4]
+ const QList<QOpenGLDebugMessage> messages = logger->loggedMessages();
+ for (const QOpenGLDebugMessage &message : messages)
+ qDebug() << message;
+//! [4]
+
+//! [5]
+ connect(logger, &QOpenGLDebugLogger::messageLogged, receiver, &LogHandler::handleLoggedMessage);
+ logger->startLogging();
+//! [5]
+
+//! [6]
+ QOpenGLDebugMessage message =
+ QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("Custom message"));
+
+ logger->logMessage(message);
+//! [6]
diff --git a/src/gui/doc/snippets/code/src_gui_opengl_qopenglfunctions.cpp b/src/gui/doc/snippets/code/src_gui_opengl_qopenglfunctions.cpp
new file mode 100644
index 0000000000..68a20dcb7c
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_opengl_qopenglfunctions.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ class MyGLWindow : public QWindow, protected QOpenGLFunctions
+ {
+ Q_OBJECT
+ public:
+ MyGLWindow(QScreen *screen = 0);
+
+ protected:
+ void initializeGL();
+ void paintGL();
+
+ QOpenGLContext *m_context;
+ };
+
+ MyGLWindow(QScreen *screen)
+ : QWindow(screen), QOpenGLWidget(parent)
+ {
+ setSurfaceType(OpenGLSurface);
+ create();
+
+ // Create an OpenGL context
+ m_context = new QOpenGLContext;
+ m_context->create();
+
+ // Setup scene and render it
+ initializeGL();
+ paintGL();
+ }
+
+ void MyGLWindow::initializeGL()
+ {
+ m_context->makeCurrent(this);
+ initializeOpenGLFunctions();
+ }
+//! [0]
+
+//! [1]
+ void MyGLWindow::paintGL()
+ {
+ m_context->makeCurrent(this);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ ...
+ m_context->swapBuffers(this);
+ m_context->doneCurrent();
+ }
+//! [1]
+
+//! [2]
+ QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
+ glFuncs.glActiveTexture(GL_TEXTURE1);
+//! [2]
+
+//! [3]
+ QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions();
+ glFuncs->glActiveTexture(GL_TEXTURE1);
+//! [3]
+
+//! [4]
+ QOpenGLFunctions funcs(QOpenGLContext::currentContext());
+ bool npot = funcs.hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
+//! [4]
diff --git a/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp b/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
index b48e9e8610..b9a8d8d90e 100644
--- a/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
+++ b/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
@@ -68,3 +68,31 @@ mailto:user@foo.com?subject=Test&body=Just a test
//! [2]
QDesktopServices::openUrl(QUrl("file:///C:/Documents and Settings/All Users/Desktop", QUrl::TolerantMode));
//! [2]
+
+//! [3]
+<key>LSApplicationQueriesSchemes</key>
+<array>
+ <string>https</string>
+</array>
+//! [3]
+
+//! [4]
+<key>CFBundleURLTypes</key>
+<array>
+ <dict>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>myapp</string>
+ </array>
+ </dict>
+</array>
+//! [4]
+
+//! [5]
+QDesktopServices::storageLocation(QDesktopServices::DataLocation)
+//! [5]
+
+//! [6]
+QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
+ "/data/organization/application"
+//! [6]
diff --git a/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp
new file mode 100644
index 0000000000..77c5444df5
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ void Window::render()
+ {
+ QVulkanInstance *inst = vulkanInstance();
+ QVulkanFunctions *f = inst->functions();
+ ...
+ VkResult err = f->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
+ ...
+ }
+//! [0]
+
+//! [1]
+ void Window::render()
+ {
+ QVulkanInstance *inst = vulkanInstance();
+ QVulkanDeviceFunctions *df = inst->deviceFunctions(device);
+ VkResult err = df->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
+ ...
+ }
+//! [1]
diff --git a/src/gui/doc/snippets/code/src_gui_vulkan_qvulkaninstance.cpp b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkaninstance.cpp
new file mode 100644
index 0000000000..50afe7c0ff
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkaninstance.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ int main(int argc, char **argv)
+ {
+ QGuiApplication app(argc, argv);
+
+ QVulkanInstance inst;
+ if (!inst.create())
+ return 1;
+
+ ...
+ window->setVulkanInstance(&inst);
+ window->show();
+
+ return app.exec();
+ }
+//! [0]
+
+//! [1]
+ QVulkanInstance inst;
+
+ // Enable validation layer, if supported. Messages go to qDebug by default.
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+
+ bool ok = inst.create();
+ if (!ok)
+ ... // Vulkan not available
+ if (!inst.layers().contains("VK_LAYER_LUNARG_standard_validation"))
+ ... // validation layer not available
+//! [1]
+
+//! [2]
+ QVulkanInstance inst;
+
+ if (inst.supportedLayers().contains("VK_LAYER_LUNARG_standard_validation"))
+ ...
+
+ bool ok = inst.create();
+ ...
+//! [2]
+
+//! [3]
+ class VulkanWindow : public QWindow
+ {
+ public:
+ VulkanWindow() {
+ setSurfaceType(VulkanSurface);
+ }
+
+ void exposeEvent(QExposeEvent *) {
+ if (isExposed()) {
+ if (!m_initialized) {
+ m_initialized = true;
+ // initialize device, swapchain, etc.
+ QVulkanInstance *inst = vulkanInstance();
+ QVulkanFunctions *f = inst->functions();
+ uint32_t devCount = 0;
+ f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr);
+ ...
+ // build the first frame
+ render();
+ }
+ }
+ }
+
+ bool event(QEvent *e) {
+ if (e->type == QEvent::UpdateRequest)
+ render();
+ return QWindow::event(e);
+ }
+
+ void render() {
+ ...
+ requestUpdate(); // render continuously
+ }
+
+ private:
+ bool m_initialized = false;
+ };
+
+ int main(int argc, char **argv)
+ {
+ QGuiApplication app(argc, argv);
+
+ QVulkanInstance inst;
+ if (!inst.create()) {
+ qWarning("Vulkan not available");
+ return 1;
+ }
+
+ VulkanWindow window;
+ window.showMaximized();
+
+ return app.exec();
+
+ }
+//! [3]
diff --git a/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanwindow.cpp b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanwindow.cpp
new file mode 100644
index 0000000000..65eca4a77f
--- /dev/null
+++ b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanwindow.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ class VulkanRenderer : public QVulkanWindowRenderer
+ {
+ public:
+ VulkanRenderer(QVulkanWindow *w) : m_window(w) { }
+
+ void initResources() override
+ {
+ m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
+ ...
+ }
+ void initSwapChainResources() override { ... }
+ void releaseSwapChainResources() override { ... }
+ void releaseResources() override { ... }
+
+ void startNextFrame() override
+ {
+ VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
+ ...
+ m_devFuncs->vkCmdBeginRenderPass(...);
+ ...
+ m_window->frameReady();
+ }
+
+ private:
+ QVulkanWindow *m_window;
+ QVulkanDeviceFunctions *m_devFuncs;
+ };
+
+ class VulkanWindow : public QVulkanWindow
+ {
+ public:
+ QVulkanWindowRenderer *createRenderer() override {
+ return new VulkanRenderer(this);
+ }
+ };
+
+ int main(int argc, char *argv[])
+ {
+ QGuiApplication app(argc, argv);
+
+ QVulkanInstance inst;
+ // enable the standard validation layers, when available
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+ if (!inst.create())
+ qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
+
+ VulkanWindow w;
+ w.setVulkanInstance(&inst);
+ w.showMaximized();
+
+ return app.exec();
+ }
+//! [0]
+
+//! [1]
+ class Renderer {
+ ...
+ VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
+ };
+
+ void Renderer::startNextFrame()
+ {
+ VkDescriptorBufferInfo &uniformBufInfo(m_uniformBufInfo[m_window->currentFrame()]);
+ ...
+ }
+//! [1]
+
+//! [2]
+ class Renderer {
+ ...
+ VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
+ };
+
+ void Renderer::startNextFrame()
+ {
+ const int count = m_window->concurrentFrameCount();
+ for (int i = 0; i < count; ++i)
+ m_uniformBufInfo[i] = ...
+ ...
+ }
+//! [2]
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri
index 76aba944b2..70fccbc378 100644
--- a/src/gui/image/image.pri
+++ b/src/gui/image/image.pri
@@ -9,6 +9,7 @@ HEADERS += \
image/qimage_p.h \
image/qimageiohandler.h \
image/qimagereader.h \
+ image/qimagereaderwriterhelpers_p.h \
image/qimagewriter.h \
image/qpaintengine_pic_p.h \
image/qpicture.h \
@@ -33,6 +34,7 @@ SOURCES += \
image/qimage_conversions.cpp \
image/qimageiohandler.cpp \
image/qimagereader.cpp \
+ image/qimagereaderwriterhelpers.cpp \
image/qimagewriter.cpp \
image/qpaintengine_pic.cpp \
image/qpicture.cpp \
@@ -80,10 +82,7 @@ qtConfig(png) {
}
# SIMD
-SSE2_SOURCES += image/qimage_sse2.cpp
SSSE3_SOURCES += image/qimage_ssse3.cpp
-SSE4_1_SOURCES += image/qimage_sse4.cpp
-AVX2_SOURCES += image/qimage_avx2.cpp
NEON_SOURCES += image/qimage_neon.cpp
MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp
MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S
diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp
index e8405a6d11..2453242fa8 100644
--- a/src/gui/image/qbitmap.cpp
+++ b/src/gui/image/qbitmap.cpp
@@ -189,9 +189,7 @@ QBitmap &QBitmap::operator=(const QPixmap &pixmap)
} else if (pixmap.depth() == 1) { // 1-bit pixmap
QPixmap::operator=(pixmap); // shallow assignment
} else { // n-bit depth pixmap
- QImage image;
- image = pixmap.toImage(); // convert pixmap to image
- *this = fromImage(image); // will dither image
+ *this = fromImage(pixmap.toImage()); // will dither image
}
return *this;
}
@@ -223,6 +221,24 @@ QBitmap::operator QVariant() const
return QVariant(QVariant::Bitmap, this);
}
+static QBitmap makeBitmap(QImage &&image, Qt::ImageConversionFlags flags)
+{
+ // make sure image.color(0) == Qt::color0 (white)
+ // and image.color(1) == Qt::color1 (black)
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (image.color(0) == c0 && image.color(1) == c1) {
+ image.invertPixels();
+ image.setColor(0, c1);
+ image.setColor(1, c0);
+ }
+
+ QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::BitmapType));
+
+ data->fromImageInPlace(image, flags | Qt::MonoOnly);
+ return QPixmap(data.take());
+}
+
/*!
Returns a copy of the given \a image converted to a bitmap using
the specified image conversion \a flags.
@@ -234,22 +250,24 @@ QBitmap QBitmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags)
if (image.isNull())
return QBitmap();
- QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ return makeBitmap(image.convertToFormat(QImage::Format_MonoLSB, flags), flags);
+}
- // make sure image.color(0) == Qt::color0 (white)
- // and image.color(1) == Qt::color1 (black)
- const QRgb c0 = QColor(Qt::black).rgb();
- const QRgb c1 = QColor(Qt::white).rgb();
- if (img.color(0) == c0 && img.color(1) == c1) {
- img.invertPixels();
- img.setColor(0, c1);
- img.setColor(1, c0);
- }
+/*!
+ \since 5.12
+ \overload
- QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::BitmapType));
+ Returns a copy of the given \a image converted to a bitmap using
+ the specified image conversion \a flags.
- data->fromImage(img, flags | Qt::MonoOnly);
- return QPixmap(data.take());
+ \sa fromData()
+*/
+QBitmap QBitmap::fromImage(QImage &&image, Qt::ImageConversionFlags flags)
+{
+ if (image.isNull())
+ return QBitmap();
+
+ return makeBitmap(std::move(image).convertToFormat(QImage::Format_MonoLSB, flags), flags);
}
/*!
@@ -277,7 +295,7 @@ QBitmap QBitmap::fromData(const QSize &size, const uchar *bits, QImage::Format m
int bytesPerLine = (size.width() + 7) / 8;
for (int y = 0; y < size.height(); ++y)
memcpy(image.scanLine(y), bits + bytesPerLine * y, bytesPerLine);
- return QBitmap::fromImage(image);
+ return QBitmap::fromImage(std::move(image));
}
/*!
diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h
index 6a8c8b3457..188064fccf 100644
--- a/src/gui/image/qbitmap.h
+++ b/src/gui/image/qbitmap.h
@@ -72,6 +72,7 @@ public:
inline void clear() { fill(Qt::color0); }
static QBitmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ static QBitmap fromImage(QImage &&image, Qt::ImageConversionFlags flags = Qt::AutoColor);
static QBitmap fromData(const QSize &size, const uchar *bits,
QImage::Format monoFormat = QImage::Format_MonoLSB);
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 635fdc3a68..c362d0dc3f 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -1196,7 +1196,7 @@ void QIcon::setFallbackSearchPaths(const QStringList &paths)
The \a name should correspond to a directory name in the
themeSearchPath() containing an index.theme
- file describing it's contents.
+ file describing its contents.
\sa themeSearchPaths(), themeName()
*/
@@ -1222,6 +1222,37 @@ QString QIcon::themeName()
}
/*!
+ \since 5.12
+
+ Returns the name of the fallback icon theme.
+
+ On X11, if not set, the fallback icon theme depends on your desktop
+ settings. On other platforms it is not set by default.
+
+ \sa setFallbackThemeName(), themeName()
+*/
+QString QIcon::fallbackThemeName()
+{
+ return QIconLoader::instance()->fallbackThemeName();
+}
+
+/*!
+ \since 5.12
+
+ Sets the fallback icon theme to \a name.
+
+ The \a name should correspond to a directory name in the
+ themeSearchPath() containing an index.theme
+ file describing its contents.
+
+ \sa fallbackThemeName(), themeSearchPaths(), themeName()
+*/
+void QIcon::setFallbackThemeName(const QString &name)
+{
+ QIconLoader::instance()->setFallbackThemeName(name);
+}
+
+/*!
\since 4.6
Returns the QIcon corresponding to \a name in the current
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
index 653ba6fda4..6a4fc8927a 100644
--- a/src/gui/image/qicon.h
+++ b/src/gui/image/qicon.h
@@ -124,6 +124,9 @@ public:
static QString themeName();
static void setThemeName(const QString &path);
+ static QString fallbackThemeName();
+ static void setFallbackThemeName(const QString &name);
+
Q_DUMMY_COMPARISON_OPERATOR(QIcon)
private:
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 1ea4f1340b..228de3adc3 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
-static QString fallbackTheme()
+static QString systemFallbackThemeName()
{
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
@@ -117,7 +117,7 @@ void QIconLoader::ensureInitialized()
m_systemTheme = systemThemeName();
if (m_systemTheme.isEmpty())
- m_systemTheme = fallbackTheme();
+ m_systemTheme = systemFallbackThemeName();
if (qt_iconEngineFactoryLoader()->keyMap().key(QLatin1String("svg"), -1) != -1)
m_supportsSvg = true;
}
@@ -137,7 +137,7 @@ void QIconLoader::updateSystemTheme()
if (m_userTheme.isEmpty()) {
QString theme = systemThemeName();
if (theme.isEmpty())
- theme = fallbackTheme();
+ theme = fallbackThemeName();
if (theme != m_systemTheme) {
m_systemTheme = theme;
invalidateKey();
@@ -151,6 +151,16 @@ void QIconLoader::setThemeName(const QString &themeName)
invalidateKey();
}
+QString QIconLoader::fallbackThemeName() const
+{
+ return m_userFallbackTheme.isEmpty() ? systemFallbackThemeName() : m_userFallbackTheme;
+}
+
+void QIconLoader::setFallbackThemeName(const QString &themeName)
+{
+ m_userFallbackTheme = themeName;
+}
+
void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
{
m_iconDirs = searchPaths;
@@ -388,7 +398,7 @@ QIconTheme::QIconTheme(const QString &themeName)
// Ensure a default platform fallback for all themes
if (m_parents.isEmpty()) {
- const QString fallback = fallbackTheme();
+ const QString fallback = QIconLoader::instance()->fallbackThemeName();
if (!fallback.isEmpty())
m_parents.append(fallback);
}
@@ -414,7 +424,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
if (!theme.isValid()) {
theme = QIconTheme(themeName);
if (!theme.isValid())
- theme = QIconTheme(fallbackTheme());
+ theme = QIconTheme(fallbackThemeName());
}
const QStringList contentDirs = theme.contentDirs();
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index 746e871fb1..fac18b5d79 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -177,6 +177,8 @@ public:
QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; }
void setThemeName(const QString &themeName);
+ QString fallbackThemeName() const;
+ void setFallbackThemeName(const QString &themeName);
QIconTheme theme() { return themeList.value(themeName()); }
void setThemeSearchPath(const QStringList &searchPaths);
QStringList themeSearchPaths() const;
@@ -200,6 +202,7 @@ private:
bool m_initialized;
mutable QString m_userTheme;
+ mutable QString m_userFallbackTheme;
mutable QString m_systemTheme;
mutable QStringList m_iconDirs;
mutable QHash <QString, QIconTheme> themeList;
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 7fcae12cbd..0105f1decd 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -118,21 +118,14 @@ QImageData::QImageData()
QImageData * QImageData::create(const QSize &size, QImage::Format format)
{
if (!size.isValid() || format == QImage::Format_Invalid)
- return 0; // invalid parameter(s)
+ return nullptr; // invalid parameter(s)
- uint width = size.width();
- uint height = size.height();
- uint depth = qt_depthForFormat(format);
-
- const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4)
-
- // sanity check for potential overflows
- if (std::numeric_limits<int>::max()/depth < width
- || bytes_per_line <= 0
- || height <= 0
- || std::numeric_limits<qsizetype>::max()/uint(bytes_per_line) < height
- || std::numeric_limits<int>::max()/sizeof(uchar *) < uint(height))
- return 0;
+ int width = size.width();
+ int height = size.height();
+ int depth = qt_depthForFormat(format);
+ auto params = calculateImageParameters(width, height, depth);
+ if (params.bytesPerLine < 0)
+ return nullptr;
QScopedPointer<QImageData> d(new QImageData);
@@ -154,18 +147,15 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format)
d->has_alpha_clut = false;
d->is_cached = false;
- d->bytes_per_line = bytes_per_line;
-
- d->nbytes = d->bytes_per_line*height;
+ d->bytes_per_line = params.bytesPerLine;
+ d->nbytes = params.totalSize;
d->data = (uchar *)malloc(d->nbytes);
- if (!d->data) {
- return 0;
- }
+ if (!d->data)
+ return nullptr;
d->ref.ref();
return d.take();
-
}
QImageData::~QImageData()
@@ -277,6 +267,16 @@ bool QImageData::checkForAlphaPixels() const
bits += bytes_per_line;
}
} break;
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied: {
+ uchar *bits = data;
+ for (int y=0; y<height && !has_alpha_pixels; ++y) {
+ for (int x=0; x<width; ++x) {
+ has_alpha_pixels |= !(((QRgba64 *)bits)[x].isOpaque());
+ }
+ bits += bytes_per_line;
+ }
+ } break;
case QImage::Format_RGB32:
case QImage::Format_RGB16:
@@ -288,6 +288,7 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_BGR30:
case QImage::Format_RGB30:
case QImage::Format_Grayscale8:
+ case QImage::Format_RGBX64:
break;
case QImage::Format_Invalid:
case QImage::NImageFormats:
@@ -651,11 +652,7 @@ bool QImageData::checkForAlphaPixels() const
/*!
\enum QImage::Format
- The following image formats are available in Qt. Values from Format_ARGB8565_Premultiplied
- to Format_ARGB4444_Premultiplied were added in Qt 4.4. Values Format_RGBX8888, Format_RGBA8888
- and Format_RGBA8888_Premultiplied were added in Qt 5.2. Values Format_BGR30, Format_A2BGR30_Premultiplied,
- Format_RGB30, Format_A2RGB30_Premultiplied were added in Qt 5.4. Format_Alpha8 and Format_Grayscale8
- were added in Qt 5.5.
+ The following image formats are available in Qt.
See the notes after the table.
\value Format_Invalid The image is invalid.
@@ -699,29 +696,32 @@ bool QImageData::checkForAlphaPixels() const
\value Format_ARGB4444_Premultiplied The image is stored using a
premultiplied 16-bit ARGB format (4-4-4-4).
\value Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
- This is the same as the Format_RGBA8888 except alpha must always be 255.
+ This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2)
\value Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
Unlike ARGB32 this is a byte-ordered format, which means the 32bit
encoding differs between big endian and little endian architectures,
being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors
- is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA.
+ is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2)
\value Format_RGBA8888_Premultiplied The image is stored using a
- premultiplied 32-bit byte-ordered RGBA format (8-8-8-8).
- \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10).
- \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10).
- \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10).
- \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10).
- \value Format_Alpha8 The image is stored using an 8-bit alpha only format.
- \value Format_Grayscale8 The image is stored using an 8-bit grayscale format.
+ premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2)
+ \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4)
+ \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4)
+ \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4)
+ \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4)
+ \value Format_Alpha8 The image is stored using an 8-bit alpha only format. (added in Qt 5.5)
+ \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5)
+ \value Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16).
+ This is the same as the Format_RGBX64 except alpha must always be 65535. (added in Qt 5.12)
+ \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12)
+ \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
+ RGBA format (16-16-16-16). (added in Qt 5.12)
\note Drawing into a QImage with QImage::Format_Indexed8 is not
supported.
- \note Do not render into ARGB32 images using QPainter. Using
- QImage::Format_ARGB32_Premultiplied is significantly faster.
-
- \note Formats with more than 8 bit per color channel will only be processed by the raster engine using 8 bit
- per color.
+ \note Avoid most rendering directly to most of these formats using QPainter. Rendering
+ is best optimized to the \c Format_RGB32 and \c Format_ARGB32_Premultiplied formats, and secondarily for rendering to the
+ \c Format_RGB16, \c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64 and \c Format_RGBA64_Premultiplied formats
\sa format(), convertToFormat()
*/
@@ -776,27 +776,27 @@ QImage::QImage(const QSize &size, Format format)
QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
{
- QImageData *d = 0;
-
- if (format == QImage::Format_Invalid)
- return d;
+ if (width <= 0 || height <= 0 || !data || format == QImage::Format_Invalid)
+ return nullptr;
const int depth = qt_depthForFormat(format);
- const int calc_bytes_per_line = ((width * depth + 31)/32) * 4;
- const int min_bytes_per_line = (width * depth + 7)/8;
-
- if (bpl <= 0)
- bpl = calc_bytes_per_line;
-
- if (width <= 0 || height <= 0 || !data
- || INT_MAX/sizeof(uchar *) < uint(height)
- || INT_MAX/uint(depth) < uint(width)
- || bpl <= 0
- || bpl < min_bytes_per_line
- || INT_MAX/uint(bpl) < uint(height))
- return d; // invalid parameter(s)
+ auto params = calculateImageParameters(width, height, depth);
+ if (params.totalSize < 0)
+ return nullptr;
+
+ if (bpl > 0) {
+ // can't overflow, because has calculateImageParameters already done this multiplication
+ const int min_bytes_per_line = (width * depth + 7)/8;
+ if (bpl < min_bytes_per_line)
+ return nullptr;
+
+ // recalculate the total with this value
+ params.bytesPerLine = bpl;
+ if (mul_overflow<qsizetype>(bpl, height, &params.totalSize))
+ return nullptr;
+ }
- d = new QImageData;
+ QImageData *d = new QImageData;
d->ref.ref();
d->own_data = false;
@@ -807,8 +807,8 @@ QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QIm
d->depth = depth;
d->format = format;
- d->bytes_per_line = bpl;
- d->nbytes = d->bytes_per_line * height;
+ d->bytes_per_line = params.bytesPerLine;
+ d->nbytes = params.totalSize;
d->cleanupFunction = cleanupFunction;
d->cleanupInfo = cleanupInfo;
@@ -1444,7 +1444,8 @@ void QImage::setDevicePixelRatio(qreal scaleFactor)
return;
detach();
- d->devicePixelRatio = scaleFactor;
+ if (d)
+ d->devicePixelRatio = scaleFactor;
}
/*!
@@ -1708,6 +1709,10 @@ void QImage::fill(uint pixel)
qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel,
0, 0, d->width, d->height, d->bytes_per_line);
return;
+ } else if (d->depth == 64) {
+ qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), QRgba64::fromArgb32(pixel),
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ return;
}
if (d->format == Format_RGB32)
@@ -1815,6 +1820,19 @@ void QImage::fill(const QColor &color)
else
fill((uint) 0);
break;
+ case QImage::Format_RGBX64: {
+ QRgba64 c = color.rgba64();
+ c.setAlpha(65535);
+ qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), c,
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ break;
+
+ }
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), color.rgba64(),
+ 0, 0, d->width, d->height, d->bytes_per_line);
+ break;
default: {
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Source);
@@ -1838,7 +1856,8 @@ void QImage::fill(const QColor &color)
changed.
If the image has a premultiplied alpha channel, the image is first
- converted to ARGB32 to be inverted and then converted back.
+ converted to an unpremultiplied image format to be inverted and
+ then converted back.
\sa {QImage#Image Transformations}{Image Transformations}
*/
@@ -1857,8 +1876,13 @@ void QImage::invertPixels(InvertMode mode)
QImage::Format originalFormat = d->format;
// Inverting premultiplied pixels would produce invalid image data.
if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied) {
- if (!d->convertInPlace(QImage::Format_ARGB32, 0))
- *this = convertToFormat(QImage::Format_ARGB32);
+ if (depth() > 32) {
+ if (!d->convertInPlace(QImage::Format_RGBA64, 0))
+ *this = convertToFormat(QImage::Format_RGBA64);
+ } else {
+ if (!d->convertInPlace(QImage::Format_ARGB32, 0))
+ *this = convertToFormat(QImage::Format_ARGB32);
+ }
}
if (depth() < 32) {
@@ -1871,6 +1895,20 @@ void QImage::invertPixels(InvertMode mode)
*sl++ ^= 0xff;
sl += pad;
}
+ }
+ else if (depth() == 64) {
+ quint16 *p = (quint16*)d->data;
+ quint16 *end = (quint16*)(d->data + d->nbytes);
+ quint16 xorbits = 0xffff;
+ while (p < end) {
+ *p++ ^= xorbits;
+ *p++ ^= xorbits;
+ *p++ ^= xorbits;
+ if (mode == InvertRgba)
+ *p++ ^= xorbits;
+ else
+ p++;
+ }
} else {
quint32 *p = (quint32*)d->data;
quint32 *end = (quint32*)(d->data + d->nbytes);
@@ -1987,6 +2025,26 @@ QImage::Format QImage::format() const
\sa {Image Formats}
*/
+static bool highColorPrecision(QImage::Format format)
+{
+ // Formats with higher color precision than ARGB32_Premultiplied.
+ switch (format) {
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_BGR30:
+ case QImage::Format_RGB30:
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_A2RGB30_Premultiplied:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
/*!
\internal
*/
@@ -1999,8 +2057,18 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl
return QImage();
Image_Converter converter = qimage_converter_map[d->format][format];
- if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8)
- converter = convert_generic;
+ if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) {
+ if (highColorPrecision(format) && highColorPrecision(d->format)) {
+ // Convert over RGBA64_Premultiplied
+ if (format == QImage::Format_RGBA64_Premultiplied)
+ converter = convert_generic_to_rgb64;
+ else {
+ Q_ASSERT(d->format != QImage::Format_RGBA64_Premultiplied);
+ return convertToFormat(Format_RGBA64_Premultiplied, flags).convertToFormat(format, flags);
+ }
+ } else
+ converter = convert_generic;
+ }
if (converter) {
QImage image(d->width, d->height, format);
@@ -2163,8 +2231,15 @@ bool QImage::reinterpretAsFormat(Format format)
return true;
if (qt_depthForFormat(format) != qt_depthForFormat(d->format))
return false;
- if (!isDetached()) // Detach only if shared, not for read-only data.
+ if (!isDetached()) { // Detach only if shared, not for read-only data.
+ QImageData *oldD = d;
detach();
+ // In case detach() ran out of memory
+ if (!d) {
+ d = oldD;
+ return false;
+ }
+ }
d->format = format;
return true;
@@ -2298,13 +2373,16 @@ QRgb QImage::pixel(int x, int y) const
return qConvertA2rgb30ToArgb32<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]);
case Format_RGB16:
return qConvertRgb16To32(reinterpret_cast<const quint16 *>(s)[x]);
+ case Format_RGBX64:
+ case Format_RGBA64: // Match ARGB32 behavior.
+ case Format_RGBA64_Premultiplied:
+ return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32();
default:
break;
}
const QPixelLayout *layout = &qPixelLayouts[d->format];
uint result;
- const uint *ptr = qFetchPixels[layout->bpp](&result, s, x, 1);
- return *layout->convertToARGB32PM(&result, ptr, 1, 0, 0);
+ return *layout->fetchToARGB32PM(&result, s, x, 1, nullptr, nullptr);
}
/*!
@@ -2405,9 +2483,7 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
}
const QPixelLayout *layout = &qPixelLayouts[d->format];
- uint result;
- const uint *ptr = layout->convertFromARGB32PM(&result, &index_or_rgb, 1, 0, 0);
- qStorePixels[layout->bpp](s, ptr, x, 1);
+ layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr);
}
/*!
@@ -2450,6 +2526,11 @@ QColor QImage::pixelColor(int x, int y) const
case Format_A2RGB30_Premultiplied:
c = qConvertA2rgb30ToRgb64<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]);
break;
+ case Format_RGBX64:
+ case Format_RGBA64:
+ case Format_RGBA64_Premultiplied:
+ c = reinterpret_cast<const QRgba64 *>(s)[x];
+ break;
default:
c = QRgba64::fromArgb32(pixel(x, y));
break;
@@ -2520,6 +2601,14 @@ void QImage::setPixelColor(int x, int y, const QColor &color)
case Format_A2RGB30_Premultiplied:
((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c);
return;
+ case Format_RGBX64:
+ ((QRgba64 *)s)[x] = color.rgba64();
+ ((QRgba64 *)s)[x].setAlpha(65535);
+ return;
+ case Format_RGBA64:
+ case Format_RGBA64_Premultiplied:
+ ((QRgba64 *)s)[x] = color.rgba64();
+ return;
default:
setPixel(x, y, c.toArgb32());
return;
@@ -2582,17 +2671,15 @@ bool QImage::allGray() const
break;
}
- const int buffer_size = 2048;
- uint buffer[buffer_size];
+ uint buffer[BufferSize];
const QPixelLayout *layout = &qPixelLayouts[d->format];
- FetchPixelsFunc fetch = qFetchPixels[layout->bpp];
+ const auto fetch = layout->fetchToARGB32PM;
for (int j = 0; j < d->height; ++j) {
const uchar *b = constScanLine(j);
int x = 0;
while (x < d->width) {
- int l = qMin(d->width - x, buffer_size);
- const uint *ptr = fetch(buffer, b, x, l);
- ptr = layout->convertToARGB32PM(buffer, ptr, l, 0, 0);
+ int l = qMin(d->width - x, BufferSize);
+ const uint *ptr = fetch(buffer, b, x, l, nullptr, nullptr);
for (int i = 0; i < l; ++i) {
if (!qIsGray(ptr[i]))
return false;
@@ -2780,6 +2867,13 @@ QMatrix QImage::trueMatrix(const QMatrix &matrix, int w, int h)
Returns a copy of the image that is transformed using the given
transformation \a matrix and transformation \a mode.
+ The returned image will normally have the same {Image Formats}{format} as
+ the original image. However, a complex transformation may result in an
+ image where not all pixels are covered by the transformed pixels of the
+ original image. In such cases, those background pixels will be assigned a
+ transparent color value, and the transformed image will be given a format
+ with an alpha channel, even if the orginal image did not have that.
+
The transformation \a matrix is internally adjusted to compensate
for unwanted translation; i.e. the image produced is the smallest
image that contains all the transformed points of the original
@@ -3098,6 +3192,9 @@ inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool ve
}
switch (depth) {
+ case 64:
+ do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
+ break;
case 32:
do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
@@ -3189,6 +3286,8 @@ void QImage::mirrored_inplace(bool horizontal, bool vertical)
return;
detach();
+ if (!d)
+ return;
if (!d->own_data)
*this = copy();
@@ -3210,33 +3309,18 @@ void QImage::mirrored_inplace(bool horizontal, bool vertical)
inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout)
{
- Q_ASSERT(layout->redWidth == layout->blueWidth);
- FetchPixelsFunc fetch = qFetchPixels[layout->bpp];
- StorePixelsFunc store = qStorePixels[layout->bpp];
-
- const uint redBlueMask = (1 << layout->redWidth) - 1;
- const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift)
- | (((1 << layout->greenWidth) - 1) << layout->greenShift);
+ const RbSwapFunc func = layout->rbSwap;
+ if (!func) {
+ qWarning("Trying to rb-swap an image format where it doesn't make sense");
+ if (src != dst)
+ *dst = *src;
+ return;
+ }
- const int buffer_size = 2048;
- uint buffer[buffer_size];
for (int i = 0; i < height; ++i) {
uchar *q = dst->scanLine(i);
const uchar *p = src->constScanLine(i);
- int x = 0;
- while (x < width) {
- int l = qMin(width - x, buffer_size);
- const uint *ptr = fetch(buffer, p, x, l);
- for (int j = 0; j < l; ++j) {
- uint red = (ptr[j] >> layout->redShift) & redBlueMask;
- uint blue = (ptr[j] >> layout->blueShift) & redBlueMask;
- buffer[j] = (ptr[j] & alphaGreenMask)
- | (red << layout->blueShift)
- | (blue << layout->redShift);
- }
- store(q, buffer, x, l);
- x += l;
- }
+ func(q, p, width);
}
}
@@ -3321,18 +3405,18 @@ QImage QImage::rgbSwapped_helper() const
}
}
break;
- case Format_BGR30:
- case Format_A2BGR30_Premultiplied:
- case Format_RGB30:
- case Format_A2RGB30_Premultiplied:
+ case Format_RGBX64:
+ case Format_RGBA64:
+ case Format_RGBA64_Premultiplied:
res = QImage(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(res);
for (int i = 0; i < d->height; i++) {
- uint *q = (uint*)res.scanLine(i);
- const uint *p = (const uint*)constScanLine(i);
- const uint *end = p + d->width;
+ QRgba64 *q = reinterpret_cast<QRgba64 *>(res.scanLine(i));
+ const QRgba64 *p = reinterpret_cast<const QRgba64 *>(constScanLine(i));
+ const QRgba64 *end = p + d->width;
while (p < end) {
- *q = qRgbSwapRgb30(*p);
+ QRgba64 c = *p;
+ *q = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha());
p++;
q++;
}
@@ -3356,6 +3440,8 @@ void QImage::rgbSwapped_inplace()
return;
detach();
+ if (!d)
+ return;
if (!d->own_data)
*this = copy();
@@ -3430,6 +3516,19 @@ void QImage::rgbSwapped_inplace()
}
}
break;
+ case Format_RGBX64:
+ case Format_RGBA64:
+ case Format_RGBA64_Premultiplied:
+ for (int i = 0; i < d->height; i++) {
+ QRgba64 *p = reinterpret_cast<QRgba64 *>(scanLine(i));
+ QRgba64 *end = p + d->width;
+ while (p < end) {
+ QRgba64 c = *p;
+ *p = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha());
+ p++;
+ }
+ }
+ break;
default:
rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]);
break;
@@ -3457,8 +3556,7 @@ void QImage::rgbSwapped_inplace()
bool QImage::load(const QString &fileName, const char* format)
{
- QImage image = QImageReader(fileName, format).read();
- operator=(image);
+ *this = QImageReader(fileName, format).read();
return !isNull();
}
@@ -3471,8 +3569,7 @@ bool QImage::load(const QString &fileName, const char* format)
bool QImage::load(QIODevice* device, const char* format)
{
- QImage image = QImageReader(device, format).read();
- operator=(image);
+ *this = QImageReader(device, format).read();
return !isNull();
}
@@ -3492,8 +3589,7 @@ bool QImage::load(QIODevice* device, const char* format)
bool QImage::loadFromData(const uchar *data, int len, const char *format)
{
- QImage image = fromData(data, len, format);
- operator=(image);
+ *this = fromData(data, len, format);
return !isNull();
}
@@ -3644,7 +3740,9 @@ QDataStream &operator>>(QDataStream &s, QImage &image)
return s;
}
}
- image = QImageReader(s.device(), 0).read();
+ image = QImageReader(s.device(), s.version() == 1 ? "bmp" : "png").read();
+ if (image.isNull() && s.version() >= 5)
+ s.setStatus(QDataStream::ReadPastEnd);
return s;
}
#endif // QT_NO_DATASTREAM
@@ -4506,6 +4604,9 @@ int QImage::bitPlaneCount() const
case QImage::Format_RGB444:
bpc = 12;
break;
+ case QImage::Format_RGBX64:
+ bpc = 48;
+ break;
default:
bpc = qt_depthForFormat(d->format);
break;
@@ -4526,6 +4627,11 @@ QImage QImage::smoothScaled(int w, int h) const {
case QImage::Format_RGBX8888:
#endif
case QImage::Format_RGBA8888_Premultiplied:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64_Premultiplied:
+ break;
+ case QImage::Format_RGBA64:
+ src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied);
break;
default:
if (src.hasAlphaChannel())
@@ -4610,6 +4716,13 @@ static QImage rotated270(const QImage &image)
Returns a copy of the image that is transformed using the given
transformation \a matrix and transformation \a mode.
+ The returned image will normally have the same {Image Formats}{format} as
+ the original image. However, a complex transformation may result in an
+ image where not all pixels are covered by the transformed pixels of the
+ original image. In such cases, those background pixels will be assigned a
+ transparent color value, and the transformed image will be given a format
+ with an alpha channel, even if the orginal image did not have that.
+
The transformation \a matrix is internally adjusted to compensate
for unwanted translation; i.e. the image produced is the smallest
image that contains all the transformed points of the original
@@ -5197,6 +5310,45 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
/*INTERPRETATION*/ QPixelFormat::UnsignedByte,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBX64:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA64:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_RGBA64_Premultiplied:
+ QPixelFormat(QPixelFormat::RGB,
+ /*RED*/ 16,
+ /*GREEN*/ 16,
+ /*BLUE*/ 16,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 16,
+ /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtEnd,
+ /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
};
Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index 9b76b62f24..4b7a3b1ead 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -96,6 +96,7 @@ typedef void (*QImageCleanupFunction)(void*);
class Q_GUI_EXPORT QImage : public QPaintDevice
{
+ Q_GADGET
public:
enum InvertMode { InvertRgb, InvertRgba };
enum Format {
@@ -124,6 +125,9 @@ public:
Format_A2RGB30_Premultiplied,
Format_Alpha8,
Format_Grayscale8,
+ Format_RGBX64,
+ Format_RGBA64,
+ Format_RGBA64_Premultiplied,
#if 0
// reserved for future use
Format_Grayscale16,
@@ -132,6 +136,7 @@ public:
NImageFormats
#endif
};
+ Q_ENUM(Format)
QImage() Q_DECL_NOEXCEPT;
QImage(const QSize &size, Format format);
@@ -329,7 +334,7 @@ public:
static QPixelFormat toPixelFormat(QImage::Format format) Q_DECL_NOTHROW;
static QImage::Format toImageFormat(QPixelFormat format) Q_DECL_NOTHROW;
- // Platform spesific conversion functions
+ // Platform specific conversion functions
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
CGImageRef toCGImage() const Q_DECL_CF_RETURNS_RETAINED;
#endif
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index d981c43711..215dd33499 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -118,26 +118,40 @@ void qGamma_correct_back_to_linear_cs(QImage *image)
Internal routines for converting image depth.
*****************************************************************************/
-// The drawhelper conversions from/to RGB32 are passthroughs which is not always correct for general image conversion.
-static const uint *QT_FASTCALL convertRGB32FromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+// The drawhelper conversions from/to RGB32 are passthroughs which is not always correct for general image conversion
+#if !defined(__ARM_NEON__)
+static void QT_FASTCALL storeRGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
for (int i = 0; i < count; ++i)
- buffer[i] = 0xff000000 | qUnpremultiply(src[i]);
- return buffer;
+ d[i] = 0xff000000 | qUnpremultiply(src[i]);
+}
+#endif
+
+static void QT_FASTCALL storeRGB32FromARGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = 0xff000000 | src[i];
}
-static const uint *QT_FASTCALL maskRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const uint *QT_FASTCALL fetchRGB32ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
for (int i = 0; i < count; ++i)
- buffer[i] = 0xff000000 |src[i];
+ buffer[i] = 0xff000000 | s[i];
return buffer;
}
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
-extern const uint *QT_FASTCALL convertRGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *);
+extern void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+#elif defined(__ARM_NEON__)
+extern void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
#endif
void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags)
@@ -145,42 +159,43 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
// Cannot be used with indexed formats.
Q_ASSERT(dest->format > QImage::Format_Indexed8);
Q_ASSERT(src->format > QImage::Format_Indexed8);
- const int buffer_size = 2048;
- uint buf[buffer_size];
+ uint buf[BufferSize];
uint *buffer = buf;
const QPixelLayout *srcLayout = &qPixelLayouts[src->format];
const QPixelLayout *destLayout = &qPixelLayouts[dest->format];
const uchar *srcData = src->data;
uchar *destData = dest->data;
- const FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp];
- const StorePixelsFunc store = qStorePixels[destLayout->bpp];
- ConvertFunc convertToARGB32PM = srcLayout->convertToARGB32PM;
- ConvertFunc convertFromARGB32PM = destLayout->convertFromARGB32PM;
- if (srcLayout->alphaWidth == 0 && destLayout->convertFromRGB32) {
- // If the source doesn't have an alpha channel, we can use the faster convertFromRGB32 method.
- convertFromARGB32PM = destLayout->convertFromRGB32;
+ FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM;
+ ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM;
+ if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
+ // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method.
+ store = destLayout->storeFromRGB32;
} else {
// The drawhelpers do not mask the alpha value in RGB32, we want to here.
if (src->format == QImage::Format_RGB32)
- convertToARGB32PM = maskRGB32;
+ fetch = fetchRGB32ToARGB32PM;
if (dest->format == QImage::Format_RGB32) {
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
if (qCpuHasFeature(SSE4_1))
- convertFromARGB32PM = convertRGB32FromARGB32PM_sse4;
+ store = storeRGB32FromARGB32PM_sse4;
else
+ store = storeRGB32FromARGB32PM;
+#elif defined(__ARM_NEON__)
+ store = storeRGB32FromARGB32PM_neon;
+#else
+ store = storeRGB32FromARGB32PM;
#endif
- convertFromARGB32PM = convertRGB32FromARGB32PM;
}
}
- if ((src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888) &&
- destLayout->alphaWidth == 0 && destLayout->convertFromRGB32) {
+ if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
+ !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
// Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
- convertToARGB32PM = qPixelLayouts[src->format + 1].convertToARGB32PM;
+ fetch = qPixelLayouts[src->format + 1].fetchToARGB32PM;
if (dest->format == QImage::Format_RGB32)
- convertFromARGB32PM = maskRGB32;
+ store = storeRGB32FromARGB32;
else
- convertFromARGB32PM = destLayout->convertFromRGB32;
+ store = destLayout->storeFromRGB32;
}
QDitherInfo dither;
QDitherInfo *ditherPtr = 0;
@@ -196,12 +211,9 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
if (destLayout->bpp == QPixelLayout::BPP32)
buffer = reinterpret_cast<uint *>(destData) + x;
else
- l = qMin(l, buffer_size);
- const uint *ptr = fetch(buffer, srcData, x, l);
- ptr = convertToARGB32PM(buffer, ptr, l, 0, ditherPtr);
- ptr = convertFromARGB32PM(buffer, ptr, l, 0, ditherPtr);
- if (ptr != reinterpret_cast<uint *>(destData))
- store(destData, ptr, x, l);
+ l = qMin(l, BufferSize);
+ const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr);
+ store(destData, ptr, x, l, 0, ditherPtr);
x += l;
}
srcData += src->bytes_per_line;
@@ -209,6 +221,26 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
}
}
+void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(src->format > QImage::Format_Indexed8);
+ const QPixelLayout *srcLayout = &qPixelLayouts[src->format];
+ const uchar *srcData = src->data;
+ uchar *destData = dest->data;
+
+ const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM;
+
+ for (int y = 0; y < src->height; ++y) {
+ const QRgba64 *ptr = fetch((QRgba64*)destData, srcData, 0, src->width, nullptr, nullptr);
+ if (ptr != (const QRgba64*)destData) {
+ memcpy(destData, ptr, dest->bytes_per_line);
+ }
+ srcData += src->bytes_per_line;
+ destData += dest->bytes_per_line;
+ }
+}
+
bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags)
{
// Cannot be used with indexed formats or between formats with different pixel depths.
@@ -217,39 +249,43 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
if (data->depth != qt_depthForFormat(dst_format))
return false;
- const int buffer_size = 2048;
- uint buffer[buffer_size];
+ uint buf[BufferSize];
+ uint *buffer = buf;
const QPixelLayout *srcLayout = &qPixelLayouts[data->format];
const QPixelLayout *destLayout = &qPixelLayouts[dst_format];
uchar *srcData = data->data;
- const FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp];
- const StorePixelsFunc store = qStorePixels[destLayout->bpp];
- ConvertFunc convertToARGB32PM = srcLayout->convertToARGB32PM;
- ConvertFunc convertFromARGB32PM = destLayout->convertFromARGB32PM;
- if (srcLayout->alphaWidth == 0 && destLayout->convertFromRGB32) {
- // If the source doesn't have an alpha channel, we can use the faster convertFromRGB32 method.
- convertFromARGB32PM = destLayout->convertFromRGB32;
+ Q_ASSERT(srcLayout->bpp == destLayout->bpp);
+ Q_ASSERT(srcLayout->bpp != QPixelLayout::BPP64);
+ FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM;
+ ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM;
+ if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
+ // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method.
+ store = destLayout->storeFromRGB32;
} else {
if (data->format == QImage::Format_RGB32)
- convertToARGB32PM = maskRGB32;
+ fetch = fetchRGB32ToARGB32PM;
if (dst_format == QImage::Format_RGB32) {
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
if (qCpuHasFeature(SSE4_1))
- convertFromARGB32PM = convertRGB32FromARGB32PM_sse4;
+ store = storeRGB32FromARGB32PM_sse4;
else
+ store = storeRGB32FromARGB32PM;
+#elif defined(__ARM_NEON__)
+ store = storeRGB32FromARGB32PM_neon;
+#else
+ store = storeRGB32FromARGB32PM;
#endif
- convertFromARGB32PM = convertRGB32FromARGB32PM;
}
}
- if ((data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888) &&
- destLayout->alphaWidth == 0 && destLayout->convertFromRGB32) {
+ if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
+ !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
// Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
- convertToARGB32PM = qPixelLayouts[data->format + 1].convertToARGB32PM;
- if (dst_format == QImage::Format_RGB32)
- convertFromARGB32PM = maskRGB32;
+ fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM;
+ if (data->format == QImage::Format_RGB32)
+ store = storeRGB32FromARGB32;
else
- convertFromARGB32PM = destLayout->convertFromRGB32;
+ store = destLayout->storeFromRGB32;
}
QDitherInfo dither;
QDitherInfo *ditherPtr = 0;
@@ -261,13 +297,13 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
int x = 0;
while (x < data->width) {
dither.x = x;
- int l = qMin(data->width - x, buffer_size);
- const uint *ptr = fetch(buffer, srcData, x, l);
- ptr = convertToARGB32PM(buffer, ptr, l, 0, ditherPtr);
- ptr = convertFromARGB32PM(buffer, ptr, l, 0, ditherPtr);
- // The conversions might be passthrough and not use the buffer, in that case we are already done.
- if (srcData != (const uchar*)ptr)
- store(srcData, ptr, x, l);
+ int l = data->width - x;
+ if (destLayout->bpp == QPixelLayout::BPP32)
+ buffer = reinterpret_cast<uint *>(srcData) + x;
+ else
+ l = qMin(l, BufferSize);
+ const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr);
+ store(srcData, ptr, x, l, nullptr, ditherPtr);
x += l;
}
srcData += data->bytes_per_line;
@@ -281,20 +317,15 @@ static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::Ima
Q_ASSERT(src->width == dest->width);
Q_ASSERT(src->height == dest->height);
- const int src_pad = (src->bytes_per_line >> 2) - src->width;
- const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
- const quint32 *src_data = (quint32 *) src->data;
- quint32 *dest_data = (quint32 *) dest->data;
+ const int src_bpl = src->bytes_per_line;
+ const int dest_bpl = dest->bytes_per_line;
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
for (int i = 0; i < src->height; ++i) {
- const quint32 *end = src_data + src->width;
- while (src_data < end) {
- *dest_data = *src_data;
- ++src_data;
- ++dest_data;
- }
- src_data += src_pad;
- dest_data += dest_pad;
+ memcpy(dest_data, src_data, src_bpl);
+ src_data += src_bpl;
+ dest_data += dest_bpl;
}
}
@@ -305,30 +336,6 @@ static bool convert_passthrough_inplace(QImageData *data, Qt::ImageConversionFla
return true;
}
-static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
-{
- Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888);
- Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied);
- Q_ASSERT(src->width == dest->width);
- Q_ASSERT(src->height == dest->height);
-
- const int src_pad = (src->bytes_per_line >> 2) - src->width;
- const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
- const QRgb *src_data = (QRgb *) src->data;
- QRgb *dest_data = (QRgb *) dest->data;
-
- for (int i = 0; i < src->height; ++i) {
- const QRgb *end = src_data + src->width;
- while (src_data < end) {
- *dest_data = qPremultiply(*src_data);
- ++src_data;
- ++dest_data;
- }
- src_data += src_pad;
- dest_data += dest_pad;
- }
-}
-
Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dest_data, const uchar *src_data, int len)
{
int pixel = 0;
@@ -431,33 +438,6 @@ static void convert_RGB888_to_RGB(QImageData *dest, const QImageData *src, Qt::I
}
}
-#ifdef __SSE2__
-extern bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags);
-#else
-static bool convert_ARGB_to_ARGB_PM_inplace(QImageData *data,Qt::ImageConversionFlags)
-{
- Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888);
-
- const int pad = (data->bytes_per_line >> 2) - data->width;
- QRgb *rgb_data = (QRgb *) data->data;
-
- for (int i = 0; i < data->height; ++i) {
- const QRgb *end = rgb_data + data->width;
- while (rgb_data < end) {
- *rgb_data = qPremultiply(*rgb_data);
- ++rgb_data;
- }
- rgb_data += pad;
- }
-
- if (data->format == QImage::Format_ARGB32)
- data->format = QImage::Format_ARGB32_Premultiplied;
- else
- data->format = QImage::Format_RGBA8888_Premultiplied;
- return true;
-}
-#endif
-
static void convert_ARGB_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
Q_ASSERT(src->format == QImage::Format_ARGB32);
@@ -573,11 +553,12 @@ static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFl
return true;
}
-template<QtPixelOrder PixelOrder>
+template<QtPixelOrder PixelOrder, bool RGBA>
static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
- Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
+ Q_ASSERT(RGBA || src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
+ Q_ASSERT(!RGBA || src->format == QImage::Format_RGBX8888 || src->format == QImage::Format_RGBA8888);
Q_ASSERT(dest->format == QImage::Format_BGR30 || dest->format == QImage::Format_RGB30);
Q_ASSERT(src->width == dest->width);
Q_ASSERT(src->height == dest->height);
@@ -590,7 +571,10 @@ static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::Im
for (int i = 0; i < src->height; ++i) {
const quint32 *end = src_data + src->width;
while (src_data < end) {
- *dest_data = qConvertRgb32ToRgb30<PixelOrder>(*src_data);
+ QRgb c = *src_data;
+ if (RGBA)
+ c = RGBA2ARGB(c);
+ *dest_data = qConvertRgb32ToRgb30<PixelOrder>(c);
++src_data;
++dest_data;
}
@@ -599,10 +583,11 @@ static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::Im
}
}
-template<QtPixelOrder PixelOrder>
+template<QtPixelOrder PixelOrder, bool RGBA>
static bool convert_RGB_to_RGB30_inplace(QImageData *data, Qt::ImageConversionFlags)
{
- Q_ASSERT(data->format == QImage::Format_RGB32 || data->format == QImage::Format_ARGB32);
+ Q_ASSERT(RGBA || (data->format == QImage::Format_RGB32 || data->format == QImage::Format_ARGB32));
+ Q_ASSERT(!RGBA || (data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888));
const int pad = (data->bytes_per_line >> 2) - data->width;
QRgb *rgb_data = (QRgb *) data->data;
@@ -610,7 +595,10 @@ static bool convert_RGB_to_RGB30_inplace(QImageData *data, Qt::ImageConversionFl
for (int i = 0; i < data->height; ++i) {
const QRgb *end = rgb_data + data->width;
while (rgb_data < end) {
- *rgb_data = qConvertRgb32ToRgb30<PixelOrder>(*rgb_data);
+ QRgb c = *rgb_data;
+ if (RGBA)
+ c = RGBA2ARGB(c);
+ *rgb_data = qConvertRgb32ToRgb30<PixelOrder>(c);
++rgb_data;
}
rgb_data += pad;
@@ -771,11 +759,11 @@ static bool convert_BGR30_to_A2RGB30_inplace(QImageData *data, Qt::ImageConversi
return true;
}
-template<QtPixelOrder PixelOrder>
+template<QtPixelOrder PixelOrder, bool RGBA>
static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
Q_ASSERT(src->format == QImage::Format_A2RGB30_Premultiplied || src->format == QImage::Format_A2BGR30_Premultiplied);
- Q_ASSERT(dest->format == QImage::Format_ARGB32);
+ Q_ASSERT(RGBA ? dest->format == QImage::Format_RGBA8888 : dest->format == QImage::Format_ARGB32);
Q_ASSERT(src->width == dest->width);
Q_ASSERT(src->height == dest->height);
@@ -788,6 +776,8 @@ static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src,
const quint32 *end = src_data + src->width;
while (src_data < end) {
*dest_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(*src_data));
+ if (RGBA)
+ *dest_data = ARGB2RGBA(*dest_data);
++src_data;
++dest_data;
}
@@ -796,7 +786,7 @@ static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src,
}
}
-template<QtPixelOrder PixelOrder>
+template<QtPixelOrder PixelOrder, bool RGBA>
static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags)
{
Q_ASSERT(data->format == QImage::Format_A2RGB30_Premultiplied || data->format == QImage::Format_A2BGR30_Premultiplied);
@@ -808,11 +798,16 @@ static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConver
const uint *end = rgb_data + data->width;
while (rgb_data < end) {
*rgb_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(*rgb_data));
+ if (RGBA)
+ *rgb_data = ARGB2RGBA(*rgb_data);
++rgb_data;
}
rgb_data += pad;
}
- data->format = QImage::Format_ARGB32;
+ if (RGBA)
+ data->format = QImage::Format_RGBA8888;
+ else
+ data->format = QImage::Format_ARGB32;
return true;
}
@@ -822,10 +817,10 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve
Q_ASSERT(data->own_data);
const int depth = 32;
-
- const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
- const qsizetype nbytes = dst_bytes_per_line * data->height;
- uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ auto params = QImageData::calculateImageParameters(data->width, data->height, depth);
+ if (params.bytesPerLine < 0)
+ return false;
+ uchar *const newData = (uchar *)realloc(data->data, params.totalSize);
if (!newData)
return false;
@@ -833,10 +828,10 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve
// start converting from the end because the end image is bigger than the source
uchar *src_data = newData + data->nbytes; // end of src
- quint32 *dest_data = (quint32 *) (newData + nbytes); // end of dest > end of src
+ quint32 *dest_data = (quint32 *) (newData + params.totalSize); // end of dest > end of src
const int width = data->width;
const int src_pad = data->bytes_per_line - width;
- const int dest_pad = (dst_bytes_per_line >> 2) - width;
+ const int dest_pad = (params.bytesPerLine >> 2) - width;
if (data->colortable.size() == 0) {
data->colortable.resize(256);
for (int i = 0; i < 256; ++i)
@@ -863,9 +858,9 @@ static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConve
data->colortable = QVector<QRgb>();
data->format = QImage::Format_ARGB32_Premultiplied;
- data->bytes_per_line = dst_bytes_per_line;
+ data->bytes_per_line = params.bytesPerLine;
data->depth = depth;
- data->nbytes = nbytes;
+ data->nbytes = params.totalSize;
return true;
}
@@ -876,10 +871,10 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi
Q_ASSERT(data->own_data);
const int depth = 32;
-
- const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
- const qsizetype nbytes = dst_bytes_per_line * data->height;
- uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ auto params = QImageData::calculateImageParameters(data->width, data->height, depth);
+ if (params.bytesPerLine < 0)
+ return false;
+ uchar *const newData = (uchar *)realloc(data->data, params.totalSize);
if (!newData)
return false;
@@ -887,10 +882,10 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi
// start converting from the end because the end image is bigger than the source
uchar *src_data = newData + data->nbytes;
- quint32 *dest_data = (quint32 *) (newData + nbytes);
+ quint32 *dest_data = (quint32 *) (newData + params.totalSize);
const int width = data->width;
const int src_pad = data->bytes_per_line - width;
- const int dest_pad = (dst_bytes_per_line >> 2) - width;
+ const int dest_pad = (params.bytesPerLine >> 2) - width;
if (data->colortable.size() == 0) {
data->colortable.resize(256);
for (int i = 0; i < 256; ++i)
@@ -914,9 +909,9 @@ static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversi
data->colortable = QVector<QRgb>();
data->format = QImage::Format_ARGB32;
- data->bytes_per_line = dst_bytes_per_line;
+ data->bytes_per_line = params.bytesPerLine;
data->depth = depth;
- data->nbytes = nbytes;
+ data->nbytes = params.totalSize;
return true;
}
@@ -944,10 +939,10 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers
Q_ASSERT(data->own_data);
const int depth = 16;
-
- const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
- const qsizetype nbytes = dst_bytes_per_line * data->height;
- uchar *const newData = (uchar *)realloc(data->data, nbytes);
+ auto params = QImageData::calculateImageParameters(data->width, data->height, depth);
+ if (params.bytesPerLine < 0)
+ return false;
+ uchar *const newData = (uchar *)realloc(data->data, params.totalSize);
if (!newData)
return false;
@@ -955,10 +950,10 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers
// start converting from the end because the end image is bigger than the source
uchar *src_data = newData + data->nbytes;
- quint16 *dest_data = (quint16 *) (newData + nbytes);
+ quint16 *dest_data = (quint16 *) (newData + params.totalSize);
const int width = data->width;
const int src_pad = data->bytes_per_line - width;
- const int dest_pad = (dst_bytes_per_line >> 1) - width;
+ const int dest_pad = (params.bytesPerLine >> 1) - width;
quint16 colorTableRGB16[256];
const int tableSize = data->colortable.size();
@@ -988,9 +983,9 @@ static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConvers
}
data->format = QImage::Format_RGB16;
- data->bytes_per_line = dst_bytes_per_line;
+ data->bytes_per_line = params.bytesPerLine;
data->depth = depth;
- data->nbytes = nbytes;
+ data->nbytes = params.totalSize;
return true;
}
@@ -1002,6 +997,7 @@ static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFl
const int depth = 16;
+ // cannot overflow, since we're shrinking the buffer
const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2;
const qsizetype src_bytes_per_line = data->bytes_per_line;
quint32 *src_data = (quint32 *) data->data;
@@ -1018,12 +1014,11 @@ static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFl
data->depth = depth;
data->nbytes = dst_bytes_per_line * data->height;
uchar *const newData = (uchar *)realloc(data->data, data->nbytes);
- if (newData) {
+ if (newData)
data->data = newData;
- return true;
- } else {
- return false;
- }
+
+ // can't fail, since we're shrinking
+ return true;
}
static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src)
@@ -1188,6 +1183,270 @@ static bool mask_alpha_converter_rgbx_inplace(QImageData *data, Qt::ImageConvers
#endif
}
+template<bool RGBA>
+static void convert_RGBA64_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64);
+ Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32);
+ Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const uchar *srcData = src->data;
+ uchar *destData = dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ uint *d = reinterpret_cast<uint *>(destData);
+ const QRgba64 *s = reinterpret_cast<const QRgba64 *>(srcData);
+ qt_convertRGBA64ToARGB32<RGBA>(d, s, src->width);
+ srcData += src->bytes_per_line;
+ destData += dest->bytes_per_line;
+ }
+}
+
+template<bool RGBA>
+static void convert_RGBA64PM_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32);
+ Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ uint *dest_data = reinterpret_cast<uint *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ QRgba64 s = src_data->unpremultiplied();
+ *dest_data = RGBA ? ARGB2RGBA(s.toArgb32()) : s.toArgb32();
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+template<bool RGBA>
+static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32);
+ Q_ASSERT(!RGBA || src->format == QImage::Format_RGBA8888);
+ Q_ASSERT(dest->format == QImage::Format_RGBA64);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 2) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
+ const uint *src_data = reinterpret_cast<const uint *>(src->data);
+ QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const uint *end = src_data + src->width;
+ while (src_data < end) {
+ if (RGBA)
+ *dest_data = QRgba64::fromArgb32(RGBA2ARGB(*src_data));
+ else
+ *dest_data = QRgba64::fromArgb32(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+template<QtPixelOrder PixelOrder>
+static void convert_RGBA64PM_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_RGB30 || dest->format == QImage::Format_BGR30);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ uint *dest_data = reinterpret_cast<uint *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = 0xc0000000 | qConvertRgb64ToRgb30<PixelOrder>(src_data->unpremultiplied());
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+template<QtPixelOrder PixelOrder>
+static void convert_RGBA64PM_to_A2RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_A2RGB30_Premultiplied
+ || dest->format == QImage::Format_A2BGR30_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ uint *dest_data = reinterpret_cast<uint *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = qConvertRgb64ToRgb30<PixelOrder>(*src_data);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static void convert_RGBA64_to_RGBx64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64);
+ Q_ASSERT(dest->format == QImage::Format_RGBX64);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = *src_data;
+ dest_data->setAlpha(65535);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static bool convert_RGBA64_to_RGBx64_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_RGBA64);
+
+ const int pad = (data->bytes_per_line >> 3) - data->width;
+ QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
+
+ for (int i = 0; i < data->height; ++i) {
+ const QRgba64 *end = rgb_data + data->width;
+ while (rgb_data < end) {
+ rgb_data->setAlpha(65535);
+ ++rgb_data;
+ }
+ rgb_data += pad;
+ }
+ data->format = QImage::Format_RGBX64;
+ return true;
+}
+
+static void convert_RGBA64_to_RGBA64PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64);
+ Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = src_data->premultiplied();
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+static bool convert_RGBA64_to_RGBA64PM_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_RGBA64);
+
+ const int pad = (data->bytes_per_line >> 3) - data->width;
+ QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
+
+ for (int i = 0; i < data->height; ++i) {
+ const QRgba64 *end = rgb_data + data->width;
+ while (rgb_data < end) {
+ *rgb_data = rgb_data->premultiplied();
+ ++rgb_data;
+ }
+ rgb_data += pad;
+ }
+ data->format = QImage::Format_RGBA64_Premultiplied;
+ return true;
+}
+
+template<bool MaskAlpha>
+static void convert_RGBA64PM_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const int src_pad = (src->bytes_per_line >> 3) - src->width;
+ const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
+ const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
+ QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
+
+ for (int i = 0; i < src->height; ++i) {
+ const QRgba64 *end = src_data + src->width;
+ while (src_data < end) {
+ *dest_data = src_data->unpremultiplied();
+ if (MaskAlpha)
+ dest_data->setAlpha(65535);
+ ++src_data;
+ ++dest_data;
+ }
+ src_data += src_pad;
+ dest_data += dest_pad;
+ }
+}
+
+template<bool MaskAlpha>
+static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_RGBA64_Premultiplied);
+
+ const int pad = (data->bytes_per_line >> 3) - data->width;
+ QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
+
+ for (int i = 0; i < data->height; ++i) {
+ const QRgba64 *end = rgb_data + data->width;
+ while (rgb_data < end) {
+ *rgb_data = rgb_data->unpremultiplied();
+ if (MaskAlpha)
+ rgb_data->setAlpha(65535);
+ ++rgb_data;
+ }
+ rgb_data += pad;
+ }
+ data->format = MaskAlpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
+ return true;
+}
+
static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format)
{
QVector<QRgb> colorTable = ctbl;
@@ -2032,7 +2291,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo
Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] =
{
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},
{
0,
@@ -2053,7 +2312,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_Mono
{
@@ -2075,7 +2334,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_MonoLSB
{
@@ -2100,6 +2359,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0, 0, 0, 0, 0,
convert_Indexed8_to_Alpha8,
convert_Indexed8_to_Grayscale8,
+ 0, 0, 0
}, // Format_Indexed8
{
@@ -2122,11 +2382,12 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- convert_RGB_to_RGB30<PixelOrderBGR>,
+ convert_RGB_to_RGB30<PixelOrderBGR, false>,
0,
- convert_RGB_to_RGB30<PixelOrderRGB>,
+ convert_RGB_to_RGB30<PixelOrderRGB, false>,
0,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_RGB32
{
@@ -2136,7 +2397,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_ARGB_to_Indexed8,
mask_alpha_converter,
0,
- convert_ARGB_to_ARGB_PM,
+ 0,
0,
0,
0,
@@ -2149,11 +2410,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_ARGB_to_RGBx,
convert_ARGB_to_RGBA,
0,
- convert_RGB_to_RGB30<PixelOrderBGR>,
+ convert_RGB_to_RGB30<PixelOrderBGR, false>,
0,
- convert_RGB_to_RGB30<PixelOrderRGB>,
+ convert_RGB_to_RGB30<PixelOrderRGB, false>,
0,
- 0, 0
+ 0, 0,
+ 0,
+ convert_ARGB32_to_RGBA64<false>,
+ 0
}, // Format_ARGB32
{
@@ -2176,11 +2440,9 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
convert_ARGB_to_RGBA,
- 0,
- 0,
- 0,
- 0,
- 0, 0
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0
}, // Format_ARGB32_Premultiplied
{
@@ -2202,7 +2464,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB16
{
@@ -2224,7 +2486,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8565_Premultiplied
{
@@ -2246,7 +2508,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB666
{
@@ -2268,7 +2530,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB6666_Premultiplied
{
@@ -2290,7 +2552,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB555
{
@@ -2312,7 +2574,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8555_Premultiplied
{
@@ -2335,7 +2597,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_RGB888_to_RGB<true>,
convert_RGB888_to_RGB<true>,
convert_RGB888_to_RGB<true>,
- 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB888
{
@@ -2357,7 +2619,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB444
{
@@ -2378,7 +2640,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB4444_Premultiplied
{
0,
@@ -2398,9 +2660,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- mask_alpha_converter_RGBx,
- mask_alpha_converter_RGBx,
- 0, 0, 0, 0, 0, 0
+ convert_passthrough,
+ convert_passthrough,
+ convert_RGB_to_RGB30<PixelOrderBGR, true>,
+ 0,
+ convert_RGB_to_RGB30<PixelOrderRGB, true>,
+ 0,
+ 0, 0,
+ 0, 0, 0
}, // Format_RGBX8888
{
0,
@@ -2420,14 +2687,16 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
mask_alpha_converter_RGBx,
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0,
- convert_ARGB_to_ARGB_PM,
-#else
0,
+ convert_RGB_to_RGB30<PixelOrderBGR, true>,
0,
-#endif
- 0, 0, 0, 0, 0, 0
+ convert_RGB_to_RGB30<PixelOrderRGB, true>,
+ 0,
+ 0, 0,
+ 0,
+ convert_ARGB32_to_RGBA64<true>,
+ 0
}, // Format_RGBA8888
{
@@ -2449,7 +2718,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGBA8888_Premultiplied
{
@@ -2476,7 +2745,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_passthrough,
convert_BGR30_to_RGB30,
convert_BGR30_to_RGB30,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_BGR30
{
0,
@@ -2484,8 +2754,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- convert_A2RGB30_PM_to_ARGB<PixelOrderBGR>,
- 0,
+ convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, false>,
0,
0,
0,
@@ -2497,12 +2766,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
+ convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, true>,
0,
convert_A2RGB30_PM_to_RGB30<false>,
0,
convert_A2RGB30_PM_to_RGB30<true>,
convert_BGR30_to_RGB30,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_BGR30A2_Premultiplied
{
0,
@@ -2528,7 +2799,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_BGR30_to_RGB30,
0,
convert_passthrough,
- 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGB30
{
0,
@@ -2536,8 +2807,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- convert_A2RGB30_PM_to_ARGB<PixelOrderRGB>,
- 0,
+ convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, false>,
0,
0,
0,
@@ -2549,12 +2819,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
+ convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, true>,
0,
convert_A2RGB30_PM_to_RGB30<true>,
convert_BGR30_to_RGB30,
convert_A2RGB30_PM_to_RGB30<false>,
0,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_RGB30A2_Premultiplied
{
0,
@@ -2574,7 +2846,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_Alpha8
{
0,
@@ -2594,20 +2866,81 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0, 0, 0, 0, 0, 0, 0
- } // Format_Grayscale8
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_Grayscale8
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // self
+ convert_passthrough,
+ convert_passthrough
+ }, // Format_RGBX64
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGBA64_to_ARGB32<false>,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGBA64_to_ARGB32<true>,
+ 0,
+ 0, 0, 0, 0,
+ 0, 0,
+ convert_RGBA64_to_RGBx64,
+ 0, // self
+ convert_RGBA64_to_RGBA64PM
+ }, // Format_RGBA64
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGBA64PM_to_ARGB32<false>,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGBA64PM_to_ARGB32<true>,
+ 0,
+ convert_RGBA64PM_to_RGB30<PixelOrderBGR>,
+ convert_RGBA64PM_to_A2RGB30<PixelOrderBGR>,
+ convert_RGBA64PM_to_RGB30<PixelOrderRGB>,
+ convert_RGBA64PM_to_A2RGB30<PixelOrderRGB>,
+ 0, 0,
+ convert_RGBA64PM_to_RGBA64<true>,
+ convert_RGBA64PM_to_RGBA64<false>,
+ 0 // self
+ } // Format_RGBA64_Premultiplied
};
InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] =
{
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_Mono
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_MonoLSB
{
0,
@@ -2631,6 +2964,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0, 0, 0, 0, 0,
convert_Indexed8_to_Alpha8_inplace,
convert_Indexed8_to_Grayscale8_inplace,
+ 0, 0, 0
}, // Format_Indexed8
{
0,
@@ -2652,11 +2986,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
- convert_RGB_to_RGB30_inplace<PixelOrderBGR>,
+ convert_RGB_to_RGB30_inplace<PixelOrderBGR, false>,
0,
- convert_RGB_to_RGB30_inplace<PixelOrderRGB>,
+ convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>,
0,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_RGB32
{
0,
@@ -2665,11 +3000,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
mask_alpha_converter_inplace<QImage::Format_RGB32>,
0,
-#ifdef __SSE2__
- convert_ARGB_to_ARGB_PM_inplace_sse2,
-#else
- convert_ARGB_to_ARGB_PM_inplace,
-#endif
+ 0,
0,
0,
0,
@@ -2682,11 +3013,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_ARGB_to_RGBA_inplace<QImage::Format_RGBX8888>,
convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888>,
0,
- convert_RGB_to_RGB30_inplace<PixelOrderBGR>,
+ convert_RGB_to_RGB30_inplace<PixelOrderBGR, false>,
0,
- convert_RGB_to_RGB30_inplace<PixelOrderRGB>,
+ convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>,
0,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_ARGB32
{
0,
@@ -2708,38 +3040,36 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>,
- 0,
- 0,
- 0,
- 0,
- 0, 0
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0
}, // Format_ARGB32_Premultiplied
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB16
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8565_Premultiplied
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB666
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB6666_Premultiplied
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB555
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8555_Premultiplied
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB888
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB444
{
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_ARGB4444_Premultiplied
{
0,
@@ -2761,7 +3091,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
convert_passthrough_inplace<QImage::Format_RGBA8888>,
convert_passthrough_inplace<QImage::Format_RGBA8888_Premultiplied>,
- 0, 0, 0, 0, 0, 0
+ convert_RGB_to_RGB30_inplace<PixelOrderBGR, true>,
+ 0,
+ convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>,
+ 0,
+ 0, 0,
+ 0, 0, 0
}, // Format_RGBX8888
{
0,
@@ -2782,14 +3117,13 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
mask_alpha_converter_rgbx_inplace,
0,
-#ifdef __SSE2__
- convert_ARGB_to_ARGB_PM_inplace_sse2,
-#elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- convert_ARGB_to_ARGB_PM_inplace,
-#else
0,
-#endif
- 0, 0, 0, 0, 0, 0
+ convert_RGB_to_RGB30_inplace<PixelOrderBGR, true>,
+ 0,
+ convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>,
+ 0,
+ 0, 0,
+ 0, 0, 0
}, // Format_RGBA8888
{
0,
@@ -2811,7 +3145,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
- 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
}, // Format_RGBA8888_Premultiplied
{
0,
@@ -2837,7 +3171,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_passthrough_inplace<QImage::Format_A2BGR30_Premultiplied>,
convert_BGR30_to_RGB30_inplace,
convert_BGR30_to_A2RGB30_inplace,
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_BGR30
{
0,
@@ -2845,8 +3180,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
- convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR>,
- 0,
+ convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, false>,
0,
0,
0,
@@ -2858,12 +3192,13 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
+ convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, true>,
0,
convert_A2RGB30_PM_to_RGB30_inplace<false>,
0, // self
convert_A2RGB30_PM_to_RGB30_inplace<true>,
convert_BGR30_to_RGB30_inplace,
- 0, 0
+ 0, 0, 0, 0, 0
}, // Format_BGR30A2_Premultiplied
{
0,
@@ -2889,7 +3224,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_BGR30_to_A2RGB30_inplace,
0, // self
convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>,
- 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGB30
{
0,
@@ -2897,8 +3232,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
- convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB>,
- 0,
+ convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, false>,
0,
0,
0,
@@ -2910,12 +3244,14 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
+ convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, true>,
0,
convert_A2RGB30_PM_to_RGB30_inplace<true>,
convert_BGR30_to_RGB30_inplace,
convert_A2RGB30_PM_to_RGB30_inplace<false>,
0, // self
- 0, 0
+ 0, 0,
+ 0, 0, 0
}, // Format_RGB30A2_Premultiplied
{
0,
@@ -2937,11 +3273,10 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
+ 0, 0, 0, 0,
+ 0, // self
0,
- 0,
- 0,
- 0,
- 0, 0
+ 0, 0, 0
}, // Format_Alpha8
{
0,
@@ -2963,12 +3298,29 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0,
0,
0,
+ 0, 0, 0, 0,
0,
- 0,
- 0,
- 0,
- 0, 0
- } // Format_Grayscale8
+ 0, // self
+ 0, 0, 0
+ }, // Format_Grayscale8
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // self
+ convert_passthrough_inplace<QImage::Format_RGBA64>,
+ convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>,
+ }, // Format_RGBX64
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ convert_RGBA64_to_RGBx64_inplace,
+ 0, // self
+ convert_RGBA64_to_RGBA64PM_inplace
+ }, // Format_RGBA64
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ convert_RGBA64PM_to_RGBA64_inplace<true>,
+ convert_RGBA64PM_to_RGBA64_inplace<false>,
+ 0 // self
+ } // Format_RGBA64_Premultiplied
};
static void qInitImageConversions()
@@ -2982,22 +3334,6 @@ static void qInitImageConversions()
}
#endif
-#if defined(QT_COMPILER_SUPPORTS_SSE4_1) && !defined(__SSE4_1__)
- if (qCpuHasFeature(SSE4_1)) {
- extern void convert_ARGB_to_ARGB_PM_sse4(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
- qimage_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_sse4;
- qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_ARGB_PM_sse4;
- }
-#endif
-
-#if defined(QT_COMPILER_SUPPORTS_AVX2) && !defined(__AVX2__)
- if (qCpuHasFeature(AVX2)) {
- extern void convert_ARGB_to_ARGB_PM_avx2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
- qimage_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_avx2;
- qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_ARGB_PM_avx2;
- }
-#endif
-
#if defined(__ARM_NEON__)
extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon;
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index f5fea2ed00..e3a6c53833 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -52,6 +52,7 @@
//
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/private/qnumeric_p.h>
#include <QMap>
#include <QVector>
@@ -104,8 +105,40 @@ struct Q_GUI_EXPORT QImageData { // internal image data
bool doImageIO(const QImage *image, QImageWriter* io, int quality) const;
QPaintEngine *paintEngine;
+
+ struct ImageSizeParameters {
+ qsizetype bytesPerLine;
+ qsizetype totalSize;
+ };
+ static ImageSizeParameters calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth);
};
+inline QImageData::ImageSizeParameters
+QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth)
+{
+ ImageSizeParameters invalid = { -1, -1 };
+ if (height <= 0)
+ return invalid;
+
+ // calculate the size, taking care of overflows
+ qsizetype bytes_per_line;
+ if (mul_overflow(width, depth, &bytes_per_line))
+ return invalid;
+ if (add_overflow(bytes_per_line, qsizetype(31), &bytes_per_line))
+ return invalid;
+ // bytes per scanline (must be multiple of 4)
+ bytes_per_line = (bytes_per_line >> 5) << 2; // can't overflow
+
+ qsizetype total_size;
+ if (mul_overflow(height, bytes_per_line, &total_size))
+ return invalid;
+ qsizetype dummy;
+ if (mul_overflow(height, qsizetype(sizeof(uchar *)), &dummy))
+ return invalid; // why is this here?
+
+ return { bytes_per_line, total_size };
+}
+
typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags);
@@ -113,6 +146,7 @@ extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImag
extern InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats];
void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags);
void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha);
@@ -164,6 +198,11 @@ inline int qt_depthForFormat(QImage::Format format)
case QImage::Format_RGB888:
depth = 24;
break;
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ depth = 64;
+ break;
}
return depth;
}
@@ -190,6 +229,9 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format)
return QImage::Format_BGR30;
case QImage::Format_A2RGB30_Premultiplied:
return QImage::Format_RGB30;
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ return QImage::Format_RGBX64;
case QImage::Format_ARGB32_Premultiplied:
case QImage::Format_ARGB32:
default:
@@ -214,6 +256,8 @@ inline QImage::Format qt_alphaVersion(QImage::Format format)
return QImage::Format_A2BGR30_Premultiplied;
case QImage::Format_RGB30:
return QImage::Format_A2RGB30_Premultiplied;
+ case QImage::Format_RGBX64:
+ return QImage::Format_RGBA64_Premultiplied;
default:
break;
}
diff --git a/src/gui/image/qimage_sse2.cpp b/src/gui/image/qimage_sse2.cpp
deleted file mode 100644
index 8f7195e0b5..0000000000
--- a/src/gui/image/qimage_sse2.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qimage.h"
-#include <private/qimage_p.h>
-#include <private/qsimd_p.h>
-#include <private/qdrawhelper_p.h>
-#include <private/qdrawingprimitive_sse2_p.h>
-
-#ifdef QT_COMPILER_SUPPORTS_SSE2
-
-QT_BEGIN_NAMESPACE
-
-bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags)
-{
- Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888);
-
- const int width = data->width;
- const int height = data->height;
- const int bpl = data->bytes_per_line;
-
- const __m128i alphaMask = _mm_set1_epi32(0xff000000);
- const __m128i nullVector = _mm_setzero_si128();
- const __m128i half = _mm_set1_epi16(0x80);
- const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
-
- uchar *d = data->data;
- for (int y = 0; y < height; ++y) {
- int i = 0;
- quint32 *d32 = reinterpret_cast<quint32 *>(d);
- ALIGNMENT_PROLOGUE_16BYTES(d, i, width) {
- const quint32 p = d32[i];
- if (p <= 0x00ffffff)
- d32[i] = 0;
- else if (p < 0xff000000)
- d32[i] = qPremultiply(p);
- }
- __m128i *d128 = reinterpret_cast<__m128i *>(d32 + i);
- for (; i < (width - 3); i += 4) {
- const __m128i srcVector = _mm_load_si128(d128);
-#ifdef __SSE4_1__
- if (_mm_testc_si128(srcVector, alphaMask)) {
- // opaque, data is unchanged
- } else if (_mm_testz_si128(srcVector, alphaMask)) {
- // fully transparent
- _mm_store_si128(d128, nullVector);
- } else {
- const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask);
-#else
- const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask);
- if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) {
- // opaque, data is unchanged
- } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) == 0xffff) {
- // fully transparent
- _mm_store_si128(d128, nullVector);
- } else {
-#endif
- __m128i alphaChannel = _mm_srli_epi32(srcVector, 24);
- alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16));
-
- __m128i result;
- BYTE_MUL_SSE2(result, srcVector, alphaChannel, colorMask, half);
- result = _mm_or_si128(_mm_andnot_si128(alphaMask, result), srcVectorAlpha);
- _mm_store_si128(d128, result);
- }
- d128++;
- }
-
- SIMD_EPILOGUE(i, width, 3) {
- const quint32 p = d32[i];
- if (p <= 0x00ffffff)
- d32[i] = 0;
- else if (p < 0xff000000)
- d32[i] = qPremultiply(p);
- }
-
- d += bpl;
- }
-
- if (data->format == QImage::Format_ARGB32)
- data->format = QImage::Format_ARGB32_Premultiplied;
- else
- data->format = QImage::Format_RGBA8888_Premultiplied;
- return true;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_COMPILER_SUPPORTS_SSE2
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 7086e102ea..0fb1d808e5 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -164,73 +164,14 @@
#include <private/qpnghandler_p.h>
#endif
+#include <private/qimagereaderwriterhelpers_p.h>
+#include <qtgui_tracepoints_p.h>
+
#include <algorithm>
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_IMAGEFORMATPLUGIN
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
- (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
-#endif
-
-enum _qt_BuiltInFormatType {
-#ifndef QT_NO_IMAGEFORMAT_PNG
- _qt_PngFormat,
-#endif
-#ifndef QT_NO_IMAGEFORMAT_BMP
- _qt_BmpFormat,
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PPM
- _qt_PpmFormat,
- _qt_PgmFormat,
- _qt_PbmFormat,
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XBM
- _qt_XbmFormat,
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XPM
- _qt_XpmFormat,
-#endif
- _qt_NumFormats,
- _qt_NoFormat = -1
-};
-
-#if !defined(QT_NO_IMAGEFORMAT_PPM)
-# define MAX_MT_SIZE 20
-#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM)
-# define MAX_MT_SIZE 10
-#else
-# define MAX_MT_SIZE 4
-#endif
-
-struct _qt_BuiltInFormatStruct
-{
- char extension[4];
- char mimeType[MAX_MT_SIZE];
-};
-
-#undef MAX_MT_SIZE
-
-static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = {
-#ifndef QT_NO_IMAGEFORMAT_PNG
- {"png", "png"},
-#endif
-#ifndef QT_NO_IMAGEFORMAT_BMP
- {"bmp", "bmp"},
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PPM
- {"ppm", "x-portable-pixmap"},
- {"pgm", "x-portable-graymap"},
- {"pbm", "x-portable-bitmap"},
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XBM
- {"xbm", "x-xbitmap"},
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XPM
- {"xpm", "x-xpixmap"},
-#endif
-};
-Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats);
+using namespace QImageReaderWriterHelpers;
static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
const QByteArray &format,
@@ -251,7 +192,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
typedef QMultiMap<int, QString> PluginKeyMap;
// check if we have plugins that support the image format
- QFactoryLoader *l = loader();
+ auto l = QImageReaderWriterHelpers::pluginLoader();
const PluginKeyMap keyMap = l->keyMap();
#ifdef QIMAGEREADER_DEBUG
@@ -1310,7 +1251,18 @@ bool QImageReader::read(QImage *image)
d->handler->setOption(QImageIOHandler::Quality, d->quality);
// read the image
- if (!d->handler->read(image)) {
+ if (Q_TRACE_ENABLED(QImageReader_read_before_reading)) {
+ QString fileName = QStringLiteral("unknown");
+ if (QFile *file = qobject_cast<QFile *>(d->device))
+ fileName = file->fileName();
+ Q_TRACE(QImageReader_read_before_reading, this, fileName);
+ }
+
+ const bool result = d->handler->read(image);
+
+ Q_TRACE(QImageReader_read_after_reading, this, result);
+
+ if (!result) {
d->imageReaderError = InvalidDataError;
d->errorString = QImageReader::tr("Unable to read image data");
return false;
@@ -1565,16 +1517,6 @@ QByteArray QImageReader::imageFormat(QIODevice *device)
return format;
}
-#ifndef QT_NO_IMAGEFORMATPLUGIN
-void supportedImageHandlerFormats(QFactoryLoader *loader,
- QImageIOPlugin::Capability cap,
- QList<QByteArray> *result);
-
-void supportedImageHandlerMimeTypes(QFactoryLoader *loader,
- QImageIOPlugin::Capability cap,
- QList<QByteArray> *result);
-#endif
-
/*!
Returns the list of image formats supported by QImageReader.
@@ -1605,18 +1547,7 @@ void supportedImageHandlerMimeTypes(QFactoryLoader *loader,
QList<QByteArray> QImageReader::supportedImageFormats()
{
- QList<QByteArray> formats;
- formats.reserve(_qt_NumFormats);
- for (int i = 0; i < _qt_NumFormats; ++i)
- formats << _qt_BuiltInFormats[i].extension;
-
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- supportedImageHandlerFormats(loader(), QImageIOPlugin::CanRead, &formats);
-#endif // QT_NO_IMAGEFORMATPLUGIN
-
- std::sort(formats.begin(), formats.end());
- formats.erase(std::unique(formats.begin(), formats.end()), formats.end());
- return formats;
+ return QImageReaderWriterHelpers::supportedImageFormats(QImageReaderWriterHelpers::CanRead);
}
/*!
@@ -1630,18 +1561,24 @@ QList<QByteArray> QImageReader::supportedImageFormats()
QList<QByteArray> QImageReader::supportedMimeTypes()
{
- QList<QByteArray> mimeTypes;
- mimeTypes.reserve(_qt_NumFormats);
- for (const auto &fmt : _qt_BuiltInFormats)
- mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType);
+ return QImageReaderWriterHelpers::supportedMimeTypes(QImageReaderWriterHelpers::CanRead);
+}
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanRead, &mimeTypes);
-#endif // QT_NO_IMAGEFORMATPLUGIN
+/*!
+ \since 5.12
- std::sort(mimeTypes.begin(), mimeTypes.end());
- mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end());
- return mimeTypes;
+ Returns the list of image formats corresponding to \a mimeType.
+
+ Note that the QGuiApplication instance must be created before this function is
+ called.
+
+ \sa supportedImageFormats(), supportedMimeTypes()
+*/
+
+QList<QByteArray> QImageReader::imageFormatsForMimeType(const QByteArray &mimeType)
+{
+ return QImageReaderWriterHelpers::imageFormatsForMimeType(mimeType,
+ QImageReaderWriterHelpers::CanRead);
}
QT_END_NAMESPACE
diff --git a/src/gui/image/qimagereader.h b/src/gui/image/qimagereader.h
index 9d6c1e0b1e..4e9a08b6e6 100644
--- a/src/gui/image/qimagereader.h
+++ b/src/gui/image/qimagereader.h
@@ -144,6 +144,7 @@ public:
static QByteArray imageFormat(QIODevice *device);
static QList<QByteArray> supportedImageFormats();
static QList<QByteArray> supportedMimeTypes();
+ static QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType);
private:
Q_DISABLE_COPY(QImageReader)
diff --git a/src/gui/image/qimagereaderwriterhelpers.cpp b/src/gui/image/qimagereaderwriterhelpers.cpp
new file mode 100644
index 0000000000..a5b7fb6449
--- /dev/null
+++ b/src/gui/image/qimagereaderwriterhelpers.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qimagereaderwriterhelpers_p.h"
+
+#include <qjsonarray.h>
+#include <qmutex.h>
+#include <private/qfactoryloader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QImageReaderWriterHelpers {
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+Q_GLOBAL_STATIC(QMutex, loaderMutex)
+
+static void appendImagePluginFormats(QFactoryLoader *loader,
+ QImageIOPlugin::Capability cap,
+ QList<QByteArray> *result)
+{
+ typedef QMultiMap<int, QString> PluginKeyMap;
+ typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
+
+ const PluginKeyMap keyMap = loader->keyMap();
+ const PluginKeyMapConstIterator cend = keyMap.constEnd();
+ int i = -1;
+ QImageIOPlugin *plugin = 0;
+ result->reserve(result->size() + keyMap.size());
+ for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) {
+ if (it.key() != i) {
+ i = it.key();
+ plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i));
+ }
+ const QByteArray key = it.value().toLatin1();
+ if (plugin && (plugin->capabilities(0, key) & cap) != 0)
+ result->append(key);
+ }
+}
+
+static void appendImagePluginMimeTypes(QFactoryLoader *loader,
+ QImageIOPlugin::Capability cap,
+ QList<QByteArray> *result,
+ QList<QByteArray> *resultKeys = nullptr)
+{
+ QList<QJsonObject> metaDataList = loader->metaData();
+
+ const int pluginCount = metaDataList.size();
+ for (int i = 0; i < pluginCount; ++i) {
+ const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
+ const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
+ const QJsonArray mimeTypes = metaData.value(QLatin1String("MimeTypes")).toArray();
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i));
+ const int keyCount = keys.size();
+ for (int k = 0; k < keyCount; ++k) {
+ const QByteArray key = keys.at(k).toString().toLatin1();
+ if (plugin && (plugin->capabilities(0, key) & cap) != 0) {
+ result->append(mimeTypes.at(k).toString().toLatin1());
+ if (resultKeys)
+ resultKeys->append(key);
+ }
+ }
+ }
+}
+
+QSharedPointer<QFactoryLoader> pluginLoader()
+{
+ loaderMutex()->lock();
+ return QSharedPointer<QFactoryLoader>(loader(), [](QFactoryLoader *) {
+ loaderMutex()->unlock();
+ });
+}
+
+static inline QImageIOPlugin::Capability pluginCapability(Capability cap)
+{
+ return cap == CanRead ? QImageIOPlugin::CanRead : QImageIOPlugin::CanWrite;
+}
+
+#endif // QT_NO_IMAGEFORMATPLUGIN
+
+QList<QByteArray> supportedImageFormats(Capability cap)
+{
+ QList<QByteArray> formats;
+ formats.reserve(_qt_NumFormats);
+ for (int i = 0; i < _qt_NumFormats; ++i)
+ formats << _qt_BuiltInFormats[i].extension;
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+ appendImagePluginFormats(loader(), pluginCapability(cap), &formats);
+#endif // QT_NO_IMAGEFORMATPLUGIN
+
+ std::sort(formats.begin(), formats.end());
+ formats.erase(std::unique(formats.begin(), formats.end()), formats.end());
+ return formats;
+}
+
+QList<QByteArray> supportedMimeTypes(Capability cap)
+{
+ QList<QByteArray> mimeTypes;
+ mimeTypes.reserve(_qt_NumFormats);
+ for (const auto &fmt : _qt_BuiltInFormats)
+ mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType);
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+ appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes);
+#endif // QT_NO_IMAGEFORMATPLUGIN
+
+ std::sort(mimeTypes.begin(), mimeTypes.end());
+ mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end());
+ return mimeTypes;
+}
+
+QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap)
+{
+ QList<QByteArray> formats;
+ if (mimeType.startsWith("image/")) {
+ const QByteArray type = mimeType.mid(sizeof("image/") - 1);
+ for (const auto &fmt : _qt_BuiltInFormats) {
+ if (fmt.mimeType == type && !formats.contains(fmt.extension))
+ formats << fmt.extension;
+ }
+ }
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+ QList<QByteArray> mimeTypes;
+ QList<QByteArray> keys;
+ appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes, &keys);
+ for (int i = 0; i < mimeTypes.size(); ++i) {
+ if (mimeTypes.at(i) == mimeType) {
+ const auto &key = keys.at(i);
+ if (!formats.contains(key))
+ formats << key;
+ }
+ }
+#endif // QT_NO_IMAGEFORMATPLUGIN
+
+ return formats;
+}
+
+} // QImageReaderWriterHelpers
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimagereaderwriterhelpers_p.h b/src/gui/image/qimagereaderwriterhelpers_p.h
new file mode 100644
index 0000000000..6fe418a8ab
--- /dev/null
+++ b/src/gui/image/qimagereaderwriterhelpers_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIMAGEREADERWRITERHELPERS_P_H
+#define QIMAGEREADERWRITERHELPERS_P_H
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <qsharedpointer.h>
+#include "qimageiohandler.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QFactoryLoader;
+
+namespace QImageReaderWriterHelpers {
+
+enum _qt_BuiltInFormatType {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ _qt_PngFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ _qt_BmpFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ _qt_PpmFormat,
+ _qt_PgmFormat,
+ _qt_PbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ _qt_XbmFormat,
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ _qt_XpmFormat,
+#endif
+ _qt_NumFormats,
+ _qt_NoFormat = -1
+};
+
+#if !defined(QT_NO_IMAGEFORMAT_PPM)
+# define MAX_MT_SIZE 20
+#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM)
+# define MAX_MT_SIZE 10
+#else
+# define MAX_MT_SIZE 4
+#endif
+
+struct _qt_BuiltInFormatStruct
+{
+ char extension[4];
+ char mimeType[MAX_MT_SIZE];
+};
+
+#undef MAX_MT_SIZE
+
+static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+ {"png", "png"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ {"bmp", "bmp"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+ {"ppm", "x-portable-pixmap"},
+ {"pgm", "x-portable-graymap"},
+ {"pbm", "x-portable-bitmap"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+ {"xbm", "x-xbitmap"},
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+ {"xpm", "x-xpixmap"},
+#endif
+};
+Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats);
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+QSharedPointer<QFactoryLoader> pluginLoader();
+#endif
+
+enum Capability {
+ CanRead,
+ CanWrite
+};
+QList<QByteArray> supportedImageFormats(Capability cap);
+QList<QByteArray> supportedMimeTypes(Capability cap);
+QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QIMAGEREADERWRITERHELPERS_P_H
diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp
index a39d204677..5ce7e309bb 100644
--- a/src/gui/image/qimagewriter.cpp
+++ b/src/gui/image/qimagewriter.cpp
@@ -102,7 +102,6 @@
#include <qfileinfo.h>
#include <qimage.h>
#include <qimageiohandler.h>
-#include <qjsonarray.h>
#include <qset.h>
#include <qvariant.h>
@@ -119,15 +118,12 @@
#include <private/qpnghandler_p.h>
#endif
+#include <private/qimagereaderwriterhelpers_p.h>
+
#include <algorithm>
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_IMAGEFORMATPLUGIN
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
- (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
-#endif
-
static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
const QByteArray &format)
{
@@ -139,7 +135,7 @@ static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
typedef QMultiMap<int, QString> PluginKeyMap;
// check if any plugins can write the image
- QFactoryLoader *l = loader();
+ auto l = QImageReaderWriterHelpers::pluginLoader();
const PluginKeyMap keyMap = l->keyMap();
int suffixPluginIndex = -1;
#endif
@@ -268,7 +264,7 @@ QImageWriterPrivate::QImageWriterPrivate(QImageWriter *qq)
deleteDevice = false;
handler = 0;
quality = -1;
- compression = 0;
+ compression = -1;
gamma = 0.0;
optimizedWrite = false;
progressiveScanWrite = false;
@@ -824,52 +820,6 @@ bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const
return d->handler->supportsOption(option);
}
-
-#ifndef QT_NO_IMAGEFORMATPLUGIN
-void supportedImageHandlerFormats(QFactoryLoader *loader,
- QImageIOPlugin::Capability cap,
- QList<QByteArray> *result)
-{
- typedef QMultiMap<int, QString> PluginKeyMap;
- typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
-
- const PluginKeyMap keyMap = loader->keyMap();
- const PluginKeyMapConstIterator cend = keyMap.constEnd();
- int i = -1;
- QImageIOPlugin *plugin = 0;
- result->reserve(result->size() + keyMap.size());
- for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) {
- if (it.key() != i) {
- i = it.key();
- plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i));
- }
- const QByteArray key = it.value().toLatin1();
- if (plugin && (plugin->capabilities(0, key) & cap) != 0)
- result->append(key);
- }
-}
-
-void supportedImageHandlerMimeTypes(QFactoryLoader *loader,
- QImageIOPlugin::Capability cap,
- QList<QByteArray> *result)
-{
- QList<QJsonObject> metaDataList = loader->metaData();
-
- const int pluginCount = metaDataList.size();
- for (int i = 0; i < pluginCount; ++i) {
- const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
- const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
- const QJsonArray mimeTypes = metaData.value(QLatin1String("MimeTypes")).toArray();
- QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i));
- const int keyCount = keys.size();
- for (int k = 0; k < keyCount; ++k) {
- if (plugin && (plugin->capabilities(0, keys.at(k).toString().toLatin1()) & cap) != 0)
- result->append(mimeTypes.at(k).toString().toLatin1());
- }
- }
-}
-#endif // QT_NO_IMAGEFORMATPLUGIN
-
/*!
Returns the list of image formats supported by QImageWriter.
@@ -897,30 +847,7 @@ void supportedImageHandlerMimeTypes(QFactoryLoader *loader,
*/
QList<QByteArray> QImageWriter::supportedImageFormats()
{
- QList<QByteArray> formats;
-#ifndef QT_NO_IMAGEFORMAT_BMP
- formats << "bmp";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PPM
- formats << "pbm" << "pgm" << "ppm";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XBM
- formats << "xbm";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XPM
- formats << "xpm";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PNG
- formats << "png";
-#endif
-
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- supportedImageHandlerFormats(loader(), QImageIOPlugin::CanWrite, &formats);
-#endif // QT_NO_IMAGEFORMATPLUGIN
-
- std::sort(formats.begin(), formats.end());
- formats.erase(std::unique(formats.begin(), formats.end()), formats.end());
- return formats;
+ return QImageReaderWriterHelpers::supportedImageFormats(QImageReaderWriterHelpers::CanWrite);
}
/*!
@@ -933,32 +860,24 @@ QList<QByteArray> QImageWriter::supportedImageFormats()
*/
QList<QByteArray> QImageWriter::supportedMimeTypes()
{
- QList<QByteArray> mimeTypes;
-#ifndef QT_NO_IMAGEFORMAT_BMP
- mimeTypes << "image/bmp";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PPM
- mimeTypes << "image/x-portable-bitmap";
- mimeTypes << "image/x-portable-graymap";
- mimeTypes << "image/x-portable-pixmap";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XBM
- mimeTypes << "image/x-xbitmap";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_XPM
- mimeTypes << "image/x-xpixmap";
-#endif
-#ifndef QT_NO_IMAGEFORMAT_PNG
- mimeTypes << "image/png";
-#endif
+ return QImageReaderWriterHelpers::supportedMimeTypes(QImageReaderWriterHelpers::CanWrite);
+}
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanWrite, &mimeTypes);
-#endif // QT_NO_IMAGEFORMATPLUGIN
+/*!
+ \since 5.12
- std::sort(mimeTypes.begin(), mimeTypes.end());
- mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end());
- return mimeTypes;
+ Returns the list of image formats corresponding to \a mimeType.
+
+ Note that the QGuiApplication instance must be created before this function is
+ called.
+
+ \sa supportedImageFormats(), supportedMimeTypes()
+*/
+
+QList<QByteArray> QImageWriter::imageFormatsForMimeType(const QByteArray &mimeType)
+{
+ return QImageReaderWriterHelpers::imageFormatsForMimeType(mimeType,
+ QImageReaderWriterHelpers::CanWrite);
}
QT_END_NAMESPACE
diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h
index fd1fdd07e8..29c06ccdd2 100644
--- a/src/gui/image/qimagewriter.h
+++ b/src/gui/image/qimagewriter.h
@@ -116,6 +116,7 @@ public:
static QList<QByteArray> supportedImageFormats();
static QList<QByteArray> supportedMimeTypes();
+ static QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType);
private:
Q_DISABLE_COPY(QImageWriter)
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
index 010760de4c..7d17b7d5ef 100644
--- a/src/gui/image/qmovie.cpp
+++ b/src/gui/image/qmovie.cpp
@@ -379,7 +379,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
}
if (frameNumber > greatestFrameNumber)
greatestFrameNumber = frameNumber;
- QPixmap aPixmap = QPixmap::fromImage(anImage);
+ QPixmap aPixmap = QPixmap::fromImage(std::move(anImage));
int aDelay = reader->nextImageDelay();
return QFrameInfo(aPixmap, aDelay);
} else if (frameNumber != 0) {
@@ -405,7 +405,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
return QFrameInfo(); // Invalid
}
greatestFrameNumber = i;
- QPixmap aPixmap = QPixmap::fromImage(anImage);
+ QPixmap aPixmap = QPixmap::fromImage(std::move(anImage));
int aDelay = reader->nextImageDelay();
QFrameInfo info(aPixmap, aDelay);
// Cache it!
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 1ea503a268..4b2334ae52 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -248,9 +248,9 @@ QPixmap::QPixmap(const char * const xpm[])
QImage image(xpm);
if (!image.isNull()) {
if (data && data->pixelType() == QPlatformPixmap::BitmapType)
- *this = QBitmap::fromImage(image);
+ *this = QBitmap::fromImage(std::move(image));
else
- *this = fromImage(image);
+ *this = fromImage(std::move(image));
}
}
#endif
@@ -691,7 +691,7 @@ QBitmap QPixmap::createHeuristicMask(bool clipTight) const
QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const
{
QImage image = toImage().convertToFormat(QImage::Format_ARGB32);
- return QBitmap::fromImage(image.createMaskFromColor(maskColor.rgba(), mode));
+ return QBitmap::fromImage(std::move(image).createMaskFromColor(maskColor.rgba(), mode));
}
/*!
@@ -1018,9 +1018,9 @@ QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap)
if (image.isNull()) {
pixmap = QPixmap();
} else if (image.depth() == 1) {
- pixmap = QBitmap::fromImage(image);
+ pixmap = QBitmap::fromImage(std::move(image));
} else {
- pixmap = QPixmap::fromImage(image);
+ pixmap = QPixmap::fromImage(std::move(image));
}
return stream;
}
diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp
index 646e737afa..649a25250c 100644
--- a/src/gui/image/qpixmap_blitter.cpp
+++ b/src/gui/image/qpixmap_blitter.cpp
@@ -150,13 +150,7 @@ void QBlittablePlatformPixmap::fill(const QColor &color)
m_alpha = true;
}
- uint pixel = qPremultiply(color.rgba());
- const QPixelLayout *layout = &qPixelLayouts[blittable()->lock()->format()];
- Q_ASSERT(layout->convertFromARGB32PM);
- layout->convertFromARGB32PM(&pixel, &pixel, 1, 0, 0);
-
- //so premultiplied formats are supported and ARGB32 and RGB32
- blittable()->lock()->fill(pixel);
+ blittable()->lock()->fill(color);
}
}
diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp
index 431002d032..13c1c29d5b 100644
--- a/src/gui/image/qpixmap_raster.cpp
+++ b/src/gui/image/qpixmap_raster.cpp
@@ -193,17 +193,12 @@ void QRasterPlatformPixmap::fill(const QColor &color)
if (alpha != 255) {
if (!image.hasAlphaChannel()) {
QImage::Format toFormat = qt_alphaVersionForPainting(image.format());
- if (!image.isNull() && qt_depthForFormat(image.format()) == qt_depthForFormat(toFormat)) {
- image.detach();
- image.d->format = toFormat;
- } else {
+ if (!image.reinterpretAsFormat(toFormat))
image = QImage(image.width(), image.height(), toFormat);
- }
}
}
- pixel = qPremultiply(color.rgba());
- const QPixelLayout *layout = &qPixelLayouts[image.format()];
- layout->convertFromARGB32PM(&pixel, &pixel, 1, 0, 0);
+ image.fill(color);
+ return;
} else if (image.format() == QImage::Format_Alpha8) {
pixel = qAlpha(color.rgba());
} else if (image.format() == QImage::Format_Grayscale8) {
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
index 92f6964783..b8d13ac092 100644
--- a/src/gui/image/qpixmap_win.cpp
+++ b/src/gui/image/qpixmap_win.cpp
@@ -42,39 +42,76 @@
#include <qpa/qplatformpixmap.h>
#include "qpixmap_raster_p.h"
-#include <qglobal.h>
+#include <qdebug.h>
#include <QScopedArrayPointer>
#include <qt_windows.h>
+#include <algorithm>
+#include <iterator>
+
QT_BEGIN_NAMESPACE
-static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, BITMAPINFOHEADER *bih)
+template <typename Int>
+static inline Int pad4(Int v)
+{
+ return (v + Int(3)) & ~Int(3);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const BITMAPINFOHEADER &bih)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d << "BITMAPINFOHEADER(" << bih.biWidth << 'x' << qAbs(bih.biHeight)
+ << (bih.biHeight < 0 ? ", top-down" : ", bottom-up")
+ << ", planes=" << bih.biPlanes << ", bitCount=" << bih.biBitCount
+ << ", compression=" << bih.biCompression << ", size="
+ << bih.biSizeImage << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+static inline void initBitMapInfoHeader(int width, int height, bool topToBottom,
+ DWORD compression, DWORD bitCount,
+ BITMAPINFOHEADER *bih)
{
memset(bih, 0, sizeof(BITMAPINFOHEADER));
bih->biSize = sizeof(BITMAPINFOHEADER);
bih->biWidth = width;
bih->biHeight = topToBottom ? -height : height;
bih->biPlanes = 1;
- bih->biBitCount = 32;
- bih->biCompression = BI_RGB;
- bih->biSizeImage = width * height * 4;
+ bih->biBitCount = WORD(bitCount);
+ bih->biCompression = compression;
+ // scan lines are word-aligned (unless RLE)
+ const DWORD bytesPerLine = pad4(DWORD(width) * bitCount / 8);
+ bih->biSizeImage = bytesPerLine * DWORD(height);
}
-static inline void initBitMapInfo(int width, int height, bool topToBottom, BITMAPINFO *bmi)
+enum { Indexed8ColorTableSize = 256 };
+
+struct BITMAPINFO_COLORTABLE256 { // BITMAPINFO with 256 entry color table for Indexed 8 format
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[Indexed8ColorTableSize];
+};
+
+template <class BITMAPINFO_T> // BITMAPINFO, BITMAPINFO_COLORTABLE256
+static inline void initBitMapInfo(int width, int height, bool topToBottom,
+ DWORD compression, DWORD bitCount,
+ BITMAPINFO_T *bmi)
{
- initBitMapInfoHeader(width, height, topToBottom, &bmi->bmiHeader);
- memset(bmi->bmiColors, 0, sizeof(RGBQUAD));
+ initBitMapInfoHeader(width, height, topToBottom, compression, bitCount, &bmi->bmiHeader);
+ memset(bmi->bmiColors, 0, sizeof(bmi->bmiColors));
}
static inline uchar *getDiBits(HDC hdc, HBITMAP bitmap, int width, int height, bool topToBottom = true)
{
BITMAPINFO bmi;
- initBitMapInfo(width, height, topToBottom, &bmi);
+ initBitMapInfo(width, height, topToBottom, BI_RGB, 32u, &bmi);
uchar *result = new uchar[bmi.bmiHeader.biSizeImage];
- if (!GetDIBits(hdc, bitmap, 0, height, result, &bmi, DIB_RGB_COLORS)) {
+ if (!GetDIBits(hdc, bitmap, 0, UINT(height), result, &bmi, DIB_RGB_COLORS)) {
delete [] result;
qErrnoWarning("%s: GetDIBits() failed to get bitmap bits.", __FUNCTION__);
- return 0;
+ return nullptr;
}
return result;
}
@@ -98,18 +135,92 @@ static inline void copyImageDataCreateAlpha(const uchar *data, QImage *target)
}
}
-static inline void copyImageData(const uchar *data, QImage *target)
+// Flip RGB triplets from DIB to QImage formats. Scan lines are padded to 32bit
+// both in QImage and DIB.
+static inline void flipRgb3(uchar *p, int width, int height)
{
- const int height = target->height();
- const int bytesPerLine = target->bytesPerLine();
+ const int lineSize = 3 * width;
+ const int linePad = pad4(lineSize) - lineSize;
for (int y = 0; y < height; ++y) {
- void *dest = static_cast<void *>(target->scanLine(y));
- const void *src = data + y * bytesPerLine;
- memcpy(dest, src, bytesPerLine);
+ uchar *end = p + lineSize;
+ for ( ; p < end; p += 3)
+ std::swap(*p, *(p + 2));
+ p += linePad;
}
+}
+static inline RGBQUAD qRgbToRgbQuad(QRgb qrgb)
+{
+ RGBQUAD result = {BYTE(qBlue(qrgb)), BYTE(qGreen(qrgb)), BYTE(qRed(qrgb)), 0};
+ return result;
+}
+
+static inline QRgb rgbQuadToQRgb(RGBQUAD quad)
+{
+ return QRgb(quad.rgbBlue) + (QRgb(quad.rgbGreen) << 8) + (QRgb(quad.rgbRed) << 16)
+ + 0xff000000;
}
+// Helper for imageFromWinHBITMAP_*(), create image in desired format
+static QImage copyImageData(const BITMAPINFOHEADER &header, const RGBQUAD *colorTableIn,
+ const void *data, QImage::Format format)
+{
+ const QSize size = QSize(header.biWidth, qAbs(header.biHeight));
+ QImage image(size, format);
+
+ int colorTableSize = 0;
+ switch (format) {
+ case QImage::Format_Mono:
+ colorTableSize = 2;
+ break;
+ case QImage::Format_Indexed8:
+ colorTableSize = Indexed8ColorTableSize;
+ break;
+ default:
+ break;
+ }
+ if (colorTableSize) {
+ Q_ASSERT(colorTableIn);
+ QVector<QRgb> colorTable;
+ colorTable.reserve(colorTableSize);
+ std::transform(colorTableIn, colorTableIn + colorTableSize,
+ std::back_inserter(colorTable), rgbQuadToQRgb);
+ image.setColorTable(colorTable);
+ }
+
+ switch (header.biBitCount) {
+ case 32:
+ copyImageDataCreateAlpha(static_cast<const uchar *>(data), &image);
+ break;
+ case 1:
+ case 8:
+ case 16:
+ case 24:
+ Q_ASSERT(DWORD(image.sizeInBytes()) == header.biSizeImage);
+ memcpy(image.bits(), data, header.biSizeImage);
+ if (format == QImage::Format_RGB888)
+ image = image.rgbSwapped();
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return image;
+}
+
+class DisplayHdc
+{
+ Q_DISABLE_COPY(DisplayHdc)
+public:
+ DisplayHdc() : m_displayDc(GetDC(nullptr)) {}
+ ~DisplayHdc() { ReleaseDC(nullptr, m_displayDc); }
+
+ operator HDC() const { return m_displayDc; }
+
+private:
+ const HDC m_displayDc;
+};
+
enum HBitmapFormat
{
HBitmapNoAlpha,
@@ -123,108 +234,228 @@ Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap)
const int w = bm.width();
const int h = bm.height();
const int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment
- QScopedArrayPointer<uchar> bits(new uchar[bpl * h]);
+ QScopedArrayPointer<uchar> bits(new uchar[size_t(bpl * h)]);
bm.invertPixels();
for (int y = 0; y < h; ++y)
- memcpy(bits.data() + y * bpl, bm.constScanLine(y), bpl);
+ memcpy(bits.data() + y * bpl, bm.constScanLine(y), size_t(bpl));
HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits.data());
return hbm;
}
-Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0)
+static inline QImage::Format format32(int hbitmapFormat)
{
- if (p.isNull())
- return 0;
-
- HBITMAP bitmap = 0;
- if (p.handle()->classId() != QPlatformPixmap::RasterClass) {
- QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ?
- QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType);
- data->fromImage(p.toImage(), Qt::AutoColor);
- return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat);
+ switch (hbitmapFormat) {
+ case HBitmapNoAlpha:
+ return QImage::Format_RGB32;
+ case HBitmapAlpha:
+ return QImage::Format_ARGB32;
+ default:
+ break;
}
+ return QImage::Format_ARGB32_Premultiplied;
+}
- QRasterPlatformPixmap *d = static_cast<QRasterPlatformPixmap*>(p.handle());
- const QImage *rasterImage = d->buffer();
- const int w = rasterImage->width();
- const int h = rasterImage->height();
-
- HDC display_dc = GetDC(0);
+Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0)
+{
+ if (imageIn.isNull())
+ return nullptr;
// Define the header
- BITMAPINFO bmi;
- initBitMapInfo(w, h, true, &bmi);
+ DWORD compression = 0;
+ DWORD bitCount = 0;
+
+ // Copy over the data
+ QImage image = imageIn;
+ switch (image.format()) {
+ case QImage::Format_Mono:
+ bitCount = 1u;
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied: {
+ compression = BI_RGB;
+ bitCount = 32u;
+ const QImage::Format targetFormat = format32(hbitmapFormat);
+ if (targetFormat != image.format())
+ image = image.convertToFormat(targetFormat);
+ }
+ break;
+ case QImage::Format_RGB888:
+ compression = BI_RGB;
+ bitCount = 24u;
+ break;
+ case QImage::Format_Indexed8:
+ bitCount = 8u;
+ break;
+ case QImage::Format_RGB555:
+ bitCount = 16u;
+ break;
+ default: {
+ QImage::Format fallbackFormat = QImage::Format_ARGB32_Premultiplied;
+ switch (image.format()) { // Convert to a suitable format.
+ case QImage::Format_MonoLSB:
+ fallbackFormat = QImage::Format_Mono;
+ break;
+ case QImage::Format_RGB16:
+ fallbackFormat = QImage::Format_RGB555;
+ break;
+ case QImage::Format_Grayscale8:
+ fallbackFormat = QImage::Format_Indexed8;
+ break;
+ default:
+ break;
+ } // switch conversion format
+ return qt_imageToWinHBITMAP(imageIn.convertToFormat(fallbackFormat), hbitmapFormat);
+ }
+ }
+
+ const int w = image.width();
+ const int h = image.height();
+
+ BITMAPINFO_COLORTABLE256 bmiColorTable256;
+ initBitMapInfo(w, h, true, compression, bitCount, &bmiColorTable256);
+ BITMAPINFO &bmi = reinterpret_cast<BITMAPINFO &>(bmiColorTable256);
+ switch (image.format()) {
+ case QImage::Format_Mono: // Color table with 2 entries
+ case QImage::Format_Indexed8:
+ std::transform(image.colorTable().constBegin(), image.colorTable().constEnd(),
+ bmiColorTable256.bmiColors, qRgbToRgbQuad);
+ break;
+ default:
+ break;
+ }
// Create the pixmap
- uchar *pixels = 0;
- bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0);
- ReleaseDC(0, display_dc);
+ uchar *pixels = nullptr;
+ const HBITMAP bitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS,
+ reinterpret_cast<void **>(&pixels), nullptr, 0);
if (!bitmap) {
qErrnoWarning("%s, failed to create dibsection", __FUNCTION__);
- return 0;
+ return nullptr;
}
if (!pixels) {
qErrnoWarning("%s, did not allocate pixel data", __FUNCTION__);
- return 0;
+ return nullptr;
}
-
- // Copy over the data
- QImage::Format imageFormat = QImage::Format_RGB32;
- if (hbitmapFormat == HBitmapAlpha)
- imageFormat = QImage::Format_ARGB32;
- else if (hbitmapFormat == HBitmapPremultipliedAlpha)
- imageFormat = QImage::Format_ARGB32_Premultiplied;
- const QImage image = rasterImage->convertToFormat(imageFormat);
- const int bytes_per_line = w * 4;
- for (int y=0; y < h; ++y)
- memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line);
-
+ memcpy(pixels, image.constBits(), bmi.bmiHeader.biSizeImage);
+ if (image.format() == QImage::Format_RGB888)
+ flipRgb3(pixels, w, h);
return bitmap;
}
+Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0)
+{
+ if (p.isNull())
+ return nullptr;
-Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0)
+ QPlatformPixmap *platformPixmap = p.handle();
+ if (platformPixmap->classId() != QPlatformPixmap::RasterClass) {
+ QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ?
+ QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType);
+ data->fromImage(p.toImage(), Qt::AutoColor);
+ return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat);
+ }
+
+ return qt_imageToWinHBITMAP(*static_cast<QRasterPlatformPixmap*>(platformPixmap)->buffer(), hbitmapFormat);
+}
+
+static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, int hbitmapFormat)
{
- // Verify size
- BITMAP bitmap_info;
- memset(&bitmap_info, 0, sizeof(BITMAP));
+ QImage::Format result = QImage::Format_Invalid;
+ switch (header.biBitCount) {
+ case 32:
+ result = hbitmapFormat == HBitmapNoAlpha
+ ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied;
+ break;
+ case 24:
+ result = QImage::Format_RGB888;
+ break;
+ case 16:
+ result = QImage::Format_RGB555;
+ break;
+ case 8:
+ result = QImage::Format_Indexed8;
+ break;
+ case 1:
+ result = QImage::Format_Mono;
+ break;
+ }
+ return result;
+}
- const int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info);
- if (!res) {
- qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info");
- return QPixmap();
+// Fast path for creating a QImage directly from a HBITMAP created by CreateDIBSection(),
+// not requiring memory allocation.
+static QImage imageFromWinHBITMAP_DibSection(HBITMAP bitmap, int hbitmapFormat)
+{
+ DIBSECTION dibSection;
+ memset(&dibSection, 0, sizeof(dibSection));
+ dibSection.dsBmih.biSize = sizeof(dibSection.dsBmih);
+
+ if (!GetObject(bitmap, sizeof(dibSection), &dibSection)
+ || !dibSection.dsBm.bmBits
+ || dibSection.dsBmih.biBitCount <= 8 // Cannot access the color table for Indexed8, Mono
+ || dibSection.dsBmih.biCompression != BI_RGB) {
+ return QImage();
}
- const int w = bitmap_info.bmWidth;
- const int h = bitmap_info.bmHeight;
-
- // Get bitmap bits
- HDC display_dc = GetDC(0);
- QScopedArrayPointer<uchar> data(getDiBits(display_dc, bitmap, w, h, true));
- if (data.isNull()) {
- ReleaseDC(0, display_dc);
- return QPixmap();
+
+ const QImage::Format imageFormat = imageFromWinHBITMAP_Format(dibSection.dsBmih, hbitmapFormat);
+ if (imageFormat == QImage::Format_Invalid)
+ return QImage();
+
+ return copyImageData(dibSection.dsBmih, nullptr, dibSection.dsBm.bmBits, imageFormat);
+}
+
+// Create QImage from a HBITMAP using GetDIBits(), potentially with conversion.
+static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int hbitmapFormat)
+{
+ BITMAPINFO_COLORTABLE256 bmiColorTable256;
+ BITMAPINFO &info = reinterpret_cast<BITMAPINFO &>(bmiColorTable256);
+ memset(&info, 0, sizeof(info));
+ info.bmiHeader.biSize = sizeof(info.bmiHeader);
+
+ DisplayHdc displayDc;
+ if (!GetDIBits(displayDc, bitmap, 0, 1, 0, &info, DIB_RGB_COLORS)) {
+ qErrnoWarning("%s: GetDIBits() failed to query data.", __FUNCTION__);
+ return QImage();
}
- const QImage::Format imageFormat = hbitmapFormat == HBitmapNoAlpha ?
- QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied;
+ if (info.bmiHeader.biHeight > 0) // Force top-down
+ info.bmiHeader.biHeight = -info.bmiHeader.biHeight;
+ info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD)
+ if (forceQuads)
+ info.bmiHeader.biBitCount = 32;
- // Create image and copy data into image.
- QImage image(w, h, imageFormat);
- if (image.isNull()) { // failed to alloc?
- ReleaseDC(0, display_dc);
- qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h);
- return QPixmap();
+ const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat);
+ if (imageFormat == QImage::Format_Invalid) {
+ qWarning().nospace() << __FUNCTION__ << ": unsupported image format:" << info.bmiHeader;
+ return QImage();
}
- copyImageDataCreateAlpha(data.data(), &image);
- ReleaseDC(0, display_dc);
- return QPixmap::fromImage(image);
+
+ QScopedPointer<uchar> data(new uchar[info.bmiHeader.biSizeImage]);
+ if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) {
+ qErrnoWarning("%s: GetDIBits() failed to get data.", __FUNCTION__);
+ return QImage();
+ }
+ return copyImageData(info.bmiHeader, bmiColorTable256.bmiColors, data.data(), imageFormat);
}
+Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0)
+{
+ QImage result = imageFromWinHBITMAP_DibSection(bitmap, hbitmapFormat);
+ if (result.isNull())
+ result = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ false, hbitmapFormat);
+ return result;
+}
+
+Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0)
+{
+ return QPixmap::fromImage(imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat));
+}
Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p)
{
if (p.isNull())
- return 0;
+ return nullptr;
QBitmap maskBitmap = p.mask();
if (maskBitmap.isNull()) {
@@ -267,7 +498,7 @@ static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true));
if (data.isNull())
return QImage();
- copyImageData(data.data(), &image);
+ memcpy(image.bits(), data.data(), size_t(image.sizeInBytes()));
return image;
}
@@ -287,9 +518,9 @@ static inline bool hasAlpha(const QImage &image)
Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
{
- HDC screenDevice = GetDC(0);
+ HDC screenDevice = GetDC(nullptr);
HDC hdc = CreateCompatibleDC(screenDevice);
- ReleaseDC(0, screenDevice);
+ ReleaseDC(nullptr, screenDevice);
ICONINFO iconinfo;
const bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center
@@ -299,25 +530,27 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
return QPixmap();
}
- const int w = iconinfo.xHotspot * 2;
- const int h = iconinfo.yHotspot * 2;
+ const int w = int(iconinfo.xHotspot) * 2;
+ const int h = int(iconinfo.yHotspot) * 2;
BITMAPINFOHEADER bitmapInfo;
- initBitMapInfoHeader(w, h, false, &bitmapInfo);
+ initBitMapInfoHeader(w, h, false, BI_RGB, 32u, &bitmapInfo);
DWORD* bits;
- HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
- HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
- DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
+ HBITMAP winBitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bitmapInfo),
+ DIB_RGB_COLORS, reinterpret_cast<VOID **>(&bits),
+ nullptr, 0);
+ HGDIOBJ oldhdc = static_cast<HBITMAP>(SelectObject(hdc, winBitmap));
+ DrawIconEx(hdc, 0, 0, icon, w, h, 0, nullptr, DI_NORMAL);
QImage image = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h);
if (!image.isNull() && !hasAlpha(image)) { //If no alpha was found, we use the mask to set alpha values
- DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK);
+ DrawIconEx( hdc, 0, 0, icon, w, h, 0, nullptr, DI_MASK);
const QImage mask = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h);
for (int y = 0 ; y < h ; y++){
QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y));
- const QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast<const QRgb *>(mask.scanLine(y));
+ const QRgb *scanlineMask = mask.isNull() ? nullptr : reinterpret_cast<const QRgb *>(mask.scanLine(y));
for (int x = 0; x < w ; x++){
if (scanlineMask && qRed(scanlineMask[x]) != 0)
scanlineImage[x] = 0; //mask out this pixel
@@ -333,7 +566,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
SelectObject(hdc, oldhdc); //restore state
DeleteObject(winBitmap);
DeleteDC(hdc);
- return QPixmap::fromImage(image);
+ return QPixmap::fromImage(std::move(image));
}
QT_END_NAMESPACE
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
index 4b8b1203d6..3d1652f68b 100644
--- a/src/gui/image/qpixmapcache.cpp
+++ b/src/gui/image/qpixmapcache.cpp
@@ -86,7 +86,17 @@ QT_BEGIN_NAMESPACE
\sa QCache, QPixmap
*/
-static int cache_limit = 10240; // 10 MB cache limit
+static const int cache_limit_default = 10240; // 10 MB cache limit
+
+static inline int cost(const QPixmap &pixmap)
+{
+ // make sure to do a 64bit calculation
+ const qint64 costKb = static_cast<qint64>(pixmap.width()) *
+ pixmap.height() * pixmap.depth() / (8 * 1024);
+ const qint64 costMax = std::numeric_limits<int>::max();
+ // a small pixmap should have at least a cost of 1(kb)
+ return static_cast<int>(qBound(1LL, costKb, costMax));
+}
/*!
\class QPixmapCache::Key
@@ -237,7 +247,7 @@ uint qHash(const QPixmapCache::Key &k)
QPMCache::QPMCache()
: QObject(0),
- QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit * 1024),
+ QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit_default),
keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false)
{
}
@@ -553,7 +563,7 @@ bool QPixmapCache::find(const Key &key, QPixmap* pixmap)
bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
{
- return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+ return pm_cache()->insert(key, pixmap, cost(pixmap));
}
/*!
@@ -573,7 +583,7 @@ bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
*/
QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
{
- return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+ return pm_cache()->insert(pixmap, cost(pixmap));
}
/*!
@@ -590,7 +600,7 @@ bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
//The key is not valid anymore, a flush happened before probably
if (!key.d || !key.d->isValid)
return false;
- return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
+ return pm_cache()->replace(key, pixmap, cost(pixmap));
}
/*!
@@ -603,7 +613,7 @@ bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
int QPixmapCache::cacheLimit()
{
- return cache_limit;
+ return pm_cache()->maxCost();
}
/*!
@@ -616,8 +626,7 @@ int QPixmapCache::cacheLimit()
void QPixmapCache::setCacheLimit(int n)
{
- cache_limit = n;
- pm_cache()->setMaxCost(1024 * cache_limit);
+ pm_cache()->setMaxCost(n);
}
/*!
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index 53d9a8a49a..8ae03d5d38 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -98,12 +98,13 @@ public:
};
QPngHandlerPrivate(QPngHandler *qq)
- : gamma(0.0), fileGamma(0.0), quality(2), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq)
+ : gamma(0.0), fileGamma(0.0), quality(50), compression(50), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq)
{ }
float gamma;
float fileGamma;
- int quality;
+ int quality; // quality is used for backward compatibility, maps to compression
+ int compression;
QString description;
QSize scaledSize;
QStringList readTexts;
@@ -160,11 +161,11 @@ public:
void setGamma(float);
bool writeImage(const QImage& img, int x, int y);
- bool writeImage(const QImage& img, volatile int quality, const QString &description, int x, int y);
+ bool writeImage(const QImage& img, volatile int compression_in, const QString &description, int x, int y);
bool writeImage(const QImage& img)
{ return writeImage(img, 0, 0); }
- bool writeImage(const QImage& img, int quality, const QString &description)
- { return writeImage(img, quality, description, 0, 0); }
+ bool writeImage(const QImage& img, int compression, const QString &description)
+ { return writeImage(img, compression, description, 0, 0); }
QIODevice* device() { return dev; }
@@ -244,7 +245,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal
png_set_interlace_handling(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY) {
- // Black & White or 8-bit grayscale
+ // Black & White or grayscale
if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
png_set_invert_mono(png_ptr);
png_read_update_info(png_ptr, info_ptr);
@@ -265,19 +266,22 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal
else if (g == 1)
image.setColor(0, qRgba(255, 255, 255, 0));
}
- } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
- png_set_expand(png_ptr);
- png_set_strip_16(png_ptr);
+ } else if (bit_depth == 16) {
+ bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+ if (!hasMask)
+ png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
+ else
+ png_set_expand(png_ptr);
png_set_gray_to_rgb(png_ptr);
- if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
- image = QImage(width, height, QImage::Format_ARGB32);
+ QImage::Format format = hasMask ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
if (image.isNull())
return;
}
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- png_set_swap_alpha(png_ptr);
-
png_read_update_info(png_ptr, info_ptr);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ png_set_swap(png_ptr);
} else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_expand(png_ptr);
if (image.size() != QSize(width, height) || image.format() != QImage::Format_Grayscale8) {
@@ -288,9 +292,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal
png_read_update_info(png_ptr, info_ptr);
} else {
- if (bit_depth == 16)
- png_set_strip_16(png_ptr);
- else if (bit_depth < 8)
+ if (bit_depth < 8)
png_set_packing(png_ptr);
int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
png_read_update_info(png_ptr, info_ptr);
@@ -351,6 +353,26 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal
);
i++;
}
+ // Qt==ARGB==Big(ARGB)==Little(BGRA)
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ png_set_bgr(png_ptr);
+ }
+ } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
+ QImage::Format format = QImage::Format_RGBA64;
+ if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
+ format = QImage::Format_RGBX64;
+ }
+ if (!(color_type & PNG_COLOR_MASK_COLOR))
+ png_set_gray_to_rgb(png_ptr);
+ if (image.size() != QSize(width, height) || image.format() != format) {
+ image = QImage(width, height, format);
+ if (image.isNull())
+ return;
+ }
+ png_read_update_info(png_ptr, info_ptr);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ png_set_swap(png_ptr);
} else {
// 32-bit
if (bit_depth == 16)
@@ -387,12 +409,12 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
png_set_swap_alpha(png_ptr);
- png_read_update_info(png_ptr, info_ptr);
- }
+ // Qt==ARGB==Big(ARGB)==Little(BGRA)
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ png_set_bgr(png_ptr);
+ }
- // Qt==ARGB==Big(ARGB)==Little(BGRA)
- if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
- png_set_bgr(png_ptr);
+ png_read_update_info(png_ptr, info_ptr);
}
}
@@ -661,11 +683,11 @@ QImage::Format QPngHandlerPrivate::readImageFormat()
int num_palette;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
if (color_type == PNG_COLOR_TYPE_GRAY) {
- // Black & White or 8-bit grayscale
+ // Black & White or grayscale
if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
format = QImage::Format_Mono;
- } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
- format = QImage::Format_ARGB32;
+ } else if (bit_depth == 16) {
+ format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
} else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
format = QImage::Format_Grayscale8;
} else {
@@ -677,6 +699,10 @@ QImage::Format QPngHandlerPrivate::readImageFormat()
{
// 1-bit and 8-bit color
format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
+ } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
+ format = QImage::Format_RGBA64;
+ if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ format = QImage::Format_RGBX64;
} else {
// 32-bit
format = QImage::Format_ARGB32;
@@ -788,7 +814,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
return writeImage(image, -1, QString(), off_x, off_y);
}
-bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description,
+bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_in, const QString &description,
int off_x_in, int off_y_in)
{
QPoint offset = image.offset();
@@ -816,13 +842,13 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
return false;
}
- int quality = quality_in;
- if (quality >= 0) {
- if (quality > 9) {
- qWarning("PNG: Quality %d out of range", quality);
- quality = 9;
+ int compression = compression_in;
+ if (compression >= 0) {
+ if (compression > 9) {
+ qWarning("PNG: Compression %d out of range", compression);
+ compression = 9;
}
- png_set_compression_level(png_ptr, quality);
+ png_set_compression_level(png_ptr, compression);
}
png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
@@ -842,8 +868,24 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
else
color_type = PNG_COLOR_TYPE_RGB;
+ int bpc = 0;
+ switch (image.format()) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ bpc = 1;
+ break;
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ bpc = 16;
+ break;
+ default:
+ bpc = 8;
+ break;
+ }
+
png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
- image.depth() == 1 ? 1 : 8, // per channel
+ bpc, // per channel
color_type, 0, 0, 0); // sets #channels
if (gamma != 0.0) {
@@ -879,13 +921,31 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
// Swap ARGB to RGBA (normal PNG format) before saving on
// BigEndian machines
if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
- png_set_swap_alpha(png_ptr);
+ switch (image.format()) {
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ break;
+ default:
+ png_set_swap_alpha(png_ptr);
+ }
}
// Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless
- if (QSysInfo::ByteOrder == QSysInfo::LittleEndian
- && image.format() != QImage::Format_RGB888) {
- png_set_bgr(png_ptr);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ switch (image.format()) {
+ case QImage::Format_RGB888:
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ break;
+ default:
+ png_set_bgr(png_ptr);
+ }
}
if (off_x || off_y) {
@@ -908,10 +968,32 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
if (image.depth() != 1)
png_set_packing(png_ptr);
- if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888)
- png_set_filler(png_ptr, 0,
- QSysInfo::ByteOrder == QSysInfo::BigEndian ?
- PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+ if (color_type == PNG_COLOR_TYPE_RGB) {
+ switch (image.format()) {
+ case QImage::Format_RGB888:
+ break;
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBX64:
+ png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+ break;
+ default:
+ png_set_filler(png_ptr, 0,
+ QSysInfo::ByteOrder == QSysInfo::BigEndian ?
+ PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
+ }
+ }
+
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ switch (image.format()) {
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ png_set_swap(png_ptr);
+ break;
+ default:
+ break;
+ }
+ }
if (looping >= 0 && frames_written == 0) {
uchar data[13] = "NETSCAPE2.0";
@@ -939,6 +1021,10 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_RGB888:
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
{
png_bytep* row_pointers = new png_bytep[height];
for (int y=0; y<height; y++)
@@ -947,6 +1033,17 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
delete [] row_pointers;
}
break;
+ case QImage::Format_RGBA64_Premultiplied:
+ {
+ QImage row;
+ png_bytep row_pointers[1];
+ for (int y=0; y<height; y++) {
+ row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64);
+ row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0));
+ png_write_rows(png_ptr, row_pointers, 1);
+ }
+ }
+ break;
default:
{
QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;
@@ -970,15 +1067,21 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
}
static bool write_png_image(const QImage &image, QIODevice *device,
- int quality, float gamma, const QString &description)
+ int compression, int quality, float gamma, const QString &description)
{
+ // quality is used for backward compatibility, maps to compression
+
QPNGImageWriter writer(device);
- if (quality >= 0) {
- quality = qMin(quality, 100);
- quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]
- }
+ if (compression >= 0)
+ compression = qMin(compression, 100);
+ else if (quality >= 0)
+ compression = 100 - qMin(quality, 100);
+
+ if (compression >= 0)
+ compression = (compression * 9) / 91; // map [0,100] -> [0,9]
+
writer.setGamma(gamma);
- return writer.writeImage(image, quality, description);
+ return writer.writeImage(image, compression, description);
}
QPngHandler::QPngHandler()
@@ -1025,7 +1128,7 @@ bool QPngHandler::read(QImage *image)
bool QPngHandler::write(const QImage &image)
{
- return write_png_image(image, device(), d->quality, d->gamma, d->description);
+ return write_png_image(image, device(), d->compression, d->quality, d->gamma, d->description);
}
bool QPngHandler::supportsOption(ImageOption option) const
@@ -1034,6 +1137,7 @@ bool QPngHandler::supportsOption(ImageOption option) const
|| option == Description
|| option == ImageFormat
|| option == Quality
+ || option == CompressionRatio
|| option == Size
|| option == ScaledSize;
}
@@ -1049,6 +1153,8 @@ QVariant QPngHandler::option(ImageOption option) const
return d->gamma == 0.0 ? d->fileGamma : d->gamma;
else if (option == Quality)
return d->quality;
+ else if (option == CompressionRatio)
+ return d->compression;
else if (option == Description)
return d->description;
else if (option == Size)
@@ -1067,6 +1173,8 @@ void QPngHandler::setOption(ImageOption option, const QVariant &value)
d->gamma = value.toFloat();
else if (option == Quality)
d->quality = value.toInt();
+ else if (option == CompressionRatio)
+ d->compression = value.toInt();
else if (option == Description)
d->description = value.toString();
else if (option == ScaledSize)
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
index 155a4f88b4..24d86e116d 100644
--- a/src/gui/image/qxbmhandler.cpp
+++ b/src/gui/image/qxbmhandler.cpp
@@ -241,7 +241,7 @@ static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const
}
}
}
-#if defined(_MSC_VER) && _MSC_VER >= 1400
+#ifdef Q_CC_MSVC
strcpy_s(p, sizeof(" };\n"), " };\n");
#else
strcpy(p, " };\n");
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
index 9c54b9ada4..17272ffe69 100644
--- a/src/gui/image/qxpmhandler.cpp
+++ b/src/gui/image/qxpmhandler.cpp
@@ -741,10 +741,6 @@ static const struct XPMRGBData {
{ QRGB(139,139, 0), "yellow4" },
{ QRGB(154,205, 50), "yellowgreen" } };
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-inline bool operator<(const XPMRGBData &data1, const XPMRGBData &data2)
-{ return qstrcmp(data1.name, data2.name) < 0; }
-#endif
inline bool operator<(const char *name, const XPMRGBData &data)
{ return qstrcmp(name, data.name) < 0; }
diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp
index 235bc5bd7d..8ec88ebc60 100644
--- a/src/gui/itemmodels/qstandarditemmodel.cpp
+++ b/src/gui/itemmodels/qstandarditemmodel.cpp
@@ -306,8 +306,11 @@ const QMap<int, QVariant> QStandardItemPrivate::itemData() const
{
QMap<int, QVariant> result;
QVector<QStandardItemData>::const_iterator it;
- for (it = values.begin(); it != values.end(); ++it)
- result.insert((*it).role, (*it).value);
+ for (it = values.cbegin(); it != values.cend(); ++it){
+ // Qt::UserRole - 1 is used internally to store the flags
+ if (it->role != Qt::UserRole - 1)
+ result.insert(it->role, it->value);
+ }
return result;
}
@@ -931,6 +934,21 @@ void QStandardItem::setData(const QVariant &value, int role)
}
/*!
+ \since 5.12
+ Removes all the data from all roles previously set.
+ \sa data(), setData()
+*/
+void QStandardItem::clearData()
+{
+ Q_D(QStandardItem);
+ if (d->values.isEmpty())
+ return;
+ d->values.clear();
+ if (d->model)
+ d->model->d_func()->itemChanged(this, QVector<int>{});
+}
+
+/*!
Returns the item's data for the given \a role, or an invalid
QVariant if there is no data for the role.
@@ -2924,8 +2942,10 @@ bool QStandardItemModel::insertRows(int row, int count, const QModelIndex &paren
QMap<int, QVariant> QStandardItemModel::itemData(const QModelIndex &index) const
{
Q_D(const QStandardItemModel);
- QStandardItem *item = d->itemFromIndex(index);
- return item ? item->d_func()->itemData() : QMap<int, QVariant>();
+ const QStandardItem *const item = d->itemFromIndex(index);
+ if (!item || item == d->root.data())
+ return QMap<int, QVariant>();
+ return item->d_func()->itemData();
}
/*!
@@ -2991,6 +3011,23 @@ bool QStandardItemModel::setData(const QModelIndex &index, const QVariant &value
}
/*!
+ \since 5.12
+ Removes the data stored in all the roles for the given \a index.
+ \sa setData(), data()
+*/
+bool QStandardItemModel::clearItemData(const QModelIndex &index)
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid))
+ return false;
+ Q_D(QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(index);
+ if (!item)
+ return false;
+ item->clearData();
+ return true;
+}
+
+/*!
\reimp
*/
bool QStandardItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h
index 827179b31d..a9ee25da75 100644
--- a/src/gui/itemmodels/qstandarditemmodel.h
+++ b/src/gui/itemmodels/qstandarditemmodel.h
@@ -69,6 +69,7 @@ public:
virtual QVariant data(int role = Qt::UserRole + 1) const;
virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
+ void clearData();
inline QString text() const {
return qvariant_cast<QString>(data(Qt::DisplayRole));
@@ -343,6 +344,8 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+ // Qt 6: add override keyword
+ bool clearItemData(const QModelIndex &index);
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri
index 3b9afdfe8b..1f137fc46f 100644
--- a/src/gui/kernel/kernel.pri
+++ b/src/gui/kernel/kernel.pri
@@ -74,8 +74,8 @@ HEADERS += \
kernel/qplatformgraphicsbufferhelper.h \
kernel/qinputdevicemanager_p.h \
kernel/qinputdevicemanager_p_p.h \
- kernel/qhighdpiscaling_p.h
-
+ kernel/qhighdpiscaling_p.h \
+ kernel/qtestsupport_gui.h
SOURCES += \
kernel/qgenericpluginfactory.cpp \
@@ -128,7 +128,8 @@ SOURCES += \
kernel/qplatformgraphicsbuffer.cpp \
kernel/qplatformgraphicsbufferhelper.cpp \
kernel/qinputdevicemanager.cpp \
- kernel/qhighdpiscaling.cpp
+ kernel/qhighdpiscaling.cpp \
+ kernel/qtestsupport_gui.cpp
qtConfig(draganddrop) {
HEADERS += \
diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h
index cc00bc1442..5f6db07987 100644
--- a/src/gui/kernel/qdnd_p.h
+++ b/src/gui/kernel/qdnd_p.h
@@ -63,10 +63,6 @@
#include "private/qobject_p.h"
#include "QtGui/qbackingstore.h"
-// ### Remove the following include, once everybody includes
-// qinternalmimedata_p.h for QInternalMimeData.
-#include "qinternalmimedata_p.h"
-
QT_REQUIRE_CONFIG(draganddrop);
QT_BEGIN_NAMESPACE
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index 94fc342e2a..53d1ac1fc0 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -783,7 +783,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient)
: QInputEvent(Wheel, modifiers), p(pos), qt4D(delta), qt4O(orient), mouseState(buttons),
- ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
+ src(Qt::MouseEventNotSynthesized), invertedScrolling(false), ph(Qt::NoScrollPhase)
{
g = QCursor::pos();
if (orient == Qt::Vertical)
@@ -818,7 +818,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), qt4D(delta), qt4O(orient), mouseState(buttons),
- ph(Qt::NoScrollPhase), src(Qt::MouseEventNotSynthesized), invertedScrolling(false)
+ src(Qt::MouseEventNotSynthesized), invertedScrolling(false), ph(Qt::NoScrollPhase)
{
if (orient == Qt::Vertical)
angleD = QPoint(0, delta);
@@ -959,10 +959,49 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos,
QPoint pixelDelta, QPoint angleDelta, int qt4Delta, Qt::Orientation qt4Orientation,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool inverted)
: QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta),
- angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), ph(phase), src(source),
- invertedScrolling(inverted)
+ angleD(angleDelta), qt4D(qt4Delta), qt4O(qt4Orientation), mouseState(buttons), src(source),
+ invertedScrolling(inverted), ph(phase)
{}
+/*!
+ Constructs a wheel event object.
+
+ The \a pos provides the location of the mouse cursor
+ within the window. The position in global coordinates is specified
+ by \a globalPos.
+
+ \a pixelDelta contains the scrolling distance in pixels on screen, while
+ \a angleDelta contains the wheel rotation distance. \a pixelDelta is
+ optional and can be null.
+
+ The mouse and keyboard states at the time of the event are specified by
+ \a buttons and \a modifiers.
+
+ The scrolling phase of the event is specified by \a phase.
+
+ If the wheel event comes from a physical mouse wheel, \a source is set to
+ Qt::MouseEventNotSynthesized. If it comes from a gesture detected by the
+ operating system, or from a non-mouse hardware device, such that \a
+ pixelDelta is directly related to finger movement, \a source is set to
+ Qt::MouseEventSynthesizedBySystem. If it comes from Qt, source would be set
+ to Qt::MouseEventSynthesizedByQt.
+
+ If the system is configured to invert the delta values delivered with the
+ event (such as natural scrolling of the touchpad on macOS), \a inverted
+ should be \c true. Otherwise, \a inverted is \c false
+
+ \sa posF(), globalPosF(), angleDelta(), pixelDelta(), phase()
+*/
+QWheelEvent::QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoint angleDelta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase,
+ bool inverted, Qt::MouseEventSource source)
+ : QInputEvent(Wheel, modifiers), p(pos), g(globalPos), pixelD(pixelDelta), angleD(angleDelta),
+ qt4O(qAbs(angleDelta.x()) > qAbs(angleDelta.y()) ? Qt::Horizontal : Qt::Vertical),
+ mouseState(buttons), src(source), invertedScrolling(inverted), ph(phase)
+{
+ qt4D = (qt4O == Qt::Horizontal ? angleDelta.x() : angleDelta.y());
+}
+
#endif // QT_CONFIG(wheelevent)
/*!
@@ -1536,8 +1575,8 @@ QMoveEvent::~QMoveEvent()
\ingroup events
- Expose events are sent to windows when an area of the window is invalidated
- or window exposure in the windowing system changes.
+ Expose events are sent to windows when an area of the window is invalidated,
+ for example when window exposure in the windowing system changes.
A Window with a client area that is completely covered by another window, or
is otherwise not visible may be considered obscured by Qt and may in such
@@ -4032,7 +4071,12 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
# if QT_CONFIG(wheelevent)
case QEvent::Wheel: {
const QWheelEvent *we = static_cast<const QWheelEvent *>(e);
- dbg << "QWheelEvent(" << "pixelDelta=" << we->pixelDelta() << ", angleDelta=" << we->angleDelta() << ')';
+ dbg << "QWheelEvent(" << we->phase();
+ if (!we->pixelDelta().isNull() || !we->angleDelta().isNull())
+ dbg << ", pixelDelta=" << we->pixelDelta() << ", angleDelta=" << we->angleDelta();
+ else if (int qt4Delta = we->delta())
+ dbg << ", delta=" << qt4Delta << ", orientation=" << we->orientation();
+ dbg << ')';
}
break;
# endif // QT_CONFIG(wheelevent)
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index 033d24d665..2b1c6a6e31 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -193,6 +193,10 @@ public:
QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pixelDelta, QPoint angleDelta,
int qt4Delta, Qt::Orientation qt4Orientation, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool inverted);
+
+ QWheelEvent(QPointF pos, QPointF globalPos, QPoint pixelDelta, QPoint angleDelta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase,
+ bool inverted, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
~QWheelEvent();
@@ -225,13 +229,14 @@ protected:
QPointF g;
QPoint pixelD;
QPoint angleD;
- int qt4D;
- Qt::Orientation qt4O;
+ int qt4D = 0;
+ Qt::Orientation qt4O = Qt::Vertical;
Qt::MouseButtons mouseState;
- uint ph : 2;
+ uint _unused_ : 2; // Kept for binary compatibility
uint src: 2;
bool invertedScrolling : 1;
- int reserved : 27;
+ uint ph : 3;
+ int reserved : 24;
friend class QApplication;
};
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 06d1fcae53..f4e2dda05a 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -84,6 +84,7 @@
#include "private/qcursor_p.h"
#include "private/qopenglcontext_p.h"
#include "private/qinputdevicemanager_p.h"
+#include "private/qinputmethod_p.h"
#include "private/qtouchdevice_p.h"
#include <qpa/qplatformthemefactory_p.h>
@@ -114,6 +115,10 @@
# include <QtCore/QLibraryInfo>
#endif // Q_OS_WIN
+#ifdef Q_OS_WASM
+#include <emscripten.h>
+#endif
+
#include <qtgui_tracepoints_p.h>
#include <ctype.h>
@@ -143,6 +148,8 @@ Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationI
bool QGuiApplicationPrivate::highDpiScalingUpdated = false;
+QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow;
+
QVector<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints;
QPlatformIntegration *QGuiApplicationPrivate::platform_integration = 0;
@@ -250,10 +257,10 @@ static inline void clearFontUnlocked()
QGuiApplicationPrivate::app_font = 0;
}
-static bool checkRunningUnderFlatpak()
+static bool checkNeedPortalSupport()
{
#if QT_CONFIG(dbus)
- return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty();
+ return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP");
#else
return false;
#endif // QT_CONFIG(dbus)
@@ -592,6 +599,9 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
By default, they will be used if the application is not an
instance of QApplication or for Qt Quick Controls 2
applications.
+
+ \li \c {altgr}, detect the key \c {AltGr} found on some keyboards as
+ Qt::GroupSwitchModifier.
\endlist
The following parameter is available for \c {-platform cocoa} (on macOS):
@@ -668,6 +678,7 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr;
QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
QGuiApplicationPrivate::highDpiScalingUpdated = false;
+ QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear();
#ifndef QT_NO_SESSIONMANAGER
QGuiApplicationPrivate::is_fallback_session_management_enabled = true;
@@ -1216,9 +1227,9 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
if (!platformThemeName.isEmpty())
themeNames.append(platformThemeName);
- // 2) Special case - check whether we are in sandbox to use flatpak platform theme for portals support
- if (checkRunningUnderFlatpak()) {
- themeNames.append(QStringLiteral("flatpak"));
+ // 2) Special case - check whether it's a flatpak or snap app to use xdg-desktop-portal platform theme for portals support
+ if (checkNeedPortalSupport()) {
+ themeNames.append(QStringLiteral("xdgdesktopportal"));
}
// 3) Ask the platform integration for a list of theme names
@@ -1410,7 +1421,7 @@ void QGuiApplicationPrivate::eventDispatcherReady()
void QGuiApplicationPrivate::init()
{
- Q_TRACE(qguiapplicationprivate_init_entry);
+ Q_TRACE(QGuiApplicationPrivate_init_entry);
#if defined(Q_OS_MACOS)
QMacAutoReleasePool pool;
@@ -1575,7 +1586,7 @@ void QGuiApplicationPrivate::init()
QObject::connect(q, &QGuiApplication::applicationNameChanged,
q, &QGuiApplication::applicationDisplayNameChanged);
- Q_TRACE(qguiapplicationprivate_init_exit);
+ Q_TRACE(QGuiApplicationPrivate_init_exit);
}
extern void qt_cleanupFontDatabase();
@@ -1615,7 +1626,13 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
qt_gl_set_global_share_context(0);
}
#endif
-
+#ifdef Q_OS_WASM
+ EM_ASM(
+ // unmount persistent directory as IDBFS
+ // see QTBUG-70002
+ FS.unmount('/home/web_user');
+ );
+#endif
platform_integration->destroy();
delete platform_theme;
@@ -1691,7 +1708,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers()
/*!
Returns the current state of the buttons on the mouse. The current state is
- updated syncronously as the event queue is emptied of events that will
+ updated synchronously as the event queue is emptied of events that will
spontaneously change the mouse state (QEvent::MouseButtonPress and
QEvent::MouseButtonRelease events).
@@ -1766,8 +1783,11 @@ int QGuiApplication::exec()
*/
bool QGuiApplication::notify(QObject *object, QEvent *event)
{
- if (object->isWindowType())
- QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event);
+ if (object->isWindowType()) {
+ if (QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event))
+ return true; // Platform plugin ate the event
+ }
+
return QCoreApplication::notify(object, event);
}
@@ -1789,18 +1809,18 @@ bool QGuiApplication::compressEvent(QEvent *event, QObject *receiver, QPostEvent
return QCoreApplication::compressEvent(event, receiver, postedEvents);
}
-void QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(QWindow *window, QEvent *event)
+bool QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(QWindow *window, QEvent *event)
{
if (!window)
- return;
+ return false;
QPlatformWindow *platformWindow = window->handle();
if (!platformWindow)
- return;
+ return false;
// spontaneous events come from the platform integration already, we don't need to send the events back
if (event->spontaneous())
- return;
+ return false;
// let the platform window do any handling it needs to as well
- platformWindow->windowEvent(event);
+ return platformWindow->windowEvent(event);
}
bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result)
@@ -1810,7 +1830,7 @@ bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArra
void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
- Q_TRACE(qguiapplicationprivate_processwsevents_entry, e->type);
+ Q_TRACE(QGuiApplicationPrivate_processWindowSystemEvent_entry, e->type);
switch(e->type) {
case QWindowSystemInterfacePrivate::Mouse:
@@ -1921,7 +1941,7 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
break;
}
- Q_TRACE(qguiapplicationprivate_processwsevents_exit, e->type);
+ Q_TRACE(QGuiApplicationPrivate_processWindowSystemEvent_exit, e->type);
}
/*! \internal
@@ -2333,11 +2353,11 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf
if (QWindow *window = wse->window.data()) {
if (window->screen() == wse->screen.data())
return;
- if (window->isTopLevel()) {
+ if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) {
if (QScreen *screen = wse->screen.data())
- window->d_func()->setTopLevelScreen(screen, false /* recreate */);
+ topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */);
else // Fall back to default behavior, and try to find some appropriate screen
- window->setScreen(0);
+ topLevelWindow->setScreen(0);
}
// we may have changed scaling, so trigger resize event if needed
if (window->handle()) {
@@ -3048,9 +3068,56 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E
#if QT_CONFIG(draganddrop)
-QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
+/*! \internal
+
+ This function updates an internal state to keep the source compatibility. Documentation of
+ QGuiApplication::mouseButtons() states - "The current state is updated synchronously as
+ the event queue is emptied of events that will spontaneously change the mouse state
+ (QEvent::MouseButtonPress and QEvent::MouseButtonRelease events)". But internally we have
+ been updating these state variables from various places to keep buttons returned by
+ mouseButtons() in sync with the systems state. This is not the documented behavior.
+
+ ### Qt6 - Remove QGuiApplication::mouseButtons()/keyboardModifiers() API? And here
+ are the reasons:
+
+ - It is an easy to misuse API by:
+
+ a) Application developers: The only place where the values of this API can be trusted is
+ when using within mouse handling callbacks. In these callbacks we work with the state
+ that was provided directly by the windowing system. Anywhere else it might not reflect what
+ user wrongly expects. We might not always receive a matching mouse release for a press event
+ (e.g. When dismissing a popup window on X11. Or when dnd enter Qt application with mouse
+ button down, we update mouse_buttons and then dnd leaves Qt application and does a drop
+ somewhere else) and hence mouseButtons() will be out-of-sync from users perspective, see
+ for example QTBUG-33161. BUT THIS IS NOT HOW THE API IS SUPPOSED TO BE USED. Since the only
+ safe place to use this API is from mouse event handlers, we might as well deprecate it and
+ pass down the button state if we are not already doing that everywhere where it matters.
+
+ b) Qt framework developers:
+
+ We see users complaining, we start adding hacks everywhere just to keep buttons in sync ;)
+ There are corner cases that can not be solved and adding this kind of hacks is never ending
+ task.
+
+ - Real mouse events, tablet mouse events, etc: all go through QGuiApplication::processMouseEvent,
+ and all share mouse_buttons. What if we want to support multiple mice in future? The API must
+ go.
+
+ - Motivation why this API is public is not clear. Could the same be achieved by a user by
+ installing an event filter?
+*/
+static void updateMouseAndModifierButtonState(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+{
+ QGuiApplicationPrivate::mouse_buttons = buttons;
+ QGuiApplicationPrivate::modifier_buttons = modifiers;
+}
+
+QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
{
- static QPointer<QWindow> currentDragWindow;
+ updateMouseAndModifierButtonState(buttons, modifiers);
+
static Qt::DropAction lastAcceptedDropAction = Qt::IgnoreAction;
QPlatformDrag *platformDrag = platformIntegration()->drag();
if (!platformDrag || (w && w->d_func()->blockedByModalWindow)) {
@@ -3059,15 +3126,13 @@ QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QM
}
if (!dropData) {
- if (currentDragWindow.data() == w)
- currentDragWindow = 0;
+ currentDragWindow = nullptr;
QDragLeaveEvent e;
QGuiApplication::sendEvent(w, &e);
lastAcceptedDropAction = Qt::IgnoreAction;
return QPlatformDragQtResponse(false, lastAcceptedDropAction, QRect());
}
- QDragMoveEvent me(p, supportedActions, dropData,
- QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+ QDragMoveEvent me(p, supportedActions, dropData, buttons, modifiers);
if (w != currentDragWindow) {
lastAcceptedDropAction = Qt::IgnoreAction;
@@ -3076,8 +3141,7 @@ QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QM
QGuiApplication::sendEvent(currentDragWindow, &e);
}
currentDragWindow = w;
- QDragEnterEvent e(p, supportedActions, dropData,
- QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+ QDragEnterEvent e(p, supportedActions, dropData, buttons, modifiers);
QGuiApplication::sendEvent(w, &e);
if (e.isAccepted() && e.dropAction() != Qt::IgnoreAction)
lastAcceptedDropAction = e.dropAction();
@@ -3095,10 +3159,15 @@ QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QM
return QPlatformDragQtResponse(me.isAccepted(), lastAcceptedDropAction, me.answerRect());
}
-QPlatformDropQtResponse QGuiApplicationPrivate::processDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
+QPlatformDropQtResponse QGuiApplicationPrivate::processDrop(QWindow *w, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
{
- QDropEvent de(p, supportedActions, dropData,
- QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+ updateMouseAndModifierButtonState(buttons, modifiers);
+
+ currentDragWindow = nullptr;
+
+ QDropEvent de(p, supportedActions, dropData, buttons, modifiers);
QGuiApplication::sendEvent(w, &de);
Qt::DropAction acceptedAction = de.isAccepted() ? de.dropAction() : Qt::IgnoreAction;
@@ -3963,18 +4032,7 @@ void QGuiApplicationPrivate::_q_updateFocusObject(QObject *object)
Q_Q(QGuiApplication);
QPlatformInputContext *inputContext = platformIntegration()->inputContext();
- bool enabled = false;
- if (object && inputContext) {
- QInputMethodQueryEvent query(Qt::ImEnabled | Qt::ImHints);
- QGuiApplication::sendEvent(object, &query);
- enabled = query.value(Qt::ImEnabled).toBool();
- if (enabled) {
- static const bool supportsHiddenText = inputContext->hasCapability(QPlatformInputContext::HiddenTextCapability);
- const Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(query.value(Qt::ImHints).toInt());
- if ((hints & Qt::ImhHiddenText) && !supportsHiddenText)
- enabled = false;
- }
- }
+ const bool enabled = inputContext && QInputMethodPrivate::objectAcceptsInputMethod(object);
QPlatformInputContextPrivate::setInputMethodAccepted(enabled);
if (inputContext)
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index f6f7aa7f8c..79c1a1c820 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -163,13 +163,17 @@ public:
#endif
#if QT_CONFIG(draganddrop)
- static QPlatformDragQtResponse processDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
- static QPlatformDropQtResponse processDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
+ static QPlatformDragQtResponse processDrag(QWindow *w, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ static QPlatformDropQtResponse processDrop(QWindow *w, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
#endif
static bool processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);
- static void sendQWindowEventToQPlatformWindow(QWindow *window, QEvent *event);
+ static bool sendQWindowEventToQPlatformWindow(QWindow *window, QEvent *event);
static inline Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
{
@@ -213,6 +217,7 @@ public:
static QWindow *currentMousePressWindow;
static Qt::ApplicationState applicationState;
static bool highDpiScalingUpdated;
+ static QPointer<QWindow> currentDragWindow;
struct TabletPointData {
TabletPointData(qint64 devId = 0) : deviceId(devId), state(Qt::NoButton), target(nullptr) {}
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 8689f9f3b1..a455aa639d 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -244,7 +244,8 @@ static inline bool usePixelDensity()
return false;
return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling)
|| (screenEnvValueOk && screenEnvValue > 0)
- || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && qgetenv(legacyDevicePixelEnvVar).toLower() == "auto");
+ || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) &&
+ qgetenv(legacyDevicePixelEnvVar).compare("auto", Qt::CaseInsensitive) == 0);
}
void QHighDpiScaling::initHighDpiScaling()
diff --git a/src/gui/kernel/qinputmethod.cpp b/src/gui/kernel/qinputmethod.cpp
index 365b088840..a319529442 100644
--- a/src/gui/kernel/qinputmethod.cpp
+++ b/src/gui/kernel/qinputmethod.cpp
@@ -390,15 +390,29 @@ void QInputMethod::invokeAction(Action a, int cursorPosition)
ic->invokeAction(a, cursorPosition);
}
+static inline bool platformSupportsHiddenText()
+{
+ const QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
+ return inputContext && inputContext->hasCapability(QPlatformInputContext::HiddenTextCapability);
+}
+
bool QInputMethodPrivate::objectAcceptsInputMethod(QObject *object)
{
bool enabled = false;
if (object) {
- QInputMethodQueryEvent query(Qt::ImEnabled);
+ // If the platform does not support hidden text, query the hints
+ // in addition and disable in case of ImhHiddenText.
+ static const bool supportsHiddenText = platformSupportsHiddenText();
+ QInputMethodQueryEvent query(supportsHiddenText
+ ? Qt::InputMethodQueries(Qt::ImEnabled)
+ : Qt::InputMethodQueries(Qt::ImEnabled | Qt::ImHints));
QGuiApplication::sendEvent(object, &query);
enabled = query.value(Qt::ImEnabled).toBool();
+ if (enabled && !supportsHiddenText
+ && Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText)) {
+ enabled = false;
+ }
}
-
return enabled;
}
diff --git a/src/gui/kernel/qinputmethod_p.h b/src/gui/kernel/qinputmethod_p.h
index 81723bbe30..0c2b739d92 100644
--- a/src/gui/kernel/qinputmethod_p.h
+++ b/src/gui/kernel/qinputmethod_p.h
@@ -80,7 +80,7 @@ public:
void _q_connectFocusObject();
void _q_checkFocusObject(QObject *object);
- bool objectAcceptsInputMethod(QObject *object);
+ static bool objectAcceptsInputMethod(QObject *object);
QTransform inputItemTransform;
QRectF inputRectangle;
diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp
index 9a9677b476..a428da8ca4 100644
--- a/src/gui/kernel/qkeysequence.cpp
+++ b/src/gui/kernel/qkeysequence.cpp
@@ -1062,6 +1062,8 @@ int QKeySequence::decodeString(const QString &str)
int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceFormat format)
{
+ Q_ASSERT(!accel.isEmpty());
+
int ret = 0;
accel = std::move(accel).toLower();
bool nativeText = (format == QKeySequence::NativeText);
@@ -1121,7 +1123,10 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
sl = accel;
}
}
+ if (accel.isEmpty()) // Incomplete, like for "Meta+Shift+"
+ return Qt::Key_unknown;
#endif
+
int i = 0;
int lastI = 0;
while ((i = sl.indexOf(QLatin1Char('+'), i + 1)) != -1) {
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 4efa5a40f3..c5d5490ea0 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -945,7 +945,7 @@ GLuint QOpenGLContext::defaultFramebufferObject() const
Avoid calling this function from a different thread than the one the
QOpenGLContext instance lives in. If you wish to use QOpenGLContext from a
- different thread you should first call make sure it's not current in the
+ different thread you should first make sure it's not current in the
current thread, by calling doneCurrent() if necessary. Then call
moveToThread(otherThread) before using it in the other thread.
@@ -977,67 +977,66 @@ bool QOpenGLContext::makeCurrent(QSurface *surface)
if (!surface->surfaceHandle())
return false;
if (!surface->supportsOpenGL()) {
+#ifndef Q_OS_WASM // ### work around the WASM platform plugin using QOpenGLContext with raster surfaces.
+ // see QTBUG-70076
qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface;
return false;
+#endif
}
- QOpenGLContext *previous = QOpenGLContextPrivate::setCurrentContext(this);
-
- if (d->platformGLContext->makeCurrent(surface->surfaceHandle())) {
- static bool needsWorkaroundSet = false;
- static bool needsWorkaround = false;
+ if (!d->platformGLContext->makeCurrent(surface->surfaceHandle()))
+ return false;
- if (!needsWorkaroundSet) {
- QByteArray env;
-#ifdef Q_OS_ANDROID
- env = qgetenv(QByteArrayLiteral("QT_ANDROID_DISABLE_GLYPH_CACHE_WORKAROUND"));
- needsWorkaround = env.isEmpty() || env == QByteArrayLiteral("0") || env == QByteArrayLiteral("false");
+ QOpenGLContextPrivate::setCurrentContext(this);
+#ifndef QT_NO_DEBUG
+ QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true);
#endif
- env = qgetenv(QByteArrayLiteral("QT_ENABLE_GLYPH_CACHE_WORKAROUND"));
- if (env == QByteArrayLiteral("1") || env == QByteArrayLiteral("true"))
- needsWorkaround = true;
-
- if (!needsWorkaround) {
- const char *rendererString = reinterpret_cast<const char *>(functions()->glGetString(GL_RENDERER));
- if (rendererString)
- needsWorkaround =
- qstrncmp(rendererString, "Mali-4xx", 6) == 0 // Mali-400, Mali-450
- || qstrcmp(rendererString, "Mali-T880") == 0
- || qstrncmp(rendererString, "Adreno (TM) 2xx", 13) == 0 // Adreno 200, 203, 205
- || qstrncmp(rendererString, "Adreno 2xx", 8) == 0 // Same as above but without the '(TM)'
- || qstrncmp(rendererString, "Adreno (TM) 3xx", 13) == 0 // Adreno 302, 305, 320, 330
- || qstrncmp(rendererString, "Adreno 3xx", 8) == 0 // Same as above but without the '(TM)'
- || qstrncmp(rendererString, "Adreno (TM) 4xx", 13) == 0 // Adreno 405, 418, 420, 430
- || qstrncmp(rendererString, "Adreno 4xx", 8) == 0 // Same as above but without the '(TM)'
- || qstrncmp(rendererString, "Adreno (TM) 5xx", 13) == 0 // Adreno 505, 506, 510, 530, 540
- || qstrncmp(rendererString, "Adreno 5xx", 8) == 0 // Same as above but without the '(TM)'
- || qstrncmp(rendererString, "Adreno (TM) 6xx", 13) == 0 // Adreno 610, 620, 630
- || qstrncmp(rendererString, "Adreno 6xx", 8) == 0 // Same as above but without the '(TM)'
- || qstrcmp(rendererString, "GC800 core") == 0
- || qstrcmp(rendererString, "GC1000 core") == 0
- || strstr(rendererString, "GC2000") != 0
- || qstrcmp(rendererString, "Immersion.16") == 0;
- }
- needsWorkaroundSet = true;
- }
-
- if (needsWorkaround)
- d->workaround_brokenFBOReadBack = true;
- d->surface = surface;
+ d->surface = surface;
- d->shareGroup->d_func()->deletePendingResources(this);
+ static bool needsWorkaroundSet = false;
+ static bool needsWorkaround = false;
-#ifndef QT_NO_DEBUG
- QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true);
+ if (!needsWorkaroundSet) {
+ QByteArray env;
+#ifdef Q_OS_ANDROID
+ env = qgetenv(QByteArrayLiteral("QT_ANDROID_DISABLE_GLYPH_CACHE_WORKAROUND"));
+ needsWorkaround = env.isEmpty() || env == QByteArrayLiteral("0") || env == QByteArrayLiteral("false");
#endif
-
- return true;
+ env = qgetenv(QByteArrayLiteral("QT_ENABLE_GLYPH_CACHE_WORKAROUND"));
+ if (env == QByteArrayLiteral("1") || env == QByteArrayLiteral("true"))
+ needsWorkaround = true;
+
+ if (!needsWorkaround) {
+ const char *rendererString = reinterpret_cast<const char *>(functions()->glGetString(GL_RENDERER));
+ if (rendererString)
+ needsWorkaround =
+ qstrncmp(rendererString, "Mali-4xx", 6) == 0 // Mali-400, Mali-450
+ || qstrcmp(rendererString, "Mali-T880") == 0
+ || qstrncmp(rendererString, "Adreno (TM) 2xx", 13) == 0 // Adreno 200, 203, 205
+ || qstrncmp(rendererString, "Adreno 2xx", 8) == 0 // Same as above but without the '(TM)'
+ || qstrncmp(rendererString, "Adreno (TM) 3xx", 13) == 0 // Adreno 302, 305, 320, 330
+ || qstrncmp(rendererString, "Adreno 3xx", 8) == 0 // Same as above but without the '(TM)'
+ || qstrncmp(rendererString, "Adreno (TM) 4xx", 13) == 0 // Adreno 405, 418, 420, 430
+ || qstrncmp(rendererString, "Adreno 4xx", 8) == 0 // Same as above but without the '(TM)'
+ || qstrncmp(rendererString, "Adreno (TM) 5xx", 13) == 0 // Adreno 505, 506, 510, 530, 540
+ || qstrncmp(rendererString, "Adreno 5xx", 8) == 0 // Same as above but without the '(TM)'
+ || qstrncmp(rendererString, "Adreno (TM) 6xx", 13) == 0 // Adreno 610, 620, 630
+ || qstrncmp(rendererString, "Adreno 6xx", 8) == 0 // Same as above but without the '(TM)'
+ || qstrcmp(rendererString, "GC800 core") == 0
+ || qstrcmp(rendererString, "GC1000 core") == 0
+ || strstr(rendererString, "GC2000") != 0
+ || qstrcmp(rendererString, "Immersion.16") == 0;
+ }
+ needsWorkaroundSet = true;
}
- QOpenGLContextPrivate::setCurrentContext(previous);
+ if (needsWorkaround)
+ d->workaround_brokenFBOReadBack = true;
+
+ d->shareGroup->d_func()->deletePendingResources(this);
- return false;
+ return true;
}
/*!
@@ -1078,7 +1077,8 @@ QSurface *QOpenGLContext::surface() const
Swap the back and front buffers of \a surface.
Call this to finish a frame of OpenGL rendering, and make sure to
- call makeCurrent() again before you begin a new frame.
+ call makeCurrent() again before issuing any further OpenGL commands,
+ for example as part of a new frame.
*/
void QOpenGLContext::swapBuffers(QSurface *surface)
{
diff --git a/src/gui/kernel/qopenglwindow.cpp b/src/gui/kernel/qopenglwindow.cpp
index c3a264f1e8..8b052d92ae 100644
--- a/src/gui/kernel/qopenglwindow.cpp
+++ b/src/gui/kernel/qopenglwindow.cpp
@@ -511,7 +511,7 @@ GLuint QOpenGLWindow::defaultFramebufferObject() const
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
/*!
- Returns a 32-bit RGB image of the framebuffer.
+ Returns a copy of the framebuffer.
\note This is a potentially expensive operation because it relies on
glReadPixels() to read back the pixels. This may be slow and can stall the
@@ -531,7 +531,9 @@ QImage QOpenGLWindow::grabFramebuffer()
return QImage();
makeCurrent();
- QImage img = qt_gl_read_framebuffer(size() * devicePixelRatio(), false, false);
+
+ const bool hasAlpha = format().hasAlpha();
+ QImage img = qt_gl_read_framebuffer(size() * devicePixelRatio(), hasAlpha, hasAlpha);
img.setDevicePixelRatio(devicePixelRatio());
return img;
}
diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp
index 4905e51e01..9ccfb9b819 100644
--- a/src/gui/kernel/qpalette.cpp
+++ b/src/gui/kernel/qpalette.cpp
@@ -316,6 +316,21 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button)
*/
/*!
+ \fn const QBrush & QPalette::placeholderText() const
+ \since 5.12
+
+ Returns the placeholder text brush of the current color group.
+
+ \note Before Qt 5.12, the placeholder text color was hard-coded in the code as
+ QPalette::text().color() where an alpha of 128 was applied.
+ We continue to support this behavior by default, unless you set your own brush.
+ One can get back the original placeholder color setting the special QBrush default
+ constructor as placeholder brush.
+
+ \sa ColorRole, brush()
+*/
+
+/*!
\fn ColorGroup QPalette::currentColorGroup() const
Returns the palette's current color group.
@@ -444,6 +459,9 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button)
of QPalette, because tool tips are not active
windows.
+ \value PlaceholderText Used as the placeholder color for various text input widgets.
+ This enum value has been introduced in Qt 5.12
+
\value Text The foreground color used with \c Base. This is usually
the same as the \c WindowText, in which case it must provide
good contrast with \c Window and \c Base.
@@ -765,11 +783,32 @@ void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b)
cg = Active;
}
+ // For placeholder we want to continue to respect the original behavior, which is
+ // derivating the text color, but only if user has not yet set his own brush.
+ // We then use Qt::NoBrush as an inernal way to know if the brush is customized or not.
+
+ // ### Qt 6 - remove this special case
+ // Part 1 - Restore initial color to the given color group
+ if (cr == PlaceholderText && b == QBrush()) {
+ QColor col = brush(Text).color();
+ col.setAlpha(128);
+ setBrush(cg, PlaceholderText, QBrush(col, Qt::NoBrush));
+ return;
+ }
+
if (d->br[cg][cr] != b) {
detach();
d->br[cg][cr] = b;
}
data.resolve_mask |= (1<<cr);
+
+ // ### Qt 6 - remove this special case
+ // Part 2 - Update initial color to the given color group
+ if (cr == Text && d->br[cg][PlaceholderText].style() == Qt::NoBrush) {
+ QColor col = brush(Text).color();
+ col.setAlpha(128);
+ setBrush(cg, PlaceholderText, QBrush(col, Qt::NoBrush));
+ }
}
/*!
@@ -962,7 +1001,7 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p)
for (int i = 0; i < NumOldRoles; ++i)
s << p.d->br[grp][oldRoles[i]].color();
} else {
- int max = QPalette::ToolTipText + 1;
+ int max = (int)QPalette::NColorRoles;
if (s.version() <= QDataStream::Qt_2_1)
max = QPalette::HighlightedText + 1;
else if (s.version() <= QDataStream::Qt_4_3)
@@ -1159,7 +1198,7 @@ QDebug operator<<(QDebug dbg, const QPalette &p)
{"WindowText", "Button", "Light", "Midlight", "Dark", "Mid", "Text",
"BrightText", "ButtonText", "Base", "Window", "Shadow", "Highlight",
"HighlightedText", "Link", "LinkVisited", "AlternateBase", "NoRole",
- "ToolTipBase","ToolTipText" };
+ "ToolTipBase","ToolTipText", "PlaceholderText" };
QDebugStateSaver saver(dbg);
QDebug nospace = dbg.nospace();
const uint mask = p.resolve();
diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h
index 71f3d0c3b8..071eddbc4d 100644
--- a/src/gui/kernel/qpalette.h
+++ b/src/gui/kernel/qpalette.h
@@ -96,7 +96,8 @@ public:
AlternateBase,
NoRole,
ToolTipBase, ToolTipText,
- NColorRoles = ToolTipText + 1,
+ PlaceholderText,
+ NColorRoles = PlaceholderText + 1,
Foreground = WindowText, Background = Window
};
Q_ENUM(ColorRole)
@@ -141,6 +142,7 @@ public:
inline const QBrush &highlightedText() const { return brush(HighlightedText); }
inline const QBrush &link() const { return brush(Link); }
inline const QBrush &linkVisited() const { return brush(LinkVisited); }
+ inline const QBrush &placeholderText() const { return brush(PlaceholderText); }
bool operator==(const QPalette &p) const;
inline bool operator!=(const QPalette &p) const { return !(operator==(p)); }
diff --git a/src/gui/kernel/qplatformdialoghelper.cpp b/src/gui/kernel/qplatformdialoghelper.cpp
index b456c1ca31..b787629f6a 100644
--- a/src/gui/kernel/qplatformdialoghelper.cpp
+++ b/src/gui/kernel/qplatformdialoghelper.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -44,6 +44,7 @@
#include <QtCore/QSharedData>
#include <QtCore/QSettings>
#include <QtCore/QUrl>
+#include <QtCore/QVector>
#include <QtGui/QColor>
#include <algorithm>
@@ -786,7 +787,8 @@ class QMessageDialogOptionsPrivate : public QSharedData
public:
QMessageDialogOptionsPrivate() :
icon(QMessageDialogOptions::NoIcon),
- buttons(QPlatformDialogHelper::Ok)
+ buttons(QPlatformDialogHelper::Ok),
+ nextCustomButtonId(QPlatformDialogHelper::LastButton + 1)
{}
QString windowTitle;
@@ -795,6 +797,8 @@ public:
QString informativeText;
QString detailedText;
QPlatformDialogHelper::StandardButtons buttons;
+ QVector<QMessageDialogOptions::CustomButton> customButtons;
+ int nextCustomButtonId;
};
QMessageDialogOptions::QMessageDialogOptions(QMessageDialogOptionsPrivate *dd)
@@ -886,6 +890,35 @@ QPlatformDialogHelper::StandardButtons QMessageDialogOptions::standardButtons()
return d->buttons;
}
+int QMessageDialogOptions::addButton(const QString &label, QPlatformDialogHelper::ButtonRole role,
+ void *buttonImpl)
+{
+ const CustomButton b(d->nextCustomButtonId++, label, role, buttonImpl);
+ d->customButtons.append(b);
+ return b.id;
+}
+
+static inline bool operator==(const QMessageDialogOptions::CustomButton &a,
+ const QMessageDialogOptions::CustomButton &b) {
+ return a.id == b.id;
+}
+
+void QMessageDialogOptions::removeButton(int id)
+{
+ d->customButtons.removeOne(CustomButton(id));
+}
+
+const QVector<QMessageDialogOptions::CustomButton> &QMessageDialogOptions::customButtons()
+{
+ return d->customButtons;
+}
+
+const QMessageDialogOptions::CustomButton *QMessageDialogOptions::customButton(int id)
+{
+ int i = d->customButtons.indexOf(CustomButton(id));
+ return (i < 0 ? nullptr : &d->customButtons.at(i));
+}
+
QPlatformDialogHelper::ButtonRole QPlatformDialogHelper::buttonRole(QPlatformDialogHelper::StandardButton button)
{
switch (button) {
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
index f58dcf17f0..bfcb658172 100644
--- a/src/gui/kernel/qplatformdialoghelper.h
+++ b/src/gui/kernel/qplatformdialoghelper.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -150,6 +150,7 @@ public:
MacModelessLayout,
AndroidLayout
};
+ Q_ENUM(ButtonLayout)
QPlatformDialogHelper();
virtual ~QPlatformDialogHelper();
@@ -459,6 +460,26 @@ public:
void setStandardButtons(QPlatformDialogHelper::StandardButtons buttons);
QPlatformDialogHelper::StandardButtons standardButtons() const;
+ struct CustomButton {
+ explicit CustomButton(
+ int id = -1, const QString &label = QString(),
+ QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::InvalidRole,
+ void *button = nullptr) :
+ label(label), role(role), id(id), button(button)
+ {}
+
+ QString label;
+ QPlatformDialogHelper::ButtonRole role;
+ int id;
+ void *button; // strictly internal use only
+ };
+
+ int addButton(const QString &label, QPlatformDialogHelper::ButtonRole role,
+ void *buttonImpl = nullptr);
+ void removeButton(int id);
+ const QVector<CustomButton> &customButtons();
+ const CustomButton *customButton(int id);
+
private:
QMessageDialogOptionsPrivate *d;
};
diff --git a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
index 2afb5e6ba5..924266997d 100644
--- a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
+++ b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
@@ -57,24 +57,28 @@
QT_BEGIN_NAMESPACE
/*!
- Convenience function to both lock and bind the buffer to a texture. This
- function will first try and lock with texture read and texture write
+ Convenience function to both lock and bind the \a graphicsBuffer to a texture.
+ This function will first try to lock with texture read and texture write
access. If this succeeds it will use the bindToTexture function to bind the
- content to the currently bound texture. If this fail it will try and lock
- with SWReadAccess and then use the bindSWToTexture convenience function.
+ content to the currently bound texture, and if \a premultiplied is provided,
+ it is set to false.
- \a swizzle is suppose to be used by the caller to figure out if the Red and
+ If it fails, it will try to lock with SWReadAccess and then use the
+ bindSWToTexture convenience function. If \a premultiplied is provided, it is
+ passed to the bindSWToTexture() function.
+
+ \a swizzle is meant to be used by the caller to figure out if the Red and
Blue color channels need to be swizzled when rendering.
\a rect is the subrect which is desired to be bounded to the texture. This
- argument has a no less than semantic, meaning more (if not all) of the buffer
+ argument has a not less than semantic, meaning more (if not all) of the buffer
can be bounded to the texture. An empty QRect is interpreted as entire buffer
should be bound.
The user should use the AccessTypes returned by isLocked to figure out what
lock has been obtained.
- returns true if the buffer has successfully been bound to the currently
+ Returns true if the buffer has successfully been bound to the currently
bound texture, otherwise returns false.
*/
bool QPlatformGraphicsBufferHelper::lockAndBindToTexture(QPlatformGraphicsBuffer *graphicsBuffer,
@@ -103,26 +107,29 @@ bool QPlatformGraphicsBufferHelper::lockAndBindToTexture(QPlatformGraphicsBuffer
}
/*!
- Convenience function that uploads the current raster content to the currently bound texture.
+ Convenience function that uploads the current raster content to the currently
+ bound texture.
- \a swizzleRandB is suppose to be used by the caller to figure out if the Red and
+ \a swizzleRandB is meant to be used by the caller to decide if the Red and
Blue color channels need to be swizzled when rendering. This is an
optimization. Qt often renders to software buffers interpreting pixels as
unsigned ints. When these buffers are uploaded to textures and each color
channel per pixel is interpreted as a byte (read sequentially), then the
- Red and Blue channels are swapped. Conveniently the Alpha buffer will be
- correct since Qt historically has had the alpha channel as the first
+ Red and Blue channels are swapped. Conveniently, the Alpha buffer will be
+ correct, since Qt historically has had the alpha channel as the first
channel, while OpenGL typically expects the alpha channel to be the last
channel.
- \a subRect is the subrect which is desired to be bounded to the texture. This
- argument has a no less than semantic, meaning more (if not all) of the buffer
- can be bounded to the texture. An empty QRect is interpreted as entire buffer
- should be bound.
+ \a subRect is the region to be bound to the texture. This argument has a
+ not less than semantic, meaning more (if not all) of the buffer can be
+ bound to the texture. An empty QRect is interpreted as meaning the entire
+ buffer should be bound.
- This function fails for buffers not capable of locking to SWAccess.
+ This function fails if the \a graphicsBuffer is not locked to SWAccess.
- Returns true on success, otherwise false.
+ Returns true on success, otherwise false. If \a premultipliedB is
+ provided, it is set according to what happens, if the function returns
+ true.
*/
bool QPlatformGraphicsBufferHelper::bindSWToTexture(const QPlatformGraphicsBuffer *graphicsBuffer,
bool *swizzleRandB, bool *premultipliedB,
diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp
index dfb8f60915..6e285a8fa5 100644
--- a/src/gui/kernel/qplatformintegration.cpp
+++ b/src/gui/kernel/qplatformintegration.cpp
@@ -99,8 +99,8 @@ QPlatformClipboard *QPlatformIntegration::clipboard() const
/*!
Accessor for the platform integration's drag object.
- Default implementation returns 0, implying no drag and drop support.
-
+ Default implementation returns QSimpleDrag. This class supports only drag
+ and drop operations within the same Qt application.
*/
QPlatformDrag *QPlatformIntegration::drag() const
{
diff --git a/src/gui/kernel/qplatformmenu.h b/src/gui/kernel/qplatformmenu.h
index e3fa5c71b1..28c29a704c 100644
--- a/src/gui/kernel/qplatformmenu.h
+++ b/src/gui/kernel/qplatformmenu.h
@@ -158,6 +158,7 @@ public:
virtual void removeMenu(QPlatformMenu *menu) = 0;
virtual void syncMenu(QPlatformMenu *menuItem) = 0;
virtual void handleReparent(QWindow *newParentWindow) = 0;
+ virtual QWindow *parentWindow() const { return nullptr; }
virtual QPlatformMenu *menuForTag(quintptr tag) const = 0;
virtual QPlatformMenu *createMenu() const;
diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp
index 358ff16930..9614be7f3e 100644
--- a/src/gui/kernel/qplatformscreen.cpp
+++ b/src/gui/kernel/qplatformscreen.cpp
@@ -72,7 +72,7 @@ QPlatformScreen::~QPlatformScreen()
This function is called when Qt needs to be able to grab the content of a window.
- Returnes the content of the window specified with the WId handle within the boundaries of
+ Returns the content of the window specified with the WId handle within the boundaries of
QRect(x,y,width,height).
*/
QPixmap QPlatformScreen::grabWindow(WId window, int x, int y, int width, int height) const
diff --git a/src/gui/kernel/qplatformsurface.cpp b/src/gui/kernel/qplatformsurface.cpp
index f091c04ebb..fdb2cf567d 100644
--- a/src/gui/kernel/qplatformsurface.cpp
+++ b/src/gui/kernel/qplatformsurface.cpp
@@ -38,6 +38,10 @@
****************************************************************************/
#include "qplatformsurface.h"
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#include <QtGui/qwindow.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -64,5 +68,26 @@ QPlatformSurface::QPlatformSurface(QSurface *surface) : m_surface(surface)
{
}
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QPlatformSurface *surface)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "QPlatformSurface(" << (const void *)surface;
+ if (surface) {
+ QSurface *s = surface->surface();
+ auto surfaceClass = s->surfaceClass();
+ debug << ", class=" << surfaceClass;
+ debug << ", type=" << s->surfaceType();
+ if (surfaceClass == QSurface::Window)
+ debug << ", window=" << static_cast<QWindow *>(s);
+ else
+ debug << ", surface=" << s;
+ }
+ debug << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformsurface.h b/src/gui/kernel/qplatformsurface.h
index 76e8767a05..4d8854fb40 100644
--- a/src/gui/kernel/qplatformsurface.h
+++ b/src/gui/kernel/qplatformsurface.h
@@ -58,6 +58,10 @@ QT_BEGIN_NAMESPACE
class QPlatformScreen;
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+#endif
+
class Q_GUI_EXPORT QPlatformSurface
{
public:
@@ -76,6 +80,11 @@ private:
friend class QPlatformOffscreenSurface;
};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QPlatformSurface *surface);
+#endif
+
QT_END_NAMESPACE
#endif //QPLATFORMSURFACE_H
diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp
index a66420c364..50f05721f7 100644
--- a/src/gui/kernel/qplatformwindow.cpp
+++ b/src/gui/kernel/qplatformwindow.cpp
@@ -140,7 +140,7 @@ void QPlatformWindow::setGeometry(const QRect &rect)
}
/*!
- Returnes the current geometry of a window
+ Returns the current geometry of a window
*/
QRect QPlatformWindow::geometry() const
{
@@ -340,6 +340,20 @@ void QPlatformWindow::setWindowFilePath(const QString &filePath) { Q_UNUSED(file
void QPlatformWindow::setWindowIcon(const QIcon &icon) { Q_UNUSED(icon); }
/*!
+ Reimplement to let the platform handle non-spontaneous window close.
+
+ When reimplementing make sure to call the base class implementation
+ or QWindowSystemInterface::handleCloseEvent(), which will prompt the
+ user to accept the window close (if needed) and then close the QWindow.
+*/
+bool QPlatformWindow::close()
+{
+ bool accepted = false;
+ QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window(), &accepted);
+ return accepted;
+}
+
+/*!
Reimplement to be able to let Qt raise windows to the top of the desktop
*/
void QPlatformWindow::raise() { qWarning("This plugin does not support raise()"); }
@@ -447,14 +461,26 @@ bool QPlatformWindow::setWindowModified(bool modified)
/*!
Reimplement this method to be able to do any platform specific event
- handling. All events for window() are passed to this function before being
- sent to QWindow::event().
+ handling. All non-synthetic events for window() are passed to this
+ function before being sent to QWindow::event().
- The default implementation is empty and does nothing with \a event.
+ Return true if the event should not be passed on to the QWindow.
+
+ Subclasses should always call the base class implementation.
*/
-void QPlatformWindow::windowEvent(QEvent *event)
+bool QPlatformWindow::windowEvent(QEvent *event)
{
- Q_UNUSED(event);
+ Q_D(QPlatformWindow);
+
+ if (event->type() == QEvent::Timer) {
+ if (static_cast<QTimerEvent *>(event)->timerId() == d->updateTimer.timerId()) {
+ d->updateTimer.stop();
+ deliverUpdateRequest();
+ return true;
+ }
+ }
+
+ return false;
}
/*!
@@ -710,7 +736,7 @@ QRect QPlatformWindow::initialGeometry(const QWindow *w,
QPlatformWindow subclasses can re-implement this function to
provide display refresh synchronized updates. The event
- should be delivered using QWindowPrivate::deliverUpdateRequest()
+ should be delivered using QPlatformWindow::deliverUpdateRequest()
to not get out of sync with the the internal state of QWindow.
The default implementation posts an UpdateRequest event to the
@@ -720,18 +746,44 @@ QRect QPlatformWindow::initialGeometry(const QWindow *w,
*/
void QPlatformWindow::requestUpdate()
{
- static int timeout = -1;
- if (timeout == -1) {
+ Q_D(QPlatformWindow);
+
+ static int updateInterval = []() {
bool ok = false;
- timeout = qEnvironmentVariableIntValue("QT_QPA_UPDATE_IDLE_TIME", &ok);
- if (!ok)
- timeout = 5;
- }
+ int customUpdateInterval = qEnvironmentVariableIntValue("QT_QPA_UPDATE_IDLE_TIME", &ok);
+ return ok ? customUpdateInterval : 5;
+ }();
+
+ Q_ASSERT(!d->updateTimer.isActive());
+ d->updateTimer.start(updateInterval, Qt::PreciseTimer, window());
+}
+
+/*!
+ Returns true if the window has a pending update request.
+
+ \sa requestUpdate(), deliverUpdateRequest()
+*/
+bool QPlatformWindow::hasPendingUpdateRequest() const
+{
+ return qt_window_private(window())->updateRequestPending;
+}
+
+/*!
+ Delivers an QEvent::UpdateRequest event to the window.
+
+ QPlatformWindow subclasses can re-implement this function to
+ provide e.g. logging or tracing of the delivery, but should
+ always call the base class function.
+*/
+void QPlatformWindow::deliverUpdateRequest()
+{
+ Q_ASSERT(hasPendingUpdateRequest());
QWindow *w = window();
- QWindowPrivate *wp = (QWindowPrivate *) QObjectPrivate::get(w);
- Q_ASSERT(wp->updateTimer == 0);
- wp->updateTimer = w->startTimer(timeout, Qt::PreciseTimer);
+ QWindowPrivate *wp = qt_window_private(w);
+ wp->updateRequestPending = false;
+ QEvent request(QEvent::UpdateRequest);
+ QCoreApplication::sendEvent(w, &request);
}
/*!
diff --git a/src/gui/kernel/qplatformwindow.h b/src/gui/kernel/qplatformwindow.h
index 84dff681d5..1590a10554 100644
--- a/src/gui/kernel/qplatformwindow.h
+++ b/src/gui/kernel/qplatformwindow.h
@@ -100,6 +100,7 @@ public:
virtual void setWindowTitle(const QString &title);
virtual void setWindowFilePath(const QString &title);
virtual void setWindowIcon(const QIcon &icon);
+ virtual bool close();
virtual void raise();
virtual void lower();
@@ -126,7 +127,7 @@ public:
virtual bool setWindowModified(bool modified);
- virtual void windowEvent(QEvent *event);
+ virtual bool windowEvent(QEvent *event);
virtual bool startSystemResize(const QPoint &pos, Qt::Corner corner);
virtual bool startSystemMove(const QPoint &pos);
@@ -143,6 +144,8 @@ public:
const QRect &initialGeometry, int defaultWidth, int defaultHeight);
virtual void requestUpdate();
+ bool hasPendingUpdateRequest() const;
+ virtual void deliverUpdateRequest();
// Window property accessors. Platform plugins should use these
// instead of accessing QWindow directly.
diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h
index 62ecd61d9e..00dae9334c 100644
--- a/src/gui/kernel/qplatformwindow_p.h
+++ b/src/gui/kernel/qplatformwindow_p.h
@@ -52,6 +52,7 @@
//
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/qbasictimer.h>
#include <QtCore/qrect.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +61,7 @@ class QPlatformWindowPrivate
{
public:
QRect rect;
+ QBasicTimer updateTimer;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp
index fa1eb6f6bf..3bb42c1c0b 100644
--- a/src/gui/kernel/qshortcutmap.cpp
+++ b/src/gui/kernel/qshortcutmap.cpp
@@ -540,6 +540,19 @@ void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl,
{
Q_D(QShortcutMap);
QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
+#if defined(DEBUG_QSHORTCUTMAP)
+ {
+ QDebug debug = qDebug().nospace();
+ debug << __FUNCTION__ << '(' << e << ", ignoredModifiers="
+ << Qt::KeyboardModifiers(ignoredModifiers) << "), possibleKeys=(";
+ for (int i = 0, size = possibleKeys.size(); i < size; ++i) {
+ if (i)
+ debug << ", ";
+ debug << QKeySequence(possibleKeys.at(i));
+ }
+ debug << ')';
+ }
+#endif // DEBUG_QSHORTCUTMAP
int pkTotal = possibleKeys.count();
if (!pkTotal)
return;
diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp
index c678a7479d..2611dc8580 100644
--- a/src/gui/kernel/qsimpledrag.cpp
+++ b/src/gui/kernel/qsimpledrag.cpp
@@ -93,11 +93,7 @@ static QWindow* topLevelAt(const QPoint &pos)
(within the Qt application or outside) accepts the drag and sets the state accordingly.
*/
-QBasicDrag::QBasicDrag() :
- m_current_window(nullptr), m_restoreCursor(false), m_eventLoop(nullptr),
- m_executed_drop_action(Qt::IgnoreAction), m_can_drop(false),
- m_drag(nullptr), m_drag_icon_window(nullptr), m_useCompositing(true),
- m_screen(nullptr)
+QBasicDrag::QBasicDrag()
{
}
@@ -157,7 +153,8 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
case QEvent::MouseMove:
{
QPoint nativePosition = getNativeMousePos(e, m_drag_icon_window);
- move(nativePosition);
+ auto mouseMove = static_cast<QMouseEvent *>(e);
+ move(nativePosition, mouseMove->buttons(), mouseMove->modifiers());
return true; // Eat all mouse move events
}
case QEvent::MouseButtonRelease:
@@ -165,7 +162,8 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
disableEventFilter();
if (canDrop()) {
QPoint nativePosition = getNativeMousePos(e, m_drag_icon_window);
- drop(nativePosition);
+ auto mouseRelease = static_cast<QMouseEvent *>(e);
+ drop(nativePosition, mouseRelease->buttons(), mouseRelease->modifiers());
} else {
cancel();
}
@@ -178,9 +176,9 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
// make the event relative to the window where the drag started. (QTBUG-66103)
const QMouseEvent *release = static_cast<QMouseEvent *>(e);
const QWindow *releaseWindow = topLevelAt(release->globalPos());
- qCDebug(lcDnd) << "mouse released over" << releaseWindow << "after drag from" << m_current_window << "globalPos" << release->globalPos();
+ qCDebug(lcDnd) << "mouse released over" << releaseWindow << "after drag from" << m_sourceWindow << "globalPos" << release->globalPos();
if (!releaseWindow)
- releaseWindow = m_current_window;
+ releaseWindow = m_sourceWindow;
QPoint releaseWindowPos = (releaseWindow ? releaseWindow->mapFromGlobal(release->globalPos()) : release->globalPos());
QMouseEvent *newRelease = new QMouseEvent(release->type(),
releaseWindowPos, releaseWindowPos, release->screenPos(),
@@ -203,18 +201,15 @@ Qt::DropAction QBasicDrag::drag(QDrag *o)
m_drag = o;
m_executed_drop_action = Qt::IgnoreAction;
m_can_drop = false;
- m_restoreCursor = true;
-#ifndef QT_NO_CURSOR
- qApp->setOverrideCursor(Qt::DragCopyCursor);
- updateCursor(m_executed_drop_action);
-#endif
+
startDrag();
m_eventLoop = new QEventLoop;
m_eventLoop->exec();
delete m_eventLoop;
- m_eventLoop = 0;
- m_drag = 0;
+ m_eventLoop = nullptr;
+ m_drag = nullptr;
endDrag();
+
return m_executed_drop_action;
}
@@ -226,16 +221,6 @@ void QBasicDrag::cancelDrag()
}
}
-void QBasicDrag::restoreCursor()
-{
- if (m_restoreCursor) {
-#ifndef QT_NO_CURSOR
- QGuiApplication::restoreOverrideCursor();
-#endif
- m_restoreCursor = false;
- }
-}
-
void QBasicDrag::startDrag()
{
QPoint pos;
@@ -286,7 +271,7 @@ void QBasicDrag::moveShapedPixmapWindow(const QPoint &globalPos)
m_drag_icon_window->updateGeometry(globalPos);
}
-void QBasicDrag::drop(const QPoint &)
+void QBasicDrag::drop(const QPoint &, Qt::MouseButtons, Qt::KeyboardModifiers)
{
disableEventFilter();
restoreCursor();
@@ -317,25 +302,34 @@ void QBasicDrag::updateCursor(Qt::DropAction action)
}
}
- QCursor *cursor = QGuiApplication::overrideCursor();
QPixmap pixmap = m_drag->dragCursor(action);
- if (!cursor) {
- QGuiApplication::changeOverrideCursor((pixmap.isNull()) ? QCursor(cursorShape) : QCursor(pixmap));
+
+ if (!m_dndHasSetOverrideCursor) {
+ QCursor newCursor = !pixmap.isNull() ? QCursor(pixmap) : QCursor(cursorShape);
+ QGuiApplication::setOverrideCursor(newCursor);
+ m_dndHasSetOverrideCursor = true;
} else {
+ QCursor *cursor = QGuiApplication::overrideCursor();
if (!pixmap.isNull()) {
- if ((cursor->pixmap().cacheKey() != pixmap.cacheKey())) {
+ if (cursor->pixmap().cacheKey() != pixmap.cacheKey())
QGuiApplication::changeOverrideCursor(QCursor(pixmap));
- }
- } else {
- if (cursorShape != cursor->shape()) {
- QGuiApplication::changeOverrideCursor(QCursor(cursorShape));
- }
+ } else if (cursorShape != cursor->shape()) {
+ QGuiApplication::changeOverrideCursor(QCursor(cursorShape));
}
}
#endif
updateAction(action);
}
+void QBasicDrag::restoreCursor()
+{
+#ifndef QT_NO_CURSOR
+ if (m_dndHasSetOverrideCursor) {
+ QGuiApplication::restoreOverrideCursor();
+ m_dndHasSetOverrideCursor = false;
+ }
+#endif
+}
static inline QPoint fromNativeGlobalPixels(const QPoint &point)
{
@@ -373,57 +367,83 @@ QSimpleDrag::QSimpleDrag()
void QSimpleDrag::startDrag()
{
+ setExecutedDropAction(Qt::IgnoreAction);
+
QBasicDrag::startDrag();
- m_current_window = topLevelAt(QCursor::pos());
- if (m_current_window) {
- QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_current_window, drag()->mimeData(), QHighDpi::toNativePixels(QCursor::pos(), m_current_window), drag()->supportedActions());
- setCanDrop(response.isAccepted());
- updateCursor(response.acceptedAction());
+ // Here we can be fairly sure that QGuiApplication::mouseButtons/keyboardModifiers() will
+ // contain sensible values as startDrag() normally is called from mouse event handlers
+ // by QDrag::exec(). A better API would be if we could pass something like "input device
+ // pointer" to QDrag::exec(). My guess is that something like that might be required for
+ // QTBUG-52430.
+ m_sourceWindow = topLevelAt(QCursor::pos());
+ m_windowUnderCursor = m_sourceWindow;
+ if (m_sourceWindow) {
+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), m_sourceWindow);
+ move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
} else {
setCanDrop(false);
updateCursor(Qt::IgnoreAction);
}
- setExecutedDropAction(Qt::IgnoreAction);
- qCDebug(lcDnd) << "drag began from" << m_current_window<< "cursor pos" << QCursor::pos() << "can drop?" << canDrop();
+
+ qCDebug(lcDnd) << "drag began from" << m_sourceWindow << "cursor pos" << QCursor::pos() << "can drop?" << canDrop();
+}
+
+static void sendDragLeave(QWindow *window)
+{
+ QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
}
void QSimpleDrag::cancel()
{
QBasicDrag::cancel();
- if (drag() && m_current_window) {
- QWindowSystemInterface::handleDrag(m_current_window, 0, QPoint(), Qt::IgnoreAction);
- m_current_window = 0;
+ if (drag() && m_sourceWindow) {
+ sendDragLeave(m_sourceWindow);
+ m_sourceWindow = nullptr;
}
}
-void QSimpleDrag::move(const QPoint &nativeGlobalPos)
+void QSimpleDrag::move(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers)
{
QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos);
moveShapedPixmapWindow(globalPos);
QWindow *window = topLevelAt(globalPos);
- if (!window)
- return;
+
+ if (!window || window != m_windowUnderCursor) {
+ if (m_windowUnderCursor)
+ sendDragLeave(m_windowUnderCursor);
+ m_windowUnderCursor = window;
+ if (!window) {
+ // QSimpleDrag supports only in-process dnd, we can't drop anywhere else.
+ setCanDrop(false);
+ updateCursor(Qt::IgnoreAction);
+ return;
+ }
+ }
const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft();
- const QPlatformDragQtResponse qt_response =
- QWindowSystemInterface::handleDrag(window, drag()->mimeData(), pos, drag()->supportedActions());
+ const QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
+ window, drag()->mimeData(), pos, drag()->supportedActions(),
+ buttons, modifiers);
- updateCursor(qt_response.acceptedAction());
setCanDrop(qt_response.isAccepted());
+ updateCursor(qt_response.acceptedAction());
}
-void QSimpleDrag::drop(const QPoint &nativeGlobalPos)
+void QSimpleDrag::drop(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers)
{
QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos);
- QBasicDrag::drop(nativeGlobalPos);
+ QBasicDrag::drop(nativeGlobalPos, buttons, modifiers);
QWindow *window = topLevelAt(globalPos);
if (!window)
return;
const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft();
- const QPlatformDropQtResponse response =
- QWindowSystemInterface::handleDrop(window, drag()->mimeData(),pos, drag()->supportedActions());
+ const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(
+ window, drag()->mimeData(), pos, drag()->supportedActions(),
+ buttons, modifiers);
if (response.isAccepted()) {
setExecutedDropAction(response.acceptedAction());
} else {
diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h
index d980a3c49d..f9e8a83a39 100644
--- a/src/gui/kernel/qsimpledrag_p.h
+++ b/src/gui/kernel/qsimpledrag_p.h
@@ -55,13 +55,14 @@
#include <qpa/qplatformdrag.h>
#include <QtCore/QObject>
+#include <QtCore/QPointer>
+#include <QtGui/QWindow>
QT_REQUIRE_CONFIG(draganddrop);
QT_BEGIN_NAMESPACE
class QMouseEvent;
-class QWindow;
class QEventLoop;
class QDropData;
class QShapedPixmapWindow;
@@ -82,8 +83,8 @@ protected:
virtual void startDrag();
virtual void cancel();
- virtual void move(const QPoint &globalPos) = 0;
- virtual void drop(const QPoint &globalPos) = 0;
+ virtual void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) = 0;
+ virtual void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) = 0;
virtual void endDrag();
@@ -106,7 +107,8 @@ protected:
QDrag *drag() const { return m_drag; }
protected:
- QWindow *m_current_window;
+ QWindow *m_sourceWindow = nullptr;
+ QPointer<QWindow> m_windowUnderCursor = nullptr;
private:
void enableEventFilter();
@@ -114,14 +116,16 @@ private:
void restoreCursor();
void exitDndEventLoop();
- bool m_restoreCursor;
- QEventLoop *m_eventLoop;
- Qt::DropAction m_executed_drop_action;
- bool m_can_drop;
- QDrag *m_drag;
- QShapedPixmapWindow *m_drag_icon_window;
- bool m_useCompositing;
- QScreen *m_screen;
+#ifndef QT_NO_CURSOR
+ bool m_dndHasSetOverrideCursor = false;
+#endif
+ QEventLoop *m_eventLoop = nullptr;
+ Qt::DropAction m_executed_drop_action = Qt::IgnoreAction;
+ bool m_can_drop = false;
+ QDrag *m_drag = nullptr;
+ QShapedPixmapWindow *m_drag_icon_window = nullptr;
+ bool m_useCompositing = true;
+ QScreen *m_screen = nullptr;
};
class Q_GUI_EXPORT QSimpleDrag : public QBasicDrag
@@ -132,8 +136,8 @@ public:
protected:
virtual void startDrag() override;
virtual void cancel() override;
- virtual void move(const QPoint &globalPos) override;
- virtual void drop(const QPoint &globalPos) override;
+ virtual void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
+ virtual void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qsurface.cpp b/src/gui/kernel/qsurface.cpp
index 63651ee822..415e64b39c 100644
--- a/src/gui/kernel/qsurface.cpp
+++ b/src/gui/kernel/qsurface.cpp
@@ -80,6 +80,10 @@ QT_BEGIN_NAMESPACE
in conjunction with OpenVG contexts.
\value VulkanSurface The surface is a Vulkan compatible surface and can be used
in conjunction with the Vulkan graphics API.
+ \value MetalSurface The surface is a Metal compatible surface and can be used
+ in conjunction with Apple's Metal graphics API. This surface type is supported
+ on macOS only.
+
*/
diff --git a/src/gui/kernel/qsurface.h b/src/gui/kernel/qsurface.h
index 7e09449d12..521593ea5c 100644
--- a/src/gui/kernel/qsurface.h
+++ b/src/gui/kernel/qsurface.h
@@ -55,19 +55,23 @@ class QSurfacePrivate;
class Q_GUI_EXPORT QSurface
{
+ Q_GADGET
public:
enum SurfaceClass {
Window,
Offscreen
};
+ Q_ENUM(SurfaceClass)
enum SurfaceType {
RasterSurface,
OpenGLSurface,
RasterGLSurface,
OpenVGSurface,
- VulkanSurface
+ VulkanSurface,
+ MetalSurface
};
+ Q_ENUM(SurfaceType)
virtual ~QSurface();
diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp
new file mode 100644
index 0000000000..56e0eb52b3
--- /dev/null
+++ b/src/gui/kernel/qtestsupport_gui.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtTest module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtestsupport_gui.h"
+#include "qwindow.h"
+
+#include <QtCore/qtestsupport_core.h>
+
+QT_BEGIN_NAMESPACE
+
+/*! \fn bool qWaitForWindowActive(QWindow *window, int timeout)
+ \relates QTest
+ \since 5.0
+
+ Waits for \a timeout milliseconds or until the \a window is active.
+
+ Returns \c true if \c window is active within \a timeout milliseconds, otherwise returns \c false.
+
+ \sa QTest::qWaitForWindowExposed(), QWindow::isActive()
+*/
+Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
+{
+ return QTest::qWaitFor([&]() { return window->isActive(); }, timeout);
+}
+
+/*! \fn bool qWaitForWindowExposed(QWindow *window, int timeout)
+ \relates QTest
+ \since 5.0
+
+ Waits for \a timeout milliseconds or until the \a window is exposed.
+ Returns \c true if \c window is exposed within \a timeout milliseconds, otherwise returns \c false.
+
+ This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen some
+ time after being asked to show itself on the screen.
+
+ Note that a window that is mapped to screen may still not be considered exposed if the window client
+ area is completely covered by other windows, or if the window is otherwise not visible. This function
+ will then time out when waiting for such a window.
+
+ \sa QTest::qWaitForWindowActive(), QWindow::isExposed()
+*/
+Q_GUI_EXPORT bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
+{
+ return QTest::qWaitFor([&]() { return window->isExposed(); }, timeout);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qtestsupport_gui.h b/src/gui/kernel/qtestsupport_gui.h
new file mode 100644
index 0000000000..82a81e9214
--- /dev/null
+++ b/src/gui/kernel/qtestsupport_gui.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtTest module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTESTSUPPORT_GUI_H
+#define QTESTSUPPORT_GUI_H
+
+#include <QtGui/qtguiglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+namespace QTest {
+Q_GUI_EXPORT Q_REQUIRED_RESULT bool qWaitForWindowActive(QWindow *window, int timeout = 5000);
+Q_GUI_EXPORT Q_REQUIRED_RESULT bool qWaitForWindowExposed(QWindow *window, int timeout = 5000);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index 55fe75d220..bc2d6e6a17 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -115,9 +115,10 @@ QT_BEGIN_NAMESPACE
physical area of the screen. On windowing systems that have exposure
notifications, the isExposed() accessor describes whether the window should
be treated as directly visible on screen. The exposeEvent() function is
- called whenever the windows exposure in the windowing system changes. On
- windowing systems that do not make this information visible to the
- application, isExposed() will simply return the same value as isVisible().
+ called whenever an area of the window is invalidated, for example due to the
+ exposure in the windowing system changing. On windowing systems that do not
+ make this information visible to the application, isExposed() will simply
+ return the same value as isVisible().
QWindow::Visibility queried through visibility() is a convenience API
combining the functions of visible() and windowStates().
@@ -545,6 +546,9 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e);
+
+ if (updateRequestPending)
+ platformWindow->requestUpdate();
}
void QWindowPrivate::clearFocusObject()
@@ -2150,15 +2154,13 @@ bool QWindow::close()
if (!d->platformWindow)
return true;
- bool accepted = false;
- QWindowSystemInterface::handleCloseEvent(this, &accepted);
- QWindowSystemInterface::flushWindowSystemEvents();
- return accepted;
+ return d->platformWindow->close();
}
/*!
- The expose event (\a ev) is sent by the window system whenever the window's
- exposure on screen changes.
+ The expose event (\a ev) is sent by the window system whenever an area of
+ the window is invalidated, for example due to the exposure in the windowing
+ system changing.
The application can start rendering into the window with QBackingStore
and QOpenGLContext as soon as it gets an exposeEvent() such that
@@ -2335,18 +2337,6 @@ bool QWindow::event(QEvent *ev)
break;
#endif
- case QEvent::Timer: {
- Q_D(QWindow);
- if (static_cast<QTimerEvent *>(ev)->timerId() == d->updateTimer) {
- killTimer(d->updateTimer);
- d->updateTimer = 0;
- d->deliverUpdateRequest();
- } else {
- QObject::event(ev);
- }
- break;
- }
-
case QEvent::PlatformSurface: {
if ((static_cast<QPlatformSurfaceEvent *>(ev))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
#ifndef QT_NO_OPENGL
@@ -2364,14 +2354,6 @@ bool QWindow::event(QEvent *ev)
return true;
}
-void QWindowPrivate::deliverUpdateRequest()
-{
- Q_Q(QWindow);
- updateRequestPending = false;
- QEvent request(QEvent::UpdateRequest);
- QCoreApplication::sendEvent(q, &request);
-}
-
/*!
Schedules a QEvent::UpdateRequest event to be delivered to this window.
@@ -2626,14 +2608,14 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed()
}
}
-QWindow *QWindowPrivate::topLevelWindow() const
+QWindow *QWindowPrivate::topLevelWindow(QWindow::AncestorMode mode) const
{
Q_Q(const QWindow);
QWindow *window = const_cast<QWindow *>(q);
while (window) {
- QWindow *parent = window->parent(QWindow::IncludeTransients);
+ QWindow *parent = window->parent(mode);
if (!parent)
break;
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 7ef73eb410..bf5e645114 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -97,7 +97,6 @@ public:
, modality(Qt::NonModal)
, blockedByModalWindow(false)
, updateRequestPending(false)
- , updateTimer(0)
, transientParent(0)
, topLevelScreen(0)
#ifndef QT_NO_CURSOR
@@ -124,11 +123,9 @@ public:
bool applyCursor();
#endif
- void deliverUpdateRequest();
-
QPoint globalPosition() const;
- QWindow *topLevelWindow() const;
+ QWindow *topLevelWindow(QWindow::AncestorMode mode = QWindow::IncludeTransients) const;
#if QT_CONFIG(opengl)
virtual QOpenGLContext *shareContext() const;
@@ -194,7 +191,6 @@ public:
bool blockedByModalWindow;
bool updateRequestPending;
- int updateTimer;
QPointer<QWindow> transientParent;
QPointer<QScreen> topLevelScreen;
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index 6edcdfc255..67e1283462 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE
QElapsedTimer QWindowSystemInterfacePrivate::eventTime;
bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false;
+bool QWindowSystemInterfacePrivate::platformFiltersEvents = false;
bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true;
QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed;
QMutex QWindowSystemInterfacePrivate::flushEventMutex;
@@ -339,12 +340,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleExposeEvent, QWindow *window, const QReg
QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
}
-void QWindowSystemInterface::handleCloseEvent(QWindow *window, bool *accepted)
+QT_DEFINE_QPA_EVENT_HANDLER(void, handleCloseEvent, QWindow *window, bool *accepted)
{
if (window) {
QWindowSystemInterfacePrivate::CloseEvent *e =
new QWindowSystemInterfacePrivate::CloseEvent(window, accepted);
- QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
+ QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
}
}
@@ -795,14 +796,44 @@ void QWindowSystemInterface::handleThemeChange(QWindow *window)
}
#if QT_CONFIG(draganddrop)
-QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
+#if QT_DEPRECATED_SINCE(5, 11)
+QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions)
+{
+ return QGuiApplicationPrivate::processDrag(window, dropData, p, supportedActions,
+ QGuiApplication::mouseButtons(),
+ QGuiApplication::keyboardModifiers());
+}
+
+QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions)
+{
+ return QGuiApplicationPrivate::processDrop(window, dropData, p, supportedActions,
+ QGuiApplication::mouseButtons(),
+ QGuiApplication::keyboardModifiers());
+}
+#endif // QT_DEPRECATED_SINCE(5, 11)
+/*!
+ Drag and drop events are sent immediately.
+
+ ### FIXME? Perhaps DnD API should add some convenience APIs that are more
+ intuitive for the possible DND operations. Here passing nullptr as drop data is used to
+ indicate that drop was canceled and QDragLeaveEvent should be sent as a result.
+*/
+QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
{
- return QGuiApplicationPrivate::processDrag(window, dropData, QHighDpi::fromNativeLocalPosition(p, window) ,supportedActions);
+ auto pos = QHighDpi::fromNativeLocalPosition(p, window);
+ return QGuiApplicationPrivate::processDrag(window, dropData, pos, supportedActions, buttons, modifiers);
}
-QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
+QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
{
- return QGuiApplicationPrivate::processDrop(window, dropData, QHighDpi::fromNativeLocalPosition(p, window),supportedActions);
+ auto pos = QHighDpi::fromNativeLocalPosition(p, window);
+ return QGuiApplicationPrivate::processDrop(window, dropData, pos, supportedActions, buttons, modifiers);
}
#endif // QT_CONFIG(draganddrop)
@@ -1017,10 +1048,15 @@ bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFla
int nevents = 0;
while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {
- QWindowSystemInterfacePrivate::WindowSystemEvent *event =
- (flags & QEventLoop::ExcludeUserInputEvents) ?
- QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
- QWindowSystemInterfacePrivate::getWindowSystemEvent();
+ QWindowSystemInterfacePrivate::WindowSystemEvent *event = nullptr;
+
+ if (QWindowSystemInterfacePrivate::platformFiltersEvents) {
+ event = QWindowSystemInterfacePrivate::getWindowSystemEvent();
+ } else {
+ event = flags & QEventLoop::ExcludeUserInputEvents ?
+ QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
+ QWindowSystemInterfacePrivate::getWindowSystemEvent();
+ }
if (!event)
break;
@@ -1059,6 +1095,21 @@ bool QWindowSystemInterface::nonUserInputEventsQueued()
return QWindowSystemInterfacePrivate::nonUserInputEventsQueued();
}
+/*!
+ Platforms that implement UserInputEvent filtering at native event level must
+ set this property to \c true. The default is \c false, which means that event
+ filtering logic is handled by QWindowSystemInterface. Doing the filtering in
+ platform plugins is necessary when supporting AbstractEventDispatcher::filterNativeEvent(),
+ which should respect flags that were passed to event dispatcher's processEvents()
+ call.
+
+ \since 5.12
+*/
+void QWindowSystemInterface::setPlatformFiltersEvents(bool enable)
+{
+ QWindowSystemInterfacePrivate::platformFiltersEvents = enable;
+}
+
// --------------------- QtTestLib support ---------------------
// The following functions are used by testlib, and need to be synchronous to avoid
diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h
index bba0b05c5a..1dde9130ac 100644
--- a/src/gui/kernel/qwindowsysteminterface.h
+++ b/src/gui/kernel/qwindowsysteminterface.h
@@ -193,6 +193,7 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleExposeEvent(QWindow *window, const QRegion &region);
+ template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleCloseEvent(QWindow *window, bool *accepted = nullptr);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
@@ -215,10 +216,19 @@ public:
static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate = false);
#if QT_CONFIG(draganddrop)
- // Drag and drop. These events are sent immediately.
- static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
- static QPlatformDropQtResponse handleDrop(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
-#endif
+#if QT_DEPRECATED_SINCE(5, 11)
+ QT_DEPRECATED static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions);
+ QT_DEPRECATED static QPlatformDropQtResponse handleDrop(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions);
+#endif // #if QT_DEPRECATED_SINCE(5, 11)
+ static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+ static QPlatformDropQtResponse handleDrop(QWindow *window, const QMimeData *dropData,
+ const QPoint &p, Qt::DropActions supportedActions,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
+#endif // QT_CONFIG(draganddrop)
static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);
@@ -282,6 +292,7 @@ public:
static void deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags);
static int windowSystemEventsQueued();
static bool nonUserInputEventsQueued();
+ static void setPlatformFiltersEvents(bool enable);
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h
index c3fb19d21a..9cb4e191cc 100644
--- a/src/gui/kernel/qwindowsysteminterface_p.h
+++ b/src/gui/kernel/qwindowsysteminterface_p.h
@@ -365,7 +365,7 @@ public:
QUrl url;
};
- class TabletEvent : public InputEvent {
+ class Q_GUI_EXPORT TabletEvent : public InputEvent {
public:
static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
@@ -525,6 +525,7 @@ public:
public:
static QElapsedTimer eventTime;
static bool synchronousWindowSystemEvents;
+ static bool platformFiltersEvents;
static QWaitCondition eventsFlushed;
static QMutex flushEventMutex;
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index fe1b0425a8..899ec12eb3 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -325,15 +325,11 @@ void QQuaternion::normalize()
Rotates \a vector with this quaternion to produce a new vector
in 3D space. The following code:
- \code
- QVector3D result = q.rotatedVector(vector);
- \endcode
+ \snippet code/src_gui_math3d_qquaternion.cpp 0
is equivalent to the following:
- \code
- QVector3D result = (q * QQuaternion(0, vector) * q.conjugated()).vector();
- \endcode
+ \snippet code/src_gui_math3d_qquaternion.cpp 1
*/
QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
{
diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp
index 7c02b5ad5d..c04f8b1cbf 100644
--- a/src/gui/math3d/qvector2d.cpp
+++ b/src/gui/math3d/qvector2d.cpp
@@ -49,6 +49,39 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR2D
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector2D>::value, "QVector2D is supposed to be standard layout");
+Q_STATIC_ASSERT_X(sizeof(QVector2D) == sizeof(float) * 2, "QVector2D is not supposed to have padding at the end");
+
+// QVector2D used to be defined as class QVector2D { float x, y; };,
+// now instead it is defined as classs QVector2D { float v[2]; };.
+// Check that binary compatibility is preserved.
+// ### Qt 6: remove all of these checks.
+
+namespace {
+
+struct QVector2DOld
+{
+ float x, y;
+};
+
+struct QVector2DNew
+{
+ float v[2];
+};
+
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector2DOld>::value, "Binary compatibility break in QVector2D");
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector2DNew>::value, "Binary compatibility break in QVector2D");
+
+Q_STATIC_ASSERT_X(sizeof(QVector2DOld) == sizeof(QVector2DNew), "Binary compatibility break in QVector2D");
+
+// requires a constexpr offsetof
+#if !defined(Q_CC_MSVC) || (_MSC_VER >= 1910)
+Q_STATIC_ASSERT_X(offsetof(QVector2DOld, x) == offsetof(QVector2DNew, v) + sizeof(QVector2DNew::v[0]) * 0, "Binary compatibility break in QVector2D");
+Q_STATIC_ASSERT_X(offsetof(QVector2DOld, y) == offsetof(QVector2DNew, v) + sizeof(QVector2DNew::v[0]) * 1, "Binary compatibility break in QVector2D");
+#endif
+
+} // anonymous namespace
+
/*!
\class QVector2D
\brief The QVector2D class represents a vector or vertex in 2D space.
@@ -105,8 +138,8 @@ QT_BEGIN_NAMESPACE
*/
QVector2D::QVector2D(const QVector3D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
}
#endif
@@ -121,8 +154,8 @@ QVector2D::QVector2D(const QVector3D& vector)
*/
QVector2D::QVector2D(const QVector4D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
}
#endif
@@ -193,8 +226,8 @@ QVector2D::QVector2D(const QVector4D& vector)
float QVector2D::length() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]);
return float(std::sqrt(len));
}
@@ -206,7 +239,7 @@ float QVector2D::length() const
*/
float QVector2D::lengthSquared() const
{
- return xp * xp + yp * yp;
+ return v[0] * v[0] + v[1] * v[1];
}
/*!
@@ -221,13 +254,13 @@ float QVector2D::lengthSquared() const
QVector2D QVector2D::normalized() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]);
if (qFuzzyIsNull(len - 1.0f)) {
return *this;
} else if (!qFuzzyIsNull(len)) {
double sqrtLen = std::sqrt(len);
- return QVector2D(float(double(xp) / sqrtLen), float(double(yp) / sqrtLen));
+ return QVector2D(float(double(v[0]) / sqrtLen), float(double(v[1]) / sqrtLen));
} else {
return QVector2D();
}
@@ -242,15 +275,15 @@ QVector2D QVector2D::normalized() const
void QVector2D::normalize()
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]);
if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
return;
len = std::sqrt(len);
- xp = float(double(xp) / len);
- yp = float(double(yp) / len);
+ v[0] = float(double(v[0]) / len);
+ v[1] = float(double(v[1]) / len);
}
/*!
@@ -344,7 +377,7 @@ float QVector2D::distanceToLine
*/
float QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2)
{
- return v1.xp * v2.xp + v1.yp * v2.yp;
+ return v1.v[0] * v2.v[0] + v1.v[1] * v2.v[1];
}
/*!
@@ -458,7 +491,7 @@ float QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2)
*/
QVector3D QVector2D::toVector3D() const
{
- return QVector3D(xp, yp, 0.0f);
+ return QVector3D(v[0], v[1], 0.0f);
}
#endif
@@ -472,7 +505,7 @@ QVector3D QVector2D::toVector3D() const
*/
QVector4D QVector2D::toVector4D() const
{
- return QVector4D(xp, yp, 0.0f, 0.0f);
+ return QVector4D(v[0], v[1], 0.0f, 0.0f);
}
#endif
diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h
index 2af5132665..88d8bc199e 100644
--- a/src/gui/math3d/qvector2d.h
+++ b/src/gui/math3d/qvector2d.h
@@ -123,7 +123,7 @@ public:
operator QVariant() const;
private:
- float xp, yp;
+ float v[2];
friend class QVector3D;
friend class QVector4D;
@@ -131,76 +131,76 @@ private:
Q_DECLARE_TYPEINFO(QVector2D, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D() : v{0.0f, 0.0f} {}
-Q_DECL_CONSTEXPR inline QVector2D::QVector2D(float xpos, float ypos) : xp(xpos), yp(ypos) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(float xpos, float ypos) : v{xpos, ypos} {}
-Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPoint& point) : v{float(point.x()), float(point.y())} {}
-Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPointF& point) : xp(float(point.x())), yp(float(point.y())) {}
+Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPointF& point) : v{float(point.x()), float(point.y())} {}
inline bool QVector2D::isNull() const
{
- return qIsNull(xp) && qIsNull(yp);
+ return qIsNull(v[0]) && qIsNull(v[1]);
}
-Q_DECL_CONSTEXPR inline float QVector2D::x() const { return xp; }
-Q_DECL_CONSTEXPR inline float QVector2D::y() const { return yp; }
+Q_DECL_CONSTEXPR inline float QVector2D::x() const { return v[0]; }
+Q_DECL_CONSTEXPR inline float QVector2D::y() const { return v[1]; }
-inline void QVector2D::setX(float aX) { xp = aX; }
-inline void QVector2D::setY(float aY) { yp = aY; }
+inline void QVector2D::setX(float aX) { v[0] = aX; }
+inline void QVector2D::setY(float aY) { v[1] = aY; }
inline float &QVector2D::operator[](int i)
{
Q_ASSERT(uint(i) < 2u);
- return *(&xp + i);
+ return v[i];
}
inline float QVector2D::operator[](int i) const
{
Q_ASSERT(uint(i) < 2u);
- return *(&xp + i);
+ return v[i];
}
inline QVector2D &QVector2D::operator+=(const QVector2D &vector)
{
- xp += vector.xp;
- yp += vector.yp;
+ v[0] += vector.v[0];
+ v[1] += vector.v[1];
return *this;
}
inline QVector2D &QVector2D::operator-=(const QVector2D &vector)
{
- xp -= vector.xp;
- yp -= vector.yp;
+ v[0] -= vector.v[0];
+ v[1] -= vector.v[1];
return *this;
}
inline QVector2D &QVector2D::operator*=(float factor)
{
- xp *= factor;
- yp *= factor;
+ v[0] *= factor;
+ v[1] *= factor;
return *this;
}
inline QVector2D &QVector2D::operator*=(const QVector2D &vector)
{
- xp *= vector.xp;
- yp *= vector.yp;
+ v[0] *= vector.v[0];
+ v[1] *= vector.v[1];
return *this;
}
inline QVector2D &QVector2D::operator/=(float divisor)
{
- xp /= divisor;
- yp /= divisor;
+ v[0] /= divisor;
+ v[1] /= divisor;
return *this;
}
inline QVector2D &QVector2D::operator/=(const QVector2D &vector)
{
- xp /= vector.xp;
- yp /= vector.yp;
+ v[0] /= vector.v[0];
+ v[1] /= vector.v[1];
return *this;
}
@@ -209,68 +209,68 @@ QT_WARNING_DISABLE_CLANG("-Wfloat-equal")
QT_WARNING_DISABLE_GCC("-Wfloat-equal")
Q_DECL_CONSTEXPR inline bool operator==(const QVector2D &v1, const QVector2D &v2)
{
- return v1.xp == v2.xp && v1.yp == v2.yp;
+ return v1.v[0] == v2.v[0] && v1.v[1] == v2.v[1];
}
Q_DECL_CONSTEXPR inline bool operator!=(const QVector2D &v1, const QVector2D &v2)
{
- return v1.xp != v2.xp || v1.yp != v2.yp;
+ return v1.v[0] != v2.v[0] || v1.v[1] != v2.v[1];
}
QT_WARNING_POP
Q_DECL_CONSTEXPR inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2)
{
- return QVector2D(v1.xp + v2.xp, v1.yp + v2.yp);
+ return QVector2D(v1.v[0] + v2.v[0], v1.v[1] + v2.v[1]);
}
Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2)
{
- return QVector2D(v1.xp - v2.xp, v1.yp - v2.yp);
+ return QVector2D(v1.v[0] - v2.v[0], v1.v[1] - v2.v[1]);
}
Q_DECL_CONSTEXPR inline const QVector2D operator*(float factor, const QVector2D &vector)
{
- return QVector2D(vector.xp * factor, vector.yp * factor);
+ return QVector2D(vector.v[0] * factor, vector.v[1] * factor);
}
Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &vector, float factor)
{
- return QVector2D(vector.xp * factor, vector.yp * factor);
+ return QVector2D(vector.v[0] * factor, vector.v[1] * factor);
}
Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2)
{
- return QVector2D(v1.xp * v2.xp, v1.yp * v2.yp);
+ return QVector2D(v1.v[0] * v2.v[0], v1.v[1] * v2.v[1]);
}
Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &vector)
{
- return QVector2D(-vector.xp, -vector.yp);
+ return QVector2D(-vector.v[0], -vector.v[1]);
}
Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, float divisor)
{
- return QVector2D(vector.xp / divisor, vector.yp / divisor);
+ return QVector2D(vector.v[0] / divisor, vector.v[1] / divisor);
}
Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, const QVector2D &divisor)
{
- return QVector2D(vector.xp / divisor.xp, vector.yp / divisor.yp);
+ return QVector2D(vector.v[0] / divisor.v[0], vector.v[1] / divisor.v[1]);
}
Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2)
{
- return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp);
+ return qFuzzyCompare(v1.v[0], v2.v[0]) && qFuzzyCompare(v1.v[1], v2.v[1]);
}
Q_DECL_CONSTEXPR inline QPoint QVector2D::toPoint() const
{
- return QPoint(qRound(xp), qRound(yp));
+ return QPoint(qRound(v[0]), qRound(v[1]));
}
Q_DECL_CONSTEXPR inline QPointF QVector2D::toPointF() const
{
- return QPointF(qreal(xp), qreal(yp));
+ return QPointF(qreal(v[0]), qreal(v[1]));
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp
index 8aaf1b0eaa..12a7902272 100644
--- a/src/gui/math3d/qvector3d.cpp
+++ b/src/gui/math3d/qvector3d.cpp
@@ -51,6 +51,41 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR3D
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector3D>::value, "QVector3D is supposed to be standard layout");
+Q_STATIC_ASSERT_X(sizeof(QVector3D) == sizeof(float) * 3, "QVector3D is not supposed to have padding at the end");
+
+// QVector3D used to be defined as class QVector3D { float x, y, z; };,
+// now instead it is defined as classs QVector3D { float v[3]; };.
+// Check that binary compatibility is preserved.
+// ### Qt 6: remove all of these checks.
+
+namespace {
+
+struct QVector3DOld
+{
+ float x, y, z;
+};
+
+struct QVector3DNew
+{
+ float v[3];
+};
+
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector3DOld>::value, "Binary compatibility break in QVector3D");
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector3DNew>::value, "Binary compatibility break in QVector3D");
+
+Q_STATIC_ASSERT_X(sizeof(QVector3DOld) == sizeof(QVector3DNew), "Binary compatibility break in QVector3D");
+
+// requires a constexpr offsetof
+#if !defined(Q_CC_MSVC) || (_MSC_VER >= 1910)
+Q_STATIC_ASSERT_X(offsetof(QVector3DOld, x) == offsetof(QVector3DNew, v) + sizeof(QVector3DNew::v[0]) * 0, "Binary compatibility break in QVector3D");
+Q_STATIC_ASSERT_X(offsetof(QVector3DOld, y) == offsetof(QVector3DNew, v) + sizeof(QVector3DNew::v[0]) * 1, "Binary compatibility break in QVector3D");
+Q_STATIC_ASSERT_X(offsetof(QVector3DOld, z) == offsetof(QVector3DNew, v) + sizeof(QVector3DNew::v[0]) * 2, "Binary compatibility break in QVector3D");
+#endif
+
+
+} // anonymous namespace
+
/*!
\class QVector3D
\brief The QVector3D class represents a vector or vertex in 3D space.
@@ -112,9 +147,9 @@ QT_BEGIN_NAMESPACE
*/
QVector3D::QVector3D(const QVector2D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = 0.0f;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = 0.0f;
}
/*!
@@ -125,9 +160,9 @@ QVector3D::QVector3D(const QVector2D& vector)
*/
QVector3D::QVector3D(const QVector2D& vector, float zpos)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = zpos;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = zpos;
}
#endif
@@ -142,9 +177,9 @@ QVector3D::QVector3D(const QVector2D& vector, float zpos)
*/
QVector3D::QVector3D(const QVector4D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = vector.zp;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = vector.v[2];
}
#endif
@@ -235,16 +270,16 @@ QVector3D::QVector3D(const QVector4D& vector)
QVector3D QVector3D::normalized() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]);
if (qFuzzyIsNull(len - 1.0f)) {
return *this;
} else if (!qFuzzyIsNull(len)) {
double sqrtLen = std::sqrt(len);
- return QVector3D(float(double(xp) / sqrtLen),
- float(double(yp) / sqrtLen),
- float(double(zp) / sqrtLen));
+ return QVector3D(float(double(v[0]) / sqrtLen),
+ float(double(v[1]) / sqrtLen),
+ float(double(v[2]) / sqrtLen));
} else {
return QVector3D();
}
@@ -259,17 +294,17 @@ QVector3D QVector3D::normalized() const
void QVector3D::normalize()
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]);
if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
return;
len = std::sqrt(len);
- xp = float(double(xp) / len);
- yp = float(double(yp) / len);
- zp = float(double(zp) / len);
+ v[0] = float(double(v[0]) / len);
+ v[1] = float(double(v[1]) / len);
+ v[2] = float(double(v[2]) / len);
}
/*!
@@ -336,7 +371,7 @@ void QVector3D::normalize()
*/
float QVector3D::dotProduct(const QVector3D& v1, const QVector3D& v2)
{
- return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp;
+ return v1.v[0] * v2.v[0] + v1.v[1] * v2.v[1] + v1.v[2] * v2.v[2];
}
/*!
@@ -347,9 +382,9 @@ float QVector3D::dotProduct(const QVector3D& v1, const QVector3D& v2)
*/
QVector3D QVector3D::crossProduct(const QVector3D& v1, const QVector3D& v2)
{
- return QVector3D(v1.yp * v2.zp - v1.zp * v2.yp,
- v1.zp * v2.xp - v1.xp * v2.zp,
- v1.xp * v2.yp - v1.yp * v2.xp);
+ return QVector3D(v1.v[1] * v2.v[2] - v1.v[2] * v2.v[1],
+ v1.v[2] * v2.v[0] - v1.v[0] * v2.v[2],
+ v1.v[0] * v2.v[1] - v1.v[1] * v2.v[0]);
}
/*!
@@ -629,7 +664,7 @@ float QVector3D::distanceToLine
*/
QVector2D QVector3D::toVector2D() const
{
- return QVector2D(xp, yp);
+ return QVector2D(v[0], v[1]);
}
#endif
@@ -643,7 +678,7 @@ QVector2D QVector3D::toVector2D() const
*/
QVector4D QVector3D::toVector4D() const
{
- return QVector4D(xp, yp, zp, 0.0f);
+ return QVector4D(v[0], v[1], v[2], 0.0f);
}
#endif
@@ -682,9 +717,9 @@ QVector3D::operator QVariant() const
float QVector3D::length() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]);
return float(std::sqrt(len));
}
@@ -696,7 +731,7 @@ float QVector3D::length() const
*/
float QVector3D::lengthSquared() const
{
- return xp * xp + yp * yp + zp * zp;
+ return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h
index f3e8c976b7..1b49f3e7b9 100644
--- a/src/gui/math3d/qvector3d.h
+++ b/src/gui/math3d/qvector3d.h
@@ -59,7 +59,7 @@ class Q_GUI_EXPORT QVector3D
public:
Q_DECL_CONSTEXPR QVector3D();
explicit QVector3D(Qt::Initialization) {}
- Q_DECL_CONSTEXPR QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {}
+ Q_DECL_CONSTEXPR QVector3D(float xpos, float ypos, float zpos) : v{xpos, ypos, zpos} {}
Q_DECL_CONSTEXPR explicit QVector3D(const QPoint& point);
Q_DECL_CONSTEXPR explicit QVector3D(const QPointF& point);
@@ -138,7 +138,7 @@ public:
operator QVariant() const;
private:
- float xp, yp, zp;
+ float v[3];
friend class QVector2D;
friend class QVector4D;
@@ -150,82 +150,82 @@ private:
Q_DECLARE_TYPEINFO(QVector3D, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D() : v{0.0f, 0.0f, 0.0f} {}
-Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPoint& point) : v{float(point.x()), float(point.y()), float(0.0f)} {}
-Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPointF& point) : xp(float(point.x())), yp(float(point.y())), zp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPointF& point) : v{float(point.x()), float(point.y()), 0.0f} {}
inline bool QVector3D::isNull() const
{
- return qIsNull(xp) && qIsNull(yp) && qIsNull(zp);
+ return qIsNull(v[0]) && qIsNull(v[1]) && qIsNull(v[2]);
}
-Q_DECL_CONSTEXPR inline float QVector3D::x() const { return xp; }
-Q_DECL_CONSTEXPR inline float QVector3D::y() const { return yp; }
-Q_DECL_CONSTEXPR inline float QVector3D::z() const { return zp; }
+Q_DECL_CONSTEXPR inline float QVector3D::x() const { return v[0]; }
+Q_DECL_CONSTEXPR inline float QVector3D::y() const { return v[1]; }
+Q_DECL_CONSTEXPR inline float QVector3D::z() const { return v[2]; }
-inline void QVector3D::setX(float aX) { xp = aX; }
-inline void QVector3D::setY(float aY) { yp = aY; }
-inline void QVector3D::setZ(float aZ) { zp = aZ; }
+inline void QVector3D::setX(float aX) { v[0] = aX; }
+inline void QVector3D::setY(float aY) { v[1] = aY; }
+inline void QVector3D::setZ(float aZ) { v[2] = aZ; }
inline float &QVector3D::operator[](int i)
{
Q_ASSERT(uint(i) < 3u);
- return *(&xp + i);
+ return v[i];
}
inline float QVector3D::operator[](int i) const
{
Q_ASSERT(uint(i) < 3u);
- return *(&xp + i);
+ return v[i];
}
inline QVector3D &QVector3D::operator+=(const QVector3D &vector)
{
- xp += vector.xp;
- yp += vector.yp;
- zp += vector.zp;
+ v[0] += vector.v[0];
+ v[1] += vector.v[1];
+ v[2] += vector.v[2];
return *this;
}
inline QVector3D &QVector3D::operator-=(const QVector3D &vector)
{
- xp -= vector.xp;
- yp -= vector.yp;
- zp -= vector.zp;
+ v[0] -= vector.v[0];
+ v[1] -= vector.v[1];
+ v[2] -= vector.v[2];
return *this;
}
inline QVector3D &QVector3D::operator*=(float factor)
{
- xp *= factor;
- yp *= factor;
- zp *= factor;
+ v[0] *= factor;
+ v[1] *= factor;
+ v[2] *= factor;
return *this;
}
inline QVector3D &QVector3D::operator*=(const QVector3D& vector)
{
- xp *= vector.xp;
- yp *= vector.yp;
- zp *= vector.zp;
+ v[0] *= vector.v[0];
+ v[1] *= vector.v[1];
+ v[2] *= vector.v[2];
return *this;
}
inline QVector3D &QVector3D::operator/=(float divisor)
{
- xp /= divisor;
- yp /= divisor;
- zp /= divisor;
+ v[0] /= divisor;
+ v[1] /= divisor;
+ v[2] /= divisor;
return *this;
}
inline QVector3D &QVector3D::operator/=(const QVector3D &vector)
{
- xp /= vector.xp;
- yp /= vector.yp;
- zp /= vector.zp;
+ v[0] /= vector.v[0];
+ v[1] /= vector.v[1];
+ v[2] /= vector.v[2];
return *this;
}
@@ -234,70 +234,70 @@ QT_WARNING_DISABLE_CLANG("-Wfloat-equal")
QT_WARNING_DISABLE_GCC("-Wfloat-equal")
Q_DECL_CONSTEXPR inline bool operator==(const QVector3D &v1, const QVector3D &v2)
{
- return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp;
+ return v1.v[0] == v2.v[0] && v1.v[1] == v2.v[1] && v1.v[2] == v2.v[2];
}
Q_DECL_CONSTEXPR inline bool operator!=(const QVector3D &v1, const QVector3D &v2)
{
- return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp;
+ return v1.v[0] != v2.v[0] || v1.v[1] != v2.v[1] || v1.v[2] != v2.v[2];
}
QT_WARNING_POP
Q_DECL_CONSTEXPR inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2)
{
- return QVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp);
+ return QVector3D(v1.v[0] + v2.v[0], v1.v[1] + v2.v[1], v1.v[2] + v2.v[2]);
}
Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2)
{
- return QVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp);
+ return QVector3D(v1.v[0] - v2.v[0], v1.v[1] - v2.v[1], v1.v[2] - v2.v[2]);
}
Q_DECL_CONSTEXPR inline const QVector3D operator*(float factor, const QVector3D &vector)
{
- return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor);
+ return QVector3D(vector.v[0] * factor, vector.v[1] * factor, vector.v[2] * factor);
}
Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &vector, float factor)
{
- return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor);
+ return QVector3D(vector.v[0] * factor, vector.v[1] * factor, vector.v[2] * factor);
}
Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2)
{
- return QVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp);
+ return QVector3D(v1.v[0] * v2.v[0], v1.v[1] * v2.v[1], v1.v[2] * v2.v[2]);
}
Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &vector)
{
- return QVector3D(-vector.xp, -vector.yp, -vector.zp);
+ return QVector3D(-vector.v[0], -vector.v[1], -vector.v[2]);
}
Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, float divisor)
{
- return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor);
+ return QVector3D(vector.v[0] / divisor, vector.v[1] / divisor, vector.v[2] / divisor);
}
Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, const QVector3D &divisor)
{
- return QVector3D(vector.xp / divisor.xp, vector.yp / divisor.yp, vector.zp / divisor.zp);
+ return QVector3D(vector.v[0] / divisor.v[0], vector.v[1] / divisor.v[1], vector.v[2] / divisor.v[2]);
}
Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2)
{
- return qFuzzyCompare(v1.xp, v2.xp) &&
- qFuzzyCompare(v1.yp, v2.yp) &&
- qFuzzyCompare(v1.zp, v2.zp);
+ return qFuzzyCompare(v1.v[0], v2.v[0]) &&
+ qFuzzyCompare(v1.v[1], v2.v[1]) &&
+ qFuzzyCompare(v1.v[2], v2.v[2]);
}
Q_DECL_CONSTEXPR inline QPoint QVector3D::toPoint() const
{
- return QPoint(qRound(xp), qRound(yp));
+ return QPoint(qRound(v[0]), qRound(v[1]));
}
Q_DECL_CONSTEXPR inline QPointF QVector3D::toPointF() const
{
- return QPointF(qreal(xp), qreal(yp));
+ return QPointF(qreal(v[0]), qreal(v[1]));
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp
index 331144ba33..3a68bd6cb7 100644
--- a/src/gui/math3d/qvector4d.cpp
+++ b/src/gui/math3d/qvector4d.cpp
@@ -49,6 +49,42 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR4D
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector4D>::value, "QVector4D is supposed to be standard layout");
+Q_STATIC_ASSERT_X(sizeof(QVector4D) == sizeof(float) * 4, "QVector4D is not supposed to have padding at the end");
+
+// QVector4D used to be defined as class QVector4D { float x, y, z, w; };,
+// now instead it is defined as classs QVector4D { float v[4]; };.
+// Check that binary compatibility is preserved.
+// ### Qt 6: remove all of these checks.
+
+namespace {
+
+struct QVector4DOld
+{
+ float x, y, z, w;
+};
+
+struct QVector4DNew
+{
+ float v[4];
+};
+
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector4DOld>::value, "Binary compatibility break in QVector4D");
+Q_STATIC_ASSERT_X(std::is_standard_layout<QVector4DNew>::value, "Binary compatibility break in QVector4D");
+
+Q_STATIC_ASSERT_X(sizeof(QVector4DOld) == sizeof(QVector4DNew), "Binary compatibility break in QVector4D");
+
+// requires a constexpr offsetof
+#if !defined(Q_CC_MSVC) || (_MSC_VER >= 1910)
+Q_STATIC_ASSERT_X(offsetof(QVector4DOld, x) == offsetof(QVector4DNew, v) + sizeof(QVector4DNew::v[0]) * 0, "Binary compatibility break in QVector4D");
+Q_STATIC_ASSERT_X(offsetof(QVector4DOld, y) == offsetof(QVector4DNew, v) + sizeof(QVector4DNew::v[0]) * 1, "Binary compatibility break in QVector4D");
+Q_STATIC_ASSERT_X(offsetof(QVector4DOld, z) == offsetof(QVector4DNew, v) + sizeof(QVector4DNew::v[0]) * 2, "Binary compatibility break in QVector4D");
+Q_STATIC_ASSERT_X(offsetof(QVector4DOld, w) == offsetof(QVector4DNew, v) + sizeof(QVector4DNew::v[0]) * 3, "Binary compatibility break in QVector4D");
+#endif
+
+
+} // anonymous namespace
+
/*!
\class QVector4D
\brief The QVector4D class represents a vector or vertex in 4D space.
@@ -106,10 +142,10 @@ QT_BEGIN_NAMESPACE
*/
QVector4D::QVector4D(const QVector2D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = 0.0f;
- wp = 0.0f;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = 0.0f;
+ v[3] = 0.0f;
}
/*!
@@ -120,10 +156,10 @@ QVector4D::QVector4D(const QVector2D& vector)
*/
QVector4D::QVector4D(const QVector2D& vector, float zpos, float wpos)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = zpos;
- wp = wpos;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = zpos;
+ v[3] = wpos;
}
#endif
@@ -138,10 +174,10 @@ QVector4D::QVector4D(const QVector2D& vector, float zpos, float wpos)
*/
QVector4D::QVector4D(const QVector3D& vector)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = vector.zp;
- wp = 0.0f;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = vector.v[2];
+ v[3] = 0.0f;
}
/*!
@@ -152,10 +188,10 @@ QVector4D::QVector4D(const QVector3D& vector)
*/
QVector4D::QVector4D(const QVector3D& vector, float wpos)
{
- xp = vector.xp;
- yp = vector.yp;
- zp = vector.zp;
- wp = wpos;
+ v[0] = vector.v[0];
+ v[1] = vector.v[1];
+ v[2] = vector.v[2];
+ v[3] = wpos;
}
#endif
@@ -258,10 +294,10 @@ QVector4D::QVector4D(const QVector3D& vector, float wpos)
float QVector4D::length() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp) +
- double(wp) * double(wp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]) +
+ double(v[3]) * double(v[3]);
return float(std::sqrt(len));
}
@@ -273,7 +309,7 @@ float QVector4D::length() const
*/
float QVector4D::lengthSquared() const
{
- return xp * xp + yp * yp + zp * zp + wp * wp;
+ return v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
}
/*!
@@ -288,18 +324,18 @@ float QVector4D::lengthSquared() const
QVector4D QVector4D::normalized() const
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp) +
- double(wp) * double(wp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]) +
+ double(v[3]) * double(v[3]);
if (qFuzzyIsNull(len - 1.0f)) {
return *this;
} else if (!qFuzzyIsNull(len)) {
double sqrtLen = std::sqrt(len);
- return QVector4D(float(double(xp) / sqrtLen),
- float(double(yp) / sqrtLen),
- float(double(zp) / sqrtLen),
- float(double(wp) / sqrtLen));
+ return QVector4D(float(double(v[0]) / sqrtLen),
+ float(double(v[1]) / sqrtLen),
+ float(double(v[2]) / sqrtLen),
+ float(double(v[3]) / sqrtLen));
} else {
return QVector4D();
}
@@ -314,19 +350,19 @@ QVector4D QVector4D::normalized() const
void QVector4D::normalize()
{
// Need some extra precision if the length is very small.
- double len = double(xp) * double(xp) +
- double(yp) * double(yp) +
- double(zp) * double(zp) +
- double(wp) * double(wp);
+ double len = double(v[0]) * double(v[0]) +
+ double(v[1]) * double(v[1]) +
+ double(v[2]) * double(v[2]) +
+ double(v[3]) * double(v[3]);
if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
return;
len = std::sqrt(len);
- xp = float(double(xp) / len);
- yp = float(double(yp) / len);
- zp = float(double(zp) / len);
- wp = float(double(wp) / len);
+ v[0] = float(double(v[0]) / len);
+ v[1] = float(double(v[1]) / len);
+ v[2] = float(double(v[2]) / len);
+ v[3] = float(double(v[3]) / len);
}
/*!
@@ -387,7 +423,7 @@ void QVector4D::normalize()
*/
float QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2)
{
- return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp + v1.wp * v2.wp;
+ return v1.v[0] * v2.v[0] + v1.v[1] * v2.v[1] + v1.v[2] * v2.v[2] + v1.v[3] * v2.v[3];
}
/*!
@@ -503,7 +539,7 @@ float QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2)
*/
QVector2D QVector4D::toVector2D() const
{
- return QVector2D(xp, yp);
+ return QVector2D(v[0], v[1]);
}
/*!
@@ -515,9 +551,9 @@ QVector2D QVector4D::toVector2D() const
*/
QVector2D QVector4D::toVector2DAffine() const
{
- if (qIsNull(wp))
+ if (qIsNull(v[3]))
return QVector2D();
- return QVector2D(xp / wp, yp / wp);
+ return QVector2D(v[0] / v[3], v[1] / v[3]);
}
#endif
@@ -531,7 +567,7 @@ QVector2D QVector4D::toVector2DAffine() const
*/
QVector3D QVector4D::toVector3D() const
{
- return QVector3D(xp, yp, zp);
+ return QVector3D(v[0], v[1], v[2]);
}
/*!
@@ -542,9 +578,9 @@ QVector3D QVector4D::toVector3D() const
*/
QVector3D QVector4D::toVector3DAffine() const
{
- if (qIsNull(wp))
+ if (qIsNull(v[3]))
return QVector3D();
- return QVector3D(xp / wp, yp / wp, zp / wp);
+ return QVector3D(v[0] / v[3], v[1] / v[3], v[2] / v[3]);
}
#endif
diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h
index 3f14b41e8e..08cb423484 100644
--- a/src/gui/math3d/qvector4d.h
+++ b/src/gui/math3d/qvector4d.h
@@ -128,7 +128,7 @@ public:
operator QVariant() const;
private:
- float xp, yp, zp, wp;
+ float v[4];
friend class QVector2D;
friend class QVector3D;
@@ -140,92 +140,92 @@ private:
Q_DECLARE_TYPEINFO(QVector4D, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D() : v{0.0f, 0.0f, 0.0f, 0.0f} {}
-Q_DECL_CONSTEXPR inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : v{xpos, ypos, zpos, wpos} {}
-Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPoint& point) : v{float(point.x()), float(point.y()), 0.0f, 0.0f} {}
-Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPointF& point) : xp(float(point.x())), yp(float(point.y())), zp(0.0f), wp(0.0f) {}
+Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPointF& point) : v{float(point.x()), float(point.y()), 0.0f, 0.0f} {}
inline bool QVector4D::isNull() const
{
- return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp);
+ return qIsNull(v[0]) && qIsNull(v[1]) && qIsNull(v[2]) && qIsNull(v[3]);
}
-Q_DECL_CONSTEXPR inline float QVector4D::x() const { return xp; }
-Q_DECL_CONSTEXPR inline float QVector4D::y() const { return yp; }
-Q_DECL_CONSTEXPR inline float QVector4D::z() const { return zp; }
-Q_DECL_CONSTEXPR inline float QVector4D::w() const { return wp; }
+Q_DECL_CONSTEXPR inline float QVector4D::x() const { return v[0]; }
+Q_DECL_CONSTEXPR inline float QVector4D::y() const { return v[1]; }
+Q_DECL_CONSTEXPR inline float QVector4D::z() const { return v[2]; }
+Q_DECL_CONSTEXPR inline float QVector4D::w() const { return v[3]; }
-inline void QVector4D::setX(float aX) { xp = aX; }
-inline void QVector4D::setY(float aY) { yp = aY; }
-inline void QVector4D::setZ(float aZ) { zp = aZ; }
-inline void QVector4D::setW(float aW) { wp = aW; }
+inline void QVector4D::setX(float aX) { v[0] = aX; }
+inline void QVector4D::setY(float aY) { v[1] = aY; }
+inline void QVector4D::setZ(float aZ) { v[2] = aZ; }
+inline void QVector4D::setW(float aW) { v[3] = aW; }
inline float &QVector4D::operator[](int i)
{
Q_ASSERT(uint(i) < 4u);
- return *(&xp + i);
+ return v[i];
}
inline float QVector4D::operator[](int i) const
{
Q_ASSERT(uint(i) < 4u);
- return *(&xp + i);
+ return v[i];
}
inline QVector4D &QVector4D::operator+=(const QVector4D &vector)
{
- xp += vector.xp;
- yp += vector.yp;
- zp += vector.zp;
- wp += vector.wp;
+ v[0] += vector.v[0];
+ v[1] += vector.v[1];
+ v[2] += vector.v[2];
+ v[3] += vector.v[3];
return *this;
}
inline QVector4D &QVector4D::operator-=(const QVector4D &vector)
{
- xp -= vector.xp;
- yp -= vector.yp;
- zp -= vector.zp;
- wp -= vector.wp;
+ v[0] -= vector.v[0];
+ v[1] -= vector.v[1];
+ v[2] -= vector.v[2];
+ v[3] -= vector.v[3];
return *this;
}
inline QVector4D &QVector4D::operator*=(float factor)
{
- xp *= factor;
- yp *= factor;
- zp *= factor;
- wp *= factor;
+ v[0] *= factor;
+ v[1] *= factor;
+ v[2] *= factor;
+ v[3] *= factor;
return *this;
}
inline QVector4D &QVector4D::operator*=(const QVector4D &vector)
{
- xp *= vector.xp;
- yp *= vector.yp;
- zp *= vector.zp;
- wp *= vector.wp;
+ v[0] *= vector.v[0];
+ v[1] *= vector.v[1];
+ v[2] *= vector.v[2];
+ v[3] *= vector.v[3];
return *this;
}
inline QVector4D &QVector4D::operator/=(float divisor)
{
- xp /= divisor;
- yp /= divisor;
- zp /= divisor;
- wp /= divisor;
+ v[0] /= divisor;
+ v[1] /= divisor;
+ v[2] /= divisor;
+ v[3] /= divisor;
return *this;
}
inline QVector4D &QVector4D::operator/=(const QVector4D &vector)
{
- xp /= vector.xp;
- yp /= vector.yp;
- zp /= vector.zp;
- wp /= vector.wp;
+ v[0] /= vector.v[0];
+ v[1] /= vector.v[1];
+ v[2] /= vector.v[2];
+ v[3] /= vector.v[3];
return *this;
}
@@ -234,71 +234,71 @@ QT_WARNING_DISABLE_CLANG("-Wfloat-equal")
QT_WARNING_DISABLE_GCC("-Wfloat-equal")
Q_DECL_CONSTEXPR inline bool operator==(const QVector4D &v1, const QVector4D &v2)
{
- return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp && v1.wp == v2.wp;
+ return v1.v[0] == v2.v[0] && v1.v[1] == v2.v[1] && v1.v[2] == v2.v[2] && v1.v[3] == v2.v[3];
}
Q_DECL_CONSTEXPR inline bool operator!=(const QVector4D &v1, const QVector4D &v2)
{
- return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp || v1.wp != v2.wp;
+ return v1.v[0] != v2.v[0] || v1.v[1] != v2.v[1] || v1.v[2] != v2.v[2] || v1.v[3] != v2.v[3];
}
QT_WARNING_POP
Q_DECL_CONSTEXPR inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2)
{
- return QVector4D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, v1.wp + v2.wp);
+ return QVector4D(v1.v[0] + v2.v[0], v1.v[1] + v2.v[1], v1.v[2] + v2.v[2], v1.v[3] + v2.v[3]);
}
Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2)
{
- return QVector4D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, v1.wp - v2.wp);
+ return QVector4D(v1.v[0] - v2.v[0], v1.v[1] - v2.v[1], v1.v[2] - v2.v[2], v1.v[3] - v2.v[3]);
}
Q_DECL_CONSTEXPR inline const QVector4D operator*(float factor, const QVector4D &vector)
{
- return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor);
+ return QVector4D(vector.v[0] * factor, vector.v[1] * factor, vector.v[2] * factor, vector.v[3] * factor);
}
Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &vector, float factor)
{
- return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor);
+ return QVector4D(vector.v[0] * factor, vector.v[1] * factor, vector.v[2] * factor, vector.v[3] * factor);
}
Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2)
{
- return QVector4D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, v1.wp * v2.wp);
+ return QVector4D(v1.v[0] * v2.v[0], v1.v[1] * v2.v[1], v1.v[2] * v2.v[2], v1.v[3] * v2.v[3]);
}
Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &vector)
{
- return QVector4D(-vector.xp, -vector.yp, -vector.zp, -vector.wp);
+ return QVector4D(-vector.v[0], -vector.v[1], -vector.v[2], -vector.v[3]);
}
Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, float divisor)
{
- return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor);
+ return QVector4D(vector.v[0] / divisor, vector.v[1] / divisor, vector.v[2] / divisor, vector.v[3] / divisor);
}
Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, const QVector4D &divisor)
{
- return QVector4D(vector.xp / divisor.xp, vector.yp / divisor.yp, vector.zp / divisor.zp, vector.wp / divisor.wp);
+ return QVector4D(vector.v[0] / divisor.v[0], vector.v[1] / divisor.v[1], vector.v[2] / divisor.v[2], vector.v[3] / divisor.v[3]);
}
Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2)
{
- return qFuzzyCompare(v1.xp, v2.xp) &&
- qFuzzyCompare(v1.yp, v2.yp) &&
- qFuzzyCompare(v1.zp, v2.zp) &&
- qFuzzyCompare(v1.wp, v2.wp);
+ return qFuzzyCompare(v1.v[0], v2.v[0]) &&
+ qFuzzyCompare(v1.v[1], v2.v[1]) &&
+ qFuzzyCompare(v1.v[2], v2.v[2]) &&
+ qFuzzyCompare(v1.v[3], v2.v[3]);
}
Q_DECL_CONSTEXPR inline QPoint QVector4D::toPoint() const
{
- return QPoint(qRound(xp), qRound(yp));
+ return QPoint(qRound(v[0]), qRound(v[1]));
}
Q_DECL_CONSTEXPR inline QPointF QVector4D::toPointF() const
{
- return QPointF(qreal(xp), qreal(yp));
+ return QPointF(qreal(v[0]), qreal(v[1]));
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri
index 4c778b184e..24758afdeb 100644
--- a/src/gui/opengl/opengl.pri
+++ b/src/gui/opengl/opengl.pri
@@ -32,6 +32,7 @@ qtConfig(opengl) {
opengl/qopengltexture.h \
opengl/qopengltexture_p.h \
opengl/qopengltexturehelper_p.h \
+ opengl/qopengltextureuploader_p.h \
opengl/qopenglpixeltransferoptions.h \
opengl/qopenglextrafunctions.h \
opengl/qopenglprogrambinarycache_p.h
@@ -56,6 +57,7 @@ qtConfig(opengl) {
opengl/qopengltextureblitter.cpp \
opengl/qopengltexture.cpp \
opengl/qopengltexturehelper.cpp \
+ opengl/qopengltextureuploader.cpp \
opengl/qopenglpixeltransferoptions.cpp \
opengl/qopenglprogrambinarycache.cpp
diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h
index b4657fa118..3a2393ea58 100644
--- a/src/gui/opengl/qopengl.h
+++ b/src/gui/opengl/qopengl.h
@@ -239,7 +239,7 @@ typedef unsigned long long int uint64_t;
typedef long int int32_t;
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
-#elif defined(_WIN32) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1600))
+#elif defined(_WIN32) && (defined(__GNUC__) || defined(_MSC_VER))
#include <stdint.h>
#elif defined(_WIN32)
typedef __int32 int32_t;
diff --git a/src/gui/opengl/qopenglbuffer.cpp b/src/gui/opengl/qopenglbuffer.cpp
index 69c2baa8d9..537097c09f 100644
--- a/src/gui/opengl/qopenglbuffer.cpp
+++ b/src/gui/opengl/qopenglbuffer.cpp
@@ -43,6 +43,10 @@
#include "qopenglbuffer.h"
#include <private/qopenglextensions_p.h>
+#ifndef GL_CONTEXT_LOST
+#define GL_CONTEXT_LOST 0x0507
+#endif
+
QT_BEGIN_NAMESPACE
/*!
@@ -59,12 +63,7 @@ QT_BEGIN_NAMESPACE
QOpenGLBuffer objects can be copied around as a reference to the
underlying OpenGL buffer object:
- \code
- QOpenGLBuffer buffer1(QOpenGLBuffer::IndexBuffer);
- buffer1.create();
-
- QOpenGLBuffer buffer2 = buffer1;
- \endcode
+ \snippet code/src_gui_opengl_qopenglbuffer.cpp 0
QOpenGLBuffer performs a shallow copy when objects are copied in this
manner, but does not implement copy-on-write semantics. The original
@@ -346,7 +345,14 @@ bool QOpenGLBuffer::read(int offset, void *data, int count)
Q_D(QOpenGLBuffer);
if (!d->funcs->hasOpenGLFeature(QOpenGLFunctions::Buffers) || !d->guard->id())
return false;
- while (d->funcs->glGetError() != GL_NO_ERROR) ; // Clear error state.
+
+ while (true) { // Clear error state.
+ GLenum error = d->funcs->glGetError();
+ if (error == GL_NO_ERROR)
+ break;
+ if (error == GL_CONTEXT_LOST)
+ return false;
+ };
d->funcs->glGetBufferSubData(d->type, offset, count, data);
return d->funcs->glGetError() == GL_NO_ERROR;
#else
@@ -473,9 +479,7 @@ void QOpenGLBuffer::release()
been bound to the context but wants to make sure that it
is released.
- \code
- QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
- \endcode
+ \snippet code/src_gui_opengl_qopenglbuffer.cpp 1
*/
void QOpenGLBuffer::release(QOpenGLBuffer::Type type)
{
diff --git a/src/gui/opengl/qopengldebug.cpp b/src/gui/opengl/qopengldebug.cpp
index f6c3af37dd..2e628a2bd5 100644
--- a/src/gui/opengl/qopengldebug.cpp
+++ b/src/gui/opengl/qopengldebug.cpp
@@ -89,16 +89,11 @@ QT_BEGIN_NAMESPACE
call. Moreover, OpenGL errors stack up, therefore glGetError should always
be used in a loop like this:
- \code
+ \snippet code/src_gui_opengl_qopengldebug.cpp 0
- GLenum error = GL_NO_ERROR;
- do {
- error = glGetError();
- if (error != GL_NO_ERROR)
- // handle the error
- } while (error != GL_NO_ERROR);
-
- \endcode
+ If you try to clear the error stack, make sure not just keep going until
+ GL_NO_ERROR is returned but also break on GL_CONTEXT_LOST as that error
+ value will keep repeating.
There are also many other information we are interested in (as application
developers), for instance performance issues, or warnings about using
@@ -121,20 +116,7 @@ QT_BEGIN_NAMESPACE
to create a debug context from Qt, you must set the QSurfaceFormat::DebugContext
format option on the QSurfaceFormat used to create the QOpenGLContext object:
- \code
-
- QSurfaceFormat format;
- // asks for a OpenGL 3.2 debug context using the Core profile
- format.setMajorVersion(3);
- format.setMinorVersion(2);
- format.setProfile(QSurfaceFormat::CoreProfile);
- format.setOption(QSurfaceFormat::DebugContext);
-
- QOpenGLContext *context = new QOpenGLContext;
- context->setFormat(format);
- context->create();
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 1
Note that requesting a 3.2 OpenGL Core Profile is just for the example's
purposes; this class is not tied to any specific OpenGL or OpenGL ES
@@ -148,24 +130,13 @@ QT_BEGIN_NAMESPACE
object), and like the other OpenGL functions in Qt you \e{must} initialize
it before usage by calling initialize() whilst there is a current OpenGL context:
- \code
-
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
- QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
-
- logger->initialize(); // initializes in the current context, i.e. ctx
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 2
Note that the \c{GL_KHR_debug} extension \e{must} be available in the context
in order to access the messages logged by OpenGL. You can check the
presence of this extension by calling:
- \code
-
- ctx->hasExtension(QByteArrayLiteral("GL_KHR_debug"))
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 3
where \c{ctx} is a valid QOpenGLContext. If the extension is not available,
initialize() will return false.
@@ -175,13 +146,7 @@ QT_BEGIN_NAMESPACE
OpenGL implementations keep an internal log of debug messages. Messages
stored in this log can be retrieved by using the loggedMessages() function:
- \code
-
- const QList<QOpenGLDebugMessage> messages = logger->loggedMessages();
- for (const QOpenGLDebugMessage &message : messages)
- qDebug() << message;
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 4
The internal log has a limited size; when it fills up, older messages will
get discarded to make room for the new incoming messages. When you call
@@ -199,12 +164,7 @@ QT_BEGIN_NAMESPACE
you need to connect a suitable slot to the messageLogged() signal, and
start logging by calling startLogging():
- \code
-
- connect(logger, &QOpenGLDebugLogger::messageLogged, receiver, &LogHandler::handleLoggedMessage);
- logger->startLogging();
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 5
Similarly, logging can be disabled at any time by calling the stopLogging()
function.
@@ -255,14 +215,7 @@ QT_BEGIN_NAMESPACE
\l{QOpenGLDebugMessage::}{createThirdPartyMessage()}, and then inserting it
into the log by calling logMessage():
- \code
-
- QOpenGLDebugMessage message =
- QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("Custom message"));
-
- logger->logMessage(message);
-
- \endcode
+ \snippet code/src_gui_opengl_qopengldebug.cpp 6
Note that OpenGL implementations have a vendor-specific limit to the length
of the messages that can be inserted in the debug log. You can retrieve
diff --git a/src/gui/opengl/qopenglengineshadermanager.cpp b/src/gui/opengl/qopenglengineshadermanager.cpp
index 2f7afa4a66..1e5a10c99c 100644
--- a/src/gui/opengl/qopenglengineshadermanager.cpp
+++ b/src/gui/opengl/qopenglengineshadermanager.cpp
@@ -153,12 +153,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core;
code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core;
- code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO_core;
- code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM_core;
code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core;
code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core;
- code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO_core;
- code[MainFragmentShader_C] = qopenglslMainFragmentShader_C_core;
code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core;
code[MainFragmentShader] = qopenglslMainFragmentShader_core;
code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core;
@@ -171,7 +167,7 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended
code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core;
- code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop_core;
+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_core;
code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core;
code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core;
code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core;
@@ -203,12 +199,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader;
code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader;
- code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO;
- code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM;
code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO;
code[MainFragmentShader_M] = qopenglslMainFragmentShader_M;
- code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO;
- code[MainFragmentShader_C] = qopenglslMainFragmentShader_C;
code[MainFragmentShader_O] = qopenglslMainFragmentShader_O;
code[MainFragmentShader] = qopenglslMainFragmentShader;
code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays;
@@ -220,10 +212,7 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader;
code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader;
- if (context->isOpenGLES())
- code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_ES;
- else
- code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop;
+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader;
code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader;
code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader;
code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader;
@@ -238,21 +227,20 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[RgbMaskWithGammaFragmentShader] = ""; //###
}
- // These shaders are not implemented yet and therefore are the same
- // for all profiles. Implementations should make a version for both
- // profiles and put the appropriate lines in the if-statement above.
+ // The composition shaders are just layout qualifiers and the same
+ // for all profiles that support them.
code[NoCompositionModeFragmentShader] = "";
- code[MultiplyCompositionModeFragmentShader] = ""; //###
- code[ScreenCompositionModeFragmentShader] = ""; //###
- code[OverlayCompositionModeFragmentShader] = ""; //###
- code[DarkenCompositionModeFragmentShader] = ""; //###
- code[LightenCompositionModeFragmentShader] = ""; //###
- code[ColorDodgeCompositionModeFragmentShader] = ""; //###
- code[ColorBurnCompositionModeFragmentShader] = ""; //###
- code[HardLightCompositionModeFragmentShader] = ""; //###
- code[SoftLightCompositionModeFragmentShader] = ""; //###
- code[DifferenceCompositionModeFragmentShader] = ""; //###
- code[ExclusionCompositionModeFragmentShader] = ""; //###
+ code[MultiplyCompositionModeFragmentShader] = qopenglslMultiplyCompositionModeFragmentShader;
+ code[ScreenCompositionModeFragmentShader] = qopenglslScreenCompositionModeFragmentShader;
+ code[OverlayCompositionModeFragmentShader] = qopenglslOverlayCompositionModeFragmentShader;
+ code[DarkenCompositionModeFragmentShader] = qopenglslDarkenCompositionModeFragmentShader;
+ code[LightenCompositionModeFragmentShader] = qopenglslLightenCompositionModeFragmentShader;
+ code[ColorDodgeCompositionModeFragmentShader] = qopenglslColorDodgeCompositionModeFragmentShader;
+ code[ColorBurnCompositionModeFragmentShader] = qopenglslColorBurnCompositionModeFragmentShader;
+ code[HardLightCompositionModeFragmentShader] = qopenglslHardLightCompositionModeFragmentShader;
+ code[SoftLightCompositionModeFragmentShader] = qopenglslSoftLightCompositionModeFragmentShader;
+ code[DifferenceCompositionModeFragmentShader] = qopenglslDifferenceCompositionModeFragmentShader;
+ code[ExclusionCompositionModeFragmentShader] = qopenglslExclusionCompositionModeFragmentShader;
#if defined(QT_DEBUG)
// Check that all the elements have been filled:
@@ -268,9 +256,13 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
QByteArray fragSource;
// Compile up the simple shader:
+#ifdef Q_OS_WASM
+ vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
+ vertexSource.append(qShaderSnippets[MainVertexShader]);
+#else
vertexSource.append(qShaderSnippets[MainVertexShader]);
vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
-
+#endif
fragSource.append(qShaderSnippets[MainFragmentShader]);
fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
@@ -396,9 +388,13 @@ QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QO
fragSource.append(qShaderSnippets[prog.maskFragShader]);
QByteArray vertexSource;
+#ifdef Q_OS_WASM
+ vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
+ vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
+#else
vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
-
+#endif
QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram);
CachedShader shaderCache(fragSource, vertexSource);
@@ -612,8 +608,11 @@ void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mo
if (compositionMode == mode)
return;
+ bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus;
+ bool isAdvanced = mode > QPainter::CompositionMode_Plus;
+
compositionMode = mode;
- shaderProgNeedsChanging = true; //###
+ shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced;
}
void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage)
@@ -783,21 +782,13 @@ bool QOpenGLEngineShaderManager::useCorrectShaderProg()
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays;
} else {
bool useGlobalOpacity = (opacityMode == UniformOpacity);
- if (hasCompose && hasMask && useGlobalOpacity)
- requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CMO;
- if (hasCompose && hasMask && !useGlobalOpacity)
- requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CM;
- if (!hasCompose && hasMask && useGlobalOpacity)
+ if (hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO;
- if (!hasCompose && hasMask && !useGlobalOpacity)
+ if (hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M;
- if (hasCompose && !hasMask && useGlobalOpacity)
- requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CO;
- if (hasCompose && !hasMask && !useGlobalOpacity)
- requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_C;
- if (!hasCompose && !hasMask && useGlobalOpacity)
+ if (!hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O;
- if (!hasCompose && !hasMask && !useGlobalOpacity)
+ if (!hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader;
}
diff --git a/src/gui/opengl/qopenglengineshadermanager_p.h b/src/gui/opengl/qopenglengineshadermanager_p.h
index 377501457d..d43788d777 100644
--- a/src/gui/opengl/qopenglengineshadermanager_p.h
+++ b/src/gui/opengl/qopenglengineshadermanager_p.h
@@ -281,12 +281,8 @@ public:
AffinePositionWithTextureBrushVertexShader,
// MainFragmentShader_CMO must be first in the list:
- MainFragmentShader_CMO,
- MainFragmentShader_CM,
MainFragmentShader_MO,
MainFragmentShader_M,
- MainFragmentShader_CO,
- MainFragmentShader_C,
MainFragmentShader_O,
MainFragmentShader,
MainFragmentShader_ImageArrays,
diff --git a/src/gui/opengl/qopenglengineshadersource_p.h b/src/gui/opengl/qopenglengineshadersource_p.h
index a165643839..3ac599b6c2 100644
--- a/src/gui/opengl/qopenglengineshadersource_p.h
+++ b/src/gui/opengl/qopenglengineshadersource_p.h
@@ -303,17 +303,7 @@ static const char* const qopenglslPositionWithTextureBrushVertexShader = "\n\
static const char* const qopenglslAffinePositionWithTextureBrushVertexShader
= qopenglslPositionWithTextureBrushVertexShader;
-// OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead,
-// we emulate GL_REPEAT by only taking the fractional part of the texture coords.
-// TODO: Special case POT textures which don't need this emulation
-static const char* const qopenglslTextureBrushSrcFragmentShader_ES = "\n\
- varying highp vec2 brushTextureCoords; \n\
- uniform sampler2D brushTexture; \n\
- lowp vec4 srcPixel() { \n\
- return texture2D(brushTexture, fract(brushTextureCoords)); \n\
- }\n";
-
-static const char* const qopenglslTextureBrushSrcFragmentShader_desktop = "\n\
+static const char* const qopenglslTextureBrushSrcFragmentShader = "\n\
varying highp vec2 brushTextureCoords; \n\
uniform sampler2D brushTexture; \n\
lowp vec4 srcPixel() \n\
@@ -403,25 +393,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays = "\n\
gl_FragColor = srcPixel() * opacity; \n\
}\n";
-static const char* const qopenglslMainFragmentShader_CMO = "\n\
- uniform lowp float globalOpacity; \n\
- lowp vec4 srcPixel(); \n\
- lowp vec4 applyMask(lowp vec4); \n\
- lowp vec4 compose(lowp vec4); \n\
- void main() \n\
- { \n\
- gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
- }\n";
-
-static const char* const qopenglslMainFragmentShader_CM = "\n\
- lowp vec4 srcPixel(); \n\
- lowp vec4 applyMask(lowp vec4); \n\
- lowp vec4 compose(lowp vec4); \n\
- void main() \n\
- { \n\
- gl_FragColor = applyMask(compose(srcPixel())); \n\
- }\n";
-
static const char* const qopenglslMainFragmentShader_MO = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
@@ -439,23 +410,6 @@ static const char* const qopenglslMainFragmentShader_M = "\n\
gl_FragColor = applyMask(srcPixel()); \n\
}\n";
-static const char* const qopenglslMainFragmentShader_CO = "\n\
- uniform lowp float globalOpacity; \n\
- lowp vec4 srcPixel(); \n\
- lowp vec4 compose(lowp vec4); \n\
- void main() \n\
- { \n\
- gl_FragColor = compose(srcPixel()*globalOpacity); \n\
- }\n";
-
-static const char* const qopenglslMainFragmentShader_C = "\n\
- lowp vec4 srcPixel(); \n\
- lowp vec4 compose(lowp vec4); \n\
- void main() \n\
- { \n\
- gl_FragColor = compose(srcPixel()); \n\
- }\n";
-
static const char* const qopenglslMainFragmentShader_O = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
@@ -513,22 +467,65 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\
return src * mask; \n\
}\n";
+static const char* const qopenglslMultiplyCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_multiply) out;\n\
+ #endif\n";
+
+static const char* const qopenglslScreenCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_screen) out;\n\
+ #endif\n";
+
+static const char* const qopenglslOverlayCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_overlay) out;\n\
+ #endif\n";
+
+static const char* const qopenglslDarkenCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_darken) out;\n\
+ #endif\n";
+
+static const char* const qopenglslLightenCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_lighten) out;\n\
+ #endif\n";
+
+static const char* const qopenglslColorDodgeCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_colordodge) out;\n\
+ #endif\n";
+
+static const char* const qopenglslColorBurnCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_colorburn) out;\n\
+ #endif\n";
+
+static const char* const qopenglslHardLightCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_hardlight) out;\n\
+ #endif\n";
+
+static const char* const qopenglslSoftLightCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_softlight) out;\n\
+ #endif\n";
+
+static const char* const qopenglslDifferenceCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_difference) out;\n\
+ #endif\n";
+
+static const char* const qopenglslExclusionCompositionModeFragmentShader = "\n\
+ #ifdef GL_KHR_blend_equation_advanced\n\
+ layout(blend_support_exclusion) out;\n\
+ #endif\n";
+
/*
Left to implement:
RgbMaskFragmentShader,
RgbMaskWithGammaFragmentShader,
-
- MultiplyCompositionModeFragmentShader,
- ScreenCompositionModeFragmentShader,
- OverlayCompositionModeFragmentShader,
- DarkenCompositionModeFragmentShader,
- LightenCompositionModeFragmentShader,
- ColorDodgeCompositionModeFragmentShader,
- ColorBurnCompositionModeFragmentShader,
- HardLightCompositionModeFragmentShader,
- SoftLightCompositionModeFragmentShader,
- DifferenceCompositionModeFragmentShader,
- ExclusionCompositionModeFragmentShader,
*/
/*
@@ -788,7 +785,7 @@ static const char* const qopenglslPositionWithTextureBrushVertexShader_core = "\
static const char* const qopenglslAffinePositionWithTextureBrushVertexShader_core
= qopenglslPositionWithTextureBrushVertexShader_core;
-static const char* const qopenglslTextureBrushSrcFragmentShader_desktop_core = "\n\
+static const char* const qopenglslTextureBrushSrcFragmentShader_core = "\n\
in vec2 brushTextureCoords; \n\
uniform sampler2D brushTexture; \n\
vec4 srcPixel() \n\
@@ -880,29 +877,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays_core =
fragColor = srcPixel() * opacity; \n\
}\n";
-static const char* const qopenglslMainFragmentShader_CMO_core =
- "#version 150 core\n\
- out vec4 fragColor; \n\
- uniform float globalOpacity; \n\
- vec4 srcPixel(); \n\
- vec4 applyMask(vec4); \n\
- vec4 compose(vec4); \n\
- void main() \n\
- { \n\
- fragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
- }\n";
-
-static const char* const qopenglslMainFragmentShader_CM_core =
- "#version 150 core\n\
- out vec4 fragColor; \n\
- vec4 srcPixel(); \n\
- vec4 applyMask(vec4); \n\
- vec4 compose(vec4); \n\
- void main() \n\
- { \n\
- fragColor = applyMask(compose(srcPixel())); \n\
- }\n";
-
static const char* const qopenglslMainFragmentShader_MO_core =
"#version 150 core\n\
out vec4 fragColor; \n\
@@ -924,27 +898,6 @@ static const char* const qopenglslMainFragmentShader_M_core =
fragColor = applyMask(srcPixel()); \n\
}\n";
-static const char* const qopenglslMainFragmentShader_CO_core =
- "#version 150 core\n\
- out vec4 fragColor; \n\
- uniform float globalOpacity; \n\
- vec4 srcPixel(); \n\
- vec4 compose(vec4); \n\
- void main() \n\
- { \n\
- fragColor = compose(srcPixel()*globalOpacity); \n\
- }\n";
-
-static const char* const qopenglslMainFragmentShader_C_core =
- "#version 150 core\n\
- out vec4 fragColor; \n\
- vec4 srcPixel(); \n\
- vec4 compose(vec4); \n\
- void main() \n\
- { \n\
- fragColor = compose(srcPixel()); \n\
- }\n";
-
static const char* const qopenglslMainFragmentShader_O_core =
"#version 150 core\n\
out vec4 fragColor; \n\
@@ -1010,18 +963,6 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\
Left to implement:
RgbMaskFragmentShader_core,
RgbMaskWithGammaFragmentShader_core,
-
- MultiplyCompositionModeFragmentShader_core,
- ScreenCompositionModeFragmentShader_core,
- OverlayCompositionModeFragmentShader_core,
- DarkenCompositionModeFragmentShader_core,
- LightenCompositionModeFragmentShader_core,
- ColorDodgeCompositionModeFragmentShader_core,
- ColorBurnCompositionModeFragmentShader_core,
- HardLightCompositionModeFragmentShader_core,
- SoftLightCompositionModeFragmentShader_core,
- DifferenceCompositionModeFragmentShader_core,
- ExclusionCompositionModeFragmentShader_core,
*/
QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h
index a5f1a2cc88..af8ee8201d 100644
--- a/src/gui/opengl/qopenglextensions_p.h
+++ b/src/gui/opengl/qopenglextensions_p.h
@@ -90,7 +90,8 @@ public:
MapBufferRange = 0x00100000,
Sized8Formats = 0x00200000,
DiscardFramebuffer = 0x00400000,
- Sized16Formats = 0x00800000
+ Sized16Formats = 0x00800000,
+ TextureSwizzle = 0x01000000,
};
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp
index 83bc568ba7..cae3d516c4 100644
--- a/src/gui/opengl/qopenglframebufferobject.cpp
+++ b/src/gui/opengl/qopenglframebufferobject.cpp
@@ -110,6 +110,10 @@ QT_BEGIN_NAMESPACE
#define GL_RGB10 0x8052
#endif
+#ifndef GL_RGB16
+#define GL_RGB16 0x8054
+#endif
+
#ifndef GL_RGBA8
#define GL_RGBA8 0x8058
#endif
@@ -118,6 +122,10 @@ QT_BEGIN_NAMESPACE
#define GL_RGB10_A2 0x8059
#endif
+#ifndef GL_RGBA16
+#define GL_RGBA16 0x805B
+#endif
+
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
@@ -134,6 +142,15 @@ QT_BEGIN_NAMESPACE
#define GL_CONTEXT_LOST 0x0507
#endif
+#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#endif
+
+#ifndef GL_DEPTH_STENCIL
+#define GL_DEPTH_STENCIL 0x84F9
+#endif
+
+
/*!
\class QOpenGLFramebufferObjectFormat
@@ -530,6 +547,8 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx)
GLuint pixelType = GL_UNSIGNED_BYTE;
if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10)
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
+ else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16)
+ pixelType = GL_UNSIGNED_SHORT;
funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0,
GL_RGBA, pixelType, NULL);
@@ -567,11 +586,16 @@ void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples)
GLenum storageFormat = color.internalFormat;
// ES requires a sized format. The older desktop extension does not. Correct the format on ES.
- if (ctx->isOpenGLES() && color.internalFormat == GL_RGBA) {
- if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats))
- storageFormat = GL_RGBA8;
- else
- storageFormat = GL_RGBA4;
+ if (ctx->isOpenGLES()) {
+ if (color.internalFormat == GL_RGBA) {
+ if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats))
+ storageFormat = GL_RGBA8;
+ else
+ storageFormat = GL_RGBA4;
+ } else if (color.internalFormat == GL_RGB10) {
+ // GL_RGB10 is not allowed in ES for glRenderbufferStorage.
+ storageFormat = GL_RGB10_A2;
+ }
}
funcs.glGenRenderbuffers(1, &color_buffer);
@@ -603,7 +627,11 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
// free existing attachments
if (depth_buffer_guard) {
+#ifdef Q_OS_WASM
+ funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+#else
funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
+#endif
depth_buffer_guard->free();
}
if (stencil_buffer_guard) {
@@ -621,7 +649,35 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
// In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
// separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
// might not be supported while separate buffers are, according to QTBUG-12861.
+#ifdef Q_OS_WASM
+ // WebGL doesn't allow separately attach buffers to
+ // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
+ // QTBUG-69913
+ if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
+ funcs.glGenRenderbuffers(1, &depth_buffer);
+ funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
+ Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
+ GLenum storageFormat = GL_DEPTH_STENCIL;
+
+ if (samples != 0 ) {
+ funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+ storageFormat, dsSize.width(), dsSize.height());
+ } else {
+ funcs.glRenderbufferStorage(GL_RENDERBUFFER, storageFormat,
+ dsSize.width(), dsSize.height());
+ }
+
+ funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_buffer);
+
+ valid = checkFramebufferStatus(ctx);
+ if (!valid) {
+ funcs.glDeleteRenderbuffers(1, &depth_buffer);
+ depth_buffer = 0;
+ }
+ }
+#else
if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
&& funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
{
@@ -713,11 +769,16 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
stencil_buffer = 0;
}
}
+#endif //Q_OS_WASM
// The FBO might have become valid after removing the depth or stencil buffer.
valid = checkFramebufferStatus(ctx);
+#ifdef Q_OS_WASM
+ if (depth_buffer) {
+#else
if (depth_buffer && stencil_buffer) {
+#endif
fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
} else if (depth_buffer) {
fbo_attachment = QOpenGLFramebufferObject::Depth;
@@ -1307,6 +1368,14 @@ static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool incl
return img;
}
+static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context)
+{
+ // We assume OpenGL 1.2+ or ES 3.0+ here.
+ QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64);
+ context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits());
+ return img;
+}
+
static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip)
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
@@ -1324,6 +1393,10 @@ static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format,
return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip);
case GL_RGB10_A2:
return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip);
+ case GL_RGB16:
+ return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip);
+ case GL_RGBA16:
+ return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip);
case GL_RGBA:
case GL_RGBA8:
default:
@@ -1352,7 +1425,8 @@ Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format,
is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2
the function will fall back to premultiplied RGBA8888 or RGBx8888 when
reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt
- 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2.
+ 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since
+ Qt 5.12 a RGBA64 image is return if the internal format is RGBA16.
If the rendering in the framebuffer was not done with premultiplied alpha in mind,
create a wrapper QImage with a non-premultiplied format. This is necessary before
diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp
index 977565516f..b4ff21f3fd 100644
--- a/src/gui/opengl/qopenglfunctions.cpp
+++ b/src/gui/opengl/qopenglfunctions.cpp
@@ -45,6 +45,7 @@
#include <QtGui/private/qopengl_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformnativeinterface.h>
#ifdef Q_OS_INTEGRITY
#include <EGL/egl.h>
@@ -89,65 +90,18 @@ void CLASS::init(QOpenGLContext *context) \
that need it. The recommended way to use QOpenGLFunctions is by
direct inheritance:
- \code
- class MyGLWindow : public QWindow, protected QOpenGLFunctions
- {
- Q_OBJECT
- public:
- MyGLWindow(QScreen *screen = 0);
-
- protected:
- void initializeGL();
- void paintGL();
-
- QOpenGLContext *m_context;
- };
-
- MyGLWindow(QScreen *screen)
- : QWindow(screen), QOpenGLWidget(parent)
- {
- setSurfaceType(OpenGLSurface);
- create();
-
- // Create an OpenGL context
- m_context = new QOpenGLContext;
- m_context->create();
-
- // Setup scene and render it
- initializeGL();
- paintGL();
- }
-
- void MyGLWindow::initializeGL()
- {
- m_context->makeCurrent(this);
- initializeOpenGLFunctions();
- }
- \endcode
+ \snippet code/src_gui_opengl_qopenglfunctions.cpp 0
The \c{paintGL()} function can then use any of the OpenGL ES 2.0
functions without explicit resolution, such as glActiveTexture()
in the following example:
- \code
- void MyGLWindow::paintGL()
- {
- m_context->makeCurrent(this);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, textureId);
- ...
- m_context->swapBuffers(this);
- m_context->doneCurrent();
- }
- \endcode
+ \snippet code/src_gui_opengl_qopenglfunctions.cpp 1
QOpenGLFunctions can also be used directly for ad-hoc invocation
of OpenGL ES 2.0 functions on all platforms:
- \code
- QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
- glFuncs.glActiveTexture(GL_TEXTURE1);
- \endcode
+ \snippet code/src_gui_opengl_qopenglfunctions.cpp 2
An alternative approach is to query the context's associated
QOpenGLFunctions instance. This is somewhat faster than the previous
@@ -156,10 +110,7 @@ void CLASS::init(QOpenGLContext *context) \
resolving happens only once for a given context, regardless of the number of
QOpenGLFunctions instances initialized for it.
- \code
- QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions();
- glFuncs->glActiveTexture(GL_TEXTURE1);
- \endcode
+ \snippet code/src_gui_opengl_qopenglfunctions.cpp 3
QOpenGLFunctions provides wrappers for all OpenGL ES 2.0
functions, including the common subset of OpenGL 1.x and ES
@@ -174,10 +125,7 @@ void CLASS::init(QOpenGLContext *context) \
feature. For example, the following checks if non power of two
textures are available:
- \code
- QOpenGLFunctions funcs(QOpenGLContext::currentContext());
- bool npot = funcs.hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
- \endcode
+ \snippet code/src_gui_opengl_qopenglfunctions.cpp 4
\sa QOpenGLContext, QSurfaceFormat
*/
@@ -294,9 +242,19 @@ QOpenGLExtensions::QOpenGLExtensions(QOpenGLContext *context)
static int qt_gl_resolve_features()
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QOpenGLExtensionMatcher extensions;
+ int features = 0;
+ if ((extensions.match("GL_KHR_blend_equation_advanced")
+ || extensions.match("GL_NV_blend_equation_advanced")) &&
+ (extensions.match("GL_KHR_blend_equation_advanced_coherent")
+ || extensions.match("GL_NV_blend_equation_advanced_coherent"))) {
+ // We need both the advanced equations and the coherency for us
+ // to be able to easily use the new blend equations
+ features |= QOpenGLFunctions::BlendEquationAdvanced;
+ }
if (ctx->isOpenGLES()) {
// OpenGL ES
- int features = QOpenGLFunctions::Multitexture |
+ features |= QOpenGLFunctions::Multitexture |
QOpenGLFunctions::Shaders |
QOpenGLFunctions::Buffers |
QOpenGLFunctions::Framebuffers |
@@ -308,7 +266,6 @@ static int qt_gl_resolve_features()
QOpenGLFunctions::CompressedTextures |
QOpenGLFunctions::Multisample |
QOpenGLFunctions::StencilSeparate;
- QOpenGLExtensionMatcher extensions;
if (extensions.match("GL_IMG_texture_npot"))
features |= QOpenGLFunctions::NPOTTextures;
if (extensions.match("GL_OES_texture_npot"))
@@ -320,14 +277,18 @@ static int qt_gl_resolve_features()
if (!(renderer && strstr(renderer, "Mesa")))
features |= QOpenGLFunctions::TextureRGFormats;
}
- if (ctx->format().majorVersion() >= 3)
+ if (ctx->format().majorVersion() >= 3) {
features |= QOpenGLFunctions::MultipleRenderTargets;
+ if (ctx->format().minorVersion() >= 2 && extensions.match("GL_KHR_blend_equation_advanced_coherent")) {
+ // GL_KHR_blend_equation_advanced is included in OpenGL ES/3.2
+ features |= QOpenGLFunctions::BlendEquationAdvanced;
+ }
+ }
return features;
} else {
// OpenGL
- int features = QOpenGLFunctions::TextureRGFormats;
+ features |= QOpenGLFunctions::TextureRGFormats;
QSurfaceFormat format = QOpenGLContext::currentContext()->format();
- QOpenGLExtensionMatcher extensions;
if (format.majorVersion() >= 3)
features |= QOpenGLFunctions::Framebuffers | QOpenGLFunctions::MultipleRenderTargets;
@@ -411,6 +372,8 @@ static int qt_gl_resolve_extensions()
extensions |= QOpenGLExtensions::NVFloatBuffer;
if (extensionMatcher.match("GL_ARB_pixel_buffer_object"))
extensions |= QOpenGLExtensions::PixelBufferObject;
+ if (extensionMatcher.match("GL_ARB_texture_swizzle") || extensionMatcher.match("GL_EXT_texture_swizzle"))
+ extensions |= QOpenGLExtensions::TextureSwizzle;
if (ctx->isOpenGLES()) {
if (format.majorVersion() >= 2)
@@ -423,7 +386,8 @@ static int qt_gl_resolve_extensions()
| QOpenGLExtensions::MapBufferRange
| QOpenGLExtensions::FramebufferBlit
| QOpenGLExtensions::FramebufferMultisample
- | QOpenGLExtensions::Sized8Formats;
+ | QOpenGLExtensions::Sized8Formats
+ | QOpenGLExtensions::TextureSwizzle;
} else {
// Recognize features by extension name.
if (extensionMatcher.match("GL_OES_packed_depth_stencil"))
@@ -449,6 +413,17 @@ static int qt_gl_resolve_extensions()
// We don't match GL_APPLE_texture_format_BGRA8888 here because it has different semantics.
if (extensionMatcher.match("GL_IMG_texture_format_BGRA8888") || extensionMatcher.match("GL_EXT_texture_format_BGRA8888"))
extensions |= QOpenGLExtensions::BGRATextureFormat;
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+ QString *deviceName =
+ static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName"));
+ static bool wrongfullyReportsBgra8888Support = deviceName != 0
+ && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0
+ || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0
+ || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0);
+ if (wrongfullyReportsBgra8888Support)
+ extensions &= ~QOpenGLExtensions::BGRATextureFormat;
+#endif
+
if (extensionMatcher.match("GL_EXT_discard_framebuffer"))
extensions |= QOpenGLExtensions::DiscardFramebuffer;
if (extensionMatcher.match("GL_EXT_texture_norm16"))
@@ -482,6 +457,9 @@ static int qt_gl_resolve_extensions()
if (format.version() >= qMakePair(3, 2) || extensionMatcher.match("GL_ARB_geometry_shader4"))
extensions |= QOpenGLExtensions::GeometryShaders;
+ if (format.version() >= qMakePair(3, 3))
+ extensions |= QOpenGLExtensions::TextureSwizzle;
+
if (extensionMatcher.match("GL_ARB_map_buffer_range"))
extensions |= QOpenGLExtensions::MapBufferRange;
diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h
index 1a43f13d9b..00287b0665 100644
--- a/src/gui/opengl/qopenglfunctions.h
+++ b/src/gui/opengl/qopenglfunctions.h
@@ -277,7 +277,8 @@ public:
NPOTTextureRepeat = 0x2000,
FixedFunctionPipeline = 0x4000,
TextureRGFormats = 0x8000,
- MultipleRenderTargets = 0x10000
+ MultipleRenderTargets = 0x10000,
+ BlendEquationAdvanced = 0x20000,
};
Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature)
diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp
index 6a89089f18..001cb839fa 100644
--- a/src/gui/opengl/qopenglpaintengine.cpp
+++ b/src/gui/opengl/qopenglpaintengine.cpp
@@ -87,8 +87,27 @@
#include <QDebug>
-QT_BEGIN_NAMESPACE
+#ifndef GL_KHR_blend_equation_advanced
+#define GL_KHR_blend_equation_advanced 1
+#define GL_MULTIPLY_KHR 0x9294
+#define GL_SCREEN_KHR 0x9295
+#define GL_OVERLAY_KHR 0x9296
+#define GL_DARKEN_KHR 0x9297
+#define GL_LIGHTEN_KHR 0x9298
+#define GL_COLORDODGE_KHR 0x9299
+#define GL_COLORBURN_KHR 0x929A
+#define GL_HARDLIGHT_KHR 0x929B
+#define GL_SOFTLIGHT_KHR 0x929C
+#define GL_DIFFERENCE_KHR 0x929E
+#define GL_EXCLUSION_KHR 0x92A0
+#endif /* GL_KHR_blend_equation_advanced */
+
+#ifndef GL_KHR_blend_equation_advanced_coherent
+#define GL_KHR_blend_equation_advanced_coherent 1
+#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285
+#endif /* GL_KHR_blend_equation_advanced_coherent */
+QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
@@ -244,7 +263,7 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QGradient &gradient)
struct ImageWithBindOptions
{
const QImage &image;
- QOpenGLTextureCache::BindOptions options;
+ QOpenGLTextureUploader::BindOptions options;
};
template<>
@@ -253,6 +272,12 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const ImageWithBindOptions &ima
return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, imageWithOptions.image, imageWithOptions.options);
}
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
void QOpenGL2PaintEngineExPrivate::updateBrushTexture()
{
Q_Q(QOpenGL2PaintEngineEx);
@@ -285,16 +310,18 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture()
currentBrushImage = currentBrush.textureImage();
int max_texture_size = ctx->d_func()->maxTextureSize();
- if (currentBrushImage.width() > max_texture_size || currentBrushImage.height() > max_texture_size)
- currentBrushImage = currentBrushImage.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
+ QSize newSize = currentBrushImage.size();
+ newSize = newSize.boundedTo(QSize(max_texture_size, max_texture_size));
+ if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) {
+ if (!isPowerOfTwo(newSize.width()) || !isPowerOfTwo(newSize.height())) {
+ newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1));
+ newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1));
+ }
+ }
+ if (currentBrushImage.size() != newSize)
+ currentBrushImage = currentBrushImage.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
GLuint wrapMode = GL_REPEAT;
- if (QOpenGLContext::currentContext()->isOpenGLES()) {
- // OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead,
- // we emulate GL_REPEAT by only taking the fractional part of the texture coords
- // in the qopenglslTextureBrushSrcFragmentShader program.
- wrapMode = GL_CLAMP_TO_EDGE;
- }
updateTexture(QT_BRUSH_TEXTURE_UNIT, currentBrushImage, wrapMode, filterMode, ForceUpdate);
}
@@ -498,6 +525,21 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode()
// NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
// composition modes look odd.
// qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
+ if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::BlendEquationAdvanced)) {
+ if (q->state()->composition_mode <= QPainter::CompositionMode_Plus) {
+ funcs.glDisable(GL_BLEND_ADVANCED_COHERENT_KHR);
+ funcs.glBlendEquation(GL_FUNC_ADD);
+ } else {
+ funcs.glEnable(GL_BLEND_ADVANCED_COHERENT_KHR);
+ }
+ shaderManager->setCompositionMode(q->state()->composition_mode);
+ } else {
+ if (q->state()->composition_mode > QPainter::CompositionMode_Plus) {
+ qWarning("Unsupported composition mode");
+ compositionModeDirty = false;
+ return;
+ }
+ }
switch(q->state()->composition_mode) {
case QPainter::CompositionMode_SourceOver:
funcs.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@@ -538,6 +580,39 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode()
case QPainter::CompositionMode_Plus:
funcs.glBlendFunc(GL_ONE, GL_ONE);
break;
+ case QPainter::CompositionMode_Multiply:
+ funcs.glBlendEquation(GL_MULTIPLY_KHR);
+ break;
+ case QPainter::CompositionMode_Screen:
+ funcs.glBlendEquation(GL_SCREEN_KHR);
+ break;
+ case QPainter::CompositionMode_Overlay:
+ funcs.glBlendEquation(GL_OVERLAY_KHR);
+ break;
+ case QPainter::CompositionMode_Darken:
+ funcs.glBlendEquation(GL_DARKEN_KHR);
+ break;
+ case QPainter::CompositionMode_Lighten:
+ funcs.glBlendEquation(GL_LIGHTEN_KHR);
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ funcs.glBlendEquation(GL_COLORDODGE_KHR);
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ funcs.glBlendEquation(GL_COLORBURN_KHR);
+ break;
+ case QPainter::CompositionMode_HardLight:
+ funcs.glBlendEquation(GL_HARDLIGHT_KHR);
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ funcs.glBlendEquation(GL_SOFTLIGHT_KHR);
+ break;
+ case QPainter::CompositionMode_Difference:
+ funcs.glBlendEquation(GL_DIFFERENCE_KHR);
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ funcs.glBlendEquation(GL_EXCLUSION_KHR);
+ break;
default:
qWarning("Unsupported composition mode");
break;
@@ -1487,25 +1562,26 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c
ensureActive();
d->transferMode(ImageDrawingMode);
- QOpenGLTextureCache::BindOptions bindOption = QOpenGLTextureCache::PremultipliedAlphaBindOption;
+ QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
// Use specialized bind for formats we have specialized shaders for.
switch (image.format()) {
case QImage::Format_RGBA8888:
case QImage::Format_ARGB32:
+ case QImage::Format_RGBA64:
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc);
bindOption = 0;
break;
case QImage::Format_Alpha8:
if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc);
- bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption;
+ bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption;
} else
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
break;
case QImage::Format_Grayscale8:
if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc);
- bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption;
+ bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption;
} else
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
break;
diff --git a/src/gui/opengl/qopenglprogrambinarycache.cpp b/src/gui/opengl/qopenglprogrambinarycache.cpp
index d16173df83..c96021a969 100644
--- a/src/gui/opengl/qopenglprogrambinarycache.cpp
+++ b/src/gui/opengl/qopenglprogrambinarycache.cpp
@@ -54,14 +54,19 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(DBG_SHADER_CACHE)
+#ifndef GL_CONTEXT_LOST
+#define GL_CONTEXT_LOST 0x0507
+#endif
+
#ifndef GL_PROGRAM_BINARY_LENGTH
#define GL_PROGRAM_BINARY_LENGTH 0x8741
#endif
const quint32 BINSHADER_MAGIC = 0x5174;
-const quint32 BINSHADER_VERSION = 0x2;
+const quint32 BINSHADER_VERSION = 0x3;
const quint32 BINSHADER_QTVERSION = QT_VERSION;
+namespace {
struct GLEnvInfo
{
GLEnvInfo();
@@ -70,6 +75,7 @@ struct GLEnvInfo
QByteArray glrenderer;
QByteArray glversion;
};
+}
GLEnvInfo::GLEnvInfo()
{
@@ -114,7 +120,7 @@ QString QOpenGLProgramBinaryCache::cacheFileName(const QByteArray &cacheKey) con
return m_cacheDir + QString::fromUtf8(cacheKey);
}
-#define BASE_HEADER_SIZE (int(3 * sizeof(quint32)))
+#define BASE_HEADER_SIZE (int(4 * sizeof(quint32)))
#define FULL_HEADER_SIZE(stringsSize) (BASE_HEADER_SIZE + 12 + stringsSize + 8)
#define PADDING_SIZE(fullHeaderSize) (((fullHeaderSize + 3) & ~3) - fullHeaderSize)
@@ -153,13 +159,21 @@ bool QOpenGLProgramBinaryCache::verifyHeader(const QByteArray &buf) const
qCDebug(DBG_SHADER_CACHE, "Qt version does not match");
return false;
}
+ if (readUInt(&p) != sizeof(quintptr)) {
+ qCDebug(DBG_SHADER_CACHE, "Architecture does not match");
+ return false;
+ }
return true;
}
bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat, const void *p, uint blobSize)
{
QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions();
- while (funcs->glGetError() != GL_NO_ERROR) { }
+ while (true) {
+ GLenum error = funcs->glGetError();
+ if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
+ break;
+ }
funcs->glProgramBinary(programId, blobFormat, p, blobSize);
GLenum err = funcs->glGetError();
@@ -335,7 +349,11 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions();
GLint blobSize = 0;
- while (funcs->glGetError() != GL_NO_ERROR) { }
+ while (true) {
+ GLenum error = funcs->glGetError();
+ if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
+ break;
+ }
funcs->glGetProgramiv(programId, GL_PROGRAM_BINARY_LENGTH, &blobSize);
const int headerSize = FULL_HEADER_SIZE(info.glvendor.size() + info.glrenderer.size() + info.glversion.size());
@@ -357,6 +375,7 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
writeUInt(&p, BINSHADER_MAGIC);
writeUInt(&p, BINSHADER_VERSION);
writeUInt(&p, BINSHADER_QTVERSION);
+ writeUInt(&p, sizeof(quintptr));
writeStr(&p, info.glvendor);
writeStr(&p, info.glrenderer);
diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp
index 2ae9c6d327..c39177080d 100644
--- a/src/gui/opengl/qopenglshaderprogram.cpp
+++ b/src/gui/opengl/qopenglshaderprogram.cpp
@@ -39,7 +39,7 @@
#include "qopenglshaderprogram.h"
#include "qopenglprogrambinarycache_p.h"
-#include "qopenglfunctions.h"
+#include "qopenglextrafunctions.h"
#include "private/qopenglcontext_p.h"
#include <QtCore/private/qobject_p.h>
#include <QtCore/qdebug.h>
@@ -170,13 +170,13 @@ QT_BEGIN_NAMESPACE
\value Vertex Vertex shader written in the OpenGL Shading Language (GLSL).
\value Fragment Fragment shader written in the OpenGL Shading Language (GLSL).
\value Geometry Geometry shaders written in the OpenGL Shading Language (GLSL)
- based on the OpenGL core feature (requires OpenGL >= 3.2).
+ (requires OpenGL >= 3.2 or OpenGL ES >= 3.2).
\value TessellationControl Tessellation control shaders written in the OpenGL
- shading language (GLSL), based on the core feature (requires OpenGL >= 4.0).
+ shading language (GLSL) (requires OpenGL >= 4.0 or OpenGL ES >= 3.2).
\value TessellationEvaluation Tessellation evaluation shaders written in the OpenGL
- shading language (GLSL), based on the core feature (requires OpenGL >= 4.0).
- \value Compute Compute shaders written in the OpenGL shading language (GLSL),
- based on the core feature (requires OpenGL >= 4.3).
+ shading language (GLSL) (requires OpenGL >= 4.0 or OpenGL ES >= 3.2).
+ \value Compute Compute shaders written in the OpenGL shading language (GLSL)
+ (requires OpenGL >= 4.3 or OpenGL ES >= 3.1).
*/
Q_LOGGING_CATEGORY(DBG_SHADER_CACHE, "qt.opengl.diskcache")
@@ -223,26 +223,18 @@ static inline bool isFormatGLES(const QSurfaceFormat &f)
static inline bool supportsGeometry(const QSurfaceFormat &f)
{
-#ifndef QT_OPENGL_ES_2
- if (!isFormatGLES(f))
- return (f.version() >= qMakePair<int, int>(3, 2));
- else
- return false;
-#else
- Q_UNUSED(f);
- return false;
-#endif
+ return f.version() >= qMakePair(3, 2);
}
static inline bool supportsCompute(const QSurfaceFormat &f)
{
#ifndef QT_OPENGL_ES_2
if (!isFormatGLES(f))
- return (f.version() >= qMakePair<int, int>(4, 3));
+ return f.version() >= qMakePair(4, 3);
else
- return (f.version() >= qMakePair<int, int>(3, 1));
+ return f.version() >= qMakePair(3, 1);
#else
- return (f.version() >= qMakePair<int, int>(3, 1));
+ return f.version() >= qMakePair(3, 1);
#endif
}
@@ -250,12 +242,11 @@ static inline bool supportsTessellation(const QSurfaceFormat &f)
{
#ifndef QT_OPENGL_ES_2
if (!isFormatGLES(f))
- return (f.version() >= qMakePair<int, int>(4, 0));
+ return f.version() >= qMakePair(4, 0);
else
- return false;
+ return f.version() >= qMakePair(3, 2);
#else
- Q_UNUSED(f);
- return false;
+ return f.version() >= qMakePair(3, 2);
#endif
}
@@ -267,7 +258,7 @@ public:
: shaderGuard(0)
, shaderType(type)
, compiled(false)
- , glfuncs(new QOpenGLFunctions(ctx))
+ , glfuncs(new QOpenGLExtraFunctions(ctx))
, supportsGeometryShaders(false)
, supportsTessellationShaders(false)
, supportsComputeShaders(false)
@@ -286,7 +277,7 @@ public:
bool compiled;
QString log;
- QOpenGLFunctions *glfuncs;
+ QOpenGLExtraFunctions *glfuncs;
// Support for geometry shaders
bool supportsGeometryShaders;
@@ -496,6 +487,13 @@ static const char redefineHighp[] =
"#endif\n";
#endif
+// Boiler-plate header to have the layout attributes available we need later
+static const char blendEquationAdvancedHeader[] =
+ "#ifdef GL_KHR_blend_equation_advanced\n"
+ "#extension GL_ARB_fragment_coord_conventions : enable\n"
+ "#extension GL_KHR_blend_equation_advanced : enable\n"
+ "#endif\n";
+
struct QVersionDirectivePosition
{
Q_DECL_CONSTEXPR QVersionDirectivePosition(int position = 0, int line = -1)
@@ -636,6 +634,10 @@ bool QOpenGLShader::compileSourceCode(const char *source)
}
}
}
+ if (d->shaderType == Fragment) {
+ sourceChunks.append(blendEquationAdvancedHeader);
+ sourceChunkLengths.append(GLint(sizeof(blendEquationAdvancedHeader) - 1));
+ }
// The precision qualifiers are useful on OpenGL/ES systems,
// but usually not present on desktop systems.
@@ -791,7 +793,7 @@ public:
, linked(false)
, inited(false)
, removingShaders(false)
- , glfuncs(new QOpenGLFunctions)
+ , glfuncs(new QOpenGLExtraFunctions)
#ifndef QT_OPENGL_ES_2
, tessellationFuncs(0)
#endif
@@ -809,10 +811,9 @@ public:
QList<QOpenGLShader *> shaders;
QList<QOpenGLShader *> anonShaders;
- QOpenGLFunctions *glfuncs;
-
+ QOpenGLExtraFunctions *glfuncs;
#ifndef QT_OPENGL_ES_2
- // Tessellation shader support
+ // for tessellation features not in GLES 3.2
QOpenGLFunctions_4_0_Core *tessellationFuncs;
#endif
@@ -902,10 +903,7 @@ bool QOpenGLShaderProgram::init()
d->glfuncs->initializeOpenGLFunctions();
#ifndef QT_OPENGL_ES_2
- // Resolve OpenGL 4 functions for tessellation shader support
- QSurfaceFormat format = context->format();
- if (!context->isOpenGLES()
- && format.version() >= qMakePair<int, int>(4, 0)) {
+ if (!context->isOpenGLES() && context->format().version() >= qMakePair(4, 0)) {
d->tessellationFuncs = context->versionFunctions<QOpenGLFunctions_4_0_Core>();
d->tessellationFuncs->initializeOpenGLFunctions();
}
@@ -3497,13 +3495,8 @@ int QOpenGLShaderProgram::maxGeometryOutputVertices() const
*/
void QOpenGLShaderProgram::setPatchVertexCount(int count)
{
-#ifndef QT_OPENGL_ES_2
Q_D(QOpenGLShaderProgram);
- if (d->tessellationFuncs)
- d->tessellationFuncs->glPatchParameteri(GL_PATCH_VERTICES, count);
-#else
- Q_UNUSED(count);
-#endif
+ d->glfuncs->glPatchParameteri(GL_PATCH_VERTICES, count);
}
/*!
@@ -3516,15 +3509,10 @@ void QOpenGLShaderProgram::setPatchVertexCount(int count)
*/
int QOpenGLShaderProgram::patchVertexCount() const
{
-#ifndef QT_OPENGL_ES_2
int patchVertices = 0;
Q_D(const QOpenGLShaderProgram);
- if (d->tessellationFuncs)
- d->tessellationFuncs->glGetIntegerv(GL_PATCH_VERTICES, &patchVertices);
+ d->glfuncs->glGetIntegerv(GL_PATCH_VERTICES, &patchVertices);
return patchVertices;
-#else
- return 0;
-#endif
}
/*!
@@ -3542,6 +3530,9 @@ int QOpenGLShaderProgram::patchVertexCount() const
function when needed, as QOpenGLShaderProgram will not apply this for
you. This is purely a convenience function.
+ \note This function is only available with OpenGL >= 4.0 and is not supported
+ with OpenGL ES 3.2.
+
\sa defaultOuterTessellationLevels(), setDefaultInnerTessellationLevels()
*/
void QOpenGLShaderProgram::setDefaultOuterTessellationLevels(const QVector<float> &levels)
@@ -3579,6 +3570,9 @@ void QOpenGLShaderProgram::setDefaultOuterTessellationLevels(const QVector<float
\note This returns the global OpenGL state value. It is not specific to
this QOpenGLShaderProgram instance.
+ \note This function is only supported with OpenGL >= 4.0 and will not
+ return valid results with OpenGL ES 3.2.
+
\sa setDefaultOuterTessellationLevels(), defaultInnerTessellationLevels()
*/
QVector<float> QOpenGLShaderProgram::defaultOuterTessellationLevels() const
@@ -3609,6 +3603,9 @@ QVector<float> QOpenGLShaderProgram::defaultOuterTessellationLevels() const
function when needed, as QOpenGLShaderProgram will not apply this for
you. This is purely a convenience function.
+ \note This function is only available with OpenGL >= 4.0 and is not supported
+ with OpenGL ES 3.2.
+
\sa defaultInnerTessellationLevels(), setDefaultOuterTessellationLevels()
*/
void QOpenGLShaderProgram::setDefaultInnerTessellationLevels(const QVector<float> &levels)
@@ -3646,6 +3643,9 @@ void QOpenGLShaderProgram::setDefaultInnerTessellationLevels(const QVector<float
\note This returns the global OpenGL state value. It is not specific to
this QOpenGLShaderProgram instance.
+ \note This function is only supported with OpenGL >= 4.0 and will not
+ return valid results with OpenGL ES 3.2.
+
\sa setDefaultInnerTessellationLevels(), defaultOuterTessellationLevels()
*/
QVector<float> QOpenGLShaderProgram::defaultInnerTessellationLevels() const
diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp
index 27aa8db33a..8de0b25fee 100644
--- a/src/gui/opengl/qopengltexturecache.cpp
+++ b/src/gui/opengl/qopengltexturecache.cpp
@@ -38,39 +38,14 @@
****************************************************************************/
#include "qopengltexturecache_p.h"
+#include "qopengltextureuploader_p.h"
#include <qmath.h>
#include <qopenglfunctions.h>
-#include <private/qopenglcontext_p.h>
-#include <private/qopenglextensions_p.h>
#include <private/qimagepixmapcleanuphooks_p.h>
#include <qpa/qplatformpixmap.h>
QT_BEGIN_NAMESPACE
-#ifndef GL_RED
-#define GL_RED 0x1903
-#endif
-
-#ifndef GL_RGB10_A2
-#define GL_RGB10_A2 0x8059
-#endif
-
-#ifndef GL_BGR
-#define GL_BGR 0x80E0
-#endif
-
-#ifndef GL_BGRA
-#define GL_BGRA 0x80E1
-#endif
-
-#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
-#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
-#endif
-
-#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
-#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
-#endif
-
class QOpenGLTextureCacheWrapper
{
public:
@@ -130,7 +105,7 @@ QOpenGLTextureCache::~QOpenGLTextureCache()
{
}
-GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, BindOptions options)
+GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureUploader::BindOptions options)
{
if (pixmap.isNull())
return 0;
@@ -153,7 +128,7 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &
return id;
}
-GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options)
+GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureUploader::BindOptions options)
{
if (image.isNull())
return 0;
@@ -170,16 +145,8 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i
}
QImage img = image;
- if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
- // Scale the pixmap if needed. GL textures needs to have the
- // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
- // 2.0 or use the GL_TEXTURE_RECTANGLE texture target
- int tx_w = qNextPowerOfTwo(image.width() - 1);
- int tx_h = qNextPowerOfTwo(image.height() - 1);
- if (tx_w != image.width() || tx_h != image.height()) {
- img = img.scaled(tx_w, tx_h);
- }
- }
+ if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures))
+ options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
GLuint id = bindTexture(context, key, img, options);
if (id > 0)
@@ -188,164 +155,16 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i
return id;
}
-GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, BindOptions options)
+GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options)
{
GLuint id;
QOpenGLFunctions *funcs = context->functions();
funcs->glGenTextures(1, &id);
funcs->glBindTexture(GL_TEXTURE_2D, id);
- QImage tx;
- GLenum externalFormat;
- GLenum internalFormat;
- GLuint pixelType;
- QImage::Format targetFormat = QImage::Format_Invalid;
- const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
-
- switch (image.format()) {
- case QImage::Format_RGB32:
- case QImage::Format_ARGB32:
- case QImage::Format_ARGB32_Premultiplied:
- if (isOpenGL12orBetter) {
- externalFormat = GL_BGRA;
- internalFormat = GL_RGBA;
- pixelType = GL_UNSIGNED_INT_8_8_8_8_REV;
- } else {
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian.
- break;
-#endif
- if (static_cast<QOpenGLExtensions*>(context->functions())->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat)) {
- // GL_EXT_bgra or GL_EXT_texture_format_BGRA8888 extensions.
- if (context->isOpenGLES()) {
- // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
- externalFormat = internalFormat = GL_BGRA;
- } else {
- // OpenGL BGRA/BGR format is not allowed as an internal format
- externalFormat = GL_BGRA;
- internalFormat = GL_RGBA;
- }
- pixelType = GL_UNSIGNED_BYTE;
- } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
- // Is only allowed as an external format like OpenGL.
- externalFormat = GL_BGRA;
- internalFormat = GL_RGBA;
- pixelType = GL_UNSIGNED_BYTE;
- } else {
- // No support for direct ARGB32 upload.
- break;
- }
- }
- targetFormat = image.format();
- break;
- case QImage::Format_BGR30:
- case QImage::Format_A2BGR30_Premultiplied:
- if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) {
- pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
- externalFormat = GL_RGBA;
- internalFormat = GL_RGB10_A2;
- targetFormat = image.format();
- }
- break;
- case QImage::Format_RGB30:
- case QImage::Format_A2RGB30_Premultiplied:
- if (isOpenGL12orBetter) {
- pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
- externalFormat = GL_BGRA;
- internalFormat = GL_RGB10_A2;
- targetFormat = image.format();
- } else if (context->isOpenGLES() && context->format().majorVersion() >= 3) {
- pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
- externalFormat = GL_RGBA;
- internalFormat = GL_RGB10_A2;
- targetFormat = QImage::Format_A2BGR30_Premultiplied;
- }
- break;
- case QImage::Format_RGB444:
- case QImage::Format_RGB555:
- case QImage::Format_RGB16:
- if (isOpenGL12orBetter || context->isOpenGLES()) {
- externalFormat = internalFormat = GL_RGB;
- pixelType = GL_UNSIGNED_SHORT_5_6_5;
- targetFormat = QImage::Format_RGB16;
- }
- break;
- case QImage::Format_RGB666:
- case QImage::Format_RGB888:
- externalFormat = internalFormat = GL_RGB;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = QImage::Format_RGB888;
- break;
- case QImage::Format_RGBX8888:
- case QImage::Format_RGBA8888:
- case QImage::Format_RGBA8888_Premultiplied:
- externalFormat = internalFormat = GL_RGBA;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- break;
- case QImage::Format_Indexed8:
- if (options & UseRedFor8BitBindOption) {
- externalFormat = internalFormat = GL_RED;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- }
- break;
- case QImage::Format_Alpha8:
- if (options & UseRedFor8BitBindOption) {
- externalFormat = internalFormat = GL_RED;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
- externalFormat = internalFormat = GL_ALPHA;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- }
- break;
- case QImage::Format_Grayscale8:
- if (options & UseRedFor8BitBindOption) {
- externalFormat = internalFormat = GL_RED;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
- externalFormat = internalFormat = GL_LUMINANCE;
- pixelType = GL_UNSIGNED_BYTE;
- targetFormat = image.format();
- }
- break;
- default:
- break;
- }
-
- if (targetFormat == QImage::Format_Invalid) {
- externalFormat = internalFormat = GL_RGBA;
- pixelType = GL_UNSIGNED_BYTE;
- if (!image.hasAlphaChannel())
- targetFormat = QImage::Format_RGBX8888;
- else
- targetFormat = QImage::Format_RGBA8888;
- }
-
- if (options & PremultipliedAlphaBindOption) {
- if (targetFormat == QImage::Format_ARGB32)
- targetFormat = QImage::Format_ARGB32_Premultiplied;
- else if (targetFormat == QImage::Format_RGBA8888)
- targetFormat = QImage::Format_RGBA8888_Premultiplied;
- } else {
- if (targetFormat == QImage::Format_ARGB32_Premultiplied)
- targetFormat = QImage::Format_ARGB32;
- else if (targetFormat == QImage::Format_RGBA8888_Premultiplied)
- targetFormat = QImage::Format_RGBA8888;
- }
-
- if (image.format() != targetFormat)
- tx = image.convertToFormat(targetFormat);
- else
- tx = image;
-
- funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, const_cast<const QImage &>(tx).bits());
+ int cost = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options);
- int cost = tx.width() * tx.height() * tx.depth() / (1024 * 8);
- m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost);
+ m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost / 1024);
return id;
}
@@ -371,7 +190,7 @@ static void freeTexture(QOpenGLFunctions *funcs, GLuint id)
funcs->glDeleteTextures(1, &id);
}
-QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context) : m_options(options)
+QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context) : m_options(options)
{
m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture);
}
diff --git a/src/gui/opengl/qopengltexturecache_p.h b/src/gui/opengl/qopengltexturecache_p.h
index b9d7df91e3..88ef06e744 100644
--- a/src/gui/opengl/qopengltexturecache_p.h
+++ b/src/gui/opengl/qopengltexturecache_p.h
@@ -56,6 +56,7 @@
#include <QObject>
#include <QCache>
#include <private/qopenglcontext_p.h>
+#include <private/qopengltextureuploader_p.h>
#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
@@ -70,15 +71,10 @@ public:
QOpenGLTextureCache(QOpenGLContext *);
~QOpenGLTextureCache();
- enum BindOption {
- NoBindOption = 0x0000,
- PremultipliedAlphaBindOption = 0x0001,
- UseRedFor8BitBindOption = 0x0002,
- };
- Q_DECLARE_FLAGS(BindOptions, BindOption)
-
- GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption);
- GLuint bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption);
+ GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap,
+ QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption);
+ GLuint bindTexture(QOpenGLContext *context, const QImage &image,
+ QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption);
void invalidate(qint64 key);
@@ -86,26 +82,24 @@ public:
void freeResource(QOpenGLContext *ctx) override;
private:
- GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureCache::BindOptions options);
+ GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options);
QMutex m_mutex;
QCache<quint64, QOpenGLCachedTexture> m_cache;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureCache::BindOptions)
-
class QOpenGLCachedTexture
{
public:
- QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context);
+ QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context);
~QOpenGLCachedTexture() { m_resource->free(); }
GLuint id() const { return m_resource->id(); }
- QOpenGLTextureCache::BindOptions options() const { return m_options; }
+ QOpenGLTextureUploader::BindOptions options() const { return m_options; }
private:
QOpenGLSharedResourceGuard *m_resource;
- QOpenGLTextureCache::BindOptions m_options;
+ QOpenGLTextureUploader::BindOptions m_options;
};
QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp
index 556d52ef99..e3cbba955d 100644
--- a/src/gui/opengl/qopengltextureglyphcache.cpp
+++ b/src/gui/opengl/qopengltextureglyphcache.cpp
@@ -344,15 +344,25 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height)
{
QString source;
+#ifdef Q_OS_WASM
+ source.append(QLatin1String(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader));
+ source.append(QLatin1String(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader));
+#else
source.append(QLatin1String(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader));
source.append(QLatin1String(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader));
+#endif
m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, source);
}
{
QString source;
+#ifdef Q_OS_WASM
+ source.append(QLatin1String(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader));
+ source.append(QLatin1String(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader));
+#else
source.append(QLatin1String(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader));
source.append(QLatin1String(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader));
+#endif
m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, source);
}
diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp
new file mode 100644
index 0000000000..42e309b733
--- /dev/null
+++ b/src/gui/opengl/qopengltextureuploader.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopengltextureuploader_p.h"
+
+#include <qimage.h>
+#include <qmath.h>
+#include <qopenglfunctions.h>
+#include <private/qopenglcontext_p.h>
+#include <private/qopenglextensions_p.h>
+
+#ifndef GL_RED
+#define GL_RED 0x1903
+#endif
+
+#ifndef GL_GREEN
+#define GL_GREEN 0x1904
+#endif
+
+#ifndef GL_BLUE
+#define GL_BLUE 0x1905
+#endif
+
+#ifndef GL_RGB10_A2
+#define GL_RGB10_A2 0x8059
+#endif
+
+#ifndef GL_RGBA16
+#define GL_RGBA16 0x805B
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#endif
+
+#ifndef GL_TEXTURE_SWIZZLE_RGBA
+#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#endif
+
+#ifndef GL_SRGB
+#define GL_SRGB 0x8C40
+#endif
+#ifndef GL_SRGB_ALPHA
+#define GL_SRGB_ALPHA 0x8C42
+#endif
+
+QT_BEGIN_NAMESPACE
+
+qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &image, QOpenGLTextureUploader::BindOptions options, QSize maxSize)
+{
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QOpenGLExtensions *funcs = static_cast<QOpenGLExtensions*>(context->functions());
+
+ QImage tx;
+ GLenum externalFormat;
+ GLenum internalFormat;
+ GLuint pixelType;
+ QImage::Format targetFormat = QImage::Format_Invalid;
+ const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
+ const bool isOpenGLES3orBetter = context->isOpenGLES() && context->format().majorVersion() >= 3;
+ const bool sRgbBinding = (options & SRgbBindOption);
+ Q_ASSERT(isOpenGL12orBetter || context->isOpenGLES());
+ Q_ASSERT((options & (SRgbBindOption | UseRedFor8BitBindOption)) != (SRgbBindOption | UseRedFor8BitBindOption));
+
+ switch (image.format()) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ if (isOpenGL12orBetter) {
+ externalFormat = GL_BGRA;
+ internalFormat = GL_RGBA;
+ pixelType = GL_UNSIGNED_INT_8_8_8_8_REV;
+ } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA };
+ funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+#else
+ GLint swizzle[4] = { GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED };
+ funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+#endif
+ externalFormat = internalFormat = GL_RGBA;
+ pixelType = GL_UNSIGNED_BYTE;
+ } else {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian.
+ if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) {
+ // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
+ externalFormat = internalFormat = GL_BGRA;
+ pixelType = GL_UNSIGNED_BYTE;
+ } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
+ // Is only allowed as an external format like OpenGL.
+ externalFormat = GL_BGRA;
+ internalFormat = GL_RGBA;
+ pixelType = GL_UNSIGNED_BYTE;
+ } else {
+ // No support for direct ARGB32 upload.
+ break;
+ }
+#else
+ // Big endian requires GL_UNSIGNED_INT_8_8_8_8_REV for ARGB to match BGRA
+ break;
+#endif
+ }
+ targetFormat = image.format();
+ break;
+ case QImage::Format_BGR30:
+ case QImage::Format_A2BGR30_Premultiplied:
+ if (sRgbBinding) {
+ // Always needs conversion
+ break;
+ } else if (isOpenGL12orBetter || isOpenGLES3orBetter) {
+ pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
+ externalFormat = GL_RGBA;
+ internalFormat = GL_RGB10_A2;
+ targetFormat = image.format();
+ }
+ break;
+ case QImage::Format_RGB30:
+ case QImage::Format_A2RGB30_Premultiplied:
+ if (sRgbBinding) {
+ // Always needs conversion
+ break;
+ } else if (isOpenGL12orBetter) {
+ pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
+ externalFormat = GL_BGRA;
+ internalFormat = GL_RGB10_A2;
+ targetFormat = image.format();
+ } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle) && (isOpenGL12orBetter || isOpenGLES3orBetter)) {
+ pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
+ externalFormat = GL_RGBA;
+ internalFormat = GL_RGB10_A2;
+ GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA };
+ funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+ targetFormat = image.format();
+ }
+ break;
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB16:
+ if (isOpenGL12orBetter || context->isOpenGLES()) {
+ externalFormat = internalFormat = GL_RGB;
+ pixelType = GL_UNSIGNED_SHORT_5_6_5;
+ targetFormat = QImage::Format_RGB16;
+ }
+ break;
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ externalFormat = internalFormat = GL_RGB;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = QImage::Format_RGB888;
+ break;
+ case QImage::Format_RGBX8888:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBA8888_Premultiplied:
+ externalFormat = internalFormat = GL_RGBA;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ break;
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ externalFormat = internalFormat = GL_RGBA;
+ if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3))
+ internalFormat = GL_RGBA16;
+ pixelType = GL_UNSIGNED_SHORT;
+ targetFormat = image.format();
+ break;
+ case QImage::Format_Indexed8:
+ if (sRgbBinding) {
+ // Always needs conversion
+ break;
+ } else if (options & UseRedFor8BitBindOption) {
+ externalFormat = internalFormat = GL_RED;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ }
+ break;
+ case QImage::Format_Alpha8:
+ if (sRgbBinding) {
+ // Always needs conversion
+ break;
+ } else if (options & UseRedFor8BitBindOption) {
+ externalFormat = internalFormat = GL_RED;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
+ externalFormat = internalFormat = GL_ALPHA;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
+ GLint swizzle[4] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED };
+ funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+ externalFormat = internalFormat = GL_RED;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ }
+ break;
+ case QImage::Format_Grayscale8:
+ if (sRgbBinding) {
+ // Always needs conversion
+ break;
+ } else if (options & UseRedFor8BitBindOption) {
+ externalFormat = internalFormat = GL_RED;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
+ externalFormat = internalFormat = GL_LUMINANCE;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
+ GLint swizzle[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
+ funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
+ externalFormat = internalFormat = GL_RED;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = image.format();
+ }
+ break;
+ default:
+ break;
+ }
+
+ // If no direct upload was detected above, convert to RGBA8888 and upload that
+ if (targetFormat == QImage::Format_Invalid) {
+ externalFormat = internalFormat = GL_RGBA;
+ pixelType = GL_UNSIGNED_BYTE;
+ if (!image.hasAlphaChannel())
+ targetFormat = QImage::Format_RGBX8888;
+ else
+ targetFormat = QImage::Format_RGBA8888;
+ }
+
+ if (options & PremultipliedAlphaBindOption) {
+ if (targetFormat == QImage::Format_ARGB32)
+ targetFormat = QImage::Format_ARGB32_Premultiplied;
+ else if (targetFormat == QImage::Format_RGBA8888)
+ targetFormat = QImage::Format_RGBA8888_Premultiplied;
+ else if (targetFormat == QImage::Format_RGBA64)
+ targetFormat = QImage::Format_RGBA64_Premultiplied;
+ } else {
+ if (targetFormat == QImage::Format_ARGB32_Premultiplied)
+ targetFormat = QImage::Format_ARGB32;
+ else if (targetFormat == QImage::Format_RGBA8888_Premultiplied)
+ targetFormat = QImage::Format_RGBA8888;
+ else if (targetFormat == QImage::Format_RGBA64_Premultiplied)
+ targetFormat = QImage::Format_RGBA64;
+ }
+
+ if (sRgbBinding) {
+ Q_ASSERT(internalFormat == GL_RGBA || internalFormat == GL_RGB);
+ if (image.hasAlphaChannel())
+ internalFormat = GL_SRGB_ALPHA;
+ else
+ internalFormat = GL_SRGB;
+ }
+
+ if (image.format() != targetFormat)
+ tx = image.convertToFormat(targetFormat);
+ else
+ tx = image;
+
+ QSize newSize = tx.size();
+ if (!maxSize.isEmpty())
+ newSize = newSize.boundedTo(maxSize);
+ if (options & PowerOfTwoBindOption) {
+ newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1));
+ newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1));
+ }
+
+ if (newSize != tx.size())
+ tx = tx.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ // Handle cases where the QImage is actually a sub image of its image data:
+ qsizetype naturalBpl = ((qsizetype(tx.width()) * tx.depth() + 31) >> 5) << 2;
+ if (tx.bytesPerLine() != naturalBpl)
+ tx = tx.copy(tx.rect());
+
+ funcs->glTexImage2D(target, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, tx.constBits());
+
+ qsizetype cost = qint64(tx.width()) * tx.height() * tx.depth() / 8;
+
+ return cost;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopengltextureuploader_p.h b/src/gui/opengl/qopengltextureuploader_p.h
new file mode 100644
index 0000000000..d758b3787b
--- /dev/null
+++ b/src/gui/opengl/qopengltextureuploader_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QOPENGLTEXTUREUPLOADER_P_H
+#define QOPENGLTEXTUREUPLOADER_P_H
+
+#include <QtCore/qsize.h>
+#include <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/private/qopenglcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImage;
+
+class Q_GUI_EXPORT QOpenGLTextureUploader
+{
+public:
+ enum BindOption {
+ NoBindOption = 0x0000,
+ PremultipliedAlphaBindOption = 0x0001,
+ UseRedFor8BitBindOption = 0x0002,
+ SRgbBindOption = 0x0004,
+ PowerOfTwoBindOption = 0x0008
+ };
+ Q_DECLARE_FLAGS(BindOptions, BindOption)
+ Q_FLAGS(BindOptions)
+
+ static qsizetype textureImage(GLenum target, const QImage &image, BindOptions options, QSize maxSize = QSize());
+
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureUploader::BindOptions)
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/gui/painting/WEBGRADIENTS_LICENSE.txt b/src/gui/painting/WEBGRADIENTS_LICENSE.txt
new file mode 100644
index 0000000000..1a4d527a4d
--- /dev/null
+++ b/src/gui/painting/WEBGRADIENTS_LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 itmeo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index 29bef14f0c..c3585a4647 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -99,8 +99,13 @@ SOURCES += \
painting/qplatformbackingstore.cpp \
painting/qpathsimplifier.cpp
+webgradients.files = painting/webgradients.binaryjson
+webgradients.prefix = qgradient
+webgradients.base = painting
+
RESOURCES += \
- painting/qpdf.qrc
+ painting/qpdf.qrc \
+ webgradients
darwin {
HEADERS += painting/qcoregraphics_p.h
@@ -122,7 +127,7 @@ SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp
SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \
painting/qimagescale_sse4.cpp
-AVX2_SOURCES += painting/qdrawhelper_avx2.cpp
+ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp
NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp
NEON_HEADERS += painting/qdrawhelper_neon_p.h
diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
index a4a091a29f..2dd5144e40 100644
--- a/src/gui/painting/qblendfunctions.cpp
+++ b/src/gui/painting/qblendfunctions.cpp
@@ -430,28 +430,28 @@ struct Blend_RGB32_on_RGB32_ConstAlpha {
};
struct Blend_ARGB32_on_ARGB32_SourceAlpha {
- inline void write(quint32 *dst, quint32 src) {
- *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ inline void write(quint32 *dst, quint32 src)
+ {
+ blend_pixel(*dst, src);
}
inline void flush(void *) {}
};
struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha {
- inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha) {
+ inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha)
+ {
m_alpha = (alpha * 255) >> 8;
- m_ialpha = 255 - m_alpha;
}
- inline void write(quint32 *dst, quint32 src) {
- src = BYTE_MUL(src, m_alpha);
- *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ inline void write(quint32 *dst, quint32 src)
+ {
+ blend_pixel(*dst, src, m_alpha);
}
inline void flush(void *) {}
quint32 m_alpha;
- quint32 m_ialpha;
};
void qt_scale_image_rgb32_on_rgb32(uchar *destPixels, int dbpl,
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index 5c13308d94..860653cc4c 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -46,9 +46,13 @@
#include "qvariant.h"
#include "qline.h"
#include "qdebug.h"
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonarray.h>
#include <QtCore/qcoreapplication.h>
#include "private/qhexstring_p.h"
#include <QtCore/qnumeric.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
@@ -1073,7 +1077,10 @@ QDataStream &operator<<(QDataStream &s, const QBrush &b)
s << type_as_int;
if (s.version() >= QDataStream::Qt_4_3) {
s << int(gradient->spread());
- s << int(gradient->coordinateMode());
+ QGradient::CoordinateMode co_mode = gradient->coordinateMode();
+ if (s.version() < QDataStream::Qt_5_12 && co_mode == QGradient::ObjectMode)
+ co_mode = QGradient::ObjectBoundingMode;
+ s << int(co_mode);
}
if (s.version() >= QDataStream::Qt_4_5)
@@ -1330,6 +1337,72 @@ QGradient::QGradient()
{
}
+/*!
+ \enum QGradient::Preset
+ \since 5.12
+
+ This enum specifies a set of predefined presets for QGradient,
+ based on the gradients from https://webgradients.com/.
+*/
+
+/*!
+ \fn QGradient::QGradient(QGradient::Preset preset)
+ \since 5.12
+
+ Constructs a gradient based on a predefined \a preset.
+
+ The coordinate mode of the resulting gradient is
+ QGradient::ObjectMode, allowing the preset
+ to be applied to arbitrary object sizes.
+*/
+QGradient::QGradient(Preset preset)
+ : QGradient()
+{
+ static QHash<int, QGradient> cachedPresets;
+ static QMutex cacheMutex;
+ QMutexLocker locker(&cacheMutex);
+ if (cachedPresets.contains(preset)) {
+ const QGradient &cachedPreset = cachedPresets.value(preset);
+ m_type = cachedPreset.m_type;
+ m_data = cachedPreset.m_data;
+ m_stops = cachedPreset.m_stops;
+ m_spread = cachedPreset.m_spread;
+ dummy = cachedPreset.dummy;
+ } else {
+ static QJsonDocument jsonPresets = []() {
+ QFile webGradients(QLatin1String(":/qgradient/webgradients.binaryjson"));
+ webGradients.open(QFile::ReadOnly);
+ return QJsonDocument::fromBinaryData(webGradients.readAll());
+ }();
+
+ const QJsonValue presetData = jsonPresets[preset - 1];
+ if (!presetData.isObject())
+ return;
+
+ m_type = LinearGradient;
+ setCoordinateMode(ObjectMode);
+ setSpread(PadSpread);
+
+ const QJsonValue start = presetData[QLatin1Literal("start")];
+ const QJsonValue end = presetData[QLatin1Literal("end")];
+ m_data.linear.x1 = start[QLatin1Literal("x")].toDouble();
+ m_data.linear.y1 = start[QLatin1Literal("y")].toDouble();
+ m_data.linear.x2 = end[QLatin1Literal("x")].toDouble();
+ m_data.linear.y2 = end[QLatin1Literal("y")].toDouble();
+
+ for (const QJsonValue &stop : presetData[QLatin1String("stops")].toArray()) {
+ setColorAt(stop[QLatin1Literal("position")].toDouble(),
+ QColor(QRgb(stop[QLatin1Literal("color")].toInt())));
+ }
+
+ cachedPresets.insert(preset, *this);
+ }
+}
+
+QT_END_NAMESPACE
+static void initGradientPresets() { Q_INIT_RESOURCE(qmake_webgradients); }
+Q_CONSTRUCTOR_FUNCTION(initGradientPresets);
+QT_BEGIN_NAMESPACE
/*!
\enum QGradient::Type
@@ -1492,14 +1565,19 @@ QGradientStops QGradient::stops() const
\value LogicalMode This is the default mode. The gradient coordinates
are specified logical space just like the object coordinates.
+ \value ObjectMode In this mode the gradient coordinates are
+ relative to the bounding rectangle of the object being drawn, with
+ (0,0) in the top left corner, and (1,1) in the bottom right corner
+ of the object's bounding rectangle. This value was added in Qt
+ 5.12.
\value StretchToDeviceMode In this mode the gradient coordinates
are relative to the bounding rectangle of the paint device,
with (0,0) in the top left corner, and (1,1) in the bottom right
corner of the paint device.
- \value ObjectBoundingMode In this mode the gradient coordinates are
- relative to the bounding rectangle of the object being drawn, with
- (0,0) in the top left corner, and (1,1) in the bottom right corner
- of the object's bounding rectangle.
+ \value ObjectBoundingMode This mode is the same as ObjectMode, except that
+ the {QBrush::transform()} {brush transform}, if any, is applied relative to
+ the logical space instead of the object space. This enum value is
+ deprecated and should not be used in new code.
*/
/*!
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
index e5cff9cc9b..aee51c526d 100644
--- a/src/gui/painting/qbrush.h
+++ b/src/gui/painting/qbrush.h
@@ -194,7 +194,8 @@ public:
enum CoordinateMode {
LogicalMode,
StretchToDeviceMode,
- ObjectBoundingMode
+ ObjectBoundingMode,
+ ObjectMode
};
Q_ENUM(CoordinateMode)
@@ -203,7 +204,180 @@ public:
ComponentInterpolation
};
+ enum Preset {
+ WarmFlame = 1,
+ NightFade = 2,
+ SpringWarmth = 3,
+ JuicyPeach = 4,
+ YoungPassion = 5,
+ LadyLips = 6,
+ SunnyMorning = 7,
+ RainyAshville = 8,
+ FrozenDreams = 9,
+ WinterNeva = 10,
+ DustyGrass = 11,
+ TemptingAzure = 12,
+ HeavyRain = 13,
+ AmyCrisp = 14,
+ MeanFruit = 15,
+ DeepBlue = 16,
+ RipeMalinka = 17,
+ CloudyKnoxville = 18,
+ MalibuBeach = 19,
+ NewLife = 20,
+ TrueSunset = 21,
+ MorpheusDen = 22,
+ RareWind = 23,
+ NearMoon = 24,
+ WildApple = 25,
+ SaintPetersburg = 26,
+ PlumPlate = 28,
+ EverlastingSky = 29,
+ HappyFisher = 30,
+ Blessing = 31,
+ SharpeyeEagle = 32,
+ LadogaBottom = 33,
+ LemonGate = 34,
+ ItmeoBranding = 35,
+ ZeusMiracle = 36,
+ OldHat = 37,
+ StarWine = 38,
+ HappyAcid = 41,
+ AwesomePine = 42,
+ NewYork = 43,
+ ShyRainbow = 44,
+ MixedHopes = 46,
+ FlyHigh = 47,
+ StrongBliss = 48,
+ FreshMilk = 49,
+ SnowAgain = 50,
+ FebruaryInk = 51,
+ KindSteel = 52,
+ SoftGrass = 53,
+ GrownEarly = 54,
+ SharpBlues = 55,
+ ShadyWater = 56,
+ DirtyBeauty = 57,
+ GreatWhale = 58,
+ TeenNotebook = 59,
+ PoliteRumors = 60,
+ SweetPeriod = 61,
+ WideMatrix = 62,
+ SoftCherish = 63,
+ RedSalvation = 64,
+ BurningSpring = 65,
+ NightParty = 66,
+ SkyGlider = 67,
+ HeavenPeach = 68,
+ PurpleDivision = 69,
+ AquaSplash = 70,
+ SpikyNaga = 72,
+ LoveKiss = 73,
+ CleanMirror = 75,
+ PremiumDark = 76,
+ ColdEvening = 77,
+ CochitiLake = 78,
+ SummerGames = 79,
+ PassionateBed = 80,
+ MountainRock = 81,
+ DesertHump = 82,
+ JungleDay = 83,
+ PhoenixStart = 84,
+ OctoberSilence = 85,
+ FarawayRiver = 86,
+ AlchemistLab = 87,
+ OverSun = 88,
+ PremiumWhite = 89,
+ MarsParty = 90,
+ EternalConstance = 91,
+ JapanBlush = 92,
+ SmilingRain = 93,
+ CloudyApple = 94,
+ BigMango = 95,
+ HealthyWater = 96,
+ AmourAmour = 97,
+ RiskyConcrete = 98,
+ StrongStick = 99,
+ ViciousStance = 100,
+ PaloAlto = 101,
+ HappyMemories = 102,
+ MidnightBloom = 103,
+ Crystalline = 104,
+ PartyBliss = 106,
+ ConfidentCloud = 107,
+ LeCocktail = 108,
+ RiverCity = 109,
+ FrozenBerry = 110,
+ ChildCare = 112,
+ FlyingLemon = 113,
+ NewRetrowave = 114,
+ HiddenJaguar = 115,
+ AboveTheSky = 116,
+ Nega = 117,
+ DenseWater = 118,
+ Seashore = 120,
+ MarbleWall = 121,
+ CheerfulCaramel = 122,
+ NightSky = 123,
+ MagicLake = 124,
+ YoungGrass = 125,
+ ColorfulPeach = 126,
+ GentleCare = 127,
+ PlumBath = 128,
+ HappyUnicorn = 129,
+ AfricanField = 131,
+ SolidStone = 132,
+ OrangeJuice = 133,
+ GlassWater = 134,
+ NorthMiracle = 136,
+ FruitBlend = 137,
+ MillenniumPine = 138,
+ HighFlight = 139,
+ MoleHall = 140,
+ SpaceShift = 142,
+ ForestInei = 143,
+ RoyalGarden = 144,
+ RichMetal = 145,
+ JuicyCake = 146,
+ SmartIndigo = 147,
+ SandStrike = 148,
+ NorseBeauty = 149,
+ AquaGuidance = 150,
+ SunVeggie = 151,
+ SeaLord = 152,
+ BlackSea = 153,
+ GrassShampoo = 154,
+ LandingAircraft = 155,
+ WitchDance = 156,
+ SleeplessNight = 157,
+ AngelCare = 158,
+ CrystalRiver = 159,
+ SoftLipstick = 160,
+ SaltMountain = 161,
+ PerfectWhite = 162,
+ FreshOasis = 163,
+ StrictNovember = 164,
+ MorningSalad = 165,
+ DeepRelief = 166,
+ SeaStrike = 167,
+ NightCall = 168,
+ SupremeSky = 169,
+ LightBlue = 170,
+ MindCrawl = 171,
+ LilyMeadow = 172,
+ SugarLollipop = 173,
+ SweetDessert = 174,
+ MagicRay = 175,
+ TeenParty = 176,
+ FrozenHeat = 177,
+ GagarinView = 178,
+ FabledSunset = 179,
+ PerfectBlue = 180,
+ };
+ Q_ENUM(Preset)
+
QGradient();
+ QGradient(Preset);
Type type() const { return m_type; }
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index c55bcb12c9..1d7375d1df 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -304,11 +304,6 @@ static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
#undef rgb
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-inline bool operator<(const RGBData &data1, const RGBData &data2)
-{ return qstrcmp(data1.name, data2.name) < 0; }
-#endif
-
inline bool operator<(const char *name, const RGBData &data)
{ return qstrcmp(name, data.name) < 0; }
inline bool operator<(const RGBData &data, const char *name)
diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolorprofile_p.h
index ca1786ee6d..425e9abace 100644
--- a/src/gui/painting/qcolorprofile_p.h
+++ b/src/gui/painting/qcolorprofile_p.h
@@ -55,6 +55,11 @@
#include <QtGui/qrgb.h>
#include <QtGui/qrgba64.h>
+#if defined(__SSE2__)
+#include <emmintrin.h>
+#elif defined(__ARM_NEON__) || defined(__ARM_NEON)
+#include <arm_neon.h>
+#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QColorProfile
@@ -67,82 +72,165 @@ public:
QRgba64 toLinear64(QRgb rgb32) const
{
- ushort r = m_toLinear[qRed(rgb32) << 4];
- ushort g = m_toLinear[qGreen(rgb32) << 4];
- ushort b = m_toLinear[qBlue(rgb32) << 4];
+#if defined(__SSE2__)
+ __m128i v = _mm_cvtsi32_si128(rgb32);
+ v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
+ const __m128i vidx = _mm_slli_epi16(v, 4);
+ const int ridx = _mm_extract_epi16(vidx, 2);
+ const int gidx = _mm_extract_epi16(vidx, 1);
+ const int bidx = _mm_extract_epi16(vidx, 0);
+ v = _mm_slli_epi16(v, 8); // a * 256
+ v = _mm_insert_epi16(v, m_toLinear[ridx], 0);
+ v = _mm_insert_epi16(v, m_toLinear[gidx], 1);
+ v = _mm_insert_epi16(v, m_toLinear[bidx], 2);
+ v = _mm_add_epi16(v, _mm_srli_epi16(v, 8));
+ QRgba64 rgba64;
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(&rgba64), v);
+ return rgba64;
+#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint8x8_t v8 = vreinterpret_u8_u32(vmov_n_u32(rgb32));
+ uint16x4_t v16 = vget_low_u16(vmovl_u8(v8));
+ const uint16x4_t vidx = vshl_n_u16(v16, 4);
+ const int ridx = vget_lane_u16(vidx, 2);
+ const int gidx = vget_lane_u16(vidx, 1);
+ const int bidx = vget_lane_u16(vidx, 0);
+ v16 = vshl_n_u16(v16, 8); // a * 256
+ v16 = vset_lane_u16(m_toLinear[ridx], v16, 0);
+ v16 = vset_lane_u16(m_toLinear[gidx], v16, 1);
+ v16 = vset_lane_u16(m_toLinear[bidx], v16, 2);
+ v16 = vadd_u16(v16, vshr_n_u16(v16, 8));
+ return QRgba64::fromRgba64(vget_lane_u64(vreinterpret_u64_u16(v16), 0));
+#else
+ uint r = m_toLinear[qRed(rgb32) << 4];
+ uint g = m_toLinear[qGreen(rgb32) << 4];
+ uint b = m_toLinear[qBlue(rgb32) << 4];
r = r + (r >> 8);
g = g + (g >> 8);
b = b + (b >> 8);
return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257);
+#endif
}
QRgb toLinear(QRgb rgb32) const
{
- uchar r = (m_toLinear[qRed(rgb32) << 4] + 0x80) >> 8;
- uchar g = (m_toLinear[qGreen(rgb32) << 4] + 0x80) >> 8;
- uchar b = (m_toLinear[qBlue(rgb32) << 4] + 0x80) >> 8;
- return qRgba(r, g, b, qAlpha(rgb32));
+ return convertWithTable(rgb32, m_toLinear);
}
QRgba64 toLinear(QRgba64 rgb64) const
{
- ushort r = rgb64.red();
- ushort g = rgb64.green();
- ushort b = rgb64.blue();
- r = r - (r >> 8);
- g = g - (g >> 8);
- b = b - (b >> 8);
- r = m_toLinear[r >> 4];
- g = m_toLinear[g >> 4];
- b = m_toLinear[b >> 4];
- r = r + (r >> 8);
- g = g + (g >> 8);
- b = b + (b >> 8);
- return QRgba64::fromRgba64(r, g, b, rgb64.alpha());
+ return convertWithTable(rgb64, m_toLinear);
}
QRgb fromLinear64(QRgba64 rgb64) const
{
- ushort r = rgb64.red();
- ushort g = rgb64.green();
- ushort b = rgb64.blue();
+#if defined(__SSE2__)
+ __m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
+ v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
+ const __m128i vidx = _mm_srli_epi16(v, 4);
+ const int ridx = _mm_extract_epi16(vidx, 0);
+ const int gidx = _mm_extract_epi16(vidx, 1);
+ const int bidx = _mm_extract_epi16(vidx, 2);
+ v = _mm_insert_epi16(v, m_fromLinear[ridx], 2);
+ v = _mm_insert_epi16(v, m_fromLinear[gidx], 1);
+ v = _mm_insert_epi16(v, m_fromLinear[bidx], 0);
+ v = _mm_add_epi16(v, _mm_set1_epi16(0x80));
+ v = _mm_srli_epi16(v, 8);
+ v = _mm_packus_epi16(v, v);
+ return _mm_cvtsi128_si32(v);
+#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
+ v = vsub_u16(v, vshr_n_u16(v, 8));
+ const uint16x4_t vidx = vshr_n_u16(v, 4);
+ const int ridx = vget_lane_u16(vidx, 0);
+ const int gidx = vget_lane_u16(vidx, 1);
+ const int bidx = vget_lane_u16(vidx, 2);
+ v = vset_lane_u16(m_fromLinear[ridx], v, 2);
+ v = vset_lane_u16(m_fromLinear[gidx], v, 1);
+ v = vset_lane_u16(m_fromLinear[bidx], v, 0);
+ uint8x8_t v8 = vrshrn_n_u16(vcombine_u16(v, v), 8);
+ return vget_lane_u32(vreinterpret_u32_u8(v8), 0);
+#else
+ uint a = rgb64.alpha();
+ uint r = rgb64.red();
+ uint g = rgb64.green();
+ uint b = rgb64.blue();
+ a = a - (a >> 8);
r = r - (r >> 8);
g = g - (g >> 8);
b = b - (b >> 8);
+ a = (a + 0x80) >> 8;
r = (m_fromLinear[r >> 4] + 0x80) >> 8;
g = (m_fromLinear[g >> 4] + 0x80) >> 8;
b = (m_fromLinear[b >> 4] + 0x80) >> 8;
- return qRgba(r, g, b, rgb64.alpha8());
+ return (a << 24) | (r << 16) | (g << 8) | b;
+#endif
}
QRgb fromLinear(QRgb rgb32) const
{
- uchar r = (m_fromLinear[qRed(rgb32) << 4] + 0x80) >> 8;
- uchar g = (m_fromLinear[qGreen(rgb32) << 4] + 0x80) >> 8;
- uchar b = (m_fromLinear[qBlue(rgb32) << 4] + 0x80) >> 8;
- return qRgba(r, g, b, qAlpha(rgb32));
+ return convertWithTable(rgb32, m_fromLinear);
}
QRgba64 fromLinear(QRgba64 rgb64) const
{
+ return convertWithTable(rgb64, m_fromLinear);
+ }
+
+private:
+ QColorProfile() { }
+
+ Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table)
+ {
+ const int r = (table[qRed(rgb32) << 4] + 0x80) >> 8;
+ const int g = (table[qGreen(rgb32) << 4] + 0x80) >> 8;
+ const int b = (table[qBlue(rgb32) << 4] + 0x80) >> 8;
+ return (rgb32 & 0xff000000) | (r << 16) | (g << 8) | b;
+ }
+ Q_ALWAYS_INLINE static QRgba64 convertWithTable(QRgba64 rgb64, const ushort *table)
+ {
+#if defined(__SSE2__)
+ __m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
+ v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
+ const __m128i vidx = _mm_srli_epi16(v, 4);
+ const int ridx = _mm_extract_epi16(vidx, 2);
+ const int gidx = _mm_extract_epi16(vidx, 1);
+ const int bidx = _mm_extract_epi16(vidx, 0);
+ v = _mm_insert_epi16(v, table[ridx], 2);
+ v = _mm_insert_epi16(v, table[gidx], 1);
+ v = _mm_insert_epi16(v, table[bidx], 0);
+ v = _mm_add_epi16(v, _mm_srli_epi16(v, 8));
+ QRgba64 rgba64;
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(&rgba64), v);
+ return rgba64;
+#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
+ v = vsub_u16(v, vshr_n_u16(v, 8));
+ const uint16x4_t vidx = vshr_n_u16(v, 4);
+ const int ridx = vget_lane_u16(vidx, 2);
+ const int gidx = vget_lane_u16(vidx, 1);
+ const int bidx = vget_lane_u16(vidx, 0);
+ v = vset_lane_u16(table[ridx], v, 2);
+ v = vset_lane_u16(table[gidx], v, 1);
+ v = vset_lane_u16(table[bidx], v, 0);
+ v = vadd_u16(v, vshr_n_u16(v, 8));
+ return QRgba64::fromRgba64(vget_lane_u64(vreinterpret_u64_u16(v), 0));
+#else
ushort r = rgb64.red();
ushort g = rgb64.green();
ushort b = rgb64.blue();
r = r - (r >> 8);
g = g - (g >> 8);
b = b - (b >> 8);
- r = m_fromLinear[r >> 4];
- g = m_fromLinear[g >> 4];
- b = m_fromLinear[b >> 4];
+ r = table[r >> 4];
+ g = table[g >> 4];
+ b = table[b >> 4];
r = r + (r >> 8);
g = g + (g >> 8);
b = b + (b >> 8);
return QRgba64::fromRgba64(r, g, b, rgb64.alpha());
+#endif
}
-private:
- QColorProfile() { }
-
// We translate to 0-65280 (255*256) instead to 0-65535 to make simple
// shifting an accurate conversion.
// We translate from 0-4080 (255*16) for the same speed up, and to keep
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index 339a9749b8..027bf23115 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -64,125 +64,354 @@ QT_BEGIN_NAMESPACE
where the source is an array of pixels.
*/
-#if defined __SSE2__
-# define LOAD(ptr) _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr))
+struct Argb32OperationsC
+{
+ typedef QRgb Type;
+ typedef quint8 Scalar;
+ typedef QRgb OptimalType;
+ typedef quint8 OptimalScalar;
+
+ static const Type clear;
+ static bool isOpaque(Type val)
+ { return qAlpha(val) == 255; }
+ static bool isTransparent(Type val)
+ { return qAlpha(val) == 0; }
+ static Scalar scalarFrom8bit(uint8_t a)
+ { return a; }
+ static void memfill(Type *ptr, Type value, qsizetype len)
+ { qt_memfill32(ptr, value, len); }
+ static void memcpy(Type *Q_DECL_RESTRICT dest, const Type *Q_DECL_RESTRICT src, qsizetype len)
+ { ::memcpy(dest, src, len * sizeof(Type)); }
+
+ static OptimalType load(const Type *ptr)
+ { return *ptr; }
+ static OptimalType convert(const Type &val)
+ { return val; }
+ static void store(Type *ptr, OptimalType value)
+ { *ptr = value; }
+ static OptimalType add(OptimalType a, OptimalType b)
+ { return a + b; }
+ static OptimalScalar add(OptimalScalar a, OptimalScalar b)
+ { return a + b; }
+ static OptimalType plus(OptimalType a, OptimalType b)
+ { return comp_func_Plus_one_pixel(a, b); }
+ static OptimalScalar alpha(OptimalType val)
+ { return qAlpha(val); }
+ static OptimalScalar invAlpha(OptimalScalar c)
+ { return 255 - c; }
+ static OptimalScalar invAlpha(OptimalType val)
+ { return alpha(~val); }
+ static OptimalScalar scalar(Scalar v)
+ { return v; }
+ static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a)
+ { return BYTE_MUL(val, a); }
+ static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
+ { return INTERPOLATE_PIXEL_255(x, a1, y, a2); }
+ static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a)
+ { return BYTE_MUL(val, a); }
+ static OptimalScalar multiplyAlpha8bit(OptimalScalar val, uint8_t a)
+ { return qt_div_255(val * a); }
+ static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2)
+ { return INTERPOLATE_PIXEL_255(x, a1, y, a2); }
+};
+
+const Argb32OperationsC::Type Argb32OperationsC::clear = 0;
+
+struct Rgba64OperationsBase
+{
+ typedef QRgba64 Type;
+ typedef quint16 Scalar;
+
+ static const Type clear;
+
+ static bool isOpaque(Type val)
+ { return val.isOpaque(); }
+ static bool isTransparent(Type val)
+ { return val.isTransparent(); }
+ static Scalar scalarFrom8bit(uint8_t a)
+ { return a * 257; }
+
+ static void memfill(Type *ptr, Type value, qsizetype len)
+ { qt_memfill64((quint64*)ptr, value, len); }
+ static void memcpy(Type *Q_DECL_RESTRICT dest, const Type *Q_DECL_RESTRICT src, qsizetype len)
+ { ::memcpy(dest, src, len * sizeof(Type)); }
+};
+
+const Rgba64OperationsBase::Type Rgba64OperationsBase::clear = QRgba64::fromRgba64(0);
+
+struct Rgba64OperationsC : public Rgba64OperationsBase
+{
+ typedef QRgba64 OptimalType;
+ typedef quint16 OptimalScalar;
+
+ static OptimalType load(const Type *ptr)
+ { return *ptr; }
+ static OptimalType convert(const Type &val)
+ { return val; }
+ static void store(Type *ptr, OptimalType value)
+ { *ptr = value; }
+ static OptimalType add(OptimalType a, OptimalType b)
+ { return QRgba64::fromRgba64((quint64)a + (quint64)b); }
+ static OptimalScalar add(OptimalScalar a, OptimalScalar b)
+ { return a + b; }
+ static OptimalType plus(OptimalType a, OptimalType b)
+ { return addWithSaturation(a, b); }
+ static OptimalScalar alpha(OptimalType val)
+ { return val.alpha(); }
+ static OptimalScalar invAlpha(Scalar c)
+ { return 65535 - c; }
+ static OptimalScalar invAlpha(OptimalType val)
+ { return 65535 - alpha(val); }
+ static OptimalScalar scalar(Scalar v)
+ { return v; }
+ static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a)
+ { return multiplyAlpha255(val, a); }
+ static OptimalScalar multiplyAlpha8bit(OptimalScalar val, uint8_t a)
+ { return qt_div_255(val * a); }
+ static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
+ { return interpolate255(x, a1, y, a2); }
+ static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a)
+ { return multiplyAlpha65535(val, a); }
+ static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2)
+ { return interpolate65535(x, a1, y, a2); }
+};
+
+#if defined(__SSE2__)
+struct Rgba64OperationsSSE2 : public Rgba64OperationsBase
+{
+ typedef __m128i OptimalType;
+ typedef __m128i OptimalScalar;
+
+ static OptimalType load(const Type *ptr)
+ {
+ return _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
+ }
+ static OptimalType convert(const Type &value)
+ {
#ifdef Q_PROCESSOR_X86_64
-# define CONVERT(value) _mm_cvtsi64_si128(value)
+ return _mm_cvtsi64_si128(value);
#else
-# define CONVERT(value) LOAD(&value)
+ return load(&value);
#endif
-# define STORE(ptr, value) _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), value)
-# define ADD(p, q) _mm_add_epi32(p, q)
-# define ALPHA(c) _mm_shufflelo_epi16(c, _MM_SHUFFLE(3, 3, 3, 3))
-# define CONST(n) _mm_shufflelo_epi16(_mm_cvtsi32_si128(n), _MM_SHUFFLE(0, 0, 0, 0))
-# define INVALPHA(c) _mm_sub_epi32(CONST(65535), ALPHA(c))
-#elif defined __ARM_NEON__
-# define LOAD(ptr) vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(ptr)))
-# define CONVERT(value) vreinterpret_u16_u64(vmov_n_u64(value))
-# define STORE(ptr, value) vst1_u64(reinterpret_cast<uint64_t *>(ptr), vreinterpret_u64_u16(value))
-# define ADD(p, q) vadd_u16(p, q)
-# define ALPHA(c) vdup_lane_u16(c, 3)
-# define CONST(n) vdup_n_u16(n)
-# define INVALPHA(c) vmvn_u16(ALPHA(c))
-#else
-# define LOAD(ptr) *ptr
-# define CONVERT(value) value
-# define STORE(ptr, value) *ptr = value
-# define ADD(p, q) (p + q)
-# define ALPHA(c) (c).alpha()
-# define CONST(n) n
-# define INVALPHA(c) (65535 - ALPHA(c))
+ }
+ static void store(Type *ptr, OptimalType value)
+ {
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), value);
+ }
+ static OptimalType add(OptimalType a, OptimalType b)
+ {
+ return _mm_add_epi16(a, b);
+ }
+// same as above:
+// static OptimalScalar add(OptimalScalar a, OptimalScalar b)
+ static OptimalType plus(OptimalType a, OptimalType b)
+ {
+ return _mm_adds_epu16(a, b);
+ }
+ static OptimalScalar alpha(OptimalType c)
+ {
+ return _mm_shufflelo_epi16(c, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+ static OptimalScalar invAlpha(Scalar c)
+ {
+ return scalar(65535 - c);
+ }
+ static OptimalScalar invAlpha(OptimalType c)
+ {
+ return _mm_xor_si128(_mm_set1_epi16(-1), alpha(c));
+ }
+ static OptimalScalar scalar(Scalar n)
+ {
+ return _mm_shufflelo_epi16(_mm_cvtsi32_si128(n), _MM_SHUFFLE(0, 0, 0, 0));
+ }
+ static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a)
+ {
+ return multiplyAlpha255(val, a);
+ }
+// same as above:
+// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a)
+ static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
+ {
+ return interpolate255(x, a1, y, a2);
+ }
+ static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a)
+ {
+ return multiplyAlpha65535(val, a);
+ }
+ // a2 is const-ref because otherwise MSVC2015@x86 complains that it can't 16-byte align the argument.
+ static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, const OptimalScalar &a2)
+ {
+ return interpolate65535(x, a1, y, a2);
+ }
+};
#endif
+#if defined(__ARM_NEON__)
+struct Rgba64OperationsNEON : public Rgba64OperationsBase
+{
+ typedef uint16x4_t OptimalType;
+ typedef uint16x4_t OptimalScalar;
+
+ static OptimalType load(const Type *ptr)
+ {
+ return vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(ptr)));
+ }
+ static OptimalType convert(const Type &val)
+ {
+ return vreinterpret_u16_u64(vmov_n_u64(val));
+ }
+ static void store(Type *ptr, OptimalType value)
+ {
+ vst1_u64(reinterpret_cast<uint64_t *>(ptr), vreinterpret_u64_u16(value));
+ }
+ static OptimalType add(OptimalType a, OptimalType b)
+ {
+ return vadd_u16(a, b);
+ }
+// same as above:
+// static OptimalScalar add(OptimalScalar a, OptimalScalar b)
+ static OptimalType plus(OptimalType a, OptimalType b)
+ {
+ return vqadd_u16(a, b);
+ }
+ static OptimalScalar alpha(OptimalType c)
+ {
+ return vdup_lane_u16(c, 3);
+ }
+ static OptimalScalar invAlpha(Scalar c)
+ {
+ return scalar(65535 - c);
+ }
+ static OptimalScalar invAlpha(OptimalType c)
+ {
+ return vmvn_u16(alpha(c));
+ }
+ static OptimalScalar scalar(Scalar n)
+ {
+ return vdup_n_u16(n);
+ }
+ static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a)
+ {
+ return multiplyAlpha255(val, a);
+ }
+// same as above:
+// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a)
+ static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
+ {
+ return interpolate255(x, a1, y, a2);
+ }
+ static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a)
+ {
+ return multiplyAlpha65535(val, a);
+ }
+ static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2)
+ {
+ return interpolate65535(x, a1, y, a2);
+ }
+};
+
+#endif
+
+typedef Argb32OperationsC Argb32Operations;
+#if defined(__SSE2__)
+typedef Rgba64OperationsSSE2 Rgba64Operations;
+#elif defined(__ARM_NEON__)
+typedef Rgba64OperationsNEON Rgba64Operations;
+#else
+typedef Rgba64OperationsC Rgba64Operations;
+#endif
/*
result = 0
d = d * cia
*/
-void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+template<class Ops>
+inline static void comp_func_Clear_template(typename Ops::Type *dest, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- qt_memfill32(dest, 0, length);
- } else {
+ if (const_alpha == 255)
+ Ops::memfill(dest, Ops::clear, length);
+ else {
uint ialpha = 255 - const_alpha;
- for (int i = 0; i < length; ++i)
- dest[i] = BYTE_MUL(dest[i], ialpha);
+ for (int i = 0; i < length; ++i) {
+ Ops::store(&dest[i], Ops::multiplyAlpha8bit(Ops::load(&dest[i]), ialpha));
+ }
}
}
+void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+{
+ comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha)
{
- if (const_alpha == 255) {
- qt_memfill64((quint64*)dest, 0, length);
- } else {
- uint ialpha = 255 - const_alpha;
- for (int i = 0; i < length; ++i)
- STORE(&dest[i], multiplyAlpha255(LOAD(&dest[i]), ialpha));
- }
+ comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha);
}
void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
{
- comp_func_solid_Clear(dest, length, 0, const_alpha);
+ comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
}
void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int length, uint const_alpha)
{
- comp_func_solid_Clear_rgb64(dest, length, QRgba64(), const_alpha);
+ comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha);
}
/*
result = s
dest = s * ca + d * cia
*/
-void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_Source_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- if (const_alpha == 255) {
- qt_memfill32(dest, color, length);
- } else {
- uint ialpha = 255 - const_alpha;
- color = BYTE_MUL(color, const_alpha);
+ if (const_alpha == 255)
+ Ops::memfill(dest, color, length);
+ else {
+ const uint ialpha = 255 - const_alpha;
+ auto s = Ops::multiplyAlpha8bit(Ops::convert(color), const_alpha);
for (int i = 0; i < length; ++i) {
- dest[i] = color + BYTE_MUL(dest[i], ialpha);
+ auto d = Ops::multiplyAlpha8bit(Ops::load(&dest[i]), ialpha);
+ Ops::store(&dest[i], Ops::add(s, d));
}
}
}
-void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_Source_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255)
- qt_memfill64((quint64*)dest, color, length);
+ Ops::memcpy(dest, src, length);
else {
- uint ialpha = 255 - const_alpha;
- auto c = multiplyAlpha255(CONVERT(color), const_alpha);
+ const uint ialpha = 255 - const_alpha;
for (int i = 0; i < length; ++i) {
- STORE(&dest[i], ADD(c, multiplyAlpha255(LOAD(&dest[i]), ialpha)));
+ auto s = Ops::load(src + i);
+ auto d = Ops::load(dest + i);
+ Ops::store(&dest[i], Ops::interpolate8bit(s, const_alpha, d, ialpha));
}
}
}
+void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- ::memcpy(dest, src, size_t(length) * sizeof(uint));
- } else {
- uint ialpha = 255 - const_alpha;
- for (int i = 0; i < length; ++i) {
- dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha);
- }
- }
+ comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255)
- ::memcpy(dest, src, size_t(length) * sizeof(quint64));
- else {
- uint ialpha = 255 - const_alpha;
- for (int i = 0; i < length; ++i) {
- STORE(&dest[i], interpolate255(LOAD(&src[i]), const_alpha, LOAD(&dest[i]), ialpha));
- }
- }
+ comp_func_Source_template<Rgba64Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint)
@@ -207,68 +436,66 @@ void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, ui
= s * ca + d * (sia * ca + cia)
= s * ca + d * (1 - sa*ca)
*/
-void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_SourceOver_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- if ((const_alpha & qAlpha(color)) == 255) {
- qt_memfill32(dest, color, length);
- } else {
+ if (const_alpha == 255 && Ops::isOpaque(color))
+ Ops::memfill(dest, color, length);
+ else {
+ auto c = Ops::convert(color);
if (const_alpha != 255)
- color = BYTE_MUL(color, const_alpha);
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
+ auto cAlpha = Ops::invAlpha(c);
for (int i = 0; i < length; ++i) {
- dest[i] = color + BYTE_MUL(dest[i], qAlpha(~color));
+ auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), cAlpha);
+ Ops::store(&dest[i], Ops::add(c, d));
}
}
}
-void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_SourceOver_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
- if (const_alpha == 255 && color.isOpaque()) {
- qt_memfill64((quint64*)dest, color, length);
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ auto c = src[i];
+ if (Ops::isOpaque(c))
+ Ops::store(&dest[i], Ops::convert(c));
+ else if (!Ops::isTransparent(c)) {
+ auto s = Ops::convert(c);
+ auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), Ops::invAlpha(s));
+ Ops::store(&dest[i], Ops::add(s, d));
+ }
+ }
} else {
- auto c = CONVERT(color);
- if (const_alpha != 255)
- c = multiplyAlpha255(c, const_alpha);
- auto cAlpha = INVALPHA(c);
for (int i = 0; i < length; ++i) {
- STORE(&dest[i], ADD(c, multiplyAlpha65535(LOAD(&dest[i]), cAlpha)));
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), Ops::invAlpha(s));
+ Ops::store(&dest[i], Ops::add(s, d));
}
}
}
+void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- uint s = src[i];
- if (s >= 0xff000000)
- dest[i] = s;
- else if (s != 0)
- dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
- }
- } else {
- for (int i = 0; i < length; ++i) {
- uint s = BYTE_MUL(src[i], const_alpha);
- dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
- }
- }
+ comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- QRgba64 s = src[i];
- if (s.isOpaque())
- dest[i] = s;
- else if (!s.isTransparent())
- STORE(&dest[i], ADD(CONVERT(s), multiplyAlpha65535(LOAD(&dest[i]), 65535 - s.alpha())));
- }
- } else {
- for (int i = 0; i < length; ++i) {
- auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha);
- STORE(&dest[i], ADD(s, multiplyAlpha65535(LOAD(&dest[i]), INVALPHA(s))));
- }
- }
+ comp_func_SourceOver_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -276,128 +503,122 @@ void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
dest = (d + s * dia) * ca + d * cia
= d + s * dia * ca
*/
-void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha != 255)
- color = BYTE_MUL(color, const_alpha);
- for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = d + BYTE_MUL(color, qAlpha(~d));
- }
-}
-
-void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_DestinationOver_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- auto c = CONVERT(color);
+ auto c = Ops::convert(color);
if (const_alpha != 255)
- c = multiplyAlpha255(c, const_alpha);
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- STORE(&dest[i], ADD(d, multiplyAlpha65535(c, INVALPHA(d))));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::multiplyAlpha(c, Ops::invAlpha(d));
+ Ops::store(&dest[i], Ops::add(s, d));
}
}
-void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_DestinationOver_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = d + BYTE_MUL(src[i], qAlpha(~d));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::multiplyAlpha(Ops::load(&src[i]), Ops::invAlpha(d));
+ Ops::store(&dest[i], Ops::add(s, d));
}
} else {
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- uint s = BYTE_MUL(src[i], const_alpha);
- dest[i] = d + BYTE_MUL(s, qAlpha(~d));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ s = Ops::multiplyAlpha(s, Ops::invAlpha(d));
+ Ops::store(&dest[i], Ops::add(s, d));
}
}
}
+void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_DestinationOver_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- STORE(&dest[i], ADD(d, multiplyAlpha65535(LOAD(&src[i]), INVALPHA(d))));
- }
- } else {
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha);
- STORE(&dest[i], ADD(d, multiplyAlpha65535(s, INVALPHA(d))));
- }
- }
+ comp_func_DestinationOver_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
result = s * da
dest = s * da * ca + d * cia
*/
-void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_SourceIn_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
if (const_alpha == 255) {
+ auto c = Ops::convert(color);
for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(color, qAlpha(dest[i]));
+ Ops::store(&dest[i], Ops::multiplyAlpha(c, Ops::alpha(Ops::load(&dest[i]))));
}
} else {
- color = BYTE_MUL(color, const_alpha);
- uint cia = 255 - const_alpha;
+ auto c = Ops::multiplyAlpha8bit(Ops::convert(color), const_alpha);
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(d), d, cia);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(c, Ops::alpha(d), d, cia));
}
}
}
-void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_SourceIn_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
- auto c = CONVERT(color);
for (int i = 0; i < length; ++i) {
- STORE(&dest[i], multiplyAlpha65535(c, dest[i].alpha()));
+ auto s = Ops::load(&src[i]);
+ Ops::store(&dest[i], Ops::multiplyAlpha(s, Ops::alpha(Ops::load(&dest[i]))));
}
} else {
- uint ca = const_alpha * 257;
- auto cia = CONST(65535 - ca);
- auto c = multiplyAlpha65535(CONVERT(color), ca);
+ auto ca = Ops::scalarFrom8bit(const_alpha);
+ auto cia = Ops::invAlpha(ca);
+ auto cav = Ops::scalar(ca);
for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- STORE(&dest[i], interpolate65535(c, ALPHA(d), d, cia));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::multiplyAlpha(Ops::load(&src[i]), cav);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, cia));
}
}
}
+void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_SourceIn_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(src[i], qAlpha(dest[i]));
- }
- } else {
- uint cia = 255 - const_alpha;
- for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- uint s = BYTE_MUL(src[i], const_alpha);
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, cia);
- }
- }
+ comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- STORE(&dest[i], multiplyAlpha65535(LOAD(&src[i]), dest[i].alpha()));
- }
- } else {
- uint ca = const_alpha * 257;
- auto cia = CONST(65535 - ca);
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- auto s = multiplyAlpha65535(LOAD(&src[i]), ca);
- STORE(&dest[i], interpolate65535(s, ALPHA(d), d, cia));
- }
- }
+ comp_func_SourceIn_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -405,128 +626,120 @@ void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q
dest = d * sa * ca + d * cia
= d * (sa * ca + cia)
*/
-void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_DestinationIn_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- uint a = qAlpha(color);
+ auto sa = Ops::alpha(Ops::convert(color));
if (const_alpha != 255) {
- a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
- }
- for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(dest[i], a);
+ sa = Ops::multiplyAlpha8bit(sa, const_alpha);
+ sa = Ops::add(sa, Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)));
}
-}
-void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- uint a = color.alpha();
- uint ca64k = const_alpha * 257;
- if (const_alpha != 255)
- a = qt_div_65535(a * ca64k) + 65535 - ca64k;
for (int i = 0; i < length; ++i) {
- STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), a));
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sa));
}
}
-void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_DestinationIn_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(dest[i], qAlpha(src[i]));
+ auto a = Ops::alpha(Ops::load(&src[i]));
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), a));
}
} else {
- uint cia = 255 - const_alpha;
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
for (int i = 0; i < length; ++i) {
- uint a = BYTE_MUL(qAlpha(src[i]), const_alpha) + cia;
- dest[i] = BYTE_MUL(dest[i], a);
+ auto sa = Ops::multiplyAlpha8bit(Ops::alpha(Ops::load(&src[i])), const_alpha);
+ sa = Ops::add(sa, cia);
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sa));
}
}
}
+void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_DestinationIn_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), src[i].alpha()));
- }
- } else {
- uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
- for (int i = 0; i < length; ++i) {
- uint a = qt_div_65535(src[i].alpha() * ca) + cia;
- STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), a));
- }
- }
+ comp_func_DestinationIn_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
result = s * dia
dest = s * dia * ca + d * cia
*/
-
-void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_SourceOut_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
+ auto c = Ops::convert(color);
if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(color, qAlpha(~dest[i]));
- }
+ for (int i = 0; i < length; ++i)
+ Ops::store(&dest[i], Ops::multiplyAlpha(c, Ops::invAlpha(Ops::load(&dest[i]))));
} else {
- color = BYTE_MUL(color, const_alpha);
- uint cia = 255 - const_alpha;
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, cia);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, cia));
}
}
}
-void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_SourceOut_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(color, 65535 - dest[i].alpha());
+ auto s = Ops::load(&src[i]);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::multiplyAlpha(s, Ops::invAlpha(d)));
}
} else {
- uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
- color = multiplyAlpha65535(color, ca);
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- dest[i] = interpolate65535(color, 65535 - d.alpha(), d, cia);
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, cia));
}
}
}
+void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_SourceOut_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(src[i], qAlpha(~dest[i]));
- }
- } else {
- uint cia = 255 - const_alpha;
- for (int i = 0; i < length; ++i) {
- uint s = BYTE_MUL(src[i], const_alpha);
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, cia);
- }
- }
+ comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(src[i], 65535 - dest[i].alpha());
- }
- } else {
- uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
- for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- QRgba64 s = multiplyAlpha65535(src[i], ca);
- dest[i] = interpolate65535(s, 65535 - d.alpha(), d, cia);
- }
- }
+ comp_func_SourceOut_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -534,56 +747,58 @@ void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
dest = d * sia * ca + d * cia
= d * (sia * ca + cia)
*/
-void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_DestinationOut_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- uint a = qAlpha(~color);
- if (const_alpha != 255)
- a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
- for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(dest[i], a);
+ auto sai = Ops::invAlpha(Ops::convert(color));
+ if (const_alpha != 255) {
+ sai = Ops::multiplyAlpha8bit(sai, const_alpha);
+ sai = Ops::add(sai, Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)));
}
-}
-void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- uint a = 65535 - color.alpha();
- uint ca64k = const_alpha * 257;
- if (const_alpha != 255)
- a = qt_div_65535(a * ca64k) + 65535 - ca64k;
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(dest[i], a);
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sai));
}
}
-void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_DestinationOut_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = BYTE_MUL(dest[i], qAlpha(~src[i]));
+ auto sia = Ops::invAlpha(Ops::load(&src[i]));
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sia));
}
} else {
- uint cia = 255 - const_alpha;
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
for (int i = 0; i < length; ++i) {
- uint sia = BYTE_MUL(qAlpha(~src[i]), const_alpha) + cia;
- dest[i] = BYTE_MUL(dest[i], sia);
+ auto sia = Ops::multiplyAlpha8bit(Ops::invAlpha(Ops::load(&src[i])), const_alpha);
+ sia = Ops::add(sia, cia);
+ Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sia));
}
}
}
+void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_DestinationOut_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(dest[i], 65535 - src[i].alpha());
- }
- } else {
- uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
- for (int i = 0; i < length; ++i) {
- uint a = qt_div_65535((65535 - src[i].alpha()) * ca) + cia;
- dest[i] = multiplyAlpha65535(dest[i], a);
- }
- }
+ comp_func_DestinationOut_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -592,59 +807,57 @@ void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, c
= s*ca * da + d * (sia*ca + cia)
= s*ca * da + d * (1 - sa*ca)
*/
-void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha != 255) {
- color = BYTE_MUL(color, const_alpha);
- }
- uint sia = qAlpha(~color);
- for (int i = 0; i < length; ++i) {
- dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(dest[i]), dest[i], sia);
- }
-}
-
-void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_SourceAtop_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
+ auto c = Ops::convert(color);
if (const_alpha != 255)
- color = multiplyAlpha255(color, const_alpha);
- uint sia = 65535 - color.alpha();
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
+ auto sia = Ops::invAlpha(c);
for (int i = 0; i < length; ++i) {
- dest[i] = interpolate65535(color, dest[i].alpha(), dest[i], sia);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(c, Ops::alpha(d), d, sia));
}
}
-void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_SourceAtop_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- uint s = src[i];
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ auto s = Ops::load(&src[i]);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, Ops::invAlpha(s)));
}
} else {
for (int i = 0; i < length; ++i) {
- uint s = BYTE_MUL(src[i], const_alpha);
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, Ops::invAlpha(s)));
}
}
}
+void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_SourceAtop_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- QRgba64 s = src[i];
- QRgba64 d = dest[i];
- dest[i] = interpolate65535(s, d.alpha(), d, 65535 - s.alpha());
- }
- } else {
- for (int i = 0; i < length; ++i) {
- QRgba64 s = multiplyAlpha255(src[i], const_alpha);
- QRgba64 d = dest[i];
- dest[i] = interpolate65535(s, d.alpha(), d, 65535 - s.alpha());
- }
- }
+ comp_func_SourceAtop_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -652,69 +865,63 @@ void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
dest = d*sa*ca + s*dia*ca + d *cia
= s*ca * dia + d * (sa*ca + cia)
*/
-void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_DestinationAtop_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- uint a = qAlpha(color);
+ auto c = Ops::convert(color);
+ auto sa = Ops::alpha(c);
if (const_alpha != 255) {
- color = BYTE_MUL(color, const_alpha);
- a = qAlpha(color) + 255 - const_alpha;
- }
- for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(d, a, color, qAlpha(~d));
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
+ sa = Ops::add(Ops::alpha(c), cia);
}
-}
-void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- uint a = color.alpha();
- if (const_alpha != 255) {
- color = multiplyAlpha255(color, const_alpha);
- a = color.alpha() + 65535 - (const_alpha * 257);
- }
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- dest[i] = interpolate65535(d, a, color, 65535 - d.alpha());
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, sa));
}
}
-void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_DestinationAtop_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- uint s = src[i];
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(d, qAlpha(s), s, qAlpha(~d));
+ auto s = Ops::load(&src[i]);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::alpha(s)));
}
} else {
- uint cia = 255 - const_alpha;
+ auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha));
for (int i = 0; i < length; ++i) {
- uint s = BYTE_MUL(src[i], const_alpha);
- uint d = dest[i];
- uint a = qAlpha(s) + cia;
- dest[i] = INTERPOLATE_PIXEL_255(d, a, s, qAlpha(~d));
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ auto d = Ops::load(&dest[i]);
+ auto sa = Ops::add(Ops::alpha(s), cia);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, sa));
}
}
}
+void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_DestinationAtop_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- QRgba64 s = src[i];
- QRgba64 d = dest[i];
- dest[i] = interpolate65535(d, s.alpha(), s, 65535 - d.alpha());
- }
- } else {
- uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
- for (int i = 0; i < length; ++i) {
- QRgba64 s = multiplyAlpha65535(src[i], ca);
- QRgba64 d = dest[i];
- uint a = s.alpha() + cia;
- dest[i] = interpolate65535(d, a, s, 65535 - d.alpha());
- }
- }
+ comp_func_DestinationAtop_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
@@ -723,62 +930,58 @@ void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest,
= s*ca * dia + d * (sia*ca + cia)
= s*ca * dia + d * (1 - sa*ca)
*/
-void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_solid_XOR_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
+ auto c = Ops::convert(color);
if (const_alpha != 255)
- color = BYTE_MUL(color, const_alpha);
- uint sia = qAlpha(~color);
+ c = Ops::multiplyAlpha8bit(c, const_alpha);
+ auto sia = Ops::invAlpha(c);
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, sia);
+ auto d = Ops::load(&dest[i]);
+ Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, sia));
}
}
-void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- if (const_alpha != 255)
- color = multiplyAlpha255(color, const_alpha);
- auto s = CONVERT(color);
- auto sia = CONST(65535 - color.alpha());
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, sia));
- }
-}
-
-void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+template<class Ops>
+inline static void comp_func_XOR_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- uint s = src[i];
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::load(&src[i]);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::invAlpha(s)));
}
} else {
for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- uint s = BYTE_MUL(src[i], const_alpha);
- dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha);
+ Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::invAlpha(s)));
}
}
}
+void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha)
+{
+ comp_func_solid_XOR_template<Argb32Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha);
+}
+
+void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha);
+}
+
void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- auto s = LOAD(&src[i]);
- STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, INVALPHA(s)));
- }
- } else {
- for (int i = 0; i < length; ++i) {
- auto d = LOAD(&dest[i]);
- auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha);
- STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, INVALPHA(s)));
- }
- }
+ comp_func_XOR_template<Rgba64Operations>(dest, src, length, const_alpha);
}
struct QFullCoverage {
@@ -827,84 +1030,67 @@ static inline uint mix_alpha_rgb64(uint da, uint sa)
Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
= Sca + Dca
*/
-template <typename T>
-Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int length, uint color, const T &coverage)
+template<class Ops>
+inline static void comp_func_solid_Plus_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha)
{
- uint s = color;
-
- for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- d = comp_func_Plus_one_pixel(d, s);
- coverage.store(&dest[i], d);
+ auto c = Ops::convert(color);
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ auto d = Ops::load(&dest[i]);
+ d = Ops::plus(d, c);
+ Ops::store(&dest[i], d);
+ }
+ } else {
+ uint ia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ auto d = Ops::load(&dest[i]);
+ d = Ops::interpolate8bit(Ops::plus(d, c), const_alpha, d, ia);
+ Ops::store(&dest[i], d);
+ }
}
}
-void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha)
+template<class Ops>
+inline static void comp_func_Plus_template(typename Ops::Type *Q_DECL_RESTRICT dest,
+ const typename Ops::Type *Q_DECL_RESTRICT src,
+ int length, uint const_alpha)
{
- if (const_alpha == 255)
- comp_func_solid_Plus_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Plus_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
-void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- auto b = CONVERT(color);
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- auto a = LOAD(&dest[i]);
- a = addWithSaturation(a, b);
- STORE(&dest[i], a);
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::load(&src[i]);
+ d = Ops::plus(d, s);
+ Ops::store(&dest[i], d);
}
} else {
+ uint ia = 255 - const_alpha;
for (int i = 0; i < length; ++i) {
- auto a = LOAD(&dest[i]);
- auto d = addWithSaturation(a, b);
- a = interpolate255(d, const_alpha, a, 255 - const_alpha);
- STORE(&dest[i], a);
+ auto d = Ops::load(&dest[i]);
+ auto s = Ops::load(&src[i]);
+ d = Ops::interpolate8bit(Ops::plus(d, s), const_alpha, d, ia);
+ Ops::store(&dest[i], d);
}
}
}
-template <typename T>
-Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
+void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha)
{
- for (int i = 0; i < length; ++i) {
- uint d = dest[i];
- uint s = src[i];
-
- d = comp_func_Plus_one_pixel(d, s);
+ comp_func_solid_Plus_template<Argb32Operations>(dest, length, color, const_alpha);
+}
- coverage.store(&dest[i], d);
- }
+void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255)
- comp_func_Plus_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Plus_impl(dest, src, length, QPartialCoverage(const_alpha));
+ comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha);
}
void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- if (const_alpha == 255) {
- for (int i = 0; i < length; ++i) {
- auto a = LOAD(&dest[i]);
- auto b = LOAD(&src[i]);
- a = addWithSaturation(a, b);
- STORE(&dest[i], a);
- }
- } else {
- for (int i = 0; i < length; ++i) {
- auto a = LOAD(&dest[i]);
- auto b = LOAD(&src[i]);
- auto d = addWithSaturation(a, b);
- a = interpolate255(d, const_alpha, a, 255 - const_alpha);
- STORE(&dest[i], a);
- }
- }
+ comp_func_Plus_template<Rgba64Operations>(dest, src, length, const_alpha);
}
/*
diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h
index 68f4e00cdc..082ddee30f 100644
--- a/src/gui/painting/qcosmeticstroker_p.h
+++ b/src/gui/painting/qcosmeticstroker_p.h
@@ -85,6 +85,7 @@ public:
// used to avoid drop outs or duplicated points
enum Direction {
+ NoDirection = 0,
TopToBottom = 0x1,
BottomToTop = 0x2,
LeftToRight = 0x4,
@@ -104,7 +105,7 @@ public:
patternOffset(0),
legacyRounding(false),
current_span(0),
- lastDir(LeftToRight),
+ lastDir(NoDirection),
lastAxisAligned(false)
{ setup(); }
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 0f2d163840..bbeb9fd9ea 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -73,9 +73,6 @@ enum {
half_point = 1 << 15
};
-// must be multiple of 4 for easier SIMD implementations
-static const int buffer_size = 2048;
-
template<QImage::Format> Q_DECL_CONSTEXPR uint redWidth();
template<QImage::Format> Q_DECL_CONSTEXPR uint redShift();
template<QImage::Format> Q_DECL_CONSTEXPR uint greenWidth();
@@ -94,6 +91,10 @@ template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied>
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBX8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB16>() { return 11; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; }
@@ -103,6 +104,15 @@ template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied>
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB6666_Premultiplied>() { return 12; }
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBX8888>() { return 24; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888>() { return 24; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888_Premultiplied>() { return 24; }
+#else
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBX8888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888_Premultiplied>() { return 0; }
+#endif
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB16>() { return 6; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; }
@@ -112,6 +122,10 @@ template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplie
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBX8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB16>() { return 5; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; }
@@ -121,6 +135,15 @@ template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplie
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBX8888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888_Premultiplied>() { return 16; }
+#else
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBX8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+#endif
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB16>() { return 5; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; }
@@ -130,6 +153,10 @@ template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBX8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB16>() { return 0; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; }
@@ -139,6 +166,15 @@ template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB6666_Premultiplied>() { return 0; }
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBX8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+#else
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBX8888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888_Premultiplied>() { return 16; }
+#endif
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB16>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB444>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB555>() { return 0; }
@@ -148,6 +184,10 @@ template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplie
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBX8888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBA8888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; }
+
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB16>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB444>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB555>() { return 0; }
@@ -157,138 +197,200 @@ template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplie
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB6666_Premultiplied>() { return 18; }
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBX8888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888_Premultiplied>() { return 0; }
+#else
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBX8888>() { return 24; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888>() { return 24; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888_Premultiplied>() { return 24; }
+#endif
-template<QImage::Format> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel();
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; }
-template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB6666_Premultiplied>() { return QPixelLayout::BPP24; }
+template<QImage::Format> constexpr QPixelLayout::BPP bitsPerPixel();
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB6666_Premultiplied>() { return QPixelLayout::BPP24; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBX8888>() { return QPixelLayout::BPP32; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBA8888>() { return QPixelLayout::BPP32; }
+template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBA8888_Premultiplied>() { return QPixelLayout::BPP32; }
-template<QImage::Format Format>
-static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+typedef const uint *(QT_FASTCALL *FetchPixelsFunc)(uint *buffer, const uchar *src, int index, int count);
+
+template <QPixelLayout::BPP bpp> static
+uint QT_FASTCALL fetchPixel(const uchar *, int)
{
- auto conversion = [](uint s) {
- // MSVC needs these constexpr defined in here otherwise it will create a capture.
- Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
- Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
- Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+ Q_UNREACHABLE();
+ return 0;
+}
- Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
- Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
- Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index)
+{
+ return (src[index >> 3] >> (index & 7)) & 1;
+}
- Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8;
- Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8;
- Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8;
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index)
+{
+ return (src[index >> 3] >> (~index & 7)) & 1;
+}
- uint red = (s >> redShift<Format>()) & redMask;
- uint green = (s >> greenShift<Format>()) & greenMask;
- uint blue = (s >> blueShift<Format>()) & blueMask;
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP8>(const uchar *src, int index)
+{
+ return src[index];
+}
- red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
- green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
- blue = (blue << blueLeftShift) | (blue >> blueRightShift);
- return 0xff000000 | red | green | blue;
- };
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16>(const uchar *src, int index)
+{
+ return reinterpret_cast<const quint16 *>(src)[index];
+}
- UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion);
- return buffer;
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP24>(const uchar *src, int index)
+{
+ return reinterpret_cast<const quint24 *>(src)[index];
}
-template<QImage::Format Format>
-static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int index)
{
- Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
- Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
- Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+ return reinterpret_cast<const uint *>(src)[index];
+}
- Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
- Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
- Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
+template <>
+inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP64>(const uchar *src, int index)
+{
+ // We have to do the conversion in fetch to fit into a 32bit uint
+ QRgba64 c = reinterpret_cast<const QRgba64 *>(src)[index];
+ return c.toArgb32();
+}
- Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8;
- Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8;
- Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8;
+template <QPixelLayout::BPP bpp>
+static quint64 QT_FASTCALL fetchPixel64(const uchar *src, int index)
+{
+ Q_STATIC_ASSERT(bpp != QPixelLayout::BPP64);
+ return fetchPixel<bpp>(src, index);
+}
- for (int i = 0; i < count; ++i) {
- uint red = (src[i] >> redShift<Format>()) & redMask;
- uint green = (src[i] >> greenShift<Format>()) & greenMask;
- uint blue = (src[i] >> blueShift<Format>()) & blueMask;
+template <QPixelLayout::BPP width> static
+void QT_FASTCALL storePixel(uchar *dest, int index, uint pixel);
- red = ((red << redLeftShift) | (red >> redRightShift));
- green = ((green << greenLeftShift) | (green >> greenRightShift));
- blue = (blue << blueLeftShift) | (blue >> blueRightShift);
- buffer[i] = QRgba64::fromRgba(red, green, blue, 255);
- }
+template <>
+inline void QT_FASTCALL storePixel<QPixelLayout::BPP16>(uchar *dest, int index, uint pixel)
+{
+ reinterpret_cast<quint16 *>(dest)[index] = quint16(pixel);
+}
- return buffer;
+template <>
+inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, uint pixel)
+{
+ reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel);
}
+typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index);
+
+static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = {
+ 0, // BPPNone
+ fetchPixel<QPixelLayout::BPP1MSB>, // BPP1MSB
+ fetchPixel<QPixelLayout::BPP1LSB>, // BPP1LSB
+ fetchPixel<QPixelLayout::BPP8>, // BPP8
+ fetchPixel<QPixelLayout::BPP16>, // BPP16
+ fetchPixel<QPixelLayout::BPP24>, // BPP24
+ fetchPixel<QPixelLayout::BPP32>, // BPP32
+ fetchPixel<QPixelLayout::BPP64> // BPP64
+};
+
template<QImage::Format Format>
-static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static Q_ALWAYS_INLINE uint convertPixelToRGB32(uint s)
{
- Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
- Q_CONSTEXPR uchar alphaLeftShift = 8 - alphaWidth<Format>();
Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
- Q_CONSTEXPR uchar alphaRightShift = 2 * alphaWidth<Format>() - 8;
Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8;
Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8;
Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8;
- Q_CONSTEXPR bool mustMin = (alphaWidth<Format>() != redWidth<Format>()) ||
- (alphaWidth<Format>() != greenWidth<Format>()) ||
- (alphaWidth<Format>() != blueWidth<Format>());
+ uint red = (s >> redShift<Format>()) & redMask;
+ uint green = (s >> greenShift<Format>()) & greenMask;
+ uint blue = (s >> blueShift<Format>()) & blueMask;
- if (mustMin) {
- for (int i = 0; i < count; ++i) {
- uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask;
- uint red = (src[i] >> redShift<Format>()) & redMask;
- uint green = (src[i] >> greenShift<Format>()) & greenMask;
- uint blue = (src[i] >> blueShift<Format>()) & blueMask;
-
- alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift);
- red = qMin(alpha, (red << redLeftShift) | (red >> redRightShift));
- green = qMin(alpha, (green << greenLeftShift) | (green >> greenRightShift));
- blue = qMin(alpha, (blue << blueLeftShift) | (blue >> blueRightShift));
- buffer[i] = (alpha << 24) | (red << 16) | (green << 8) | blue;
- }
- } else {
- for (int i = 0; i < count; ++i) {
- uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask;
- uint red = (src[i] >> redShift<Format>()) & redMask;
- uint green = (src[i] >> greenShift<Format>()) & greenMask;
- uint blue = (src[i] >> blueShift<Format>()) & blueMask;
-
- alpha = ((alpha << alphaLeftShift) | (alpha >> alphaRightShift)) << 24;
- red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
- green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
- blue = (blue << blueLeftShift) | (blue >> blueRightShift);
- buffer[i] = alpha | red | green | blue;
- }
+ red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
+ green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
+ blue = (blue << blueLeftShift) | (blue >> blueRightShift);
+ return 0xff000000 | red | green | blue;
+}
+
+template<QImage::Format Format>
+static void QT_FASTCALL convertToRGB32(uint *buffer, int count, const QVector<QRgb> *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGB32<Format>(buffer[i]);
+}
+
+#if defined(__SSE2__) && !defined(__SSSE3__) && QT_COMPILER_SUPPORTS_SSSE3
+extern const uint * QT_FASTCALL fetchPixelsBPP24_ssse3(uint *dest, const uchar*src, int index, int count);
+#endif
+
+template<QImage::Format Format>
+static const uint *QT_FASTCALL fetchRGBToRGB32(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>();
+#if defined(__SSE2__) && !defined(__SSSE3__) && QT_COMPILER_SUPPORTS_SSSE3
+ if (BPP == QPixelLayout::BPP24 && qCpuHasFeature(SSSE3)) {
+ // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3
+ // to vectorize the deforested version below.
+ fetchPixelsBPP24_ssse3(buffer, src, index, count);
+ convertToRGB32<Format>(buffer, count, nullptr);
+ return buffer;
}
+#endif
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGB32<Format>(fetchPixel<BPP>(src, index + i));
+ return buffer;
+}
+template<QImage::Format Format>
+static Q_ALWAYS_INLINE QRgba64 convertPixelToRGB64(uint s)
+{
+ return QRgba64::fromArgb32(convertPixelToRGB32<Format>(s));
+}
+
+template<QImage::Format Format>
+static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGB64<Format>(src[i]);
return buffer;
}
template<QImage::Format Format>
-static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const QRgba64 *QT_FASTCALL fetchRGBToRGB64(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGB64<Format>(fetchPixel<bitsPerPixel<Format>()>(src, index + i));
+ return buffer;
+}
+
+template<QImage::Format Format>
+static Q_ALWAYS_INLINE uint convertPixelToARGB32PM(uint s)
{
Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
@@ -309,62 +411,101 @@ static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const
(alphaWidth<Format>() != greenWidth<Format>()) ||
(alphaWidth<Format>() != blueWidth<Format>());
+ uint alpha = (s >> alphaShift<Format>()) & alphaMask;
+ uint red = (s >> redShift<Format>()) & redMask;
+ uint green = (s >> greenShift<Format>()) & greenMask;
+ uint blue = (s >> blueShift<Format>()) & blueMask;
+
+ alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift);
+ red = (red << redLeftShift) | (red >> redRightShift);
+ green = (green << greenLeftShift) | (green >> greenRightShift);
+ blue = (blue << blueLeftShift) | (blue >> blueRightShift);
+
if (mustMin) {
- for (int i = 0; i < count; ++i) {
- uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask;
- uint red = (src[i] >> redShift<Format>()) & redMask;
- uint green = (src[i] >> greenShift<Format>()) & greenMask;
- uint blue = (src[i] >> blueShift<Format>()) & blueMask;
-
- alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift);
- red = qMin(alpha, (red << redLeftShift) | (red >> redRightShift));
- green = qMin(alpha, (green << greenLeftShift) | (green >> greenRightShift));
- blue = qMin(alpha, (blue << blueLeftShift) | (blue >> blueRightShift));
- buffer[i] = QRgba64::fromRgba(red, green, blue, alpha);
- }
- } else {
- for (int i = 0; i < count; ++i) {
- uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask;
- uint red = (src[i] >> redShift<Format>()) & redMask;
- uint green = (src[i] >> greenShift<Format>()) & greenMask;
- uint blue = (src[i] >> blueShift<Format>()) & blueMask;
-
- alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift);
- red = (red << redLeftShift) | (red >> redRightShift);
- green = (green << greenLeftShift) | (green >> greenRightShift);
- blue = (blue << blueLeftShift) | (blue >> blueRightShift);
- buffer[i] = QRgba64::fromRgba(red, green, blue, alpha);
- }
+ red = qMin(alpha, red);
+ green = qMin(alpha, green);
+ blue = qMin(alpha, blue);
}
+ return (alpha << 24) | (red << 16) | (green << 8) | blue;
+}
+
+template<QImage::Format Format>
+static void QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToARGB32PM<Format>(buffer[i]);
+}
+
+template<QImage::Format Format>
+static const uint *QT_FASTCALL fetchARGBPMToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>();
+#if defined(__SSE2__) && !defined(__SSSE3__) && QT_COMPILER_SUPPORTS_SSSE3
+ if (BPP == QPixelLayout::BPP24 && qCpuHasFeature(SSSE3)) {
+ // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3
+ // to vectorize the deforested version below.
+ fetchPixelsBPP24_ssse3(buffer, src, index, count);
+ convertARGBPMToARGB32PM<Format>(buffer, count, nullptr);
+ return buffer;
+ }
+#endif
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToARGB32PM<Format>(fetchPixel<BPP>(src, index + i));
+ return buffer;
+}
+
+template<QImage::Format Format>
+static Q_ALWAYS_INLINE QRgba64 convertPixelToRGBA64PM(uint s)
+{
+ return QRgba64::fromArgb32(convertPixelToARGB32PM<Format>(s));
+}
+
+template<QImage::Format Format>
+static const QRgba64 *QT_FASTCALL convertARGBPMToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGB64<Format>(src[i]);
+ return buffer;
+}
+
+template<QImage::Format Format>
+static const QRgba64 *QT_FASTCALL fetchARGBPMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>();
+ for (int i = 0; i < count; ++i)
+ buffer[i] = convertPixelToRGBA64PM<Format>(fetchPixel<bpp>(src, index + i));
return buffer;
}
template<QImage::Format Format, bool fromRGB>
-static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *dither)
+static void QT_FASTCALL storeRGBFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *dither)
{
Q_CONSTEXPR uchar rWidth = redWidth<Format>();
Q_CONSTEXPR uchar gWidth = greenWidth<Format>();
Q_CONSTEXPR uchar bWidth = blueWidth<Format>();
+ constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>();
// RGB32 -> RGB888 is not a precision loss.
if (!dither || (rWidth == 8 && gWidth == 8 && bWidth == 8)) {
- auto conversion = [](uint s) {
- const uint c = fromRGB ? s : qUnpremultiply(s);
- Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1;
- Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1;
- Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1;
- Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
- Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
- Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
+ Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1;
+ Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1;
+ Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1;
+ Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
+ Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
+ Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
+ for (int i = 0; i < count; ++i) {
+ const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]);
const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
- return r | g | b;
+ storePixel<BPP>(dest, index + i, r | g | b);
};
- UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion);
} else {
// We do ordered dither by using a rounding conversion, but instead of
// adding half of input precision, we add the adjusted result from the
@@ -384,38 +525,39 @@ static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *
r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth);
g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth);
b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth);
- buffer[i] = (r << redShift<Format>())
- | (g << greenShift<Format>())
- | (b << blueShift<Format>());
+ const uint s = (r << redShift<Format>())
+ | (g << greenShift<Format>())
+ | (b << blueShift<Format>());
+ storePixel<BPP>(dest, index + i, s);
}
}
- return buffer;
}
template<QImage::Format Format, bool fromRGB>
-static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *dither)
+static void QT_FASTCALL storeARGBPMFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *dither)
{
+ constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>();
if (!dither) {
- auto conversion = [](uint c) {
- Q_CONSTEXPR uint aMask = (1 << alphaWidth<Format>()) - 1;
- Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1;
- Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1;
- Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1;
-
- Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth<Format>();
- Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
- Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
- Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
-
- Q_CONSTEXPR uint aOpaque = aMask << alphaShift<Format>();
+ Q_CONSTEXPR uint aMask = (1 << alphaWidth<Format>()) - 1;
+ Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1;
+ Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1;
+ Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1;
+
+ Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth<Format>();
+ Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
+ Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
+ Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
+
+ Q_CONSTEXPR uint aOpaque = aMask << alphaShift<Format>();
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
const uint a = fromRGB ? aOpaque : (((c >> aRightShift) & aMask) << alphaShift<Format>());
const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
- return a | r | g | b;
+ storePixel<BPP>(dest, index + i, a | r | g | b);
};
- UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion);
} else {
Q_CONSTEXPR uchar aWidth = alphaWidth<Format>();
Q_CONSTEXPR uchar rWidth = redWidth<Format>();
@@ -441,59 +583,147 @@ static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uin
r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth);
g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth);
b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth);
- buffer[i] = (a << alphaShift<Format>())
- | (r << redShift<Format>())
- | (g << greenShift<Format>())
- | (b << blueShift<Format>());
+ uint s = (a << alphaShift<Format>())
+ | (r << redShift<Format>())
+ | (g << greenShift<Format>())
+ | (b << blueShift<Format>());
+ storePixel<BPP>(dest, index + i, s);
}
}
- return buffer;
}
+template<QImage::Format Format>
+static void QT_FASTCALL rbSwap(uchar *dst, const uchar *src, int count)
+{
+ Q_CONSTEXPR uchar aWidth = alphaWidth<Format>();
+ Q_CONSTEXPR uchar aShift = alphaShift<Format>();
+ Q_CONSTEXPR uchar rWidth = redWidth<Format>();
+ Q_CONSTEXPR uchar rShift = redShift<Format>();
+ Q_CONSTEXPR uchar gWidth = greenWidth<Format>();
+ Q_CONSTEXPR uchar gShift = greenShift<Format>();
+ Q_CONSTEXPR uchar bWidth = blueWidth<Format>();
+ Q_CONSTEXPR uchar bShift = blueShift<Format>();
#ifdef Q_COMPILER_CONSTEXPR
+ Q_STATIC_ASSERT(rWidth == bWidth);
+#endif
+ Q_CONSTEXPR uint redBlueMask = (1 << rWidth) - 1;
+ Q_CONSTEXPR uint alphaGreenMask = (((1 << aWidth) - 1) << aShift)
+ | (((1 << gWidth) - 1) << gShift);
+ constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>();
+
+ for (int i = 0; i < count; ++i) {
+ const uint c = fetchPixel<bpp>(src, i);
+ const uint r = (c >> rShift) & redBlueMask;
+ const uint b = (c >> bShift) & redBlueMask;
+ const uint t = (c & alphaGreenMask)
+ | (r << bShift)
+ | (b << rShift);
+ storePixel<bpp>(dst, i, t);
+ }
+}
+
+static void QT_FASTCALL rbSwap_rgb32(uchar *d, const uchar *s, int count)
+{
+ const uint *src = reinterpret_cast<const uint *>(s);
+ uint *dest = reinterpret_cast<uint *>(d);
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
+ const uint ag = c & 0xff00ff00;
+ const uint rb = c & 0x00ff00ff;
+ dest[i] = ag | (rb << 16) | (rb >> 16);
+ }
+}
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template<>
+void QT_FASTCALL rbSwap<QImage::Format_RGBA8888>(uchar *d, const uchar *s, int count)
+{
+ return rbSwap_rgb32(d, s, count);
+}
+#else
+template<>
+void QT_FASTCALL rbSwap<QImage::Format_RGBA8888>(uchar *d, const uchar *s, int count)
+{
+ const uint *src = reinterpret_cast<const uint *>(s);
+ uint *dest = reinterpret_cast<uint *>(d);
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
+ const uint rb = c & 0xff00ff00;
+ const uint ga = c & 0x00ff00ff;
+ dest[i] = ga | (rb << 16) | (rb >> 16);
+ }
+}
+#endif
+
+static void QT_FASTCALL rbSwap_rgb30(uchar *d, const uchar *s, int count)
+{
+ const uint *src = reinterpret_cast<const uint *>(s);
+ uint *dest = reinterpret_cast<uint *>(d);
+ for (int i = 0; i < count; ++i)
+ dest[i] = qRgbSwapRgb30(src[i]);
+}
template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutRGB()
{
return QPixelLayout{
- uchar(redWidth<Format>()), uchar(redShift<Format>()),
- uchar(greenWidth<Format>()), uchar(greenShift<Format>()),
- uchar(blueWidth<Format>()), uchar(blueShift<Format>()),
- 0, 0,
- false, bitsPerPixel<Format>(),
+ false,
+ false,
+ bitsPerPixel<Format>(),
+ rbSwap<Format>,
convertToRGB32<Format>,
- convertRGBFromARGB32PM<Format, false>,
- convertRGBFromARGB32PM<Format, true>,
- convertToRGB64<Format>
+ convertToRGB64<Format>,
+ fetchRGBToRGB32<Format>,
+ fetchRGBToRGB64<Format>,
+ storeRGBFromARGB32PM<Format, false>,
+ storeRGBFromARGB32PM<Format, true>
};
}
template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutARGBPM()
{
return QPixelLayout{
- uchar(redWidth<Format>()), uchar(redShift<Format>()),
- uchar(greenWidth<Format>()), uchar(greenShift<Format>()),
- uchar(blueWidth<Format>()), uchar(blueShift<Format>()),
- uchar(alphaWidth<Format>()), uchar(alphaShift<Format>()),
- true, bitsPerPixel<Format>(),
+ true,
+ true,
+ bitsPerPixel<Format>(),
+ rbSwap<Format>,
convertARGBPMToARGB32PM<Format>,
- convertARGBPMFromARGB32PM<Format, false>,
- convertARGBPMFromARGB32PM<Format, true>,
- convertARGBPMToARGB64PM<Format>
+ convertARGBPMToRGBA64PM<Format>,
+ fetchARGBPMToARGB32PM<Format>,
+ fetchARGBPMToRGBA64PM<Format>,
+ storeARGBPMFromARGB32PM<Format, false>,
+ storeARGBPMFromARGB32PM<Format, true>
};
}
-#endif
-
-// To convert in place, let 'dest' and 'src' be the same.
-static const uint *QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *clut, QDitherInfo *)
+static void QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, int count, const QVector<QRgb> *clut)
{
for (int i = 0; i < count; ++i)
- buffer[i] = qPremultiply(clut->at(src[i]));
+ buffer[i] = qPremultiply(clut->at(buffer[i]));
+}
+
+template<QPixelLayout::BPP BPP>
+static const uint *QT_FASTCALL fetchIndexedToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i) {
+ const uint s = fetchPixel<BPP>(src, index + i);
+ buffer[i] = qPremultiply(clut->at(s));
+ }
+ return buffer;
+}
+
+template<QPixelLayout::BPP BPP>
+static const QRgba64 *QT_FASTCALL fetchIndexedToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i) {
+ const uint s = fetchPixel<BPP>(src, index + i);
+ buffer[i] = QRgba64::fromArgb32(clut->at(s)).premultiplied();
+ }
return buffer;
}
-static const QRgba64 *QT_FASTCALL convertIndexedToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL convertIndexedToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *clut, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
@@ -501,44 +731,77 @@ static const QRgba64 *QT_FASTCALL convertIndexedToARGB64PM(QRgba64 *buffer, cons
return buffer;
}
-static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL convertPassThrough(uint *, int, const QVector<QRgb> *)
{
- return src;
}
-static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const uint *QT_FASTCALL fetchPassThrough(uint *, const uchar *src, int index, int,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return qt_convertARGB32ToARGB32PM(buffer, src, count);
+ return reinterpret_cast<const uint *>(src) + index;
}
-static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const QRgba64 *QT_FASTCALL fetchPassThrough64(QRgba64 *, const uchar *src, int index, int,
+ const QVector<QRgb> *, QDitherInfo *)
{
- UNALIASED_CONVERSION_LOOP(buffer, src, count, RGBA2ARGB);
- return buffer;
+ return reinterpret_cast<const QRgba64 *>(src) + index;
}
-static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storePassThrough(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return qt_convertRGBA8888ToARGB32PM(buffer, src, count);
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ if (d != src)
+ memcpy(d, src, count * sizeof(uint));
}
-static const uint *QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, int count, const QVector<QRgb> *)
+{
+ qt_convertARGB32ToARGB32PM(buffer, buffer, count);
+}
+
+static const uint *QT_FASTCALL fetchARGB32ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return qt_convertARGB32ToARGB32PM(buffer, reinterpret_cast<const uint *>(src) + index, count);
+}
+
+static void QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = qRgba(0, 0, 0, src[i]);
+ buffer[i] = RGBA2ARGB(buffer[i]);
+}
+
+static const uint *QT_FASTCALL fetchRGBA8888PMToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ UNALIASED_CONVERSION_LOOP(buffer, s, count, RGBA2ARGB);
return buffer;
}
-static const uint *QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, int count, const QVector<QRgb> *)
+{
+ qt_convertRGBA8888ToARGB32PM(buffer, buffer, count);
+}
+
+static const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return qt_convertRGBA8888ToARGB32PM(buffer, reinterpret_cast<const uint *>(src) + index, count);
+}
+
+static void QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, int count, const QVector<QRgb> *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = qRgb(src[i], src[i], src[i]);
+ buffer[i] = qRgba(0, 0, 0, buffer[i]);
+}
+
+static const uint *QT_FASTCALL fetchAlpha8ToRGB32(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qRgba(0, 0, 0, src[index + i]);
return buffer;
}
@@ -549,6 +812,31 @@ static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const ui
buffer[i] = QRgba64::fromRgba(0, 0, 0, src[i]);
return buffer;
}
+static const QRgba64 *QT_FASTCALL fetchAlpha8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QRgba64::fromRgba(0, 0, 0, src[index + i]);
+ return buffer;
+}
+
+static void QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, int count, const QVector<QRgb> *)
+{
+ for (int i = 0; i < count; ++i) {
+ const uint s = buffer[i];
+ buffer[i] = qRgb(s, s, s);
+ }
+}
+
+static const uint *QT_FASTCALL fetchGrayscale8ToRGB32(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i) {
+ const uint s = src[index + i];
+ buffer[i] = qRgb(s, s, s);
+ }
+ return buffer;
+}
static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
@@ -558,24 +846,33 @@ static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, cons
return buffer;
}
-static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL fetchGrayscale8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qUnpremultiply(src[i]);
+ for (int i = 0; i < count; ++i) {
+ const uint s = src[index + i];
+ buffer[i] = QRgba64::fromRgba(s, s, s, 255);
+ }
return buffer;
}
-static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storeARGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- UNALIASED_CONVERSION_LOOP(buffer, src, count, ARGB2RGBA);
- return buffer;
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return qUnpremultiply(c); });
+}
+
+static void QT_FASTCALL storeRGBA8888PMFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, ARGB2RGBA);
}
#ifdef __SSE2__
template<bool RGBA, bool maskAlpha>
-static inline void qConvertARGB32PMToARGB64PM_sse2(QRgba64 *buffer, const uint *src, int count)
+static inline void qConvertARGB32PMToRGBA64PM_sse2(QRgba64 *buffer, const uint *src, int count)
{
if (count <= 0)
return;
@@ -618,6 +915,66 @@ static inline void qConvertARGB32PMToARGB64PM_sse2(QRgba64 *buffer, const uint *
*buffer++ = QRgba64::fromArgb32(s);
}
}
+
+template<QtPixelOrder PixelOrder>
+static inline void qConvertRGBA64PMToA2RGB30PM_sse2(uint *dest, const QRgba64 *buffer, int count)
+{
+ const __m128i gmask = _mm_set1_epi32(0x000ffc00);
+ const __m128i cmask = _mm_set1_epi32(0x000003ff);
+ int i = 0;
+ __m128i vr, vg, vb, va;
+ for (; i < count && uintptr_t(buffer) & 0xF; ++i) {
+ *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
+ }
+
+ for (; i < count-15; i += 16) {
+ // Repremultiplying is really expensive and hard to do in SIMD without AVX2,
+ // so we try to avoid it by checking if it is needed 16 samples at a time.
+ __m128i vOr = _mm_set1_epi32(0);
+ __m128i vAnd = _mm_set1_epi32(0xffffffff);
+ for (int j = 0; j < 16; j += 2) {
+ __m128i vs = _mm_load_si128((const __m128i*)(buffer + j));
+ vOr = _mm_or_si128(vOr, vs);
+ vAnd = _mm_and_si128(vAnd, vs);
+ }
+ const quint16 orAlpha = ((uint)_mm_extract_epi16(vOr, 3)) | ((uint)_mm_extract_epi16(vOr, 7));
+ const quint16 andAlpha = ((uint)_mm_extract_epi16(vAnd, 3)) & ((uint)_mm_extract_epi16(vAnd, 7));
+
+ if (andAlpha == 0xffff) {
+ for (int j = 0; j < 16; j += 2) {
+ __m128i vs = _mm_load_si128((const __m128i*)buffer);
+ buffer += 2;
+ vr = _mm_srli_epi64(vs, 6);
+ vg = _mm_srli_epi64(vs, 16 + 6 - 10);
+ vb = _mm_srli_epi64(vs, 32 + 6);
+ vr = _mm_and_si128(vr, cmask);
+ vg = _mm_and_si128(vg, gmask);
+ vb = _mm_and_si128(vb, cmask);
+ va = _mm_srli_epi64(vs, 48 + 14);
+ if (PixelOrder == PixelOrderRGB)
+ vr = _mm_slli_epi32(vr, 20);
+ else
+ vb = _mm_slli_epi32(vb, 20);
+ va = _mm_slli_epi32(va, 30);
+ __m128i vd = _mm_or_si128(_mm_or_si128(vr, vg), _mm_or_si128(vb, va));
+ vd = _mm_shuffle_epi32(vd, _MM_SHUFFLE(3, 1, 2, 0));
+ _mm_storel_epi64((__m128i*)dest, vd);
+ dest += 2;
+ }
+ } else if (orAlpha == 0) {
+ for (int j = 0; j < 16; ++j) {
+ *dest++ = 0;
+ buffer++;
+ }
+ } else {
+ for (int j = 0; j < 16; ++j)
+ *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
+ }
+ }
+
+ SIMD_EPILOGUE(i, count, 15)
+ *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
+}
#elif defined(__ARM_NEON__)
template<bool RGBA, bool maskAlpha>
static inline void qConvertARGB32PMToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count)
@@ -671,7 +1028,7 @@ static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uin
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertARGB32PMToARGB64PM_sse2<false, true>(buffer, src, count);
+ qConvertARGB32PMToRGBA64PM_sse2<false, true>(buffer, src, count);
#elif defined(__ARM_NEON__)
qConvertARGB32PMToRGBA64PM_neon<false, true>(buffer, src, count);
#else
@@ -681,11 +1038,17 @@ static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uin
return buffer;
}
-static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL fetchRGB32ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return convertRGB32ToRGB64(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
+}
+
+static const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count);
+ qConvertARGB32PMToRGBA64PM_sse2<false, false>(buffer, src, count);
for (int i = 0; i < count; ++i)
buffer[i] = buffer[i].premultiplied();
#elif defined(__ARM_NEON__)
@@ -699,11 +1062,17 @@ static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const
return buffer;
}
-static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return convertARGB32ToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
+}
+
+static const QRgba64 *QT_FASTCALL convertARGB32PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count);
+ qConvertARGB32PMToRGBA64PM_sse2<false, false>(buffer, src, count);
#elif defined(__ARM_NEON__)
qConvertARGB32PMToRGBA64PM_neon<false, false>(buffer, src, count);
#else
@@ -713,11 +1082,36 @@ static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, con
return buffer;
}
-static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL fetchARGB32PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return convertARGB32PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
+}
+
+static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = buffer[i].premultiplied();
+}
+
+static void convertRGBA64PMToRGBA64PM(QRgba64 *, int)
+{
+}
+
+static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QRgba64::fromRgba64(s[i]).premultiplied();
+ return buffer;
+}
+
+static const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count);
+ qConvertARGB32PMToRGBA64PM_sse2<true, false>(buffer, src, count);
for (int i = 0; i < count; ++i)
buffer[i] = buffer[i].premultiplied();
#elif defined(__ARM_NEON__)
@@ -731,11 +1125,17 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, con
return buffer;
}
-static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ return convertRGBA8888ToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
+}
+
+static const QRgba64 *QT_FASTCALL convertRGBA8888PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count);
+ qConvertARGB32PMToRGBA64PM_sse2<true, false>(buffer, src, count);
#elif defined(__ARM_NEON__)
qConvertARGB32PMToRGBA64PM_neon<true, false>(buffer, src, count);
#else
@@ -745,33 +1145,45 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, c
return buffer;
}
-static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const QRgba64 *QT_FASTCALL fetchRGBA8888PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(qUnpremultiply(src[i]));
- return buffer;
+ return convertRGBA8888PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
}
-static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storeRGBA8888FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | c); });
- return buffer;
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(qUnpremultiply(c)); });
}
-static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storeRGBXFromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | c); });
+}
+
+static void QT_FASTCALL storeRGBXFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | qUnpremultiply(c)); });
+}
+
+template<QtPixelOrder PixelOrder>
+static void QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply(src[i]));
- return buffer;
+ buffer[i] = qConvertA2rgb30ToArgb32<PixelOrder>(buffer[i]);
}
template<QtPixelOrder PixelOrder>
-static const uint *QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *dither)
+static const uint *QT_FASTCALL fetchA2RGB30PMToARGB32PM(uint *buffer, const uchar *s, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *dither)
{
+ const uint *src = reinterpret_cast<const uint *>(s) + index;
if (!dither) {
UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertA2rgb30ToArgb32<PixelOrder>);
} else {
@@ -796,7 +1208,7 @@ static const uint *QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, const ui
#ifdef __SSE2__
template<QtPixelOrder PixelOrder>
-static inline void qConvertA2RGB30PMToARGB64PM_sse2(QRgba64 *buffer, const uint *src, int count)
+static inline void qConvertA2RGB30PMToRGBA64PM_sse2(QRgba64 *buffer, const uint *src, int count)
{
if (count <= 0)
return;
@@ -839,11 +1251,11 @@ static inline void qConvertA2RGB30PMToARGB64PM_sse2(QRgba64 *buffer, const uint
#endif
template<QtPixelOrder PixelOrder>
-static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
+static const QRgba64 *QT_FASTCALL convertA2RGB30PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
- qConvertA2RGB30PMToARGB64PM_sse2<PixelOrder>(buffer, src, count);
+ qConvertA2RGB30PMToRGBA64PM_sse2<PixelOrder>(buffer, src, count);
#else
for (int i = 0; i < count; ++i)
buffer[i] = qConvertA2rgb30ToRgb64<PixelOrder>(src[i]);
@@ -852,290 +1264,334 @@ static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, co
}
template<QtPixelOrder PixelOrder>
-static const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static const QRgba64 *QT_FASTCALL fetchA2RGB30PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertArgb32ToA2rgb30<PixelOrder>);
- return buffer;
+ return convertA2RGB30PMToRGBA64PM<PixelOrder>(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
}
template<QtPixelOrder PixelOrder>
-static const uint *QT_FASTCALL convertRGB30FromRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storeA2RGB30PMFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qConvertRgb32ToRgb30<PixelOrder>(src[i]);
- return buffer;
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, qConvertArgb32ToA2rgb30<PixelOrder>);
}
template<QtPixelOrder PixelOrder>
-static const uint *QT_FASTCALL convertRGB30FromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static void QT_FASTCALL storeRGB30FromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertRgb32ToRgb30<PixelOrder>);
- return buffer;
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, qConvertRgb32ToRgb30<PixelOrder>);
}
-static const uint *QT_FASTCALL convertAlpha8FromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+template<QtPixelOrder PixelOrder>
+static void QT_FASTCALL storeRGB30FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qAlpha(src[i]);
- return buffer;
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ UNALIASED_CONVERSION_LOOP(d, src, count, qConvertRgb32ToRgb30<PixelOrder>);
}
-static const uint *QT_FASTCALL convertGrayscale8FromRGB32(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+template<bool RGBA>
+void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qGray(src[i]);
- return buffer;
+ int i = 0;
+#ifdef __SSE2__
+ if (((uintptr_t)dst & 0x7) && count > 0) {
+ uint s = (*src++).toArgb32();
+ if (RGBA)
+ s = ARGB2RGBA(s);
+ *dst++ = s;
+ i++;
+ }
+ const __m128i vhalf = _mm_set1_epi32(0x80);
+ const __m128i vzero = _mm_setzero_si128();
+ for (; i < count-1; i += 2) {
+ __m128i vs = _mm_loadu_si128((const __m128i*)src);
+ src += 2;
+ if (!RGBA) {
+ vs = _mm_shufflelo_epi16(vs, _MM_SHUFFLE(3, 0, 1, 2));
+ vs = _mm_shufflehi_epi16(vs, _MM_SHUFFLE(3, 0, 1, 2));
+ }
+ __m128i v1 = _mm_unpacklo_epi16(vs, vzero);
+ __m128i v2 = _mm_unpackhi_epi16(vs, vzero);
+ v1 = _mm_add_epi32(v1, vhalf);
+ v2 = _mm_add_epi32(v2, vhalf);
+ v1 = _mm_sub_epi32(v1, _mm_srli_epi32(v1, 8));
+ v2 = _mm_sub_epi32(v2, _mm_srli_epi32(v2, 8));
+ v1 = _mm_srli_epi32(v1, 8);
+ v2 = _mm_srli_epi32(v2, 8);
+ v1 = _mm_packs_epi32(v1, v2);
+ v1 = _mm_packus_epi16(v1, vzero);
+ _mm_storel_epi64((__m128i*)(dst), v1);
+ dst += 2;
+ }
+#endif
+ for (; i < count; i++) {
+ uint s = (*src++).toArgb32();
+ if (RGBA)
+ s = ARGB2RGBA(s);
+ *dst++ = s;
+ }
}
+template void qt_convertRGBA64ToARGB32<false>(uint *dst, const QRgba64 *src, int count);
+template void qt_convertRGBA64ToARGB32<true>(uint *dst, const QRgba64 *src, int count);
-static const uint *QT_FASTCALL convertGrayscale8FromARGB32PM(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+
+static void QT_FASTCALL storeAlpha8FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = qGray(qUnpremultiply(src[i]));
- return buffer;
+ dest[index + i] = qAlpha(src[i]);
}
-template <QPixelLayout::BPP bpp> static
-uint QT_FASTCALL fetchPixel(const uchar *, int)
+static void QT_FASTCALL storeGrayscale8FromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- Q_UNREACHABLE();
- return 0;
+ for (int i = 0; i < count; ++i)
+ dest[index + i] = qGray(src[i]);
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index)
+static void QT_FASTCALL storeGrayscale8FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return (src[index >> 3] >> (index & 7)) & 1;
+ for (int i = 0; i < count; ++i)
+ dest[index + i] = qGray(qUnpremultiply(src[i]));
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index)
+static const uint *QT_FASTCALL fetchRGB64ToRGB32(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return (src[index >> 3] >> (~index & 7)) & 1;
+ const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = toArgb32(s[i]);
+ return buffer;
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP8>(const uchar *src, int index)
+static void QT_FASTCALL storeRGB64FromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return src[index];
+ QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = QRgba64::fromArgb32(src[i]);
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16>(const uchar *src, int index)
+static const uint *QT_FASTCALL fetchRGBA64ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return reinterpret_cast<const quint16 *>(src)[index];
+ const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = toArgb32(s[i].premultiplied());
+ return buffer;
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP24>(const uchar *src, int index)
+static void QT_FASTCALL storeRGBA64FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- return reinterpret_cast<const quint24 *>(src)[index];
+ QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = QRgba64::fromArgb32(src[i]).unpremultiplied();
}
-template <>
-inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int index)
-{
- return reinterpret_cast<const uint *>(src)[index];
-}
+// Note:
+// convertToArgb32() assumes that no color channel is less than 4 bits.
+// storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits.
+// QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits.
+QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
+ { false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid
+ { false, false, QPixelLayout::BPP1MSB, nullptr,
+ convertIndexedToARGB32PM, convertIndexedToRGBA64PM,
+ fetchIndexedToARGB32PM<QPixelLayout::BPP1MSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1MSB>,
+ nullptr, nullptr }, // Format_Mono
+ { false, false, QPixelLayout::BPP1LSB, nullptr,
+ convertIndexedToARGB32PM, convertIndexedToRGBA64PM,
+ fetchIndexedToARGB32PM<QPixelLayout::BPP1LSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1LSB>,
+ nullptr, nullptr }, // Format_MonoLSB
+ { false, false, QPixelLayout::BPP8, nullptr,
+ convertIndexedToARGB32PM, convertIndexedToRGBA64PM,
+ fetchIndexedToARGB32PM<QPixelLayout::BPP8>, fetchIndexedToRGBA64PM<QPixelLayout::BPP8>,
+ nullptr, nullptr }, // Format_Indexed8
+ // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong,
+ // but everywhere this generic conversion would be wrong is currently overloaded.
+ { false, false, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough,
+ convertRGB32ToRGB64, fetchPassThrough, fetchRGB32ToRGB64, storePassThrough, storePassThrough }, // Format_RGB32
+ { true, false, QPixelLayout::BPP32, rbSwap_rgb32, convertARGB32ToARGB32PM,
+ convertARGB32ToRGBA64PM, fetchARGB32ToARGB32PM, fetchARGB32ToRGBA64PM, storeARGB32FromARGB32PM, storePassThrough }, // Format_ARGB32
+ { true, true, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough,
+ convertARGB32PMToRGBA64PM, fetchPassThrough, fetchARGB32PMToRGBA64PM, storePassThrough, storePassThrough }, // Format_ARGB32_Premultiplied
+ pixelLayoutRGB<QImage::Format_RGB16>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB8565_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB666>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB6666_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB555>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB8555_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB888>(),
+ pixelLayoutRGB<QImage::Format_RGB444>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB4444_Premultiplied>(),
+ { false, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM,
+ convertRGBA8888PMToRGBA64PM, fetchRGBA8888PMToARGB32PM, fetchRGBA8888PMToRGBA64PM, storeRGBXFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBX8888
+ { true, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888ToARGB32PM,
+ convertRGBA8888ToRGBA64PM, fetchRGBA8888ToARGB32PM, fetchRGBA8888ToRGBA64PM, storeRGBA8888FromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888
+ { true, true, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM,
+ convertRGBA8888PMToRGBA64PM, fetchRGBA8888PMToARGB32PM, fetchRGBA8888PMToRGBA64PM, storeRGBA8888PMFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888_Premultiplied
+ { false, false, QPixelLayout::BPP32, rbSwap_rgb30,
+ convertA2RGB30PMToARGB32PM<PixelOrderBGR>,
+ convertA2RGB30PMToRGBA64PM<PixelOrderBGR>,
+ fetchA2RGB30PMToARGB32PM<PixelOrderBGR>,
+ fetchA2RGB30PMToRGBA64PM<PixelOrderBGR>,
+ storeRGB30FromARGB32PM<PixelOrderBGR>,
+ storeRGB30FromRGB32<PixelOrderBGR>
+ }, // Format_BGR30
+ { true, true, QPixelLayout::BPP32, rbSwap_rgb30,
+ convertA2RGB30PMToARGB32PM<PixelOrderBGR>,
+ convertA2RGB30PMToRGBA64PM<PixelOrderBGR>,
+ fetchA2RGB30PMToARGB32PM<PixelOrderBGR>,
+ fetchA2RGB30PMToRGBA64PM<PixelOrderBGR>,
+ storeA2RGB30PMFromARGB32PM<PixelOrderBGR>,
+ storeRGB30FromRGB32<PixelOrderBGR>
+ }, // Format_A2BGR30_Premultiplied
+ { false, false, QPixelLayout::BPP32, rbSwap_rgb30,
+ convertA2RGB30PMToARGB32PM<PixelOrderRGB>,
+ convertA2RGB30PMToRGBA64PM<PixelOrderRGB>,
+ fetchA2RGB30PMToARGB32PM<PixelOrderRGB>,
+ fetchA2RGB30PMToRGBA64PM<PixelOrderRGB>,
+ storeRGB30FromARGB32PM<PixelOrderRGB>,
+ storeRGB30FromRGB32<PixelOrderRGB>
+ }, // Format_RGB30
+ { true, true, QPixelLayout::BPP32, rbSwap_rgb30,
+ convertA2RGB30PMToARGB32PM<PixelOrderRGB>,
+ convertA2RGB30PMToRGBA64PM<PixelOrderRGB>,
+ fetchA2RGB30PMToARGB32PM<PixelOrderRGB>,
+ fetchA2RGB30PMToRGBA64PM<PixelOrderRGB>,
+ storeA2RGB30PMFromARGB32PM<PixelOrderRGB>,
+ storeRGB30FromRGB32<PixelOrderRGB>
+ }, // Format_A2RGB30_Premultiplied
+ { true, true, QPixelLayout::BPP8, nullptr,
+ convertAlpha8ToRGB32, convertAlpha8ToRGB64,
+ fetchAlpha8ToRGB32, fetchAlpha8ToRGB64,
+ storeAlpha8FromARGB32PM, nullptr }, // Format_Alpha8
+ { false, false, QPixelLayout::BPP8, nullptr,
+ convertGrayscale8ToRGB32, convertGrayscale8ToRGB64,
+ fetchGrayscale8ToRGB32, fetchGrayscale8ToRGB64,
+ storeGrayscale8FromARGB32PM, storeGrayscale8FromRGB32 }, // Format_Grayscale8
+ { false, false, QPixelLayout::BPP64, nullptr,
+ convertPassThrough, nullptr,
+ fetchRGB64ToRGB32, fetchPassThrough64,
+ storeRGB64FromRGB32, storeRGB64FromRGB32 }, // Format_RGBX64
+ { true, false, QPixelLayout::BPP64, nullptr,
+ convertARGB32ToARGB32PM, nullptr,
+ fetchRGBA64ToARGB32PM, fetchRGBA64ToRGBA64PM,
+ storeRGBA64FromARGB32PM, storeRGB64FromRGB32 }, // Format_RGBA64
+ { true, true, QPixelLayout::BPP64, nullptr,
+ convertPassThrough, nullptr,
+ fetchRGB64ToRGB32, fetchPassThrough64,
+ storeRGB64FromRGB32, storeRGB64FromRGB32 } // Format_RGBA64_Premultiplied
+};
-template <QPixelLayout::BPP bpp>
-inline const uint *QT_FASTCALL fetchPixels(uint *buffer, const uchar *src, int index, int count)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = fetchPixel<bpp>(src, index + i);
- return buffer;
-}
+Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats);
-template <>
-inline const uint *QT_FASTCALL fetchPixels<QPixelLayout::BPP32>(uint *, const uchar *src, int index, int)
+static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length)
{
- return reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < length; ++i) {
+ dest[i] = toArgb32(src[i]);
+ }
}
-template <QPixelLayout::BPP width> static
-void QT_FASTCALL storePixel(uchar *dest, int index, uint pixel);
-
-template <>
-inline void QT_FASTCALL storePixel<QPixelLayout::BPP1LSB>(uchar *dest, int index, uint pixel)
+template<QImage::Format format>
+static void QT_FASTCALL storeGenericFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither)
{
- if (pixel)
- dest[index >> 3] |= 1 << (index & 7);
- else
- dest[index >> 3] &= ~(1 << (index & 7));
+ uint buffer[BufferSize];
+ convertFromRgb64(buffer, src, count);
+ qPixelLayouts[format].storeFromARGB32PM(dest, buffer, index, count, clut, dither);
}
-template <>
-inline void QT_FASTCALL storePixel<QPixelLayout::BPP1MSB>(uchar *dest, int index, uint pixel)
+static void QT_FASTCALL storeARGB32FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- if (pixel)
- dest[index >> 3] |= 1 << (~index & 7);
- else
- dest[index >> 3] &= ~(1 << (~index & 7));
+ uint *d = (uint*)dest + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = toArgb32(src[i].unpremultiplied());
}
-template <>
-inline void QT_FASTCALL storePixel<QPixelLayout::BPP8>(uchar *dest, int index, uint pixel)
+static void QT_FASTCALL storeRGBA8888FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- dest[index] = uchar(pixel);
+ uint *d = (uint*)dest + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = toRgba8888(src[i].unpremultiplied());
}
-template <>
-inline void QT_FASTCALL storePixel<QPixelLayout::BPP16>(uchar *dest, int index, uint pixel)
+template<QtPixelOrder PixelOrder>
+static void QT_FASTCALL storeRGB30FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- reinterpret_cast<quint16 *>(dest)[index] = quint16(pixel);
+ uint *d = (uint*)dest + index;
+#ifdef __SSE2__
+ qConvertRGBA64PMToA2RGB30PM_sse2<PixelOrder>(d, src, count);
+#else
+ for (int i = 0; i < count; ++i)
+ d[i] = qConvertRgb64ToRgb30<PixelOrder>(src[i]);
+#endif
}
-template <>
-inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, uint pixel)
+static void QT_FASTCALL storeRGBX64FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel);
+ QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ d[i] = src[i].unpremultiplied();
+ d[i].setAlpha(65535);
+ }
}
-template <QPixelLayout::BPP width>
-inline void QT_FASTCALL storePixels(uchar *dest, const uint *src, int index, int count)
+static void QT_FASTCALL storeRGBA64FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
+ QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index;
for (int i = 0; i < count; ++i)
- storePixel<width>(dest, index + i, src[i]);
+ d[i] = src[i].unpremultiplied();
}
-template <>
-inline void QT_FASTCALL storePixels<QPixelLayout::BPP32>(uchar *dest, const uint *src, int index, int count)
+static void QT_FASTCALL storeRGBA64PMFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- memcpy(reinterpret_cast<uint *>(dest) + index, src, count * sizeof(uint));
-}
-
-// Note:
-// convertToArgb32() assumes that no color channel is less than 4 bits.
-// convertFromArgb32() assumes that no color channel is more than 8 bits.
-// QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits.
-QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
- { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPPNone, 0, 0, 0, 0 }, // Format_Invalid
- { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1MSB, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_Mono
- { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1LSB, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_MonoLSB
- { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_Indexed8
- // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong,
- // but everywhere this generic conversion would be wrong is currently overloaded.
- { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough, convertRGB32ToRGB64 }, // Format_RGB32
- { 8, 16, 8, 8, 8, 0, 8, 24, false, QPixelLayout::BPP32, convertARGB32ToARGB32PM, convertARGB32FromARGB32PM, convertPassThrough, convertARGB32ToARGB64PM }, // Format_ARGB32
- { 8, 16, 8, 8, 8, 0, 8, 24, true, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough, convertARGB32PMToARGB64PM }, // Format_ARGB32_Premultiplied
-#ifdef Q_COMPILER_CONSTEXPR
- pixelLayoutRGB<QImage::Format_RGB16>(),
- pixelLayoutARGBPM<QImage::Format_ARGB8565_Premultiplied>(),
- pixelLayoutRGB<QImage::Format_RGB666>(),
- pixelLayoutARGBPM<QImage::Format_ARGB6666_Premultiplied>(),
- pixelLayoutRGB<QImage::Format_RGB555>(),
- pixelLayoutARGBPM<QImage::Format_ARGB8555_Premultiplied>(),
- pixelLayoutRGB<QImage::Format_RGB888>(),
- pixelLayoutRGB<QImage::Format_RGB444>(),
- pixelLayoutARGBPM<QImage::Format_ARGB4444_Premultiplied>(),
-#else
- { 5, 11, 6, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16,
- convertToRGB32<QImage::Format_RGB16>,
- convertRGBFromARGB32PM<QImage::Format_RGB16, false>,
- convertRGBFromARGB32PM<QImage::Format_RGB16, true>,
- convertToRGB64<QImage::Format_RGB16>,
- },
- { 5, 19, 6, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24,
- convertARGBPMToARGB32PM<QImage::Format_ARGB8565_Premultiplied>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB8565_Premultiplied, false>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB8565_Premultiplied, true>,
- convertARGBPMToARGB64PM<QImage::Format_ARGB8565_Premultiplied>,
- },
- { 6, 12, 6, 6, 6, 0, 0, 0, false, QPixelLayout::BPP24,
- convertToRGB32<QImage::Format_RGB666>,
- convertRGBFromARGB32PM<QImage::Format_RGB666, false>,
- convertRGBFromARGB32PM<QImage::Format_RGB666, true>,
- convertToRGB64<QImage::Format_RGB666>,
- },
- { 6, 12, 6, 6, 6, 0, 6, 18, true, QPixelLayout::BPP24,
- convertARGBPMToARGB32PM<QImage::Format_ARGB6666_Premultiplied>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB6666_Premultiplied, false>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB6666_Premultiplied, true>,
- convertARGBPMToARGB64PM<QImage::Format_ARGB6666_Premultiplied>,
- },
- { 5, 10, 5, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16,
- convertToRGB32<QImage::Format_RGB555>,
- convertRGBFromARGB32PM<QImage::Format_RGB555, false>,
- convertRGBFromARGB32PM<QImage::Format_RGB555, true>,
- convertToRGB64<QImage::Format_RGB555>,
- },
- { 5, 18, 5, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24,
- convertARGBPMToARGB32PM<QImage::Format_ARGB8555_Premultiplied>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB8555_Premultiplied, false>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB8555_Premultiplied, true>,
- convertARGBPMToARGB64PM<QImage::Format_ARGB8555_Premultiplied>,
- },
- { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP24,
- convertToRGB32<QImage::Format_RGB888>,
- convertRGBFromARGB32PM<QImage::Format_RGB888, false>,
- convertRGBFromARGB32PM<QImage::Format_RGB888, true>,
- convertToRGB64<QImage::Format_RGB888>,
- },
- { 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16,
- convertToRGB32<QImage::Format_RGB444>,
- convertRGBFromARGB32PM<QImage::Format_RGB444, false>,
- convertRGBFromARGB32PM<QImage::Format_RGB444, true>,
- convertToRGB64<QImage::Format_RGB444>,
- },
- { 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16,
- convertARGBPMToARGB32PM<QImage::Format_ARGB4444_Premultiplied>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB4444_Premultiplied, false>,
- convertARGBPMFromARGB32PM<QImage::Format_ARGB4444_Premultiplied, true>,
- convertARGBPMToARGB64PM<QImage::Format_ARGB4444_Premultiplied>,
- },
-#endif
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBX8888
- { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, convertRGBXFromRGB32, convertRGBA8888ToARGB64PM }, // Format_RGBA8888
- { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM}, // Format_RGBA8888_Premultiplied
-#else
- { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBX8888
- { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, convertRGBXFromRGB32, convertRGBA8888ToARGB64PM }, // Format_RGBA8888 (ABGR32)
- { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBA8888_Premultiplied
-#endif
- { 10, 20, 10, 10, 10, 0, 0, 30, false, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, convertRGB30FromARGB32PM<PixelOrderBGR>, convertRGB30FromRGB32<PixelOrderBGR>, convertA2RGB30PMToARGB64PM<PixelOrderBGR> }, // Format_BGR30
- { 10, 20, 10, 10, 10, 0, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, convertA2RGB30PMFromARGB32PM<PixelOrderBGR>, convertRGB30FromRGB32<PixelOrderBGR>, convertA2RGB30PMToARGB64PM<PixelOrderBGR> }, // Format_A2BGR30_Premultiplied
- { 10, 0, 10, 10, 10, 20, 0, 30, false, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertRGB30FromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_RGB30
- { 10, 0, 10, 10, 10, 20, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertA2RGB30PMFromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_A2RGB30_Premultiplied
- { 0, 0, 0, 0, 0, 0, 8, 0, true, QPixelLayout::BPP8, convertAlpha8ToRGB32, convertAlpha8FromARGB32PM, 0, convertAlpha8ToRGB64 }, // Format_Alpha8
- { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertGrayscale8ToRGB32, convertGrayscale8FromARGB32PM, convertGrayscale8FromRGB32, convertGrayscale8ToRGB64 } // Format_Grayscale8
-};
-
-const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount] = {
- 0, // BPPNone
- fetchPixels<QPixelLayout::BPP1MSB>, // BPP1MSB
- fetchPixels<QPixelLayout::BPP1LSB>, // BPP1LSB
- fetchPixels<QPixelLayout::BPP8>, // BPP8
- fetchPixels<QPixelLayout::BPP16>, // BPP16
- fetchPixels<QPixelLayout::BPP24>, // BPP24
- fetchPixels<QPixelLayout::BPP32> // BPP32
-};
-
-StorePixelsFunc qStorePixels[QPixelLayout::BPPCount] = {
- 0, // BPPNone
- storePixels<QPixelLayout::BPP1MSB>, // BPP1MSB
- storePixels<QPixelLayout::BPP1LSB>, // BPP1LSB
- storePixels<QPixelLayout::BPP8>, // BPP8
- storePixels<QPixelLayout::BPP16>, // BPP16
- storePixels<QPixelLayout::BPP24>, // BPP24
- storePixels<QPixelLayout::BPP32> // BPP32
-};
-
-typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index);
-
-static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = {
- 0, // BPPNone
- fetchPixel<QPixelLayout::BPP1MSB>, // BPP1MSB
- fetchPixel<QPixelLayout::BPP1LSB>, // BPP1LSB
- fetchPixel<QPixelLayout::BPP8>, // BPP8
- fetchPixel<QPixelLayout::BPP16>, // BPP16
- fetchPixel<QPixelLayout::BPP24>, // BPP24
- fetchPixel<QPixelLayout::BPP32> // BPP32
+ QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index;
+ if (d != src)
+ memcpy(d, src, count * sizeof(QRgba64));
+}
+
+ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ storeGenericFromRGBA64PM<QImage::Format_RGB32>,
+ storeARGB32FromRGBA64PM,
+ storeGenericFromRGBA64PM<QImage::Format_ARGB32_Premultiplied>,
+ storeGenericFromRGBA64PM<QImage::Format_RGB16>,
+ storeGenericFromRGBA64PM<QImage::Format_ARGB8565_Premultiplied>,
+ storeGenericFromRGBA64PM<QImage::Format_RGB666>,
+ storeGenericFromRGBA64PM<QImage::Format_ARGB6666_Premultiplied>,
+ storeGenericFromRGBA64PM<QImage::Format_RGB555>,
+ storeGenericFromRGBA64PM<QImage::Format_ARGB8555_Premultiplied>,
+ storeGenericFromRGBA64PM<QImage::Format_RGB888>,
+ storeGenericFromRGBA64PM<QImage::Format_RGB444>,
+ storeGenericFromRGBA64PM<QImage::Format_ARGB4444_Premultiplied>,
+ storeGenericFromRGBA64PM<QImage::Format_RGBX8888>,
+ storeRGBA8888FromRGBA64PM,
+ storeGenericFromRGBA64PM<QImage::Format_RGBA8888_Premultiplied>,
+ storeRGB30FromRGBA64PM<PixelOrderBGR>,
+ storeRGB30FromRGBA64PM<PixelOrderBGR>,
+ storeRGB30FromRGBA64PM<PixelOrderRGB>,
+ storeRGB30FromRGBA64PM<PixelOrderRGB>,
+ storeGenericFromRGBA64PM<QImage::Format_Alpha8>,
+ storeGenericFromRGBA64PM<QImage::Format_Grayscale8>,
+ storeRGBX64FromRGBA64PM,
+ storeRGBA64FromRGBA64PM,
+ storeRGBA64PMFromRGBA64PM
};
/*
@@ -1185,23 +1641,23 @@ static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuff
static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
{
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- const uint *ptr = qFetchPixels[layout->bpp](buffer, rasterBuffer->scanLine(y), x, length);
- return const_cast<uint *>(layout->convertToARGB32PM(buffer, ptr, length, 0, 0));
+ return const_cast<uint *>(layout->fetchToARGB32PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
+}
+
+static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, int, int)
+{
+ return buffer;
}
static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
{
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- uint buffer32[buffer_size];
- const uint *ptr = qFetchPixels[layout->bpp](buffer32, rasterBuffer->scanLine(y), x, length);
- return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, ptr, length, 0, 0));
+ return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
}
-static QRgba64 *QT_FASTCALL destFetch64uint32(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int)
{
- const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- const uint *src = ((const uint *)rasterBuffer->scanLine(y)) + x;
- return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, src, length, 0, 0));
+ return (QRgba64 *)rasterBuffer->scanLine(y) + x;
}
static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int)
@@ -1236,6 +1692,9 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_A2RGB30_Premultiplied
destFetch, // Format_Alpha8
destFetch, // Format_Grayscale8
+ destFetch, // Format_RGBX64
+ destFetch, // Format_RGBA64
+ destFetch, // Format_RGBA64_Premultiplied
};
static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
@@ -1244,9 +1703,9 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
0, // Format_Mono,
0, // Format_MonoLSB
0, // Format_Indexed8
- destFetch64uint32, // Format_RGB32
- destFetch64uint32, // Format_ARGB32,
- destFetch64uint32, // Format_ARGB32_Premultiplied
+ destFetch64, // Format_RGB32
+ destFetch64, // Format_ARGB32,
+ destFetch64, // Format_ARGB32_Premultiplied
destFetch64, // Format_RGB16
destFetch64, // Format_ARGB8565_Premultiplied
destFetch64, // Format_RGB666
@@ -1256,15 +1715,18 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetch64, // Format_RGB888
destFetch64, // Format_RGB444
destFetch64, // Format_ARGB4444_Premultiplied
- destFetch64uint32, // Format_RGBX8888
- destFetch64uint32, // Format_RGBA8888
- destFetch64uint32, // Format_RGBA8888_Premultiplied
- destFetch64uint32, // Format_BGR30
- destFetch64uint32, // Format_A2BGR30_Premultiplied
- destFetch64uint32, // Format_RGB30
- destFetch64uint32, // Format_A2RGB30_Premultiplied
+ destFetch64, // Format_RGBX8888
+ destFetch64, // Format_RGBA8888
+ destFetch64, // Format_RGBA8888_Premultiplied
+ destFetch64, // Format_BGR30
+ destFetch64, // Format_A2BGR30_Premultiplied
+ destFetch64, // Format_RGB30
+ destFetch64, // Format_A2RGB30_Premultiplied
destFetch64, // Format_Alpha8
destFetch64, // Format_Grayscale8
+ destFetchRGB64, // Format_RGBX64
+ destFetch64, // Format_RGBA64
+ destFetchRGB64, // Format_RGBA64_Premultiplied
};
/*
@@ -1365,143 +1827,29 @@ static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y
static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
{
- uint buf[buffer_size];
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- StorePixelsFunc store = qStorePixels[layout->bpp];
+ ConvertAndStorePixelsFunc store = layout->storeFromARGB32PM;
+ if (!layout->premultiplied && !layout->hasAlphaChannel)
+ store = layout->storeFromRGB32;
uchar *dest = rasterBuffer->scanLine(y);
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *ptr = 0;
- if (!layout->premultiplied && !layout->alphaWidth)
- ptr = layout->convertFromRGB32(buf, buffer, l, 0, 0);
- else
- ptr = layout->convertFromARGB32PM(buf, buffer, l, 0, 0);
- store(dest, ptr, x, l);
- length -= l;
- buffer += l;
- x += l;
- }
-}
-
-static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length)
-{
- for (int i = 0; i < length; ++i) {
- dest[i] = toArgb32(src[i]);
- }
+ store(dest, buffer, x, length, nullptr, nullptr);
}
static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
{
- uint buf[buffer_size];
- const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- StorePixelsFunc store = qStorePixels[layout->bpp];
+ auto store = qStoreFromRGBA64PM[rasterBuffer->format];
uchar *dest = rasterBuffer->scanLine(y);
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *ptr = 0;
- convertFromRgb64(buf, buffer, l);
- if (!layout->premultiplied && !layout->alphaWidth)
- ptr = layout->convertFromRGB32(buf, buf, l, 0, 0);
- else
- ptr = layout->convertFromARGB32PM(buf, buf, l, 0, 0);
- store(dest, ptr, x, l);
- length -= l;
- buffer += l;
- x += l;
- }
+ store(dest, buffer, x, length, nullptr, nullptr);
}
-#ifdef __SSE2__
-template<QtPixelOrder PixelOrder>
-static inline void qConvertARGB64PMToA2RGB30PM_sse2(uint *dest, const QRgba64 *buffer, int count)
+static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
{
- const __m128i gmask = _mm_set1_epi32(0x000ffc00);
- const __m128i cmask = _mm_set1_epi32(0x000003ff);
- int i = 0;
- __m128i vr, vg, vb, va;
- for (; i < count && uintptr_t(buffer) & 0xF; ++i) {
- *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
- }
-
- for (; i < count-15; i += 16) {
- // Repremultiplying is really expensive and hard to do in SIMD without AVX2,
- // so we try to avoid it by checking if it is needed 16 samples at a time.
- __m128i vOr = _mm_set1_epi32(0);
- __m128i vAnd = _mm_set1_epi32(0xffffffff);
- for (int j = 0; j < 16; j += 2) {
- __m128i vs = _mm_load_si128((const __m128i*)(buffer + j));
- vOr = _mm_or_si128(vOr, vs);
- vAnd = _mm_and_si128(vAnd, vs);
- }
- const quint16 orAlpha = ((uint)_mm_extract_epi16(vOr, 3)) | ((uint)_mm_extract_epi16(vOr, 7));
- const quint16 andAlpha = ((uint)_mm_extract_epi16(vAnd, 3)) & ((uint)_mm_extract_epi16(vAnd, 7));
-
- if (andAlpha == 0xffff) {
- for (int j = 0; j < 16; j += 2) {
- __m128i vs = _mm_load_si128((const __m128i*)buffer);
- buffer += 2;
- vr = _mm_srli_epi64(vs, 6);
- vg = _mm_srli_epi64(vs, 16 + 6 - 10);
- vb = _mm_srli_epi64(vs, 32 + 6);
- vr = _mm_and_si128(vr, cmask);
- vg = _mm_and_si128(vg, gmask);
- vb = _mm_and_si128(vb, cmask);
- va = _mm_srli_epi64(vs, 48 + 14);
- if (PixelOrder == PixelOrderRGB)
- vr = _mm_slli_epi32(vr, 20);
- else
- vb = _mm_slli_epi32(vb, 20);
- va = _mm_slli_epi32(va, 30);
- __m128i vd = _mm_or_si128(_mm_or_si128(vr, vg), _mm_or_si128(vb, va));
- vd = _mm_shuffle_epi32(vd, _MM_SHUFFLE(3, 1, 2, 0));
- _mm_storel_epi64((__m128i*)dest, vd);
- dest += 2;
- }
- } else if (orAlpha == 0) {
- for (int j = 0; j < 16; ++j) {
- *dest++ = 0;
- buffer++;
- }
- } else {
- for (int j = 0; j < 16; ++j)
- *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
- }
- }
-
- SIMD_EPILOGUE(i, count, 15)
- *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++);
-}
-#endif
-
-static void QT_FASTCALL destStore64ARGB32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
-{
- uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
+ QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x;
for (int i = 0; i < length; ++i) {
- dest[i] = toArgb32(buffer[i].unpremultiplied());
+ dest[i] = buffer[i].unpremultiplied();
}
}
-static void QT_FASTCALL destStore64RGBA8888(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
-{
- uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
- for (int i = 0; i < length; ++i) {
- dest[i] = toRgba8888(buffer[i].unpremultiplied());
- }
-}
-
-template<QtPixelOrder PixelOrder>
-static void QT_FASTCALL destStore64RGB30(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
-{
- uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
-#ifdef __SSE2__
- qConvertARGB64PMToA2RGB30PM_sse2<PixelOrder>(dest, buffer, length);
-#else
- for (int i = 0; i < length; ++i) {
- dest[i] = qConvertRgb64ToRgb30<PixelOrder>(buffer[i]);
- }
-#endif
-}
-
static DestStoreProc destStoreProc[QImage::NImageFormats] =
{
0, // Format_Invalid
@@ -1529,16 +1877,19 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_A2RGB30_Premultiplied
destStore, // Format_Alpha8
destStore, // Format_Grayscale8
+ destStore, // Format_RGBX64
+ destStore, // Format_RGBA64
+ destStore, // Format_RGBA64_Premultiplied
};
static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
{
0, // Format_Invalid
- destStore64, // Format_Mono,
- destStore64, // Format_MonoLSB
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB
0, // Format_Indexed8
destStore64, // Format_RGB32
- destStore64ARGB32, // Format_ARGB32,
+ destStore64, // Format_ARGB32,
destStore64, // Format_ARGB32_Premultiplied
destStore64, // Format_RGB16
destStore64, // Format_ARGB8565_Premultiplied
@@ -1550,14 +1901,17 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
destStore64, // Format_RGB444
destStore64, // Format_ARGB4444_Premultiplied
destStore64, // Format_RGBX8888
- destStore64RGBA8888, // Format_RGBA8888
+ destStore64, // Format_RGBA8888
destStore64, // Format_RGBA8888_Premultiplied
- destStore64RGB30<PixelOrderBGR>, // Format_BGR30
- destStore64RGB30<PixelOrderBGR>, // Format_A2BGR30_Premultiplied
- destStore64RGB30<PixelOrderRGB>, // Format_RGB30
- destStore64RGB30<PixelOrderRGB>, // Format_A2RGB30_Premultiplied
+ destStore64, // Format_BGR30
+ destStore64, // Format_A2BGR30_Premultiplied
+ destStore64, // Format_RGB30
+ destStore64, // Format_A2RGB30_Premultiplied
destStore64, // Format_Alpha8
destStore64, // Format_Grayscale8
+ 0, // Format_RGBX64
+ destStore64RGBA64, // Format_RGBA64
+ 0 // Format_RGBA64_Premultiplied
};
/*
@@ -1589,15 +1943,14 @@ static const uint *QT_FASTCALL fetchUntransformed(uint *buffer, const Operator *
const QSpanData *data, int y, int x, int length)
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- const uint *ptr = qFetchPixels[layout->bpp](buffer, data->texture.scanLine(y), x, length);
- return layout->convertToARGB32PM(buffer, ptr, length, data->texture.colorTable, 0);
+ return layout->fetchToARGB32PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr);
}
static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator *,
const QSpanData *data, int y, int x, int)
{
const uchar *scanLine = data->texture.scanLine(y);
- return ((const uint *)scanLine) + x;
+ return reinterpret_cast<const uint *>(scanLine) + x;
}
static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Operator *,
@@ -1614,39 +1967,47 @@ static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Op
const QSpanData *data, int y, int x, int length)
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- if (layout->bpp != QPixelLayout::BPP32) {
- uint buffer32[buffer_size];
- const uint *ptr = qFetchPixels[layout->bpp](buffer32, data->texture.scanLine(y), x, length);
- return layout->convertToARGB64PM(buffer, ptr, length, data->texture.colorTable, 0);
+ return layout->fetchToRGBA64PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr);
+}
+
+static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Operator *,
+ const QSpanData *data, int y, int x, int)
+{
+ const uchar *scanLine = data->texture.scanLine(y);
+ return reinterpret_cast<const QRgba64 *>(scanLine) + x;
+}
+
+template<TextureBlendType blendType>
+inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
+{
+ Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled);
+ if (blendType == BlendTransformedTiled) {
+ if (v < 0 || v >= max) {
+ v %= max;
+ if (v < 0) v += max;
+ }
} else {
- const uint *src = (const uint *)data->texture.scanLine(y) + x;
- return layout->convertToARGB64PM(buffer, src, length, data->texture.colorTable, 0);
+ v = qBound(l1, v, l2);
}
}
-template<TextureBlendType blendType, QPixelLayout::BPP bpp>
-static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
- int y, int x, int length)
+template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
+static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
+ int y, int x, int length)
{
Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled);
- const int image_width = data->texture.width;
- const int image_height = data->texture.height;
- const int image_x1 = data->texture.x1;
- const int image_y1 = data->texture.y1;
- const int image_x2 = data->texture.x2 - 1;
- const int image_y2 = data->texture.y2 - 1;
+ const QTextureData &image = data->texture;
const qreal cx = x + qreal(0.5);
const qreal cy = y + qreal(0.5);
+ constexpr bool useFetch = (bpp < QPixelLayout::BPP32) && sizeof(T) == sizeof(uint);
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- if (bpp != QPixelLayout::BPPNone) // Like this to not ICE on GCC 5.3.1
+ if (!useFetch)
Q_ASSERT(layout->bpp == bpp);
// When templated 'fetch' should be inlined at compile time:
const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>);
- uint *const end = buffer + length;
- uint *b = buffer;
if (data->fast_matrix) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale);
@@ -1657,24 +2018,105 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
int fy = int((data->m22 * cy
+ data->m12 * cx + data->dy) * fixed_scale);
- while (b < end) {
- int px = fx >> 16;
- int py = fy >> 16;
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(image_x1, px, image_x2);
- py = qBound(image_y1, py, image_y2);
+ if (fdy == 0) { // simple scale, no rotation or shear
+ int py = (fy >> 16);
+ fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
+ const uchar *src = image.scanLine(py);
+
+ int i = 0;
+ if (blendType == BlendTransformed) {
+ int fastLen = length;
+ if (fdx > 0)
+ fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
+ else if (fdx < 0)
+ fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx));
+
+ for (; i < fastLen; ++i) {
+ int x1 = (fx >> 16);
+ int x2 = x1;
+ fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1);
+ if (x1 == x2)
+ break;
+ if (useFetch)
+ buffer[i] = fetch(src, x1);
+ else
+ buffer[i] = reinterpret_cast<const T*>(src)[x1];
+ fx += fdx;
+ }
+
+ for (; i < fastLen; ++i) {
+ int px = (fx >> 16);
+ if (useFetch)
+ buffer[i] = fetch(src, px);
+ else
+ buffer[i] = reinterpret_cast<const T*>(src)[px];
+ fx += fdx;
+ }
}
- *b = fetch(data->texture.scanLine(py), px);
- fx += fdx;
- fy += fdy;
- ++b;
+ for (; i < length; ++i) {
+ int px = (fx >> 16);
+ fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
+ if (useFetch)
+ buffer[i] = fetch(src, px);
+ else
+ buffer[i] = reinterpret_cast<const T*>(src)[px];
+ fx += fdx;
+ }
+ } else { // rotation or shear
+ int i = 0;
+ if (blendType == BlendTransformed) {
+ int fastLen = length;
+ if (fdx > 0)
+ fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
+ else if (fdx < 0)
+ fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx));
+ if (fdy > 0)
+ fastLen = qMin(fastLen, int((qint64(image.y2 - 1) * fixed_scale - fy) / fdy));
+ else if (fdy < 0)
+ fastLen = qMin(fastLen, int((qint64(image.y1) * fixed_scale - fy) / fdy));
+
+ for (; i < fastLen; ++i) {
+ int x1 = (fx >> 16);
+ int y1 = (fy >> 16);
+ int x2 = x1;
+ int y2 = y1;
+ fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1);
+ fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1);
+ if (x1 == x2 && y1 == y2)
+ break;
+ if (useFetch)
+ buffer[i] = fetch(image.scanLine(y1), x1);
+ else
+ buffer[i] = reinterpret_cast<const T*>(image.scanLine(y1))[x1];
+ fx += fdx;
+ fy += fdy;
+ }
+
+ for (; i < fastLen; ++i) {
+ int px = (fx >> 16);
+ int py = (fy >> 16);
+ if (useFetch)
+ buffer[i] = fetch(image.scanLine(py), px);
+ else
+ buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px];
+ fx += fdx;
+ fy += fdy;
+ }
+ }
+
+ for (; i < length; ++i) {
+ int px = (fx >> 16);
+ int py = (fy >> 16);
+ fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
+ fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
+ if (useFetch)
+ buffer[i] = fetch(image.scanLine(py), px);
+ else
+ buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px];
+ fx += fdx;
+ fy += fdy;
+ }
}
} else {
const qreal fdx = data->m11;
@@ -1685,23 +2127,21 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+ T *const end = buffer + length;
+ T *b = buffer;
while (b < end) {
const qreal iw = fw == 0 ? 1 : 1 / fw;
const qreal tx = fx * iw;
const qreal ty = fy * iw;
- int px = int(tx) - (tx < 0);
- int py = int(ty) - (ty < 0);
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(image_x1, px, image_x2);
- py = qBound(image_y1, py, image_y2);
- }
- *b = fetch(data->texture.scanLine(py), px);
+ int px = qFloor(tx);
+ int py = qFloor(ty);
+
+ fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
+ fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
+ if (useFetch)
+ *b = fetch(image.scanLine(py), px);
+ else
+ *b = reinterpret_cast<const T*>(image.scanLine(py))[px];
fx += fdx;
fy += fdy;
@@ -1713,115 +2153,37 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
++b;
}
}
- return layout->convertToARGB32PM(buffer, buffer, length, data->texture.colorTable, 0);
+}
+
+template<TextureBlendType blendType, QPixelLayout::BPP bpp>
+static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled);
+ const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
+ fetchTransformed_fetcher<blendType, bpp, uint>(buffer, data, y, x, length);
+ layout->convertToARGB32PM(buffer, length, data->texture.colorTable);
+ return buffer;
}
template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data,
int y, int x, int length)
{
- const int image_width = data->texture.width;
- const int image_height = data->texture.height;
- const int image_x1 = data->texture.x1;
- const int image_y1 = data->texture.y1;
- const int image_x2 = data->texture.x2 - 1;
- const int image_y2 = data->texture.y2 - 1;
-
- const qreal cx = x + qreal(0.5);
- const qreal cy = y + qreal(0.5);
-
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- FetchPixelFunc fetch = qFetchPixel[layout->bpp];
- const QVector<QRgb> *clut = data->texture.colorTable;
-
- uint buffer32[buffer_size];
- QRgba64 *b = buffer;
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- int fdx = (int)(data->m11 * fixed_scale);
- int fdy = (int)(data->m12 * fixed_scale);
-
- int fx = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int fy = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
-
- int i = 0, j = 0;
- while (i < length) {
- if (j == buffer_size) {
- layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0);
- b += buffer_size;
- j = 0;
- }
- int px = fx >> 16;
- int py = fy >> 16;
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(image_x1, px, image_x2);
- py = qBound(image_y1, py, image_y2);
- }
- buffer32[j] = fetch(data->texture.scanLine(py), px);
-
- fx += fdx;
- fy += fdy;
- ++i; ++j;
- }
- if (j > 0) {
- layout->convertToARGB64PM(b, buffer32, j, clut, 0);
- b += j;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
-
- qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
- qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
- qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
-
- int i = 0, j = 0;
- while (i < length) {
- if (j == buffer_size) {
- layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0);
- b += buffer_size;
- j = 0;
- }
- const qreal iw = fw == 0 ? 1 : 1 / fw;
- const qreal tx = fx * iw;
- const qreal ty = fy * iw;
- int px = int(tx) - (tx < 0);
- int py = int(ty) - (ty < 0);
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(image_x1, px, image_x2);
- py = qBound(image_y1, py, image_y2);
- }
- buffer32[j] = fetch(data->texture.scanLine(py), px);
-
- fx += fdx;
- fy += fdy;
- fw += fdw;
- //force increment to avoid /0
- if (!fw) {
- fw += fdw;
- }
- ++i; ++j;
- }
- if (j > 0) {
- layout->convertToARGB64PM(b, buffer32, j, clut, 0);
- b += j;
- }
+ if (layout->bpp != QPixelLayout::BPP64) {
+ uint buffer32[BufferSize];
+ Q_ASSERT(length <= BufferSize);
+ if (layout->bpp == QPixelLayout::BPP32)
+ fetchTransformed_fetcher<blendType, QPixelLayout::BPP32, uint>(buffer32, data, y, x, length);
+ else
+ fetchTransformed_fetcher<blendType, QPixelLayout::BPPNone, uint>(buffer32, data, y, x, length);
+ return layout->convertToRGBA64PM(buffer, buffer32, length, data->texture.colorTable, nullptr);
}
+
+ fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, QRgba64>(buffer, data, y, x, length);
+ if (data->texture.format == QImage::Format_RGBA64)
+ convertRGBA64ToRGBA64PM(buffer, length);
return buffer;
}
@@ -1915,43 +2277,6 @@ static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, u
}
#endif
-#if defined(__SSE2__)
-static inline QRgba64 interpolate_4_pixels_rgb64(QRgba64 t[], QRgba64 b[], uint distx, uint disty)
-{
- __m128i vt = _mm_loadu_si128((const __m128i*)t);
- if (disty) {
- __m128i vb = _mm_loadu_si128((const __m128i*)b);
- vt = _mm_mulhi_epu16(vt, _mm_set1_epi16(0x10000 - disty));
- vb = _mm_mulhi_epu16(vb, _mm_set1_epi16(disty));
- vt = _mm_add_epi16(vt, vb);
- }
- if (distx) {
- const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
- const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
- vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
- vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
- }
-#ifdef Q_PROCESSOR_X86_64
- return QRgba64::fromRgba64(_mm_cvtsi128_si64(vt));
-#else
- QRgba64 out;
- _mm_storel_epi64((__m128i*)&out, vt);
- return out;
-#endif
-}
-#else
-static inline QRgba64 interpolate_4_pixels_rgb64(QRgba64 t[], QRgba64 b[], uint distx, uint disty)
-{
- const uint dx = distx>>8;
- const uint dy = disty>>8;
- const uint idx = 256 - dx;
- const uint idy = 256 - dy;
- QRgba64 xtop = interpolate256(t[0], idx, t[1], dx);
- QRgba64 xbot = interpolate256(b[0], idx, b[1], dx);
- return interpolate256(xtop, idy, xbot, dy);
-}
-#endif
-
template<TextureBlendType blendType>
void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2);
@@ -1982,7 +2307,7 @@ inline void fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(int,
}
enum FastTransformTypes {
- SimpleUpscaleTransform,
+ SimpleScaleTransform,
UpscaleTransform,
DownscaleTransform,
RotateTransform,
@@ -1990,11 +2315,38 @@ enum FastTransformTypes {
NFastTransformTypes
};
+// Completes the partial interpolation stored in IntermediateBuffer.
+// by performing the x-axis interpolation and joining the RB and AG buffers.
+static void QT_FASTCALL intermediate_adder(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx)
+{
+#if defined(QT_COMPILER_SUPPORTS_AVX2)
+ extern void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx);
+ if (qCpuHasFeature(AVX2))
+ return intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx);
+#endif
+
+ // Switch to intermediate buffer coordinates
+ fx -= offset * fixed_scale;
+
+ while (b < end) {
+ const int x = (fx >> 16);
+
+ const uint distx = (fx & 0x0000ffff) >> 8;
+ const uint idistx = 256 - distx;
+ const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00;
+ const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00;
+ *b = (rb >> 8) | ag;
+ b++;
+ fx += fdx;
+ }
+ fx += offset * fixed_scale;
+}
+
typedef void (QT_FASTCALL *BilinearFastTransformHelper)(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy);
template<TextureBlendType blendType>
-static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(uint *b, uint *end, const QTextureData &image,
- int &fx, int &fy, int fdx, int /*fdy*/)
+static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper(uint *b, uint *end, const QTextureData &image,
+ int &fx, int &fy, int fdx, int /*fdy*/)
{
int y1 = (fy >> 16);
int y2;
@@ -2011,16 +2363,12 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u
const int offset = (fx + adjust) >> 16;
int x = offset;
- // The idea is first to do the interpolation between the row s1 and the row s2
- // into an intermediate buffer, then we interpolate between two pixel of this buffer.
-
- // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
- // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG
- // +1 for the last pixel to interpolate with, and +1 for rounding errors.
- quint32 intermediate_buffer[2][buffer_size + 2];
- // count is the size used in the intermediate_buffer.
+ IntermediateBuffer intermediate;
+ // count is the size used in the intermediate.buffer.
int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2;
- Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case
+ // length is supposed to be <= BufferSize either because data->m11 < 1 or
+ // data->m11 < 2, and any larger buffers split
+ Q_ASSERT(count <= BufferSize + 2);
int f = 0;
int lim = count;
if (blendType == BlendTransformedBilinearTiled) {
@@ -2035,8 +2383,8 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u
quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
do {
- intermediate_buffer[0][f] = rb;
- intermediate_buffer[1][f] = ag;
+ intermediate.buffer_rb[f] = rb;
+ intermediate.buffer_ag[f] = ag;
f++;
x++;
} while (x < image.x1 && f < lim);
@@ -2069,10 +2417,10 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u
// Add the values, and shift to only keep 8 significant bits per colors
__m128i rAG =_mm_add_epi16(topAG, bottomAG);
rAG = _mm_srli_epi16(rAG, 8);
- _mm_storeu_si128((__m128i*)(&intermediate_buffer[1][f]), rAG);
+ _mm_storeu_si128((__m128i*)(&intermediate.buffer_ag[f]), rAG);
__m128i rRB =_mm_add_epi16(topRB, bottomRB);
rRB = _mm_srli_epi16(rRB, 8);
- _mm_storeu_si128((__m128i*)(&intermediate_buffer[0][f]), rRB);
+ _mm_storeu_si128((__m128i*)(&intermediate.buffer_rb[f]), rRB);
}
#elif defined(__ARM_NEON__)
const int16x8_t disty_ = vdupq_n_s16(disty);
@@ -2099,10 +2447,10 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u
// Add the values, and shift to only keep 8 significant bits per colors
int16x8_t rAG = vaddq_s16(topAG, bottomAG);
rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8));
- vst1q_s16((int16_t*)(&intermediate_buffer[1][f]), rAG);
+ vst1q_s16((int16_t*)(&intermediate.buffer_ag[f]), rAG);
int16x8_t rRB = vaddq_s16(topRB, bottomRB);
rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8));
- vst1q_s16((int16_t*)(&intermediate_buffer[0][f]), rRB);
+ vst1q_s16((int16_t*)(&intermediate.buffer_rb[f]), rRB);
}
#endif
}
@@ -2116,28 +2464,13 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u
uint t = s1[x];
uint b = s2[x];
- intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
- intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
x++;
}
- // Now interpolate the values from the intermediate_buffer to get the final result.
- fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates
-
- while (b < end) {
- int x1 = (fx >> 16);
- int x2 = x1 + 1;
- Q_ASSERT(x1 >= 0);
- Q_ASSERT(x2 < count);
-
- int distx = (fx & 0x0000ffff) >> 8;
- int idistx = 256 - distx;
- int rb = ((intermediate_buffer[0][x1] * idistx + intermediate_buffer[0][x2] * distx) >> 8) & 0xff00ff;
- int ag = (intermediate_buffer[1][x1] * idistx + intermediate_buffer[1][x2] * distx) & 0xff00ff00;
- *b = rb | ag;
- b++;
- fx += fdx;
- }
+ // Now interpolate the values from the intermediate.buffer to get the final result.
+ intermediate_adder(b, end, intermediate, offset, fx, fdx);
}
template<TextureBlendType blendType>
@@ -2603,14 +2936,14 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper(uint
static BilinearFastTransformHelper bilinearFastTransformHelperARGB32PM[2][NFastTransformTypes] = {
{
- fetchTransformedBilinearARGB32PM_simple_upscale_helper<BlendTransformedBilinear>,
+ fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinear>,
fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinear>,
fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinear>,
fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinear>,
fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinear>
},
{
- fetchTransformedBilinearARGB32PM_simple_upscale_helper<BlendTransformedBilinearTiled>,
+ fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinearTiled>,
fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinearTiled>,
fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinearTiled>,
fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinearTiled>,
@@ -2645,7 +2978,13 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
if (fdy == 0) { // simple scale, no rotation or shear
if (qAbs(fdx) <= fixed_scale) {
// simple scale up on X
- bilinearFastTransformHelperARGB32PM[tiled][SimpleUpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
+ bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
+ } else if (qAbs(fdx) <= 2 * fixed_scale) {
+ // simple scale down on X, less than 2x
+ const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
+ bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
+ if (mid != length)
+ bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
} else if (qAbs(data->m22) < qreal(1./8.)) {
// scale up more than 8x (on Y)
bilinearFastTransformHelperARGB32PM[tiled][UpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
@@ -2713,16 +3052,13 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
return buffer;
}
-template<TextureBlendType blendType, QPixelLayout::BPP bpp>
-static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, uint *end, const QTextureData &image,
- int &fx, int &fy, int fdx, int /*fdy*/)
+template<TextureBlendType blendType>
+static void QT_FASTCALL fetchTransformedBilinear_simple_scale_helper(uint *b, uint *end, const QTextureData &image,
+ int &fx, int &fy, int fdx, int /*fdy*/)
{
const QPixelLayout *layout = &qPixelLayouts[image.format];
const QVector<QRgb> *clut = image.colorTable;
- Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout->bpp);
- // When templated 'fetch' should be inlined at compile time:
- const FetchPixelsFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixels[layout->bpp] : fetchPixels<bpp>;
- const ConvertFunc convertToARGB32PM = layout->convertToARGB32PM;
+ const FetchAndConvertPixelsFunc fetch = layout->fetchToARGB32PM;
int y1 = (fy >> 16);
int y2;
@@ -2739,16 +3075,14 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
const int offset = (fx + adjust) >> 16;
int x = offset;
- // The idea is first to do the interpolation between the row s1 and the row s2
- // into an intermediate buffer, then we interpolate between two pixel of this buffer.
- // +1 for the last pixel to interpolate with, and +1 for rounding errors.
- uint buf1[buffer_size + 2];
- uint buf2[buffer_size + 2];
+ IntermediateBuffer intermediate;
+ uint *buf1 = intermediate.buffer_rb;
+ uint *buf2 = intermediate.buffer_ag;
const uint *ptr1;
const uint *ptr2;
int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2;
- Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case
+ Q_ASSERT(count <= BufferSize + 2);
if (blendType == BlendTransformedBilinearTiled) {
x %= image.width;
@@ -2757,10 +3091,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
int len1 = qMin(count, image.width - x);
int len2 = qMin(x, count - len1);
- ptr1 = fetch(buf1, s1, x, len1);
- ptr1 = convertToARGB32PM(buf1, ptr1, len1, clut, 0);
- ptr2 = fetch(buf2, s2, x, len1);
- ptr2 = convertToARGB32PM(buf2, ptr2, len1, clut, 0);
+ ptr1 = fetch(buf1, s1, x, len1, clut, nullptr);
+ ptr2 = fetch(buf2, s2, x, len1, clut, nullptr);
for (int i = 0; i < len1; ++i) {
uint t = ptr1[i];
uint b = ptr2[i];
@@ -2769,10 +3101,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
}
if (len2) {
- ptr1 = fetch(buf1 + len1, s1, 0, len2);
- ptr1 = convertToARGB32PM(buf1 + len1, ptr1, len2, clut, 0);
- ptr2 = fetch(buf2 + len1, s2, 0, len2);
- ptr2 = convertToARGB32PM(buf2 + len1, ptr2, len2, clut, 0);
+ ptr1 = fetch(buf1 + len1, s1, 0, len2, clut, nullptr);
+ ptr2 = fetch(buf2 + len1, s2, 0, len2, clut, nullptr);
for (int i = 0; i < len2; ++i) {
uint t = ptr1[i];
uint b = ptr2[i];
@@ -2780,6 +3110,7 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
}
}
+ // Generate the rest by repeatedly repeating the previous set of pixels
for (int i = image.width; i < count; ++i) {
buf1[i] = buf1[i - image.width];
buf2[i] = buf2[i - image.width];
@@ -2790,10 +3121,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
int len = qMax(1, end - start);
int leading = start - x;
- ptr1 = fetch(buf1 + leading, s1, start, len);
- ptr1 = convertToARGB32PM(buf1 + leading, ptr1, len, clut, 0);
- ptr2 = fetch(buf2 + leading, s2, start, len);
- ptr2 = convertToARGB32PM(buf2 + leading, ptr2, len, clut, 0);
+ ptr1 = fetch(buf1 + leading, s1, start, len, clut, nullptr);
+ ptr2 = fetch(buf2 + leading, s2, start, len, clut, nullptr);
for (int i = 0; i < len; ++i) {
uint t = ptr1[i];
@@ -2812,35 +3141,21 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b,
}
}
- // Now interpolate the values from the intermediate_buffer to get the final result.
- fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates
-
- while (b < end) {
- int x1 = (fx >> 16);
- int x2 = x1 + 1;
- Q_ASSERT(x1 >= 0);
- Q_ASSERT(x2 < count);
-
- int distx = (fx & 0x0000ffff) >> 8;
- int idistx = 256 - distx;
- int rb = ((buf1[x1] * idistx + buf1[x2] * distx) >> 8) & 0xff00ff;
- int ag = (buf2[x1] * idistx + buf2[x2] * distx) & 0xff00ff00;
- *b++ = rb | ag;
- fx += fdx;
- }
+ // Now interpolate the values from the intermediate.buffer to get the final result.
+ intermediate_adder(b, end, intermediate, offset, fx, fdx);
}
-typedef void (QT_FASTCALL *BilinearFastTransformFetcher)(uint *buf1, uint *buf2, const int len, const QTextureData &image,
- int fx, int fy, const int fdx, const int fdy);
-
-template<TextureBlendType blendType, QPixelLayout::BPP bpp>
-static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, const int len, const QTextureData &image,
+template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
+static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int len, const QTextureData &image,
int fx, int fy, const int fdx, const int fdy)
{
const QPixelLayout &layout = qPixelLayouts[image.format];
- Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout.bpp);
- // When templated 'fetch1' should be inlined at compile time:
+ constexpr bool useFetch = (bpp < QPixelLayout::BPP32);
+ if (useFetch)
+ Q_ASSERT(sizeof(T) == sizeof(uint));
+ else
+ Q_ASSERT(layout.bpp == bpp);
const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout.bpp] : fetchPixel<bpp>;
if (fdy == 0) {
int y1 = (fy >> 16);
@@ -2857,8 +3172,13 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
if (x1 != x2)
break;
- buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1);
- buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1);
+ if (useFetch) {
+ buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1);
+ buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1);
+ } else {
+ buf1[i * 2 + 0] = buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x1];
+ buf2[i * 2 + 0] = buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x1];
+ }
fx += fdx;
}
int fastLen = len;
@@ -2869,10 +3189,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
for (; i < fastLen; ++i) {
int x = (fx >> 16);
- buf1[i * 2 + 0] = fetch1(s1, x);
- buf1[i * 2 + 1] = fetch1(s1, x + 1);
- buf2[i * 2 + 0] = fetch1(s2, x);
- buf2[i * 2 + 1] = fetch1(s2, x + 1);
+ if (useFetch) {
+ buf1[i * 2 + 0] = fetch1(s1, x);
+ buf1[i * 2 + 1] = fetch1(s1, x + 1);
+ buf2[i * 2 + 0] = fetch1(s2, x);
+ buf2[i * 2 + 1] = fetch1(s2, x + 1);
+ } else {
+ buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
+ buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
+ buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
+ buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
+ }
fx += fdx;
}
}
@@ -2881,10 +3208,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
int x1 = (fx >> 16);
int x2;
fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
- buf1[i * 2 + 0] = fetch1(s1, x1);
- buf1[i * 2 + 1] = fetch1(s1, x2);
- buf2[i * 2 + 0] = fetch1(s2, x1);
- buf2[i * 2 + 1] = fetch1(s2, x2);
+ if (useFetch) {
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
+ } else {
+ buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
+ buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
+ buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
+ buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
+ }
fx += fdx;
}
} else {
@@ -2901,10 +3235,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
break;
const uchar *s1 = image.scanLine(y1);
const uchar *s2 = image.scanLine(y2);
- buf1[i * 2 + 0] = fetch1(s1, x1);
- buf1[i * 2 + 1] = fetch1(s1, x2);
- buf2[i * 2 + 0] = fetch1(s2, x1);
- buf2[i * 2 + 1] = fetch1(s2, x2);
+ if (useFetch) {
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
+ } else {
+ buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
+ buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
+ buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
+ buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
+ }
fx += fdx;
fy += fdy;
}
@@ -2923,10 +3264,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
int y = (fy >> 16);
const uchar *s1 = image.scanLine(y);
const uchar *s2 = s1 + image.bytesPerLine;
- buf1[i * 2 + 0] = fetch1(s1, x);
- buf1[i * 2 + 1] = fetch1(s1, x + 1);
- buf2[i * 2 + 0] = fetch1(s2, x);
- buf2[i * 2 + 1] = fetch1(s2, x + 1);
+ if (useFetch) {
+ buf1[i * 2 + 0] = fetch1(s1, x);
+ buf1[i * 2 + 1] = fetch1(s1, x + 1);
+ buf2[i * 2 + 0] = fetch1(s2, x);
+ buf2[i * 2 + 1] = fetch1(s2, x + 1);
+ } else {
+ buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
+ buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
+ buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
+ buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
+ }
fx += fdx;
fy += fdy;
}
@@ -2942,10 +3290,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2,
const uchar *s1 = image.scanLine(y1);
const uchar *s2 = image.scanLine(y2);
- buf1[i * 2 + 0] = fetch1(s1, x1);
- buf1[i * 2 + 1] = fetch1(s1, x2);
- buf2[i * 2 + 0] = fetch1(s2, x1);
- buf2[i * 2 + 1] = fetch1(s2, x2);
+ if (useFetch) {
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
+ } else {
+ buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
+ buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
+ buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
+ buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
+ }
fx += fdx;
fy += fdy;
}
@@ -2977,18 +3332,23 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
if (fdy == 0) { // simple scale, no rotation or shear
if (qAbs(fdx) <= fixed_scale) { // scale up on X
- fetchTransformedBilinear_simple_upscale_helper<blendType, bpp>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy);
+ fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy);
+ } else if (qAbs(fdx) <= 2 * fixed_scale) { // scale down on X less than 2x
+ const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
+ fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
+ if (mid != length)
+ fetchTransformedBilinear_simple_scale_helper<blendType>(buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
} else {
- const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>;
+ const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
- uint buf1[buffer_size];
- uint buf2[buffer_size];
+ uint buf1[BufferSize];
+ uint buf2[BufferSize];
uint *b = buffer;
while (length) {
- int len = qMin(length, buffer_size / 2);
+ int len = qMin(length, BufferSize / 2);
fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, 0);
- layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
- layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf1, len * 2, clut);
+ layout->convertToARGB32PM(buf2, len * 2, clut);
if (hasFastInterpolate4() || qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y)
int disty = (fy & 0x0000ffff) >> 8;
@@ -3014,16 +3374,16 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
}
}
} else { // rotation or shear
- const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>;
+ const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
- uint buf1[buffer_size];
- uint buf2[buffer_size];
+ uint buf1[BufferSize];
+ uint buf2[BufferSize];
uint *b = buffer;
while (length) {
- int len = qMin(length, buffer_size / 2);
+ int len = qMin(length, BufferSize / 2);
fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
- layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
- layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf1, len * 2, clut);
+ layout->convertToARGB32PM(buf2, len * 2, clut);
if (hasFastInterpolate4() || qAbs(data->m11) < qreal(1./8.) || qAbs(data->m22) < qreal(1./8.)) {
// If we are zooming more than 8 times, we use 8bit precision for the position.
@@ -3070,15 +3430,15 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
- uint buf1[buffer_size];
- uint buf2[buffer_size];
+ uint buf1[BufferSize];
+ uint buf2[BufferSize];
uint *b = buffer;
- int distxs[buffer_size / 2];
- int distys[buffer_size / 2];
+ int distxs[BufferSize / 2];
+ int distys[BufferSize / 2];
while (length) {
- int len = qMin(length, buffer_size / 2);
+ int len = qMin(length, BufferSize / 2);
for (int i = 0; i < len; ++i) {
const qreal iw = fw == 0 ? 1 : 1 / fw;
const qreal px = fx * iw - qreal(0.5);
@@ -3110,8 +3470,8 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
fw += fdw;
}
- layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
- layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf1, len * 2, clut);
+ layout->convertToARGB32PM(buf2, len * 2, clut);
for (int i = 0; i < len; ++i) {
int distx = distxs[i];
@@ -3128,19 +3488,27 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
}
template<TextureBlendType blendType>
-static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *,
- const QSpanData *data, int y, int x, int length)
+static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data,
+ int y, int x, int length)
{
- const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
+ const QTextureData &texture = data->texture;
+ const QPixelLayout *layout = &qPixelLayouts[texture.format];
const QVector<QRgb> *clut = data->texture.colorTable;
const qreal cx = x + qreal(0.5);
const qreal cy = y + qreal(0.5);
+ uint sbuf1[BufferSize];
+ uint sbuf2[BufferSize];
+ QRgba64 buf1[BufferSize];
+ QRgba64 buf2[BufferSize];
+ QRgba64 *end = buffer + length;
+ QRgba64 *b = buffer;
+
if (data->fast_matrix) {
// The increment pr x in the scanline
- int fdx = (int)(data->m11 * fixed_scale);
- int fdy = (int)(data->m12 * fixed_scale);
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
@@ -3148,20 +3516,14 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fx -= half_point;
fy -= half_point;
- const BilinearFastTransformFetcher fetcher =
+ const auto fetcher =
(layout->bpp == QPixelLayout::BPP32)
- ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32>
- : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone>;
+ ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint>
+ : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>;
if (fdy == 0) { //simple scale, no rotation
-
- uint sbuf1[buffer_size];
- uint sbuf2[buffer_size];
- quint64 buf1[buffer_size];
- quint64 buf2[buffer_size];
- QRgba64 *b = buffer;
while (length) {
- int len = qMin(length, buffer_size / 2);
+ int len = qMin(length, BufferSize / 2);
int disty = (fy & 0x0000ffff);
#if defined(__SSE2__)
const __m128i vdy = _mm_set1_epi16(disty);
@@ -3169,9 +3531,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
#endif
fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
- layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0);
+ layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0);
if (disty)
- layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0);
+ layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = (fx & 0x0000ffff);
@@ -3191,33 +3553,26 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
}
_mm_storel_epi64((__m128i*)(b+i), vt);
#else
- b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty);
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
#endif
fx += fdx;
}
length -= len;
b += len;
}
- } else { //rotation
- uint sbuf1[buffer_size];
- uint sbuf2[buffer_size];
- quint64 buf1[buffer_size];
- quint64 buf2[buffer_size];
- QRgba64 *end = buffer + length;
- QRgba64 *b = buffer;
-
+ } else { // rotation or shear
while (b < end) {
- int len = qMin(length, buffer_size / 2);
+ int len = qMin(length, BufferSize / 2);
fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
- layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0);
- layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0);
+ layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0);
+ layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = (fx & 0x0000ffff);
int disty = (fy & 0x0000ffff);
- b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty);
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
fx += fdx;
fy += fdy;
}
@@ -3226,7 +3581,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
b += len;
}
}
- } else {
+ } else { // !(data->fast_matrix)
const QTextureData &image = data->texture;
const qreal fdx = data->m11;
@@ -3238,17 +3593,12 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
FetchPixelFunc fetch = qFetchPixel[layout->bpp];
- uint sbuf1[buffer_size];
- uint sbuf2[buffer_size];
- quint64 buf1[buffer_size];
- quint64 buf2[buffer_size];
- QRgba64 *b = buffer;
- int distxs[buffer_size / 2];
- int distys[buffer_size / 2];
+ int distxs[BufferSize / 2];
+ int distys[BufferSize / 2];
- while (length) {
- int len = qMin(length, buffer_size / 2);
+ while (b < end) {
+ int len = qMin(length, BufferSize / 2);
for (int i = 0; i < len; ++i) {
const qreal iw = fw == 0 ? 1 : 1 / fw;
const qreal px = fx * iw - qreal(0.5);
@@ -3265,21 +3615,165 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
- const uchar *s1 = data->texture.scanLine(y1);
- const uchar *s2 = data->texture.scanLine(y2);
+ const uchar *s1 = texture.scanLine(y1);
+ const uchar *s2 = texture.scanLine(y2);
- if (layout->bpp == QPixelLayout::BPP32) {
- sbuf1[i * 2 + 0] = ((const uint*)s1)[x1];
- sbuf1[i * 2 + 1] = ((const uint*)s1)[x2];
- sbuf2[i * 2 + 0] = ((const uint*)s2)[x1];
- sbuf2[i * 2 + 1] = ((const uint*)s2)[x2];
+ sbuf1[i * 2 + 0] = fetch(s1, x1);
+ sbuf1[i * 2 + 1] = fetch(s1, x2);
+ sbuf2[i * 2 + 0] = fetch(s2, x1);
+ sbuf2[i * 2 + 1] = fetch(s2, x2);
- } else {
- sbuf1[i * 2 + 0] = fetch(s1, x1);
- sbuf1[i * 2 + 1] = fetch(s1, x2);
- sbuf2[i * 2 + 0] = fetch(s2, x1);
- sbuf2[i * 2 + 1] = fetch(s2, x2);
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw)
+ fw += fdw;
+ }
+
+ layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0);
+ layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0);
+
+ for (int i = 0; i < len; ++i) {
+ int distx = distxs[i];
+ int disty = distys[i];
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
+ }
+
+ length -= len;
+ b += len;
+ }
+ }
+ return buffer;
+}
+
+template<TextureBlendType blendType>
+static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buffer, const QSpanData *data,
+ int y, int x, int length)
+{
+ const QTextureData &texture = data->texture;
+ Q_ASSERT(qPixelLayouts[texture.format].bpp == QPixelLayout::BPP64);
+ const auto convert = (data->texture.format == QImage::Format_RGBA64) ? convertRGBA64ToRGBA64PM : convertRGBA64PMToRGBA64PM;
+
+ const qreal cx = x + qreal(0.5);
+ const qreal cy = y + qreal(0.5);
+
+ QRgba64 buf1[BufferSize];
+ QRgba64 buf2[BufferSize];
+ QRgba64 *end = buffer + length;
+ QRgba64 *b = buffer;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
+
+ fx -= half_point;
+ fy -= half_point;
+ const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, QRgba64>;
+
+ if (fdy == 0) { //simple scale, no rotation
+ while (length) {
+ int len = qMin(length, BufferSize / 2);
+ int disty = (fy & 0x0000ffff);
+#if defined(__SSE2__)
+ const __m128i vdy = _mm_set1_epi16(disty);
+ const __m128i vidy = _mm_set1_epi16(0x10000 - disty);
+#endif
+ fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
+
+ convert(buf1, len * 2);
+ if (disty)
+ convert(buf2, len * 2);
+
+ for (int i = 0; i < len; ++i) {
+ int distx = (fx & 0x0000ffff);
+#if defined(__SSE2__)
+ __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2));
+ if (disty) {
+ __m128i vb = _mm_loadu_si128((const __m128i*)(buf2 + i*2));
+ vt = _mm_mulhi_epu16(vt, vidy);
+ vb = _mm_mulhi_epu16(vb, vdy);
+ vt = _mm_add_epi16(vt, vb);
+ }
+ if (distx) {
+ const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
+ const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
+ vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
+ vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
+ }
+ _mm_storel_epi64((__m128i*)(b+i), vt);
+#else
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
+#endif
+ fx += fdx;
}
+ length -= len;
+ b += len;
+ }
+ } else { // rotation or shear
+ while (b < end) {
+ int len = qMin(length, BufferSize / 2);
+
+ fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
+
+ convert(buf1, len * 2);
+ convert(buf2, len * 2);
+
+ for (int i = 0; i < len; ++i) {
+ int distx = (fx & 0x0000ffff);
+ int disty = (fy & 0x0000ffff);
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
+ fx += fdx;
+ fy += fdy;
+ }
+
+ length -= len;
+ b += len;
+ }
+ }
+ } else { // !(data->fast_matrix)
+ const QTextureData &image = data->texture;
+
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int distxs[BufferSize / 2];
+ int distys[BufferSize / 2];
+
+ while (b < end) {
+ int len = qMin(length, BufferSize / 2);
+ for (int i = 0; i < len; ++i) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal px = fx * iw - qreal(0.5);
+ const qreal py = fy * iw - qreal(0.5);
+
+ int x1 = int(px) - (px < 0);
+ int x2;
+ int y1 = int(py) - (py < 0);
+ int y2;
+
+ distxs[i] = int((px - x1) * (1<<16));
+ distys[i] = int((py - y1) * (1<<16));
+
+ fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
+ fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
+
+ const uchar *s1 = texture.scanLine(y1);
+ const uchar *s2 = texture.scanLine(y2);
+
+ buf1[i * 2 + 0] = reinterpret_cast<const QRgba64 *>(s1)[x1];
+ buf1[i * 2 + 1] = reinterpret_cast<const QRgba64 *>(s1)[x2];
+ buf2[i * 2 + 0] = reinterpret_cast<const QRgba64 *>(s2)[x1];
+ buf2[i * 2 + 1] = reinterpret_cast<const QRgba64 *>(s2)[x2];
fx += fdx;
fy += fdy;
@@ -3289,23 +3783,31 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fw += fdw;
}
- layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0);
- layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0);
+ convert(buf1, len * 2);
+ convert(buf2, len * 2);
for (int i = 0; i < len; ++i) {
int distx = distxs[i];
int disty = distys[i];
- b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty);
+ b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
}
length -= len;
b += len;
}
}
-
return buffer;
}
+template<TextureBlendType blendType>
+static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *,
+ const QSpanData *data, int y, int x, int length)
+{
+ if (qPixelLayouts[data->texture.format].bpp == QPixelLayout::BPP64)
+ return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length);
+ return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length);
+}
+
// FetchUntransformed can have more specialized methods added depending on SIMD features.
static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
0, // Invalid
@@ -3333,6 +3835,9 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
fetchUntransformed, // Format_A2RGB30_Premultiplied
fetchUntransformed, // Alpha8
fetchUntransformed, // Grayscale8
+ fetchUntransformed, // RGBX64
+ fetchUntransformed, // RGBA64
+ fetchUntransformed, // RGBA64_Premultiplied
};
static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
@@ -3353,6 +3858,15 @@ static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
};
+static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
+ fetchUntransformed, // Untransformed
+ fetchUntransformed, // Tiled
+ fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
+ fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP16>, // TransformedTiled
+ fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP16>, // TransformedBilinear
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
+};
+
static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
@@ -3371,17 +3885,35 @@ static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
+static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
+ fetchUntransformedRGBA64PM, // Untransformed
+ fetchUntransformedRGBA64PM, // Tiled
+ fetchTransformed64<BlendTransformed>, // Transformed
+ fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
+ fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
+ fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
+};
+
static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
return sourceFetchARGB32PM[blendType];
if (blendType == BlendUntransformed || blendType == BlendTiled)
return sourceFetchUntransformed[format];
+ if (qPixelLayouts[format].bpp == QPixelLayout::BPP16)
+ return sourceFetchAny16[blendType];
if (qPixelLayouts[format].bpp == QPixelLayout::BPP32)
return sourceFetchAny32[blendType];
return sourceFetchGeneric[blendType];
}
+static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
+{
+ if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
+ return sourceFetchRGBA64PM[blendType];
+ return sourceFetchGeneric64[blendType];
+}
+
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
@@ -3724,7 +4256,7 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
case QSpanData::Texture:
solidSource = !data->texture.hasAlpha;
op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format);
- op.srcFetch64 = sourceFetchGeneric64[getBlendType(data)];
+ op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);;
break;
default:
Q_UNREACHABLE();
@@ -3752,8 +4284,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
// If all spans are opaque we do not need to fetch dest.
// But don't clear passthrough destFetch as they are just as fast and save destStore.
if (op.destFetch != destFetchARGB32P)
- op.destFetch = 0;
- op.destFetch64 = destFetch64Undefined;
+ op.destFetch = destFetchUndefined;
+ if (op.destFetch64 != destFetchRGB64)
+ op.destFetch64 = destFetch64Undefined;
}
}
@@ -3778,7 +4311,7 @@ static
void blend_color_generic(int count, const QSpan *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- uint buffer[buffer_size];
+ uint buffer[BufferSize];
Operator op = getOperator(data, spans, count);
const uint color = data->solid.color.toArgb32();
@@ -3786,8 +4319,8 @@ void blend_color_generic(int count, const QSpan *spans, void *userData)
int x = spans->x;
int length = spans->len;
while (length) {
- int l = qMin(buffer_size, length);
- uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ int l = qMin(BufferSize, length);
+ uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l);
op.funcSolid(dest, l, color, spans->coverage);
if (op.destStore)
op.destStore(data->rasterBuffer, x, spans->y, dest, l);
@@ -3802,7 +4335,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- Operator op = getOperator(data, spans, count);
+ const Operator op = getOperator(data, spans, count);
const uint color = data->solid.color.toArgb32();
if (op.mode == QPainter::CompositionMode_Source) {
@@ -3838,17 +4371,31 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData)
return blend_color_generic(count, spans, userData);
}
- quint64 buffer[buffer_size];
+ quint64 buffer[BufferSize];
const QRgba64 color = data->solid.color;
+ bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source
+ || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && color.isOpaque());
+ bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32;
while (count--) {
int x = spans->x;
int length = spans->len;
+ if (solidFill && isBpp32 && spans->coverage == 255) {
+ // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
+ if (length > 0) {
+ op.destStore64(data->rasterBuffer, x, spans->y, &color, 1);
+ uint *dest = (uint*)data->rasterBuffer->scanLine(spans->y) + x;
+ qt_memfill32(dest + 1, dest[0], length - 1);
+ length = 0;
+ }
+ }
+
while (length) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l);
op.funcSolid64(dest, l, color, spans->coverage);
- op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
+ if (op.destStore64)
+ op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
length -= l;
x += l;
}
@@ -3950,7 +4497,7 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle
int length = right - x;
while (length) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
length -= l;
int process_length = l;
@@ -3997,8 +4544,8 @@ struct QBlendBase
BlendType *dest;
- BlendType buffer[buffer_size];
- BlendType src_buffer[buffer_size];
+ BlendType buffer[BufferSize];
+ BlendType src_buffer[BufferSize];
};
class BlendSrcGeneric : public QBlendBase<uint>
@@ -4011,7 +4558,7 @@ public:
const uint *fetch(int x, int y, int len)
{
- dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, y, len) : buffer;
+ dest = op.destFetch(buffer, data->rasterBuffer, x, y, len);
return op.srcFetch(src_buffer, &op, data, y, x, len);
}
@@ -4037,7 +4584,7 @@ public:
bool isSupported() const
{
- return op.func64 && op.destFetch64 && op.destStore64;
+ return op.func64 && op.destFetch64;
}
const quint64 *fetch(int x, int y, int len)
@@ -4053,7 +4600,8 @@ public:
void store(int x, int y, int len)
{
- op.destStore64(data->rasterBuffer, x, y, (QRgba64 *)dest, len);
+ if (op.destStore64)
+ op.destStore64(data->rasterBuffer, x, y, (QRgba64 *)dest, len);
}
};
@@ -4082,8 +4630,8 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- uint buffer[buffer_size];
- uint src_buffer[buffer_size];
+ uint buffer[BufferSize];
+ uint src_buffer[BufferSize];
Operator op = getOperator(data, spans, count);
const int image_width = data->texture.width;
@@ -4107,9 +4655,9 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use
if (length > 0) {
const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
while (length) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
- uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l);
op.func(dest, src, l, coverage);
if (op.destStore)
op.destStore(data->rasterBuffer, x, spans->y, dest, l);
@@ -4132,8 +4680,8 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
return blend_untransformed_generic(count, spans, userData);
}
- quint64 buffer[buffer_size];
- quint64 src_buffer[buffer_size];
+ quint64 buffer[BufferSize];
+ quint64 src_buffer[BufferSize];
const int image_width = data->texture.width;
const int image_height = data->texture.height;
@@ -4156,11 +4704,12 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
if (length > 0) {
const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
while (length) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l);
QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l);
op.func64(dest, src, l, coverage);
- op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
+ if (op.destStore64)
+ op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
x += l;
sx += l;
length -= l;
@@ -4320,8 +4869,8 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- uint buffer[buffer_size];
- uint src_buffer[buffer_size];
+ uint buffer[BufferSize];
+ uint src_buffer[BufferSize];
Operator op = getOperator(data, spans, count);
const int image_width = data->texture.width;
@@ -4347,10 +4896,10 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData)
const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
while (length) {
int l = qMin(image_width - sx, length);
- if (buffer_size < l)
- l = buffer_size;
+ if (BufferSize < l)
+ l = BufferSize;
const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
- uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l);
op.func(dest, src, l, coverage);
if (op.destStore)
op.destStore(data->rasterBuffer, x, spans->y, dest, l);
@@ -4373,8 +4922,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD
qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
return blend_tiled_generic(count, spans, userData);
}
- quint64 buffer[buffer_size];
- quint64 src_buffer[buffer_size];
+ quint64 buffer[BufferSize];
+ quint64 src_buffer[BufferSize];
const int image_width = data->texture.width;
const int image_height = data->texture.height;
@@ -4386,6 +4935,50 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD
if (yoff < 0)
yoff += image_height;
+ bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32;
+ if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && isBpp32) {
+ // If destination isn't blended into the result, we can do the tiling directly on destination pixels.
+ while (count--) {
+ int x = spans->x;
+ int y = spans->y;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ int sl = qMin(image_width, length);
+ if (sx > 0 && sl > 0) {
+ int l = qMin(image_width - sx, sl);
+ const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l);
+ op.destStore64(data->rasterBuffer, x, y, src, l);
+ x += l;
+ sx += l;
+ sl -= l;
+ if (sx >= image_width)
+ sx = 0;
+ }
+ if (sl > 0) {
+ Q_ASSERT(sx == 0);
+ const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, sl);
+ op.destStore64(data->rasterBuffer, x, y, src, sl);
+ x += sl;
+ sx += sl;
+ sl -= sl;
+ if (sx >= image_width)
+ sx = 0;
+ }
+ uint *dest = (uint*)data->rasterBuffer->scanLine(y) + x - image_width;
+ for (int i = image_width; i < length; ++i) {
+ dest[i] = dest[i - image_width];
+ }
+ ++spans;
+ }
+ return;
+ }
+
while (count--) {
int x = spans->x;
int length = spans->len;
@@ -4399,12 +4992,13 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD
const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
while (length) {
int l = qMin(image_width - sx, length);
- if (buffer_size < l)
- l = buffer_size;
+ if (BufferSize < l)
+ l = BufferSize;
const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l);
QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l);
op.func64(dest, src, l, coverage);
- op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
+ if (op.destStore64)
+ op.destStore64(data->rasterBuffer, x, spans->y, dest, l);
x += l;
sx += l;
length -= l;
@@ -4449,8 +5043,8 @@ static void blend_tiled_argb(int count, const QSpan *spans, void *userData)
const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
while (length) {
int l = qMin(image_width - sx, length);
- if (buffer_size < l)
- l = buffer_size;
+ if (BufferSize < l)
+ l = BufferSize;
const uint *src = (const uint *)data->texture.scanLine(sy) + sx;
uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x;
op.func(dest, src, l, coverage);
@@ -4509,8 +5103,8 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
int tx = x;
while (length) {
int l = qMin(image_width - sx, length);
- if (buffer_size < l)
- l = buffer_size;
+ if (BufferSize < l)
+ l = BufferSize;
quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans->y)) + tx;
const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
memcpy(dest, src, l * sizeof(quint16));
@@ -4545,8 +5139,8 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
if (alpha > 0) {
while (length) {
int l = qMin(image_width - sx, length);
- if (buffer_size < l)
- l = buffer_size;
+ if (BufferSize < l)
+ l = BufferSize;
quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans->y)) + x;
const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
blend_sourceOver_rgb16_rgb16(dest, src, l, alpha, ialpha);
@@ -4562,723 +5156,12 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
}
}
-static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, void *userData)
-{
- QSpanData *data = reinterpret_cast<QSpanData*>(userData);
- QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
-
- if (data->texture.format != QImage::Format_RGB16
- || (mode != QPainter::CompositionMode_SourceOver
- && mode != QPainter::CompositionMode_Source))
- {
- blend_src_generic(count, spans, userData);
- return;
- }
-
- quint16 buffer[buffer_size];
-
- const int src_minx = data->texture.x1;
- const int src_miny = data->texture.y1;
- const int src_maxx = data->texture.x2 - 1;
- const int src_maxy = data->texture.y2 - 1;
-
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- const int fdx = (int)(data->m11 * fixed_scale);
- const int fdy = (int)(data->m12 * fixed_scale);
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
- int x = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale) - half_point;
- int y = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale) - half_point;
- int length = spans->len;
-
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- int x1 = (x >> 16);
- int x2;
- int y1 = (y >> 16);
- int y2;
-
- fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_minx, src_maxx, x1, x2);
- fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_miny, src_maxy, y1, y2);
-
- const quint16 *src1 = (const quint16*)data->texture.scanLine(y1);
- const quint16 *src2 = (const quint16*)data->texture.scanLine(y2);
- quint16 tl = src1[x1];
- const quint16 tr = src1[x2];
- quint16 bl = src2[x1];
- const quint16 br = src2[x2];
-
- const uint distxsl8 = x & 0xff00;
- const uint distysl8 = y & 0xff00;
- const uint distx = distxsl8 >> 8;
- const uint disty = distysl8 >> 8;
- const uint distxy = distx * disty;
-
- const uint tlw = 0x10000 - distxsl8 - distysl8 + distxy; // (256 - distx) * (256 - disty)
- const uint trw = distxsl8 - distxy; // distx * (256 - disty)
- const uint blw = distysl8 - distxy; // (256 - distx) * disty
- const uint brw = distxy; // distx * disty
- uint red = ((tl & 0xf800) * tlw + (tr & 0xf800) * trw
- + (bl & 0xf800) * blw + (br & 0xf800) * brw) & 0xf8000000;
- uint green = ((tl & 0x07e0) * tlw + (tr & 0x07e0) * trw
- + (bl & 0x07e0) * blw + (br & 0x07e0) * brw) & 0x07e00000;
- uint blue = ((tl & 0x001f) * tlw + (tr & 0x001f) * trw
- + (bl & 0x001f) * blw + (br & 0x001f) * brw);
- *b = quint16((red | green | blue) >> 16);
-
- ++b;
- x += fdx;
- y += fdy;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- qreal x = data->m21 * cy + data->m11 * cx + data->dx;
- qreal y = data->m22 * cy + data->m12 * cx + data->dy;
- qreal w = data->m23 * cy + data->m13 * cx + data->m33;
-
- int length = spans->len;
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- const qreal iw = w == 0 ? 1 : 1 / w;
- const qreal px = x * iw - qreal(0.5);
- const qreal py = y * iw - qreal(0.5);
-
- int x1 = int(px) - (px < 0);
- int x2;
- int y1 = int(py) - (py < 0);
- int y2;
-
- fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_minx, src_maxx, x1, x2);
- fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_miny, src_maxy, y1, y2);
-
- const quint16 *src1 = (const quint16 *)data->texture.scanLine(y1);
- const quint16 *src2 = (const quint16 *)data->texture.scanLine(y2);
- quint16 tl = src1[x1];
- const quint16 tr = src1[x2];
- quint16 bl = src2[x1];
- const quint16 br = src2[x2];
-
- const uint distx = uint((px - x1) * 256);
- const uint disty = uint((py - y1) * 256);
- const uint distxsl8 = distx << 8;
- const uint distysl8 = disty << 8;
- const uint distxy = distx * disty;
-
- const uint tlw = 0x10000 - distxsl8 - distysl8 + distxy; // (256 - distx) * (256 - disty)
- const uint trw = distxsl8 - distxy; // distx * (256 - disty)
- const uint blw = distysl8 - distxy; // (256 - distx) * disty
- const uint brw = distxy; // distx * disty
- uint red = ((tl & 0xf800) * tlw + (tr & 0xf800) * trw
- + (bl & 0xf800) * blw + (br & 0xf800) * brw) & 0xf8000000;
- uint green = ((tl & 0x07e0) * tlw + (tr & 0x07e0) * trw
- + (bl & 0x07e0) * blw + (br & 0x07e0) * brw) & 0x07e00000;
- uint blue = ((tl & 0x001f) * tlw + (tr & 0x001f) * trw
- + (bl & 0x001f) * blw + (br & 0x001f) * brw);
- *b = quint16((red | green | blue) >> 16);
-
- ++b;
- x += fdx;
- y += fdy;
- w += fdw;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- }
-}
-
-static void blend_transformed_argb(int count, const QSpan *spans, void *userData)
-{
- QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- if (data->texture.format != QImage::Format_ARGB32_Premultiplied
- && data->texture.format != QImage::Format_RGB32) {
- blend_src_generic(count, spans, userData);
- return;
- }
-
- CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
- uint buffer[buffer_size];
- quint32 mask = (data->texture.format == QImage::Format_RGB32) ? 0xff000000 : 0;
-
- const int image_x1 = data->texture.x1;
- const int image_y1 = data->texture.y1;
- const int image_x2 = data->texture.x2 - 1;
- const int image_y2 = data->texture.y2 - 1;
-
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- int fdx = (int)(data->m11 * fixed_scale);
- int fdy = (int)(data->m12 * fixed_scale);
-
- while (count--) {
- void *t = data->rasterBuffer->scanLine(spans->y);
-
- uint *target = ((uint *)t) + spans->x;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- int x = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int y = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
-
- int length = spans->len;
- const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *end = buffer + l;
- uint *b = buffer;
- while (b < end) {
- int px = qBound(image_x1, x >> 16, image_x2);
- int py = qBound(image_y1, y >> 16, image_y2);
- *b = reinterpret_cast<const uint *>(data->texture.scanLine(py))[px] | mask;
-
- x += fdx;
- y += fdy;
- ++b;
- }
- func(target, buffer, l, coverage);
- target += l;
- length -= l;
- }
- ++spans;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
- while (count--) {
- void *t = data->rasterBuffer->scanLine(spans->y);
-
- uint *target = ((uint *)t) + spans->x;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- qreal x = data->m21 * cy + data->m11 * cx + data->dx;
- qreal y = data->m22 * cy + data->m12 * cx + data->dy;
- qreal w = data->m23 * cy + data->m13 * cx + data->m33;
-
- int length = spans->len;
- const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *end = buffer + l;
- uint *b = buffer;
- while (b < end) {
- const qreal iw = w == 0 ? 1 : 1 / w;
- const qreal tx = x * iw;
- const qreal ty = y * iw;
- const int px = qBound(image_x1, int(tx) - (tx < 0), image_x2);
- const int py = qBound(image_y1, int(ty) - (ty < 0), image_y2);
-
- *b = reinterpret_cast<const uint *>(data->texture.scanLine(py))[px] | mask;
- x += fdx;
- y += fdy;
- w += fdw;
-
- ++b;
- }
- func(target, buffer, l, coverage);
- target += l;
- length -= l;
- }
- ++spans;
- }
- }
-}
-
-static void blend_transformed_rgb565(int count, const QSpan *spans, void *userData)
-{
- QSpanData *data = reinterpret_cast<QSpanData*>(userData);
- QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
-
- if (data->texture.format != QImage::Format_RGB16
- || (mode != QPainter::CompositionMode_SourceOver
- && mode != QPainter::CompositionMode_Source))
- {
- blend_src_generic(count, spans, userData);
- return;
- }
-
- quint16 buffer[buffer_size];
- const int image_x1 = data->texture.x1;
- const int image_y1 = data->texture.y1;
- const int image_x2 = data->texture.x2 - 1;
- const int image_y2 = data->texture.y2 - 1;
-
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- const int fdx = (int)(data->m11 * fixed_scale);
- const int fdy = (int)(data->m12 * fixed_scale);
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
- int x = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int y = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
- int length = spans->len;
-
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- const int px = qBound(image_x1, x >> 16, image_x2);
- const int py = qBound(image_y1, y >> 16, image_y2);
-
- *b = ((const quint16 *)data->texture.scanLine(py))[px];
- ++b;
-
- x += fdx;
- y += fdy;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- qreal x = data->m21 * cy + data->m11 * cx + data->dx;
- qreal y = data->m22 * cy + data->m12 * cx + data->dy;
- qreal w = data->m23 * cy + data->m13 * cx + data->m33;
-
- int length = spans->len;
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- const qreal iw = w == 0 ? 1 : 1 / w;
- const qreal tx = x * iw;
- const qreal ty = y * iw;
-
- const int px = qBound(image_x1, int(tx) - (tx < 0), image_x2);
- const int py = qBound(image_y1, int(ty) - (ty < 0), image_y2);
-
- *b = ((const quint16 *)data->texture.scanLine(py))[px];
- ++b;
-
- x += fdx;
- y += fdy;
- w += fdw;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- }
-}
-
-static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData)
-{
- QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- if (data->texture.format != QImage::Format_ARGB32_Premultiplied
- && data->texture.format != QImage::Format_RGB32) {
- blend_src_generic(count, spans, userData);
- return;
- }
-
- CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
- uint buffer[buffer_size];
-
- int image_width = data->texture.width;
- int image_height = data->texture.height;
- const qsizetype scanline_offset = data->texture.bytesPerLine / 4;
-
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- int fdx = (int)(data->m11 * fixed_scale);
- int fdy = (int)(data->m12 * fixed_scale);
-
- while (count--) {
- void *t = data->rasterBuffer->scanLine(spans->y);
-
- uint *target = ((uint *)t) + spans->x;
- const uint *image_bits = (const uint *)data->texture.imageData;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- int x = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int y = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
-
- const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
- int length = spans->len;
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *end = buffer + l;
- uint *b = buffer;
- int px16 = x % (image_width << 16);
- int py16 = y % (image_height << 16);
- int px_delta = fdx % (image_width << 16);
- int py_delta = fdy % (image_height << 16);
- while (b < end) {
- if (px16 < 0) px16 += image_width << 16;
- if (py16 < 0) py16 += image_height << 16;
- int px = px16 >> 16;
- int py = py16 >> 16;
- int y_offset = py * scanline_offset;
-
- Q_ASSERT(px >= 0 && px < image_width);
- Q_ASSERT(py >= 0 && py < image_height);
-
- *b = image_bits[y_offset + px];
- x += fdx;
- y += fdy;
- px16 += px_delta;
- if (px16 >= image_width << 16)
- px16 -= image_width << 16;
- py16 += py_delta;
- if (py16 >= image_height << 16)
- py16 -= image_height << 16;
- ++b;
- }
- func(target, buffer, l, coverage);
- target += l;
- length -= l;
- }
- ++spans;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
- while (count--) {
- void *t = data->rasterBuffer->scanLine(spans->y);
-
- uint *target = ((uint *)t) + spans->x;
- const uint *image_bits = (const uint *)data->texture.imageData;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- qreal x = data->m21 * cy + data->m11 * cx + data->dx;
- qreal y = data->m22 * cy + data->m12 * cx + data->dy;
- qreal w = data->m23 * cy + data->m13 * cx + data->m33;
-
- const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
- int length = spans->len;
- while (length) {
- int l = qMin(length, buffer_size);
- const uint *end = buffer + l;
- uint *b = buffer;
- while (b < end) {
- const qreal iw = w == 0 ? 1 : 1 / w;
- const qreal tx = x * iw;
- const qreal ty = y * iw;
- int px = int(tx) - (tx < 0);
- int py = int(ty) - (ty < 0);
-
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- int y_offset = py * scanline_offset;
-
- Q_ASSERT(px >= 0 && px < image_width);
- Q_ASSERT(py >= 0 && py < image_height);
-
- *b = image_bits[y_offset + px];
- x += fdx;
- y += fdy;
- w += fdw;
- //force increment to avoid /0
- if (!w) {
- w += fdw;
- }
- ++b;
- }
- func(target, buffer, l, coverage);
- target += l;
- length -= l;
- }
- ++spans;
- }
- }
-}
-
-static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void *userData)
-{
- QSpanData *data = reinterpret_cast<QSpanData*>(userData);
- QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
-
- if (data->texture.format != QImage::Format_RGB16
- || (mode != QPainter::CompositionMode_SourceOver
- && mode != QPainter::CompositionMode_Source))
- {
- blend_src_generic(count, spans, userData);
- return;
- }
-
- quint16 buffer[buffer_size];
- const int image_width = data->texture.width;
- const int image_height = data->texture.height;
-
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- const int fdx = (int)(data->m11 * fixed_scale);
- const int fdy = (int)(data->m12 * fixed_scale);
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
- int x = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int y = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
- int length = spans->len;
-
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- int px = (x >> 16) % image_width;
- int py = (y >> 16) % image_height;
-
- if (px < 0)
- px += image_width;
- if (py < 0)
- py += image_height;
-
- *b = ((const quint16 *)data->texture.scanLine(py))[px];
- ++b;
-
- x += fdx;
- y += fdy;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
-
- while (count--) {
- const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
- const quint8 alpha = (coverage + 1) >> 3;
- const quint8 ialpha = 0x20 - alpha;
- if (alpha == 0) {
- ++spans;
- continue;
- }
-
- quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x;
-
- const qreal cx = spans->x + qreal(0.5);
- const qreal cy = spans->y + qreal(0.5);
-
- qreal x = data->m21 * cy + data->m11 * cx + data->dx;
- qreal y = data->m22 * cy + data->m12 * cx + data->dy;
- qreal w = data->m23 * cy + data->m13 * cx + data->m33;
-
- int length = spans->len;
- while (length) {
- int l;
- quint16 *b;
- if (ialpha == 0) {
- l = length;
- b = dest;
- } else {
- l = qMin(length, buffer_size);
- b = buffer;
- }
- const quint16 *end = b + l;
-
- while (b < end) {
- const qreal iw = w == 0 ? 1 : 1 / w;
- const qreal tx = x * iw;
- const qreal ty = y * iw;
-
- int px = int(tx) - (tx < 0);
- int py = int(ty) - (ty < 0);
-
- px %= image_width;
- py %= image_height;
- if (px < 0)
- px += image_width;
- if (py < 0)
- py += image_height;
-
- *b = ((const quint16 *)data->texture.scanLine(py))[px];
- ++b;
-
- x += fdx;
- y += fdy;
- w += fdw;
- // force increment to avoid /0
- if (!w)
- w += fdw;
- }
-
- if (ialpha != 0)
- blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha);
-
- dest += l;
- length -= l;
- }
- ++spans;
- }
- }
-}
-
-
/* Image formats here are target formats */
static const ProcessSpans processTextureSpansARGB32PM[NBlendTypes] = {
blend_untransformed_argb, // Untransformed
blend_tiled_argb, // Tiled
- blend_transformed_argb, // Transformed
- blend_transformed_tiled_argb, // TransformedTiled
+ blend_src_generic, // Transformed
+ blend_src_generic, // TransformedTiled
blend_src_generic, // TransformedBilinear
blend_src_generic // TransformedBilinearTiled
};
@@ -5286,9 +5169,9 @@ static const ProcessSpans processTextureSpansARGB32PM[NBlendTypes] = {
static const ProcessSpans processTextureSpansRGB16[NBlendTypes] = {
blend_untransformed_rgb565, // Untransformed
blend_tiled_rgb565, // Tiled
- blend_transformed_rgb565, // Transformed
- blend_transformed_tiled_rgb565, // TransformedTiled
- blend_transformed_bilinear_rgb565, // TransformedBilinear
+ blend_src_generic, // Transformed
+ blend_src_generic, // TransformedTiled
+ blend_src_generic, // TransformedBilinear
blend_src_generic // TransformedBilinearTiled
};
@@ -5330,6 +5213,9 @@ void qBlendTexture(int count, const QSpan *spans, void *userData)
case QImage::Format_A2BGR30_Premultiplied:
case QImage::Format_RGB30:
case QImage::Format_A2RGB30_Premultiplied:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
proc = processTextureSpansGeneric64[blendType];
break;
case QImage::Format_Invalid:
@@ -5586,7 +5472,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
}
- quint64 buffer[buffer_size];
+ quint64 buffer[BufferSize];
const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
@@ -5595,13 +5481,14 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
int i = x;
int length = mapWidth;
while (length > 0) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l);
for (int j=0; j < l; ++j) {
const int coverage = map[j + (i - x)];
alphamapblend_generic(coverage, dest, j, srcColor, color, colorProfile);
}
- destStore64(rasterBuffer, i, y + ly, dest, l);
+ if (destStore64)
+ destStore64(rasterBuffer, i, y + ly, dest, l);
length -= l;
i += l;
}
@@ -5624,14 +5511,15 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
if (end <= start)
continue;
- Q_ASSERT(end - start <= buffer_size);
+ Q_ASSERT(end - start <= BufferSize);
QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start);
for (int xp=start; xp<end; ++xp) {
const int coverage = map[xp - x];
alphamapblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
}
- destStore64(rasterBuffer, start, clip.y, dest, end - start);
+ if (destStore64)
+ destStore64(rasterBuffer, start, clip.y, dest, end - start);
} // for (i -> line.count)
map += mapStride;
} // for (yp -> bottom)
@@ -5903,7 +5791,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
}
- quint64 buffer[buffer_size];
+ quint64 buffer[BufferSize];
const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
@@ -5912,13 +5800,14 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
int i = x;
int length = mapWidth;
while (length > 0) {
- int l = qMin(buffer_size, length);
+ int l = qMin(BufferSize, length);
QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l);
for (int j=0; j < l; ++j) {
const uint coverage = src[j + (i - x)];
alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile);
}
- destStore64(rasterBuffer, i, y + ly, dest, l);
+ if (destStore64)
+ destStore64(rasterBuffer, i, y + ly, dest, l);
length -= l;
i += l;
}
@@ -5941,14 +5830,15 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
if (end <= start)
continue;
- Q_ASSERT(end - start <= buffer_size);
+ Q_ASSERT(end - start <= BufferSize);
QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start);
for (int xp=start; xp<end; ++xp) {
const uint coverage = src[xp - x];
alphargbblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
}
- destStore64(rasterBuffer, start, clip.y, dest, end - start);
+ if (destStore64)
+ destStore64(rasterBuffer, start, clip.y, dest, end - start);
} // for (i -> line.count)
src += srcStride;
} // for (yp -> bottom)
@@ -6031,8 +5921,24 @@ static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer,
int x, int y, int width, int height,
const QRgba64 &color)
{
+ const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
+ quint32 c32 = color.toArgb32();
+ quint16 c16;
+ layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c16), &c32, 0, 1, nullptr, nullptr);
qt_rectfill<quint16>(reinterpret_cast<quint16 *>(rasterBuffer->buffer()),
- color.toRgb16(), x, y, width, height, rasterBuffer->bytesPerLine());
+ c16, x, y, width, height, rasterBuffer->bytesPerLine());
+}
+
+static void qt_rectfill_quint24(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ const QRgba64 &color)
+{
+ const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
+ quint32 c32 = color.toArgb32();
+ quint24 c24;
+ layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c24), &c32, 0, 1, nullptr, nullptr);
+ qt_rectfill<quint24>(reinterpret_cast<quint24 *>(rasterBuffer->buffer()),
+ c24, x, y, width, height, rasterBuffer->bytesPerLine());
}
static void qt_rectfill_nonpremul_argb32(QRasterBuffer *rasterBuffer,
@@ -6084,6 +5990,17 @@ static void qt_rectfill_gray(QRasterBuffer *rasterBuffer,
qGray(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
}
+static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ const QRgba64 &color)
+{
+ const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
+ quint64 c64;
+ store(reinterpret_cast<uchar *>(&c64), &color, 0, 1, nullptr, nullptr);
+ qt_rectfill<quint64>(reinterpret_cast<quint64 *>(rasterBuffer->buffer()),
+ c64, x, y, width, height, rasterBuffer->bytesPerLine());
+}
+
// Map table for destination image format. Contains function pointers
// for blends of various types unto the destination
@@ -6152,7 +6069,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint24
},
// Format_RGB666
{
@@ -6161,7 +6078,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint24
},
// Format_ARGB6666_Premultiplied
{
@@ -6170,7 +6087,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint24
},
// Format_RGB555
{
@@ -6179,7 +6096,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint16
},
// Format_ARGB8555_Premultiplied
{
@@ -6188,7 +6105,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint24
},
// Format_RGB888
{
@@ -6197,7 +6114,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint24
},
// Format_RGB444
{
@@ -6206,7 +6123,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint16
},
// Format_ARGB4444_Premultiplied
{
@@ -6215,7 +6132,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
0,
qt_alphamapblit_generic,
qt_alphargbblit_generic,
- 0
+ qt_rectfill_quint16
},
// Format_RGBX8888
{
@@ -6298,6 +6215,33 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
qt_alphargbblit_generic,
qt_rectfill_gray
},
+ // Format_RGBX64
+ {
+ blend_color_generic_rgb64,
+ blend_src_generic_rgb64,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ qt_rectfill_quint64
+ },
+ // Format_RGBA64
+ {
+ blend_color_generic_rgb64,
+ blend_src_generic_rgb64,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ qt_rectfill_quint64
+ },
+ // Format_RGBA64_Premultiplied
+ {
+ blend_color_generic_rgb64,
+ blend_src_generic_rgb64,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ qt_rectfill_quint64
+ },
};
#if defined(Q_CC_MSVC) && !defined(_MIPS_)
@@ -6370,7 +6314,7 @@ void qt_memfill32(quint32 *dest, quint32 color, int count)
#endif
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
-template<QtPixelOrder> const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *);
+template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *);
#endif
extern void qInitBlendFunctions();
@@ -6439,42 +6383,54 @@ static void qInitDrawhelperFunctions()
int w, int h,
int const_alpha);
- extern void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count);
extern const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Operator *, const QSpanData *data,
int y, int x, int length);
qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
- qStorePixels[QPixelLayout::BPP24] = storePixelsBPP24_ssse3;
sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
}
#endif // SSSE3
#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
if (qCpuHasFeature(SSE4_1)) {
- extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ extern void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *);
+ extern void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *);
+ extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *);
- extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *);
- extern const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *);
- extern const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *);
- extern const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
+ extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
+ qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
+ qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4;
- qPixelLayouts[QImage::Format_ARGB32].convertFromARGB32PM = convertARGB32FromARGB32PM_sse4;
- qPixelLayouts[QImage::Format_RGBA8888].convertFromARGB32PM = convertRGBA8888FromARGB32PM_sse4;
- qPixelLayouts[QImage::Format_RGBX8888].convertFromARGB32PM = convertRGBXFromARGB32PM_sse4;
- qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>;
- qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
+ qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4;
+ qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4;
+ qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4;
+ qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>;
+ qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
+ qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4;
+ qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4;
+ destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4;
+ destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4;
}
#endif
#if defined(QT_COMPILER_SUPPORTS_AVX2)
- if (qCpuHasFeature(AVX2)) {
+ if (qCpuHasFeature(ArchHaswell)) {
extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
const uchar *srcPixels, int sbpl,
int w, int h, int const_alpha);
@@ -6504,14 +6460,14 @@ static void qInitDrawhelperFunctions()
qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2;
- extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
- int &fx, int &fy, int fdx, int /*fdy*/);
+ extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
+ int &fx, int &fy, int fdx, int /*fdy*/);
extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
int &fx, int &fy, int fdx, int /*fdy*/);
extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint *b, uint *end, const QTextureData &image,
int &fx, int &fy, int fdx, int fdy);
- bilinearFastTransformHelperARGB32PM[0][SimpleUpscaleTransform] = fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2;
+ bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2;
bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2;
bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2;
}
@@ -6543,12 +6499,25 @@ static void qInitDrawhelperFunctions()
sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, const uint *src, int count,
+ extern void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *);
+ extern void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *);
+ extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *);
- extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern void QT_FASTCALL storeRGBXFromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon;
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon;
+ qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon;
+ qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon;
qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon;
+ qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon;
+ qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon;
#endif
#if defined(ENABLE_PIXMAN_DRAWHELPERS)
diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp
index 3a70524a9d..ec6643deed 100644
--- a/src/gui/painting/qdrawhelper_avx2.cpp
+++ b/src/gui/painting/qdrawhelper_avx2.cpp
@@ -45,8 +45,6 @@
QT_BEGIN_NAMESPACE
-static Q_CONSTEXPR int BufferSize = 2048;
-
enum {
FixedScale = 1 << 16,
HalfPoint = 1 << 15
@@ -576,8 +574,10 @@ inline void fetchTransformedBilinear_pixelBounds(int, int l1, int l2, int &v1, i
Q_ASSERT(v2 >= l1 && v2 <= l2);
}
-void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
- int &fx, int &fy, int fdx, int /*fdy*/)
+void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx);
+
+void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
+ int &fx, int &fy, int fdx, int /*fdy*/)
{
int y1 = (fy >> 16);
int y2;
@@ -594,16 +594,12 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin
const int offset = (fx + adjust) >> 16;
int x = offset;
- // The idea is first to do the interpolation between the row s1 and the row s2
- // into an intermediate buffer, then we interpolate between two pixel of this buffer.
-
- // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
- // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG
- // +1 for the last pixel to interpolate with, and +1 for rounding errors.
- quint32 intermediate_buffer[2][BufferSize + 2];
+ IntermediateBuffer intermediate;
// count is the size used in the intermediate_buffer.
int count = (qint64(length) * qAbs(fdx) + FixedScale - 1) / FixedScale + 2;
- Q_ASSERT(count <= BufferSize + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case
+ // length is supposed to be <= BufferSize either because data->m11 < 1 or
+ // data->m11 < 2, and any larger buffers split
+ Q_ASSERT(count <= BufferSize + 2);
int f = 0;
int lim = qMin(count, image.x2 - x);
if (x < image.x1) {
@@ -613,8 +609,8 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin
quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
do {
- intermediate_buffer[0][f] = rb;
- intermediate_buffer[1][f] = ag;
+ intermediate.buffer_rb[f] = rb;
+ intermediate.buffer_ag[f] = ag;
f++;
x++;
} while (x < image.x1 && f < lim);
@@ -644,10 +640,10 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin
// Add the values, and shift to only keep 8 significant bits per colors
__m256i rAG =_mm256_add_epi16(topAG, bottomAG);
rAG = _mm256_srli_epi16(rAG, 8);
- _mm256_storeu_si256((__m256i*)(&intermediate_buffer[1][f]), rAG);
+ _mm256_storeu_si256((__m256i*)(&intermediate.buffer_ag[f]), rAG);
__m256i rRB =_mm256_add_epi16(topRB, bottomRB);
rRB = _mm256_srli_epi16(rRB, 8);
- _mm256_storeu_si256((__m256i*)(&intermediate_buffer[0][f]), rRB);
+ _mm256_storeu_si256((__m256i*)(&intermediate.buffer_rb[f]), rRB);
}
for (; f < count; f++) { // Same as above but without simd
@@ -656,30 +652,37 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin
uint t = s1[x];
uint b = s2[x];
- intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
- intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
x++;
}
+
// Now interpolate the values from the intermediate_buffer to get the final result.
+ intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx);
+}
+
+void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx)
+{
fx -= offset * FixedScale;
const __m128i v_fdx = _mm_set1_epi32(fdx * 4);
const __m128i v_blend = _mm_set1_epi32(0x00800080);
+ const __m128i vdx_shuffle = _mm_set_epi8(char(0x80), 13, char(0x80), 13, char(0x80), 9, char(0x80), 9,
+ char(0x80), 5, char(0x80), 5, char(0x80), 1, char(0x80), 1);
__m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx);
while (b < end - 3) {
const __m128i offset = _mm_srli_epi32(v_fx, 16);
- __m256i vrb = _mm256_i32gather_epi64((const long long *)intermediate_buffer[0], offset, 4);
- __m256i vag = _mm256_i32gather_epi64((const long long *)intermediate_buffer[1], offset, 4);
+ __m256i vrb = _mm256_i32gather_epi64((const long long *)intermediate.buffer_rb, offset, 4);
+ __m256i vag = _mm256_i32gather_epi64((const long long *)intermediate.buffer_ag, offset, 4);
- __m128i vdx = _mm_and_si128(v_fx, _mm_set1_epi32(0x0000ffff));
- vdx = _mm_srli_epi16(vdx, 8);
- __m128i vidx = _mm_sub_epi32(_mm_set1_epi32(256), vdx);
+ __m128i vdx = _mm_shuffle_epi8(v_fx, vdx_shuffle);
+ __m128i vidx = _mm_sub_epi16(_mm_set1_epi16(256), vdx);
__m256i vmulx = _mm256_castsi128_si256(_mm_unpacklo_epi32(vidx, vdx));
vmulx = _mm256_inserti128_si256(vmulx, _mm_unpackhi_epi32(vidx, vdx), 1);
- vrb = _mm256_mullo_epi32(vrb, vmulx);
- vag = _mm256_mullo_epi32(vag, vmulx);
+ vrb = _mm256_mullo_epi16(vrb, vmulx);
+ vag = _mm256_mullo_epi16(vag, vmulx);
__m256i vrbag = _mm256_hadd_epi32(vrb, vag);
vrbag = _mm256_permute4x64_epi64(vrbag, _MM_SHUFFLE(3, 1, 2, 0));
@@ -691,21 +694,21 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin
_mm_storeu_si128((__m128i*)b, _mm_blendv_epi8(ag, rb, v_blend));
b += 4;
- fx += 4 * fdx;
v_fx = _mm_add_epi32(v_fx, v_fdx);
}
+ fx = _mm_cvtsi128_si32(v_fx);
while (b < end) {
- int x = (fx >> 16);
-
- uint distx = (fx & 0x0000ffff) >> 8;
- uint idistx = 256 - distx;
+ const int x = (fx >> 16);
- uint rb = ((intermediate_buffer[0][x] * idistx + intermediate_buffer[0][x + 1] * distx) >> 8) & 0xff00ff;
- uint ag = (intermediate_buffer[1][x] * idistx + intermediate_buffer[1][x + 1] * distx) & 0xff00ff00;
- *b = rb | ag;
+ const uint distx = (fx & 0x0000ffff) >> 8;
+ const uint idistx = 256 - distx;
+ const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00;
+ const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00;
+ *b = (rb >> 8) | ag;
b++;
fx += fdx;
}
+ fx += offset * FixedScale;
}
void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp
index e126f4b670..629dfe2358 100644
--- a/src/gui/painting/qdrawhelper_neon.cpp
+++ b/src/gui/painting/qdrawhelper_neon.cpp
@@ -1081,15 +1081,28 @@ const uint * QT_FASTCALL qt_fetchUntransformed_888_neon(uint *buffer, const Oper
}
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
-template<bool RGBA>
-static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int count)
+static inline uint32x4_t vrgba2argb(uint32x4_t srcVector)
{
- int i = 0;
#if defined(Q_PROCESSOR_ARM_64)
const uint8x16_t rgbaMask = { 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15};
#else
const uint8x8_t rgbaMask = { 2, 1, 0, 3, 6, 5, 4, 7 };
#endif
+#if defined(Q_PROCESSOR_ARM_64)
+ srcVector = vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(srcVector), rgbaMask));
+#else
+ // no vqtbl1q_u8, so use two vtbl1_u8
+ const uint8x8_t low = vtbl1_u8(vreinterpret_u8_u32(vget_low_u32(srcVector)), rgbaMask);
+ const uint8x8_t high = vtbl1_u8(vreinterpret_u8_u32(vget_high_u32(srcVector)), rgbaMask);
+ srcVector = vcombine_u32(vreinterpret_u32_u8(low), vreinterpret_u32_u8(high));
+#endif
+ return srcVector;
+}
+
+template<bool RGBA>
+static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int count)
+{
+ int i = 0;
const uint8x8_t shuffleMask = { 3, 3, 3, 3, 7, 7, 7, 7};
const uint32x4_t blendMask = vdupq_n_u32(0xff000000);
@@ -1105,16 +1118,8 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int
#endif
if (alphaSum) {
if (alphaSum != 255 * 4) {
- if (RGBA) {
-#if defined(Q_PROCESSOR_ARM_64)
- srcVector = vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(srcVector), rgbaMask));
-#else
- // no vqtbl1q_u8
- const uint8x8_t low = vtbl1_u8(vreinterpret_u8_u32(vget_low_u32(srcVector)), rgbaMask);
- const uint8x8_t high = vtbl1_u8(vreinterpret_u8_u32(vget_high_u32(srcVector)), rgbaMask);
- srcVector = vcombine_u32(vreinterpret_u32_u8(low), vreinterpret_u32_u8(high));
-#endif
- }
+ if (RGBA)
+ srcVector = vrgba2argb(srcVector);
const uint8x8_t s1 = vreinterpret_u8_u32(vget_low_u32(srcVector));
const uint8x8_t s2 = vreinterpret_u8_u32(vget_high_u32(srcVector));
const uint8x8_t alpha1 = vtbl1_u8(s1, shuffleMask);
@@ -1128,19 +1133,10 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int
const uint32x4_t d = vbslq_u32(blendMask, srcVector, vreinterpretq_u32_u8(vcombine_u8(d1, d2)));
vst1q_u32(buffer + i, d);
} else {
- if (RGBA) {
-#if defined(Q_PROCESSOR_ARM_64)
- srcVector = vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(srcVector), rgbaMask));
-#else
- // no vqtbl1q_u8
- const uint8x8_t low = vtbl1_u8(vreinterpret_u8_u32(vget_low_u32(srcVector)), rgbaMask);
- const uint8x8_t high = vtbl1_u8(vreinterpret_u8_u32(vget_high_u32(srcVector)), rgbaMask);
- srcVector = vcombine_u32(vreinterpret_u32_u8(low), vreinterpret_u32_u8(high));
-#endif
- vst1q_u32(buffer + i, srcVector);
- } else if (buffer != src) {
+ if (RGBA)
+ vst1q_u32(buffer + i, vrgba2argb(srcVector));
+ else if (buffer != src)
vst1q_u32(buffer + i, srcVector);
- }
}
} else {
vst1q_u32(buffer + i, vdupq_n_u32(0));
@@ -1153,20 +1149,143 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int
}
}
-const uint *QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static inline float32x4_t reciprocal_mul_ps(float32x4_t a, float mul)
+{
+ float32x4_t ia = vrecpeq_f32(a); // estimate 1/a
+ ia = vmulq_f32(vrecpsq_f32(a, ia), vmulq_n_f32(ia, mul)); // estimate improvement step * mul
+ return ia;
+}
+
+template<bool RGBA, bool RGBx>
+static inline void convertARGBFromARGB32PM_neon(uint *buffer, const uint *src, int count)
+{
+ int i = 0;
+ const uint32x4_t alphaMask = vdupq_n_u32(0xff000000);
+
+ for (; i < count - 3; i += 4) {
+ uint32x4_t srcVector = vld1q_u32(src + i);
+ uint32x4_t alphaVector = vshrq_n_u32(srcVector, 24);
+#if defined(Q_PROCESSOR_ARM_64)
+ uint32_t alphaSum = vaddvq_u32(alphaVector);
+#else
+ // no vaddvq_u32
+ uint32x2_t tmp = vpadd_u32(vget_low_u32(alphaVector), vget_high_u32(alphaVector));
+ uint32_t alphaSum = vget_lane_u32(vpadd_u32(tmp, tmp), 0);
+#endif
+ if (alphaSum) {
+ if (alphaSum != 255 * 4) {
+ if (RGBA)
+ srcVector = vrgba2argb(srcVector);
+ const float32x4_t a = vcvtq_f32_u32(alphaVector);
+ const float32x4_t ia = reciprocal_mul_ps(a, 255.0f);
+ // Convert 4x(4xU8) to 4x(4xF32)
+ uint16x8_t tmp1 = vmovl_u8(vget_low_u8(vreinterpretq_u8_u32(srcVector)));
+ uint16x8_t tmp3 = vmovl_u8(vget_high_u8(vreinterpretq_u8_u32(srcVector)));
+ float32x4_t src1 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(tmp1)));
+ float32x4_t src2 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(tmp1)));
+ float32x4_t src3 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(tmp3)));
+ float32x4_t src4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(tmp3)));
+ src1 = vmulq_lane_f32(src1, vget_low_f32(ia), 0);
+ src2 = vmulq_lane_f32(src2, vget_low_f32(ia), 1);
+ src3 = vmulq_lane_f32(src3, vget_high_f32(ia), 0);
+ src4 = vmulq_lane_f32(src4, vget_high_f32(ia), 1);
+ // Convert 4x(4xF32) back to 4x(4xU8) (over a 8.1 fixed point format to get rounding)
+ tmp1 = vcombine_u16(vrshrn_n_u32(vcvtq_n_u32_f32(src1, 1), 1),
+ vrshrn_n_u32(vcvtq_n_u32_f32(src2, 1), 1));
+ tmp3 = vcombine_u16(vrshrn_n_u32(vcvtq_n_u32_f32(src3, 1), 1),
+ vrshrn_n_u32(vcvtq_n_u32_f32(src4, 1), 1));
+ uint32x4_t dstVector = vreinterpretq_u32_u8(vcombine_u8(vmovn_u16(tmp1), vmovn_u16(tmp3)));
+ // Overwrite any undefined results from alpha==0 with zeros:
+#if defined(Q_PROCESSOR_ARM_64)
+ uint32x4_t srcVectorAlphaMask = vceqzq_u32(alphaVector);
+#else
+ uint32x4_t srcVectorAlphaMask = vceqq_u32(alphaVector, vdupq_n_u32(0));
+#endif
+ dstVector = vbicq_u32(dstVector, srcVectorAlphaMask);
+ // Restore or mask alpha values:
+ if (RGBx)
+ dstVector = vorrq_u32(alphaMask, dstVector);
+ else
+ dstVector = vbslq_u32(alphaMask, srcVector, dstVector);
+ vst1q_u32(&buffer[i], dstVector);
+ } else {
+ // 4xAlpha==255, no change except if we are doing RGBA->ARGB:
+ if (RGBA)
+ vst1q_u32(&buffer[i], vrgba2argb(srcVector));
+ else if (buffer != src)
+ vst1q_u32(&buffer[i], srcVector);
+ }
+ } else {
+ // 4xAlpha==0, always zero, except if output is RGBx:
+ if (RGBx)
+ vst1q_u32(&buffer[i], alphaMask);
+ else
+ vst1q_u32(&buffer[i], vdupq_n_u32(0));
+ }
+ }
+
+ SIMD_EPILOGUE(i, count, 3) {
+ uint v = qUnpremultiply(src[i]);
+ if (RGBx)
+ v = 0xff000000 | v;
+ if (RGBA)
+ v = ARGB2RGBA(v);
+ buffer[i] = v;
+ }
+}
+
+void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *)
+{
+ convertARGBToARGB32PM_neon<false>(buffer, buffer, count);
+}
+
+void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *)
+{
+ convertARGBToARGB32PM_neon<true>(buffer, buffer, count);
+}
+
+const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- convertARGBToARGB32PM_neon<false>(buffer, src, count);
+ convertARGBToARGB32PM_neon<false>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
-const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
- convertARGBToARGB32PM_neon<true>(buffer, src, count);
+ convertARGBToARGB32PM_neon<true>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
+void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_neon<false,true>(d, src, count);
+}
+
+void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_neon<false,false>(d, src, count);
+}
+
+void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_neon<true,false>(d, src, count);
+}
+
+void QT_FASTCALL storeRGBXFromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_neon<true,true>(d, src, count);
+}
+
#endif // Q_BYTE_ORDER == Q_LITTLE_ENDIAN
QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index 6f3c92ca64..fb08261205 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -747,6 +747,77 @@ static constexpr inline bool hasFastInterpolate4() { return false; }
#endif
+static inline QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
+{
+ return QRgba64::fromRgba64((rgba64.red() * alpha256) >> 8,
+ (rgba64.green() * alpha256) >> 8,
+ (rgba64.blue() * alpha256) >> 8,
+ (rgba64.alpha() * alpha256) >> 8);
+}
+static inline QRgba64 interpolate256(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
+{
+ return QRgba64::fromRgba64(multiplyAlpha256(x, alpha1) + multiplyAlpha256(y, alpha2));
+}
+
+#ifdef __SSE2__
+static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
+{
+ __m128i vt = _mm_loadu_si128((const __m128i*)t);
+ if (disty) {
+ __m128i vb = _mm_loadu_si128((const __m128i*)b);
+ vt = _mm_mulhi_epu16(vt, _mm_set1_epi16(0x10000 - disty));
+ vb = _mm_mulhi_epu16(vb, _mm_set1_epi16(disty));
+ vt = _mm_add_epi16(vt, vb);
+ }
+ if (distx) {
+ const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
+ const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
+ vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
+ vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
+ }
+#ifdef Q_PROCESSOR_X86_64
+ return QRgba64::fromRgba64(_mm_cvtsi128_si64(vt));
+#else
+ QRgba64 out;
+ _mm_storel_epi64((__m128i*)&out, vt);
+ return out;
+#endif // Q_PROCESSOR_X86_64
+}
+#elif defined(__ARM_NEON__)
+static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
+{
+ uint64x1x2_t vt = vld2_u64(reinterpret_cast<const uint64_t *>(t));
+ if (disty) {
+ uint64x1x2_t vb = vld2_u64(reinterpret_cast<const uint64_t *>(b));
+ uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - disty);
+ uint32x4_t vt1 = vmull_n_u16(vreinterpret_u16_u64(vt.val[1]), 0x10000 - disty);
+ vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vb.val[0]), disty);
+ vt1 = vmlal_n_u16(vt1, vreinterpret_u16_u64(vb.val[1]), disty);
+ vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
+ vt.val[1] = vreinterpret_u64_u16(vshrn_n_u32(vt1, 16));
+ }
+ if (distx) {
+ uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - distx);
+ vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vt.val[1]), distx);
+ vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
+ }
+ QRgba64 out;
+ vst1_u64(reinterpret_cast<uint64_t *>(&out), vt.val[0]);
+ return out;
+}
+#else
+static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
+{
+ const uint dx = distx>>8;
+ const uint dy = disty>>8;
+ const uint idx = 256 - dx;
+ const uint idy = 256 - dy;
+ QRgba64 xtop = interpolate256(t[0], idx, t[1], dx);
+ QRgba64 xbot = interpolate256(b[0], idx, b[1], dx);
+ return interpolate256(xtop, idy, xbot, dy);
+}
+#endif // __SSE2__
+
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
static Q_ALWAYS_INLINE quint32 RGBA2ARGB(quint32 x) {
quint32 rgb = x >> 8;
@@ -798,6 +869,7 @@ static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c)
}
struct quint24 {
+ quint24() = default;
quint24(uint value);
operator uint() const;
uchar data[3];
@@ -1142,6 +1214,8 @@ static Q_ALWAYS_INLINE const uint *qt_convertRGBA8888ToARGB32PM(uint *buffer, co
return buffer;
}
+template<bool RGBA> void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count);
+
const uint qt_bayer_matrix[16][16] = {
{ 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc,
0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff},
@@ -1205,15 +1279,43 @@ inline uint comp_func_Plus_one_pixel(uint d, const uint s)
#undef MIX
#undef AMIX
+// must be multiple of 4 for easier SIMD implementations
+static Q_CONSTEXPR int BufferSize = 2048;
+
+// A buffer of intermediate results used by simple bilinear scaling.
+struct IntermediateBuffer
+{
+ // The idea is first to do the interpolation between the row s1 and the row s2
+ // into this intermediate buffer, then later interpolate between two pixel of this buffer.
+ //
+ // buffer_rb is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
+ // buffer_ag is the alpha-green component of the pixel, in the form 0x00AA00GG
+ // +1 for the last pixel to interpolate with, and +1 for rounding errors.
+ quint32 buffer_rb[BufferSize+2];
+ quint32 buffer_ag[BufferSize+2];
+};
+
struct QDitherInfo {
int x;
int y;
};
-typedef const uint *(QT_FASTCALL *ConvertFunc)(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *clut, QDitherInfo *dither);
-typedef const QRgba64 *(QT_FASTCALL *ConvertFunc64)(QRgba64 *buffer, const uint *src, int count,
- const QVector<QRgb> *clut, QDitherInfo *dither);
+typedef const uint *(QT_FASTCALL *FetchAndConvertPixelsFunc)(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither);
+typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither);
+
+typedef const QRgba64 *(QT_FASTCALL *FetchAndConvertPixelsFunc64)(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither);
+typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc64)(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither);
+
+typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QVector<QRgb> *clut);
+typedef void (QT_FASTCALL *Convert64Func)(quint64 *buffer, int count, const QVector<QRgb> *clut);
+typedef const QRgba64 *(QT_FASTCALL *ConvertTo64Func)(QRgba64 *buffer, const uint *src, int count,
+ const QVector<QRgb> *clut, QDitherInfo *dither);
+typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count);
+
struct QPixelLayout
{
@@ -1226,36 +1328,28 @@ struct QPixelLayout
BPP16,
BPP24,
BPP32,
+ BPP64,
BPPCount
};
- // All numbers in bits.
- uchar redWidth;
- uchar redShift;
- uchar greenWidth;
- uchar greenShift;
- uchar blueWidth;
- uchar blueShift;
- uchar alphaWidth;
- uchar alphaShift;
+ bool hasAlphaChannel;
bool premultiplied;
BPP bpp;
+ RbSwapFunc rbSwap;
ConvertFunc convertToARGB32PM;
- ConvertFunc convertFromARGB32PM;
- ConvertFunc convertFromRGB32;
- ConvertFunc64 convertToARGB64PM;
+ ConvertTo64Func convertToRGBA64PM;
+ FetchAndConvertPixelsFunc fetchToARGB32PM;
+ FetchAndConvertPixelsFunc64 fetchToRGBA64PM;
+ ConvertAndStorePixelsFunc storeFromARGB32PM;
+ ConvertAndStorePixelsFunc storeFromRGB32;
};
-typedef const uint *(QT_FASTCALL *FetchPixelsFunc)(uint *buffer, const uchar *src, int index, int count);
-typedef void (QT_FASTCALL *StorePixelsFunc)(uchar *dest, const uint *src, int index, int count);
+extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats];
extern QPixelLayout qPixelLayouts[QImage::NImageFormats];
-extern const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount];
-extern StorePixelsFunc qStorePixels[QPixelLayout::BPPCount];
extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3];
-
QT_END_NAMESPACE
#endif // QDRAWHELPER_P_H
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index 14bfaabf09..e3cc1dd43e 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -39,13 +39,14 @@
#include <private/qdrawhelper_p.h>
#include <private/qdrawingprimitive_sse2_p.h>
+#include <private/qpaintengine_raster_p.h>
#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
QT_BEGIN_NAMESPACE
template<bool RGBA>
-static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count)
+static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count)
{
int i = 0;
const __m128i alphaMask = _mm_set1_epi32(0xff000000);
@@ -83,7 +84,7 @@ static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int
_mm_storeu_si128((__m128i *)&buffer[i], srcVector);
}
} else {
- _mm_storeu_si128((__m128i *)&buffer[i], _mm_setzero_si128());
+ _mm_storeu_si128((__m128i *)&buffer[i], zero);
}
}
@@ -93,59 +94,264 @@ static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int
}
}
-const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+static inline __m128 reciprocal_mul_ps(__m128 a, float mul)
{
- convertARGBToARGB32PM_sse4<false>(buffer, src, count);
- return buffer;
+ __m128 ia = _mm_rcp_ps(a); // Approximate 1/a
+ // Improve precision of ia using Newton-Raphson
+ ia = _mm_sub_ps(_mm_add_ps(ia, ia), _mm_mul_ps(ia, _mm_mul_ps(ia, a)));
+ ia = _mm_mul_ps(ia, _mm_set1_ps(mul));
+ return ia;
}
-const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+template<bool RGBA, bool RGBx>
+static inline void convertARGBFromARGB32PM_sse4(uint *buffer, const uint *src, int count)
{
- convertARGBToARGB32PM_sse4<true>(buffer, src, count);
- return buffer;
+ int i = 0;
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+ const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
+ const __m128i zero = _mm_setzero_si128();
+
+ for (; i < count - 3; i += 4) {
+ __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[i]);
+ if (!_mm_testz_si128(srcVector, alphaMask)) {
+ if (!_mm_testc_si128(srcVector, alphaMask)) {
+ __m128i srcVectorAlpha = _mm_srli_epi32(srcVector, 24);
+ if (RGBA)
+ srcVector = _mm_shuffle_epi8(srcVector, rgbaMask);
+ const __m128 a = _mm_cvtepi32_ps(srcVectorAlpha);
+ const __m128 ia = reciprocal_mul_ps(a, 255.0f);
+ __m128i src1 = _mm_unpacklo_epi8(srcVector, zero);
+ __m128i src3 = _mm_unpackhi_epi8(srcVector, zero);
+ __m128i src2 = _mm_unpackhi_epi16(src1, zero);
+ __m128i src4 = _mm_unpackhi_epi16(src3, zero);
+ src1 = _mm_unpacklo_epi16(src1, zero);
+ src3 = _mm_unpacklo_epi16(src3, zero);
+ __m128 ia1 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0, 0, 0, 0));
+ __m128 ia2 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(1, 1, 1, 1));
+ __m128 ia3 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(2, 2, 2, 2));
+ __m128 ia4 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(3, 3, 3, 3));
+ src1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src1), ia1));
+ src2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src2), ia2));
+ src3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src3), ia3));
+ src4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src4), ia4));
+ src1 = _mm_packus_epi32(src1, src2);
+ src3 = _mm_packus_epi32(src3, src4);
+ src1 = _mm_packus_epi16(src1, src3);
+ // Handle potential alpha == 0 values:
+ __m128i srcVectorAlphaMask = _mm_cmpeq_epi32(srcVectorAlpha, zero);
+ src1 = _mm_andnot_si128(srcVectorAlphaMask, src1);
+ // Fixup alpha values:
+ if (RGBx)
+ srcVector = _mm_or_si128(src1, alphaMask);
+ else
+ srcVector = _mm_blendv_epi8(src1, srcVector, alphaMask);
+ _mm_storeu_si128((__m128i *)&buffer[i], srcVector);
+ } else {
+ if (RGBA)
+ _mm_storeu_si128((__m128i *)&buffer[i], _mm_shuffle_epi8(srcVector, rgbaMask));
+ else if (buffer != src)
+ _mm_storeu_si128((__m128i *)&buffer[i], srcVector);
+ }
+ } else {
+ if (RGBx)
+ _mm_storeu_si128((__m128i *)&buffer[i], alphaMask);
+ else
+ _mm_storeu_si128((__m128i *)&buffer[i], zero);
+ }
+ }
+
+ SIMD_EPILOGUE(i, count, 3) {
+ uint v = qUnpremultiply_sse4(src[i]);
+ if (RGBx)
+ v = 0xff000000 | v;
+ if (RGBA)
+ v = ARGB2RGBA(v);
+ buffer[i] = v;
+ }
}
-const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+template<bool RGBA>
+static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qUnpremultiply_sse4(src[i]);
- return buffer;
+ int i = 0;
+ const __m128i alphaMask = _mm_set1_epi64x(Q_UINT64_C(0xffff) << 48);
+ const __m128i alphaMask32 = _mm_set1_epi32(0xff000000);
+ const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
+ const __m128i zero = _mm_setzero_si128();
+
+ for (; i < count - 3; i += 4) {
+ __m128i srcVector1 = _mm_loadu_si128((const __m128i *)&src[i]);
+ __m128i srcVector2 = _mm_loadu_si128((const __m128i *)&src[i + 2]);
+ bool transparent1 = _mm_testz_si128(srcVector1, alphaMask);
+ bool opaque1 = _mm_testc_si128(srcVector1, alphaMask);
+ bool transparent2 = _mm_testz_si128(srcVector2, alphaMask);
+ bool opaque2 = _mm_testc_si128(srcVector2, alphaMask);
+
+ if (!(transparent1 && transparent2)) {
+ if (!(opaque1 && opaque2)) {
+ __m128i srcVector1Alpha = _mm_srli_epi64(srcVector1, 48);
+ __m128i srcVector2Alpha = _mm_srli_epi64(srcVector2, 48);
+ __m128i srcVectorAlpha = _mm_packus_epi32(srcVector1Alpha, srcVector2Alpha);
+ const __m128 a = _mm_cvtepi32_ps(srcVectorAlpha);
+ // Convert srcVectorAlpha to final 8-bit alpha channel
+ srcVectorAlpha = _mm_add_epi32(srcVectorAlpha, _mm_set1_epi32(128));
+ srcVectorAlpha = _mm_sub_epi32(srcVectorAlpha, _mm_srli_epi32(srcVectorAlpha, 8));
+ srcVectorAlpha = _mm_srli_epi32(srcVectorAlpha, 8);
+ srcVectorAlpha = _mm_slli_epi32(srcVectorAlpha, 24);
+ const __m128 ia = reciprocal_mul_ps(a, 255.0f);
+ __m128i src1 = _mm_unpacklo_epi16(srcVector1, zero);
+ __m128i src2 = _mm_unpackhi_epi16(srcVector1, zero);
+ __m128i src3 = _mm_unpacklo_epi16(srcVector2, zero);
+ __m128i src4 = _mm_unpackhi_epi16(srcVector2, zero);
+ __m128 ia1 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0, 0, 0, 0));
+ __m128 ia2 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(1, 1, 1, 1));
+ __m128 ia3 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(2, 2, 2, 2));
+ __m128 ia4 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(3, 3, 3, 3));
+ src1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src1), ia1));
+ src2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src2), ia2));
+ src3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src3), ia3));
+ src4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src4), ia4));
+ src1 = _mm_packus_epi32(src1, src2);
+ src3 = _mm_packus_epi32(src3, src4);
+ // Handle potential alpha == 0 values:
+ __m128i srcVector1AlphaMask = _mm_cmpeq_epi64(srcVector1Alpha, zero);
+ __m128i srcVector2AlphaMask = _mm_cmpeq_epi64(srcVector2Alpha, zero);
+ src1 = _mm_andnot_si128(srcVector1AlphaMask, src1);
+ src3 = _mm_andnot_si128(srcVector2AlphaMask, src3);
+ src1 = _mm_packus_epi16(src1, src3);
+ // Fixup alpha values:
+ src1 = _mm_blendv_epi8(src1, srcVectorAlpha, alphaMask32);
+ // Fix RGB order
+ if (!RGBA)
+ src1 = _mm_shuffle_epi8(src1, rgbaMask);
+ _mm_storeu_si128((__m128i *)&buffer[i], src1);
+ } else {
+ __m128i src1 = _mm_unpacklo_epi16(srcVector1, zero);
+ __m128i src2 = _mm_unpackhi_epi16(srcVector1, zero);
+ __m128i src3 = _mm_unpacklo_epi16(srcVector2, zero);
+ __m128i src4 = _mm_unpackhi_epi16(srcVector2, zero);
+ src1 = _mm_add_epi32(src1, _mm_set1_epi32(128));
+ src2 = _mm_add_epi32(src2, _mm_set1_epi32(128));
+ src3 = _mm_add_epi32(src3, _mm_set1_epi32(128));
+ src4 = _mm_add_epi32(src4, _mm_set1_epi32(128));
+ src1 = _mm_sub_epi32(src1, _mm_srli_epi32(src1, 8));
+ src2 = _mm_sub_epi32(src2, _mm_srli_epi32(src2, 8));
+ src3 = _mm_sub_epi32(src3, _mm_srli_epi32(src3, 8));
+ src4 = _mm_sub_epi32(src4, _mm_srli_epi32(src4, 8));
+ src1 = _mm_srli_epi32(src1, 8);
+ src2 = _mm_srli_epi32(src2, 8);
+ src3 = _mm_srli_epi32(src3, 8);
+ src4 = _mm_srli_epi32(src4, 8);
+ src1 = _mm_packus_epi32(src1, src2);
+ src3 = _mm_packus_epi32(src3, src4);
+ src1 = _mm_packus_epi16(src1, src3);
+ if (!RGBA)
+ src1 = _mm_shuffle_epi8(src1, rgbaMask);
+ _mm_storeu_si128((__m128i *)&buffer[i], src1);
+ }
+ } else {
+ _mm_storeu_si128((__m128i *)&buffer[i], zero);
+ }
+ }
+
+ SIMD_EPILOGUE(i, count, 3) {
+ buffer[i] = qConvertRgba64ToRgb32_sse4<RGBA ? PixelOrderRGB : PixelOrderBGR>(src[i]);
+ }
}
-const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(qUnpremultiply_sse4(src[i]));
+ convertARGBToARGB32PM_sse4<false>(buffer, buffer, count);
+}
+
+void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *)
+{
+ convertARGBToARGB32PM_sse4<true>(buffer, buffer, count);
+}
+
+const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ convertARGBToARGB32PM_sse4<false>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
-const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply_sse4(src[i]));
+ convertARGBToARGB32PM_sse4<true>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
+void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_sse4<false,true>(d, src, count);
+}
+
+void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_sse4<false,false>(d, src, count);
+}
+
+void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_sse4<true,false>(d, src, count);
+}
+
+void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ convertARGBFromARGB32PM_sse4<true,true>(d, src, count);
+}
+
template<QtPixelOrder PixelOrder>
-const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
+void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
for (int i = 0; i < count; ++i)
- buffer[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]);
- return buffer;
+ d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]);
+}
+
+void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
+{
+ uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
+ convertARGBFromRGBA64PM_sse4<false>(dest, buffer, length);
+}
+
+void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
+{
+ uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
+ convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length);
+}
+
+void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = (uint*)dest + index;
+ convertARGBFromRGBA64PM_sse4<false>(d, src, count);
+}
+
+void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ uint *d = (uint*)dest + index;
+ convertARGBFromRGBA64PM_sse4<true>(d, src, count);
}
template
-const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *);
+void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
template
-const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *);
+void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *);
QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp
index 45ecc8b422..42d760d5cc 100644
--- a/src/gui/painting/qdrawhelper_ssse3.cpp
+++ b/src/gui/painting/qdrawhelper_ssse3.cpp
@@ -167,61 +167,12 @@ void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
}
}
-static inline void store_uint24_ssse3(uchar *dst, const uint *src, int len)
+const uint *QT_FASTCALL fetchPixelsBPP24_ssse3(uint *buffer, const uchar *src, int index, int count)
{
- int i = 0;
-
- quint24 *dst24 = reinterpret_cast<quint24*>(dst);
- // Align dst on 16 bytes
- for (; i < len && (reinterpret_cast<quintptr>(dst24) & 0xf); ++i)
- *dst24++ = quint24(*src++);
-
- // Shuffle masks for first and second half of every output, all outputs are aligned so the shuffled ends are not used.
- const __m128i shuffleMask1 = _mm_setr_epi8(char(0x80), char(0x80), char(0x80), char(0x80), 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12);
- const __m128i shuffleMask2 = _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, char(0x80), char(0x80), char(0x80), char(0x80));
-
- const __m128i *inVectorPtr = (const __m128i *)src;
- __m128i *dstVectorPtr = (__m128i *)dst24;
-
- for (; i < (len - 15); i += 16) {
- // Load four vectors, store three.
- // Create each output vector by combining two shuffled input vectors.
- __m128i srcVector1 = _mm_loadu_si128(inVectorPtr);
- ++inVectorPtr;
- __m128i srcVector2 = _mm_loadu_si128(inVectorPtr);
- ++inVectorPtr;
- __m128i outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1);
- __m128i outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2);
- __m128i outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 4);
- _mm_store_si128(dstVectorPtr, outputVector);
- ++dstVectorPtr;
-
- srcVector1 = _mm_loadu_si128(inVectorPtr);
- ++inVectorPtr;
- outputVector1 = _mm_shuffle_epi8(srcVector2, shuffleMask1);
- outputVector2 = _mm_shuffle_epi8(srcVector1, shuffleMask2);
- outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 8);
- _mm_store_si128(dstVectorPtr, outputVector);
- ++dstVectorPtr;
-
- srcVector2 = _mm_loadu_si128(inVectorPtr);
- ++inVectorPtr;
- outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1);
- outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2);
- outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 12);
- _mm_store_si128(dstVectorPtr, outputVector);
- ++dstVectorPtr;
- }
- dst24 = reinterpret_cast<quint24*>(dstVectorPtr);
- src = reinterpret_cast<const uint*>(inVectorPtr);
-
- SIMD_EPILOGUE(i, len, 15)
- *dst24++ = quint24(*src++);
-}
-
-void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count)
-{
- store_uint24_ssse3(dest + index * 3, src, count);
+ const quint24 *s = reinterpret_cast<const quint24 *>(src);
+ for (int i = 0; i < count; ++i)
+ buffer[i] = s[index + i];
+ return buffer;
}
extern void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len);
diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h
index 93e4b9f572..b237ea1611 100644
--- a/src/gui/painting/qdrawingprimitive_sse2_p.h
+++ b/src/gui/painting/qdrawingprimitive_sse2_p.h
@@ -43,6 +43,7 @@
#include <QtGui/private/qtguiglobal_p.h>
#include <private/qsimd_p.h>
#include "qdrawhelper_p.h"
+#include "qrgba64_p.h"
#ifdef __SSE2__
@@ -230,21 +231,31 @@ QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
#if QT_COMPILER_SUPPORTS_HERE(SSE4_1)
+QT_FUNCTION_TARGET(SSE2)
+Q_ALWAYS_INLINE void reciprocal_mul_ss(__m128 &ia, const __m128 a, float mul)
+{
+ ia = _mm_rcp_ss(a); // Approximate 1/a
+ // Improve precision of ia using Newton-Raphson
+ ia = _mm_sub_ss(_mm_add_ss(ia, ia), _mm_mul_ss(ia, _mm_mul_ss(ia, a)));
+ ia = _mm_mul_ss(ia, _mm_set_ss(mul));
+ ia = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0,0,0,0));
+}
+
QT_FUNCTION_TARGET(SSE4_1)
inline QRgb qUnpremultiply_sse4(QRgb p)
{
const uint alpha = qAlpha(p);
- if (alpha == 255 || alpha == 0)
+ if (alpha == 255)
return p;
- const uint invAlpha = qt_inv_premul_factor[alpha];
- const __m128i via = _mm_set1_epi32(invAlpha);
- const __m128i vr = _mm_set1_epi32(0x8000);
+ if (alpha == 0)
+ return 0;
+ const __m128 va = _mm_set1_ps(alpha);
+ __m128 via;
+ reciprocal_mul_ss(via, va, 255.0f); // Approximate 1/a
__m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p));
- vl = _mm_mullo_epi32(vl, via);
- vl = _mm_add_epi32(vl, vr);
- vl = _mm_srai_epi32(vl, 16);
- vl = _mm_insert_epi32(vl, alpha, 3);
+ vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via));
vl = _mm_packus_epi32(vl, vl);
+ vl = _mm_insert_epi16(vl, alpha, 3);
vl = _mm_packus_epi16(vl, vl);
return _mm_cvtsi128_si32(vl);
}
@@ -258,21 +269,14 @@ inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p)
return qConvertRgb32ToRgb30<PixelOrder>(p);
if (alpha == 0)
return 0;
- Q_CONSTEXPR uint mult = 255 / (255 >> 6);
- const uint invAlpha = qt_inv_premul_factor[alpha];
+ Q_CONSTEXPR float mult = 1023.0f / (255 >> 6);
const uint newalpha = (alpha >> 6);
- const __m128i via = _mm_set1_epi32(invAlpha);
- const __m128i vna = _mm_set1_epi32(mult * newalpha);
- const __m128i vr1 = _mm_set1_epi32(0x1000);
- const __m128i vr2 = _mm_set1_epi32(0x80);
- __m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p));
- vl = _mm_mullo_epi32(vl, via);
- vl = _mm_add_epi32(vl, vr1);
- vl = _mm_srli_epi32(vl, 14);
- vl = _mm_mullo_epi32(vl, vna);
- vl = _mm_add_epi32(vl, _mm_srli_epi32(vl, 8));
- vl = _mm_add_epi32(vl, vr2);
- vl = _mm_srli_epi32(vl, 8);
+ const __m128 va = _mm_set1_ps(alpha);
+ __m128 via;
+ reciprocal_mul_ss(via, va, mult * newalpha);
+ __m128i vl = _mm_cvtsi32_si128(p);
+ vl = _mm_cvtepu8_epi32(vl);
+ vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via));
vl = _mm_packus_epi32(vl, vl);
uint rgb30 = (newalpha << 30);
rgb30 |= ((uint)_mm_extract_epi16(vl, 1)) << 10;
@@ -285,6 +289,27 @@ inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p)
}
return rgb30;
}
+
+template<enum QtPixelOrder PixelOrder>
+QT_FUNCTION_TARGET(SSE4_1)
+inline uint qConvertRgba64ToRgb32_sse4(QRgba64 p)
+{
+ if (p.isTransparent())
+ return 0;
+ __m128i vl = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&p));
+ if (!p.isOpaque()) {
+ const __m128 va = _mm_set1_ps(p.alpha());
+ __m128 via;
+ reciprocal_mul_ss(via, va, 65535.0f);
+ vl = _mm_unpacklo_epi16(vl, _mm_setzero_si128());
+ vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl) , via));
+ vl = _mm_packus_epi32(vl, vl);
+ vl = _mm_insert_epi16(vl, p.alpha(), 3);
+ }
+ if (PixelOrder == PixelOrderBGR)
+ vl = _mm_shufflelo_epi16(vl, _MM_SHUFFLE(3, 0, 1, 2));
+ return toArgb32(vl);
+}
#endif
QT_END_NAMESPACE
diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp
index e6686e3721..0c0df0fb13 100644
--- a/src/gui/painting/qemulationpaintengine.cpp
+++ b/src/gui/painting/qemulationpaintengine.cpp
@@ -74,10 +74,11 @@ QPainterState *QEmulationPaintEngine::createState(QPainterState *orig) const
static inline void combineXForm(QBrush *brush, const QRectF &r)
{
- QTransform t = brush->transform();
- t.translate(r.x(), r.y());
- t.scale(r.width(), r.height());
- brush->setTransform(t);
+ QTransform t(r.width(), 0, 0, r.height(), r.x(), r.y());
+ if (brush->gradient() && brush->gradient()->coordinateMode() != QGradient::ObjectMode)
+ brush->setTransform(t * brush->transform()); // compat mode
+ else
+ brush->setTransform(brush->transform() * t);
}
void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
@@ -96,11 +97,19 @@ void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
if (coMode > QGradient::LogicalMode) {
QBrush copy = brush;
const QPaintDevice *d = real_engine->painter()->device();
- QRectF r = (coMode == QGradient::ObjectBoundingMode) ? path.controlPointRect() : QRectF(0, 0, d->width(), d->height());
+ QRectF r = (coMode == QGradient::StretchToDeviceMode) ? QRectF(0, 0, d->width(), d->height()) : path.controlPointRect();
combineXForm(&copy, r);
real_engine->fill(path, copy);
return;
}
+ } else if (style == Qt::TexturePattern) {
+ qreal dpr = qHasPixmapTexture(brush) ? brush.texture().devicePixelRatioF() : brush.textureImage().devicePixelRatioF();
+ if (!qFuzzyCompare(dpr, 1.0)) {
+ QBrush copy = brush;
+ combineXForm(&copy, QRectF(0, 0, 1.0/dpr, 1.0/dpr));
+ real_engine->fill(path, copy);
+ return;
+ }
}
real_engine->fill(path, brush);
@@ -124,7 +133,7 @@ void QEmulationPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
QGradient::CoordinateMode coMode = brush.gradient()->coordinateMode();
if (coMode > QGradient::LogicalMode) {
const QPaintDevice *d = real_engine->painter()->device();
- QRectF r = (coMode == QGradient::ObjectBoundingMode) ? path.controlPointRect() : QRectF(0, 0, d->width(), d->height());
+ QRectF r = (coMode == QGradient::StretchToDeviceMode) ? QRectF(0, 0, d->width(), d->height()) : path.controlPointRect();
combineXForm(&brush, r);
copy.setBrush(brush);
real_engine->stroke(path, copy);
@@ -166,9 +175,9 @@ void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &text
QBrush copy = s->pen.brush();
const QPaintDevice *d = real_engine->painter()->device();
const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
- QRectF r = (g.coordinateMode() == QGradient::ObjectBoundingMode) ?
- QRectF(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()) :
- QRectF(0, 0, d->width(), d->height());
+ QRectF r = (g.coordinateMode() == QGradient::StretchToDeviceMode) ?
+ QRectF(0, 0, d->width(), d->height()) :
+ QRectF(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
combineXForm(&copy, r);
g.setCoordinateMode(QGradient::LogicalMode);
QBrush brush(g);
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
index 22787b91fe..8a5274bd37 100644
--- a/src/gui/painting/qimagescale.cpp
+++ b/src/gui/painting/qimagescale.cpp
@@ -41,6 +41,7 @@
#include "qimage.h"
#include "qcolor.h"
+#include "qrgba64_p.h"
QT_BEGIN_NAMESPACE
@@ -85,7 +86,7 @@ QT_BEGIN_NAMESPACE
* #ifdef'ed code, and removal of unneeded border calculation code.
* Later the code has been refactored, an SSE4.1 optimizated path have been
* added instead of the removed MMX assembler, and scaling of clipped area
- * removed.
+ * removed, and an RGBA64 version written
*
* Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
* is by Willem Monsuwe <willem@stack.nl>. All other modifications are
@@ -94,12 +95,11 @@ QT_BEGIN_NAMESPACE
namespace QImageScale {
- const unsigned int** qimageCalcYPoints(const unsigned int *src, int sw, int sh, int dh);
- int* qimageCalcXPoints(int sw, int dw);
- int* qimageCalcApoints(int s, int d, int up);
- QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
- QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh,
- int dw, int dh, char aa);
+ static const unsigned int** qimageCalcYPoints(const unsigned int *src, int sw, int sh, int dh);
+ static int* qimageCalcXPoints(int sw, int dw);
+ static int* qimageCalcApoints(int s, int d, int up);
+ static QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
+ static QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh, int dw, int dh, char aa);
}
using namespace QImageScale;
@@ -108,8 +108,8 @@ using namespace QImageScale;
// Code ported from Imlib...
//
-const unsigned int** QImageScale::qimageCalcYPoints(const unsigned int *src,
- int sw, int sh, int dh)
+static const unsigned int** QImageScale::qimageCalcYPoints(const unsigned int *src,
+ int sw, int sh, int dh)
{
const unsigned int **p;
int j = 0, rv = 0;
@@ -138,7 +138,7 @@ const unsigned int** QImageScale::qimageCalcYPoints(const unsigned int *src,
return(p);
}
-int* QImageScale::qimageCalcXPoints(int sw, int dw)
+static int* QImageScale::qimageCalcXPoints(int sw, int dw)
{
int *p, j = 0, rv = 0;
qint64 val, inc;
@@ -167,7 +167,7 @@ int* QImageScale::qimageCalcXPoints(int sw, int dw)
return p;
}
-int* QImageScale::qimageCalcApoints(int s, int d, int up)
+static int* QImageScale::qimageCalcApoints(int s, int d, int up)
{
int *p, j = 0, rv = 0;
@@ -214,7 +214,7 @@ int* QImageScale::qimageCalcApoints(int s, int d, int up)
return p;
}
-QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
+static QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
{
if (isi) {
delete[] isi->xpoints;
@@ -226,9 +226,9 @@ QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
return 0;
}
-QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
- int sw, int sh,
- int dw, int dh, char aa)
+static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
+ int sw, int sh,
+ int dw, int dh, char aa)
{
QImageScaleInfo *isi;
int scw, sch;
@@ -239,7 +239,6 @@ QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
isi = new QImageScaleInfo;
if (!isi)
return 0;
- memset(isi, 0, sizeof(QImageScaleInfo));
isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1);
@@ -333,7 +332,7 @@ static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest,
}
}
-/* scale by area sampling */
+/* scale by area sampling - with alpha */
static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow)
{
@@ -529,6 +528,207 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des
}
}
+static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow);
+
+static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow);
+
+static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow);
+
+static void qt_qimageScaleRgba64_up_xy(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow)
+{
+ const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ for (int y = 0; y < dh; y++) {
+ const QRgba64 *sptr = ypoints[y];
+ QRgba64 *dptr = dest + (y * dow);
+ const int yap = yapoints[y];
+ if (yap > 0) {
+ for (int x = 0; x < dw; x++) {
+ const QRgba64 *pix = sptr + xpoints[x];
+ const int xap = xapoints[x];
+ if (xap > 0)
+ *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256);
+ else
+ *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap);
+ dptr++;
+ }
+ } else {
+ for (int x = 0; x < dw; x++) {
+ const QRgba64 *pix = sptr + xpoints[x];
+ const int xap = xapoints[x];
+ if (xap > 0)
+ *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap);
+ else
+ *dptr = pix[0];
+ dptr++;
+ }
+ }
+ }
+}
+
+void qt_qimageScaleRgba64(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow)
+{
+ if (isi->xup_yup == 3)
+ qt_qimageScaleRgba64_up_xy(isi, dest, dw, dh, dow, sow);
+ else if (isi->xup_yup == 1)
+ qt_qimageScaleRgba64_up_x_down_y(isi, dest, dw, dh, dow, sow);
+ else if (isi->xup_yup == 2)
+ qt_qimageScaleRgba64_down_x_up_y(isi, dest, dw, dh, dow, sow);
+ else
+ qt_qimageScaleRgba64_down_xy(isi, dest, dw, dh, dow, sow);
+}
+
+inline static void qt_qimageScaleRgba64_helper(const QRgba64 *pix, int xyap, int Cxy, int step, qint64 &r, qint64 &g, qint64 &b, qint64 &a)
+{
+ r = pix->red() * xyap;
+ g = pix->green() * xyap;
+ b = pix->blue() * xyap;
+ a = pix->alpha() * xyap;
+ int j;
+ for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy ){
+ pix += step;
+ r += pix->red() * Cxy;
+ g += pix->green() * Cxy;
+ b += pix->blue() * Cxy;
+ a += pix->alpha() * Cxy;
+ }
+ pix += step;
+ r += pix->red() * j;
+ g += pix->green() * j;
+ b += pix->blue() * j;
+ a += pix->alpha() * j;
+}
+
+static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow)
+{
+ const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ for (int y = 0; y < dh; y++) {
+ int Cy = (yapoints[y]) >> 16;
+ int yap = (yapoints[y]) & 0xffff;
+
+ QRgba64 *dptr = dest + (y * dow);
+ for (int x = 0; x < dw; x++) {
+ const QRgba64 *sptr = ypoints[y] + xpoints[x];
+ qint64 r, g, b, a;
+ qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a);
+
+ int xap = xapoints[x];
+ if (xap > 0) {
+ qint64 rr, gg, bb, aa;
+ qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa);
+
+ r = r * (256 - xap);
+ g = g * (256 - xap);
+ b = b * (256 - xap);
+ a = a * (256 - xap);
+ r = (r + (rr * xap)) >> 8;
+ g = (g + (gg * xap)) >> 8;
+ b = (b + (bb * xap)) >> 8;
+ a = (a + (aa * xap)) >> 8;
+ }
+ *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
+ }
+ }
+}
+
+static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow)
+{
+ const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ for (int y = 0; y < dh; y++) {
+ QRgba64 *dptr = dest + (y * dow);
+ for (int x = 0; x < dw; x++) {
+ int Cx = xapoints[x] >> 16;
+ int xap = xapoints[x] & 0xffff;
+
+ const QRgba64 *sptr = ypoints[y] + xpoints[x];
+ qint64 r, g, b, a;
+ qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a);
+
+ int yap = yapoints[y];
+ if (yap > 0) {
+ qint64 rr, gg, bb, aa;
+ qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa);
+
+ r = r * (256 - yap);
+ g = g * (256 - yap);
+ b = b * (256 - yap);
+ a = a * (256 - yap);
+ r = (r + (rr * yap)) >> 8;
+ g = (g + (gg * yap)) >> 8;
+ b = (b + (bb * yap)) >> 8;
+ a = (a + (aa * yap)) >> 8;
+ }
+ *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
+ dptr++;
+ }
+ }
+}
+
+static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
+ int dw, int dh, int dow, int sow)
+{
+ const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ for (int y = 0; y < dh; y++) {
+ int Cy = (yapoints[y]) >> 16;
+ int yap = (yapoints[y]) & 0xffff;
+
+ QRgba64 *dptr = dest + (y * dow);
+ for (int x = 0; x < dw; x++) {
+ int Cx = xapoints[x] >> 16;
+ int xap = xapoints[x] & 0xffff;
+
+ const QRgba64 *sptr = ypoints[y] + xpoints[x];
+ qint64 rx, gx, bx, ax;
+ qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
+
+ qint64 r = rx * yap;
+ qint64 g = gx * yap;
+ qint64 b = bx * yap;
+ qint64 a = ax * yap;
+ int j;
+ for (j = (1 << 14) - yap; j > Cy; j -= Cy) {
+ sptr += sow;
+ qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
+ r += rx * Cy;
+ g += gx * Cy;
+ b += bx * Cy;
+ a += ax * Cy;
+ }
+ sptr += sow;
+ qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
+ r += rx * j;
+ g += gx * j;
+ b += bx * j;
+ a += ax * j;
+
+ *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28);
+ dptr++;
+ }
+ }
+}
+
static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
@@ -745,7 +945,10 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
return QImage();
}
- if (src.hasAlphaChannel())
+ if (src.depth() > 32)
+ qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0),
+ dw, dh, dw, src.bytesPerLine() / 8);
+ else if (src.hasAlphaChannel())
qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 4);
else
diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h
index 415623a575..244d681718 100644
--- a/src/gui/painting/qimagescale_p.h
+++ b/src/gui/painting/qimagescale_p.h
@@ -61,10 +61,11 @@ QImage qSmoothScaleImage(const QImage &img, int w, int h);
namespace QImageScale {
struct QImageScaleInfo {
- int *xpoints;
- const unsigned int **ypoints;
- int *xapoints, *yapoints;
- int xup_yup;
+ int *xpoints{nullptr};
+ const unsigned int **ypoints{nullptr};
+ int *xapoints{nullptr};
+ int *yapoints{nullptr};
+ int xup_yup{0};
};
}
diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp
index 25aa6a3122..43aeff3268 100644
--- a/src/gui/painting/qmemrotate.cpp
+++ b/src/gui/painting/qmemrotate.cpp
@@ -239,6 +239,12 @@ inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, i
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
}
+template <>
+inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
+{
+ qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
+}
+
template <class T>
Q_STATIC_TEMPLATE_FUNCTION
inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride)
@@ -275,6 +281,12 @@ inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h,
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
}
+template <>
+inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
+{
+ qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
+}
+
#define QT_IMPL_MEMROTATE(type) \
Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \
type *dest, int dstride) \
@@ -309,9 +321,7 @@ Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \
}
-
-
-
+QT_IMPL_MEMROTATE(quint64)
QT_IMPL_MEMROTATE(quint32)
QT_IMPL_MEMROTATE(quint16)
QT_IMPL_MEMROTATE(quint24)
@@ -377,6 +387,22 @@ void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *d
qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
}
+
+void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate90((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
+}
+
+void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate180((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
+}
+
+void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
+}
+
MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] =
// 90, 180, 270
{
@@ -387,6 +413,7 @@ MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] =
{ qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // BPP16,
{ qt_memrotate90_24, qt_memrotate180_24, qt_memrotate270_24 }, // BPP24
{ qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // BPP32
+ { qt_memrotate90_64, qt_memrotate180_64, qt_memrotate270_64 }, // BPP64
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h
index 9bc3fd1010..677fd57af9 100644
--- a/src/gui/painting/qmemrotate_p.h
+++ b/src/gui/painting/qmemrotate_p.h
@@ -65,6 +65,7 @@ QT_DECL_MEMROTATE(quint32);
QT_DECL_MEMROTATE(quint16);
QT_DECL_MEMROTATE(quint24);
QT_DECL_MEMROTATE(quint8);
+QT_DECL_MEMROTATE(quint64);
#undef QT_DECL_MEMROTATE
diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp
index 1c7d6471b6..72b2834470 100644
--- a/src/gui/painting/qpagedpaintdevice.cpp
+++ b/src/gui/painting/qpagedpaintdevice.cpp
@@ -42,6 +42,41 @@
QT_BEGIN_NAMESPACE
+class QDummyPagedPaintDevicePrivate : public QPagedPaintDevicePrivate
+{
+ bool setPageLayout(const QPageLayout &newPageLayout) override
+ {
+ m_pageLayout = newPageLayout;
+ return m_pageLayout.isEquivalentTo(newPageLayout);
+ }
+
+ bool setPageSize(const QPageSize &pageSize) override
+ {
+ m_pageLayout.setPageSize(pageSize);
+ return m_pageLayout.pageSize().isEquivalentTo(pageSize);
+ }
+
+ bool setPageOrientation(QPageLayout::Orientation orientation) override
+ {
+ m_pageLayout.setOrientation(orientation);
+ return m_pageLayout.orientation() == orientation;
+ }
+
+ bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) override
+ {
+ m_pageLayout.setUnits(units);
+ m_pageLayout.setMargins(margins);
+ return m_pageLayout.margins() == margins && m_pageLayout.units() == units;
+ }
+
+ QPageLayout pageLayout() const override
+ {
+ return m_pageLayout;
+ }
+
+ QPageLayout m_pageLayout;
+};
+
QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate()
{
}
@@ -61,9 +96,11 @@ QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate()
/*!
Constructs a new paged paint device.
+
+ \deprecated
*/
QPagedPaintDevice::QPagedPaintDevice()
- : d(new QPagedPaintDevicePrivate)
+ : d(new QDummyPagedPaintDevicePrivate)
{
}
@@ -253,7 +290,8 @@ QPagedPaintDevicePrivate *QPagedPaintDevice::dd()
\value PdfVersion_A1b A PDF/A-1b compatible document is produced.
- \since 5.10
+ \value PdfVersion_1_6 A PDF 1.6 compatible document is produced.
+ This value was added in Qt 5.12.
*/
/*!
@@ -263,7 +301,7 @@ QPagedPaintDevicePrivate *QPagedPaintDevice::dd()
*/
void QPagedPaintDevice::setPageSize(PageSize size)
{
- d->m_pageLayout.setPageSize(QPageSize(QPageSize::PageSizeId(size)));
+ d->setPageSize(QPageSize(QPageSize::PageSizeId(size)));
}
/*!
@@ -271,7 +309,7 @@ void QPagedPaintDevice::setPageSize(PageSize size)
*/
QPagedPaintDevice::PageSize QPagedPaintDevice::pageSize() const
{
- return PageSize(d->m_pageLayout.pageSize().id());
+ return PageSize(d->pageLayout().pageSize().id());
}
/*!
@@ -282,7 +320,7 @@ QPagedPaintDevice::PageSize QPagedPaintDevice::pageSize() const
*/
void QPagedPaintDevice::setPageSizeMM(const QSizeF &size)
{
- d->m_pageLayout.setPageSize(QPageSize(size, QPageSize::Millimeter));
+ d->setPageSize(QPageSize(size, QPageSize::Millimeter));
}
/*!
@@ -290,7 +328,7 @@ void QPagedPaintDevice::setPageSizeMM(const QSizeF &size)
*/
QSizeF QPagedPaintDevice::pageSizeMM() const
{
- return d->m_pageLayout.pageSize().size(QPageSize::Millimeter);
+ return d->pageLayout().pageSize().size(QPageSize::Millimeter);
}
/*!
@@ -305,8 +343,7 @@ QSizeF QPagedPaintDevice::pageSizeMM() const
*/
void QPagedPaintDevice::setMargins(const Margins &margins)
{
- d->m_pageLayout.setUnits(QPageLayout::Millimeter);
- d->m_pageLayout.setMargins(QMarginsF(margins.left, margins.top, margins.right, margins.bottom));
+ d->setPageMargins(QMarginsF(margins.left, margins.top, margins.right, margins.bottom), QPageLayout::Millimeter);
}
/*!
@@ -318,7 +355,7 @@ void QPagedPaintDevice::setMargins(const Margins &margins)
*/
QPagedPaintDevice::Margins QPagedPaintDevice::margins() const
{
- QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Millimeter);
+ QMarginsF margins = d->pageLayout().margins(QPageLayout::Millimeter);
Margins result;
result.left = margins.left();
result.top = margins.top();
@@ -413,7 +450,7 @@ bool QPagedPaintDevice::setPageOrientation(QPageLayout::Orientation orientation)
bool QPagedPaintDevice::setPageMargins(const QMarginsF &margins)
{
- return d->setPageMargins(margins);
+ return setPageMargins(margins, pageLayout().units());
}
/*!
@@ -458,23 +495,30 @@ QPageLayout QPagedPaintDevice::pageLayout() const
/*!
\internal
+ \deprecated
+
Returns the internal device page layout.
*/
QPageLayout QPagedPaintDevice::devicePageLayout() const
{
- return d->m_pageLayout;
+ qWarning("QPagedPaintDevice::devicePageLayout() is deprecated, just use QPagedPaintDevice::pageLayout()");
+ return d->pageLayout();
}
/*!
\internal
+ \deprecated
+
Returns the internal device page layout.
*/
QPageLayout &QPagedPaintDevice::devicePageLayout()
{
- return d->m_pageLayout;
+ qWarning("QPagedPaintDevice::devicePageLayout() is deprecated, you shouldn't be using this at all.");
+ static QPageLayout dummy;
+ return dummy;
}
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h
index 66dd6fa8cf..1c37c17fa3 100644
--- a/src/gui/painting/qpagedpaintdevice.h
+++ b/src/gui/painting/qpagedpaintdevice.h
@@ -55,7 +55,7 @@ class QPagedPaintDevicePrivate;
class Q_GUI_EXPORT QPagedPaintDevice : public QPaintDevice
{
public:
- QPagedPaintDevice();
+ QT_DEPRECATED QPagedPaintDevice();
~QPagedPaintDevice();
virtual bool newPage() = 0;
@@ -213,7 +213,7 @@ public:
Envelope10 = Comm10E
};
- enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b };
+ enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b, PdfVersion_1_6 };
// ### Qt6 Make these virtual
bool setPageLayout(const QPageLayout &pageLayout);
@@ -243,8 +243,8 @@ public:
protected:
QPagedPaintDevice(QPagedPaintDevicePrivate *dd);
QPagedPaintDevicePrivate *dd();
- QPageLayout devicePageLayout() const;
- QPageLayout &devicePageLayout();
+ QT_DEPRECATED QPageLayout devicePageLayout() const;
+ QT_DEPRECATED QPageLayout &devicePageLayout();
friend class QPagedPaintDevicePrivate;
QPagedPaintDevicePrivate *d;
};
diff --git a/src/gui/painting/qpagedpaintdevice_p.h b/src/gui/painting/qpagedpaintdevice_p.h
index a993ea4cac..3a43bd7828 100644
--- a/src/gui/painting/qpagedpaintdevice_p.h
+++ b/src/gui/painting/qpagedpaintdevice_p.h
@@ -60,8 +60,7 @@ class Q_GUI_EXPORT QPagedPaintDevicePrivate
{
public:
QPagedPaintDevicePrivate()
- : m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0, 0, 0, 0)),
- fromPage(0),
+ : fromPage(0),
toPage(0),
pageOrderAscending(true),
printSelectionOnly(false)
@@ -70,46 +69,19 @@ public:
virtual ~QPagedPaintDevicePrivate();
- // ### Qt6 Remove these and make public class methods virtual
- virtual bool setPageLayout(const QPageLayout &newPageLayout)
- {
- m_pageLayout = newPageLayout;
- return m_pageLayout.isEquivalentTo(newPageLayout);;
- }
- virtual bool setPageSize(const QPageSize &pageSize)
- {
- m_pageLayout.setPageSize(pageSize);
- return m_pageLayout.pageSize().isEquivalentTo(pageSize);
- }
+ virtual bool setPageLayout(const QPageLayout &newPageLayout) = 0;
- virtual bool setPageOrientation(QPageLayout::Orientation orientation)
- {
- m_pageLayout.setOrientation(orientation);
- return m_pageLayout.orientation() == orientation;
- }
+ virtual bool setPageSize(const QPageSize &pageSize) = 0;
- virtual bool setPageMargins(const QMarginsF &margins)
- {
- return setPageMargins(margins, m_pageLayout.units());
- }
+ virtual bool setPageOrientation(QPageLayout::Orientation orientation) = 0;
- virtual bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units)
- {
- m_pageLayout.setUnits(units);
- m_pageLayout.setMargins(margins);
- return m_pageLayout.margins() == margins && m_pageLayout.units() == units;
- }
+ virtual bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) = 0;
- virtual QPageLayout pageLayout() const
- {
- return m_pageLayout;
- }
+ virtual QPageLayout pageLayout() const = 0;
static inline QPagedPaintDevicePrivate *get(QPagedPaintDevice *pd) { return pd->d; }
- QPageLayout m_pageLayout;
-
// These are currently required to keep QPrinter functionality working in QTextDocument::print()
int fromPage;
int toPage;
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index cade334ea6..7caaf3a8fa 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -2590,10 +2590,13 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
if (image.depth() == 1)
image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
- if (s->matrix.type() > QTransform::TxTranslate) {
+ const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
+ if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
QTransform copy = s->matrix;
copy.translate(r.x(), r.y());
copy.translate(-sr.x(), -sr.y());
+ const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
+ copy.scale(inverseDpr, inverseDpr);
d->image_filler_xform.clip = d->clip();
d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
if (!d->image_filler_xform.blend)
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 0643a7cbb6..9f07af92e4 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -948,6 +948,8 @@ void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, con
{
QBrush brush(state()->pen.color(), pixmap);
QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
+ if (!qFuzzyCompare(pixmap.devicePixelRatioF(), 1.0))
+ xform.scale(1.0/pixmap.devicePixelRatioF(), 1.0/pixmap.devicePixelRatioF());
brush.setTransform(xform);
qreal pts[] = { r.x(), r.y(),
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index b992e8f55d..b70b29e54e 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -190,6 +190,13 @@ void QPainterPrivate::checkEmulation()
if (pg && pg->coordinateMode() > QGradient::LogicalMode)
doEmulation = true;
+ if (state->brush.style() == Qt::TexturePattern) {
+ if (qHasPixmapTexture(state->brush))
+ doEmulation |= !qFuzzyCompare(state->brush.texture().devicePixelRatioF(), 1.0);
+ else
+ doEmulation |= !qFuzzyCompare(state->brush.textureImage().devicePixelRatioF(), 1.0);
+ }
+
if (doEmulation && extended->flags() & QPaintEngineEx::DoNotEmulate)
return;
@@ -523,7 +530,10 @@ static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRect
g.setCoordinateMode(QGradient::LogicalMode);
QBrush b(g);
- b.setTransform(gradientToUser * b.transform());
+ if (brush.gradient()->coordinateMode() == QGradient::ObjectMode)
+ b.setTransform(b.transform() * gradientToUser);
+ else
+ b.setTransform(gradientToUser * b.transform());
return b;
}
@@ -562,7 +572,7 @@ void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperat
} else {
needsFill = true;
- if (brushMode == QGradient::ObjectBoundingMode) {
+ if (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode) {
Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
boundingRect = path.boundingRect();
q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
@@ -606,11 +616,11 @@ void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperat
changedBrush = true;
}
- if (penMode == QGradient::ObjectBoundingMode) {
+ if (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode) {
Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
// avoid computing the bounding rect twice
- if (!needsFill || brushMode != QGradient::ObjectBoundingMode)
+ if (!needsFill || (brushMode != QGradient::ObjectBoundingMode && brushMode != QGradient::ObjectMode))
boundingRect = path.boundingRect();
QPen p = pen;
@@ -842,8 +852,8 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
- objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode);
- objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode);
+ objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode);
+ objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode);
}
if (gradientStretch)
s->emulationSpecifier |= QGradient_StretchToDevice;
@@ -1287,7 +1297,7 @@ void QPainterPrivate::updateState(QPainterState *newState)
itself and its bounding rectangle: The bounding rect contains
pixels with alpha == 0 (i.e the pixels surrounding the
primitive). These pixels will overwrite the other image's pixels,
- affectively clearing those, while the primitive only overwrites
+ effectively clearing those, while the primitive only overwrites
its own area.
\table 100%
@@ -1377,7 +1387,7 @@ void QPainterPrivate::updateState(QPainterState *newState)
clip.
\li Composition Modes \c QPainter::CompositionMode_Source and
- QPainter::CompositionMode_SourceOver
+ QPainter::CompositionMode_SourceOver.
\li Rounded rectangle filling using solid color and two-color
linear gradients fills.
@@ -6657,6 +6667,16 @@ QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextO
potentially much more efficient depending on the underlying window
system.
+ drawTiledPixmap() will produce the same visual tiling pattern on
+ high-dpi displays (with devicePixelRatio > 1), compared to normal-
+ dpi displays. Set the devicePixelRatio on the \a pixmap to control
+ the tile size. For example, setting it to 2 halves the tile width
+ and height (on both 1x and 2x displays), and produces high-resolution
+ output on 2x displays.
+
+ The \a position offset is always in the painter coordinate system,
+ indepentent of display devicePixelRatio.
+
\sa drawPixmap()
*/
void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp)
@@ -6840,7 +6860,8 @@ static inline bool needsResolving(const QBrush &brush)
Qt::BrushStyle s = brush.style();
return ((s == Qt::LinearGradientPattern || s == Qt::RadialGradientPattern ||
s == Qt::ConicalGradientPattern) &&
- brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode);
+ (brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ||
+ brush.gradient()->coordinateMode() == QGradient::ObjectMode));
}
/*!
@@ -7068,6 +7089,37 @@ void QPainter::fillRect(const QRectF &r, const QColor &color)
*/
/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, QGradient::Preset preset)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given gradient \a preset.
+
+ \since 5.12
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, QGradient::Preset preset);
+
+ \overload
+
+ Fills the given \a rectangle with the specified gradient \a preset.
+
+ \since 5.12
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, QGradient::Preset preset);
+
+ \overload
+
+ Fills the given \a rectangle with the specified gradient \a preset.
+
+ \since 5.12
+*/
+
+/*!
Sets the given render \a hint on the painter if \a on is true;
otherwise clears the render hint.
@@ -8207,6 +8259,7 @@ void QPainter::setTransform(const QTransform &transform, bool combine )
}
/*!
+ Alias for worldTransform().
Returns the world transformation matrix.
\sa worldTransform()
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
index 12a9c720a8..482f5fb63d 100644
--- a/src/gui/painting/qpainter.h
+++ b/src/gui/painting/qpainter.h
@@ -448,6 +448,10 @@ public:
inline void fillRect(const QRect &r, Qt::BrushStyle style);
inline void fillRect(const QRectF &r, Qt::BrushStyle style);
+ inline void fillRect(int x, int y, int w, int h, QGradient::Preset preset);
+ inline void fillRect(const QRect &r, QGradient::Preset preset);
+ inline void fillRect(const QRectF &r, QGradient::Preset preset);
+
void eraseRect(const QRectF &);
inline void eraseRect(int x, int y, int w, int h);
inline void eraseRect(const QRect &);
@@ -746,6 +750,20 @@ inline void QPainter::fillRect(const QRectF &r, Qt::BrushStyle style)
fillRect(r, QBrush(style));
}
+inline void QPainter::fillRect(int x, int y, int w, int h, QGradient::Preset p)
+{
+ fillRect(QRect(x, y, w, h), QGradient(p));
+}
+
+inline void QPainter::fillRect(const QRect &r, QGradient::Preset p)
+{
+ fillRect(r, QGradient(p));
+}
+
+inline void QPainter::fillRect(const QRectF &r, QGradient::Preset p)
+{
+ fillRect(r, QGradient(p));
+}
inline void QPainter::setBrushOrigin(int x, int y)
{
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index 2574a00838..c5ccf0003d 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -2476,7 +2476,7 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
s >> p.d_func()->cStart;
int fillRule;
s >> fillRule;
- Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill);
+ Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
p.d_func()->fillRule = Qt::FillRule(fillRule);
p.d_func()->dirtyBounds = true;
p.d_func()->dirtyControlBounds = true;
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index 8a0020bd2d..e69726b617 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -953,7 +953,18 @@ void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, con
if (object < 0)
return;
- *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage << "q\n";
+
+ if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) {
+ int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity));
+ if (stateObject)
+ *d->currentPage << "/GState" << stateObject << "gs\n";
+ else
+ *d->currentPage << "/GSa gs\n";
+ } else {
+ *d->currentPage << "/GSa gs\n";
+ }
+
*d->currentPage
<< QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
@@ -981,7 +992,18 @@ void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const Q
if (object < 0)
return;
- *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage << "q\n";
+
+ if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) {
+ int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity));
+ if (stateObject)
+ *d->currentPage << "/GState" << stateObject << "gs\n";
+ else
+ *d->currentPage << "/GSa gs\n";
+ } else {
+ *d->currentPage << "/GSa gs\n";
+ }
+
*d->currentPage
<< QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
@@ -1114,8 +1136,9 @@ void QPdfEngine::updateState(const QPaintEngineState &state)
d->hasPen = d->pen.style() != Qt::NoPen;
d->stroker.setPen(d->pen, state.renderHints());
QBrush penBrush = d->pen.brush();
+ bool cosmeticPen = qt_pen_is_cosmetic(d->pen, state.renderHints());
bool oldSimple = d->simplePen;
- d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0);
+ d->simplePen = (d->hasPen && !cosmeticPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0);
if (oldSimple != d->simplePen)
flags |= DirtyTransform;
} else if (flags & DirtyHints) {
@@ -1546,7 +1569,14 @@ void QPdfEnginePrivate::writeHeader()
{
addXrefEntry(0,false);
- xprintf("%%PDF-1.4\n");
+ static const QHash<QPdfEngine::PdfVersion, const char *> mapping {
+ {QPdfEngine::Version_1_4, "1.4"},
+ {QPdfEngine::Version_A1b, "1.4"},
+ {QPdfEngine::Version_1_6, "1.6"}
+ };
+ const char *verStr = mapping.value(pdfVersion, "1.4");
+
+ xprintf("%%PDF-%s\n", verStr);
xprintf("%%\303\242\303\243\n");
writeInfo();
@@ -1879,6 +1909,19 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font)
}
}
+qreal QPdfEnginePrivate::calcUserUnit() const
+{
+ // PDF standards < 1.6 support max 200x200in pages (no UserUnit)
+ if (pdfVersion < QPdfEngine::Version_1_6)
+ return 1.0;
+
+ const int maxLen = qMax(currentPage->pageSize.width(), currentPage->pageSize.height());
+ if (maxLen <= 14400)
+ return 1.0; // for pages up to 200x200in (14400x14400 units) use default scaling
+
+ // for larger pages, rescale units so we can have up to 381x381km
+ return qMin(maxLen / 14400.0, 75000.0);
+}
void QPdfEnginePrivate::writeFonts()
{
@@ -1901,6 +1944,8 @@ void QPdfEnginePrivate::writePage()
uint resources = requestObject();
uint annots = requestObject();
+ qreal userUnit = calcUserUnit();
+
addXrefEntry(pages.constLast());
xprintf("<<\n"
"/Type /Page\n"
@@ -1908,12 +1953,16 @@ void QPdfEnginePrivate::writePage()
"/Contents %d 0 R\n"
"/Resources %d 0 R\n"
"/Annots %d 0 R\n"
- "/MediaBox [0 0 %d %d]\n"
- ">>\n"
- "endobj\n",
+ "/MediaBox [0 0 %f %f]\n",
pageRoot, pageStream, resources, annots,
// make sure we use the pagesize from when we started the page, since the user may have changed it
- currentPage->pageSize.width(), currentPage->pageSize.height());
+ currentPage->pageSize.width() / userUnit, currentPage->pageSize.height() / userUnit);
+
+ if (pdfVersion >= QPdfEngine::Version_1_6)
+ xprintf("/UserUnit %f\n", userUnit);
+
+ xprintf(">>\n"
+ "endobj\n");
addXrefEntry(resources);
xprintf("<<\n"
@@ -2982,8 +3031,9 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
QTransform QPdfEnginePrivate::pageMatrix() const
{
- qreal scale = 72./resolution;
- QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, m_pageLayout.fullRectPoints().height());
+ qreal userUnit = calcUserUnit();
+ qreal scale = 72. / userUnit / resolution;
+ QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, m_pageLayout.fullRectPoints().height() / userUnit);
if (m_pageLayout.mode() != QPageLayout::FullPageMode) {
QRect r = m_pageLayout.paintRectPixels(resolution);
tmp.translate(r.left(), r.top());
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
index 5a909f2ede..e2526de67d 100644
--- a/src/gui/painting/qpdf_p.h
+++ b/src/gui/painting/qpdf_p.h
@@ -171,7 +171,8 @@ public:
enum PdfVersion
{
Version_1_4,
- Version_A1b
+ Version_A1b,
+ Version_1_6
};
QPdfEngine();
@@ -300,6 +301,7 @@ private:
void writePageRoot();
void writeFonts();
void embedFont(QFontSubset *font);
+ qreal calcUserUnit() const;
QVector<int> xrefPositions;
QDataStream* stream;
diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp
index 2f24c7efcb..6c85d65538 100644
--- a/src/gui/painting/qpdfwriter.cpp
+++ b/src/gui/painting/qpdfwriter.cpp
@@ -83,41 +83,28 @@ public:
{
// Try to set the paint engine page layout
pd->engine->setPageLayout(newPageLayout);
- // Set QPagedPaintDevice layout to match the current paint engine layout
- m_pageLayout = pd->engine->pageLayout();
- return m_pageLayout.isEquivalentTo(newPageLayout);
+ return pageLayout().isEquivalentTo(newPageLayout);
}
bool setPageSize(const QPageSize &pageSize) override
{
// Try to set the paint engine page size
pd->engine->setPageSize(pageSize);
- // Set QPagedPaintDevice layout to match the current paint engine layout
- m_pageLayout = pd->engine->pageLayout();
- return m_pageLayout.pageSize().isEquivalentTo(pageSize);
+ return pageLayout().pageSize().isEquivalentTo(pageSize);
}
bool setPageOrientation(QPageLayout::Orientation orientation) override
{
// Set the print engine value
pd->engine->setPageOrientation(orientation);
- // Set QPagedPaintDevice layout to match the current paint engine layout
- m_pageLayout = pd->engine->pageLayout();
- return m_pageLayout.orientation() == orientation;
- }
-
- bool setPageMargins(const QMarginsF &margins) override
- {
- return setPageMargins(margins, pageLayout().units());
+ return pageLayout().orientation() == orientation;
}
bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) override
{
// Try to set engine margins
pd->engine->setPageMargins(margins, units);
- // Set QPagedPaintDevice layout to match the current paint engine layout
- m_pageLayout = pd->engine->pageLayout();
- return m_pageLayout.margins() == margins && m_pageLayout.units() == units;
+ return pageLayout().margins() == margins && pageLayout().units() == units;
}
QPageLayout pageLayout() const override
@@ -150,9 +137,6 @@ QPdfWriter::QPdfWriter(const QString &filename)
Q_D(QPdfWriter);
d->engine->setOutputFilename(filename);
-
- // Set QPagedPaintDevice layout to match the current paint engine layout
- devicePageLayout() = d->engine->pageLayout();
}
/*!
@@ -165,9 +149,6 @@ QPdfWriter::QPdfWriter(QIODevice *device)
Q_D(QPdfWriter);
d->engine->d_func()->outDevice = device;
-
- // Set QPagedPaintDevice layout to match the current paint engine layout
- devicePageLayout() = d->engine->pageLayout();
}
/*!
@@ -189,11 +170,17 @@ void QPdfWriter::setPdfVersion(PdfVersion version)
{
Q_D(QPdfWriter);
+ static const QHash<QPdfWriter::PdfVersion, QPdfEngine::PdfVersion> engineMapping {
+ {QPdfWriter::PdfVersion_1_4, QPdfEngine::Version_1_4},
+ {QPdfWriter::PdfVersion_A1b, QPdfEngine::Version_A1b},
+ {QPdfWriter::PdfVersion_1_6, QPdfEngine::Version_1_6}
+ };
+
if (d->pdfVersion == version)
return;
d->pdfVersion = version;
- d->engine->setPdfVersion(d->pdfVersion == QPdfWriter::PdfVersion_1_4 ? QPdfEngine::Version_1_4 : QPdfEngine::Version_A1b);
+ d->engine->setPdfVersion(engineMapping.value(version, QPdfEngine::Version_1_4));
}
/*!
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index e05efca9c8..3081a4b1b6 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -80,6 +80,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore", QtWarningMsg);
+
class QPlatformBackingStorePrivate
{
public:
@@ -331,16 +333,19 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
d_ptr->context->setScreen(d_ptr->window->screen());
d_ptr->context->setShareContext(qt_window_private(d_ptr->window)->shareContext());
if (!d_ptr->context->create()) {
- qWarning("composeAndFlush: QOpenGLContext creation failed");
+ qCWarning(lcQpaBackingStore, "composeAndFlush: QOpenGLContext creation failed");
return;
}
}
if (!d_ptr->context->makeCurrent(window)) {
- qWarning("composeAndFlush: makeCurrent() failed");
+ qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed");
return;
}
+ qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window
+ << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures;
+
QWindowPrivate::get(window)->lastComposeTime.start();
QOpenGLFunctions *funcs = d_ptr->context->functions();
diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h
index 381c564079..de5ba964dc 100644
--- a/src/gui/painting/qplatformbackingstore.h
+++ b/src/gui/painting/qplatformbackingstore.h
@@ -50,6 +50,7 @@
//
#include <QtGui/qtguiglobal.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qrect.h>
#include <QtCore/qobject.h>
@@ -59,6 +60,7 @@
QT_BEGIN_NAMESPACE
+Q_GUI_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaBackingStore)
class QRegion;
class QRect;
diff --git a/src/gui/painting/qrgba64.h b/src/gui/painting/qrgba64.h
index 2c8f8fa8c4..0e5344cacb 100644
--- a/src/gui/painting/qrgba64.h
+++ b/src/gui/painting/qrgba64.h
@@ -127,6 +127,10 @@ public:
Q_DECL_RELAXED_CONSTEXPR QRgba64 premultiplied() const
{
+ if (isOpaque())
+ return *this;
+ if (isTransparent())
+ return QRgba64::fromRgba64(0);
const quint32 a = alpha();
const quint16 r = div_65535(red() * a);
const quint16 g = div_65535(green() * a);
diff --git a/src/gui/painting/qrgba64.qdoc b/src/gui/painting/qrgba64.qdoc
index 26c78a5dcd..3ee8a355e6 100644
--- a/src/gui/painting/qrgba64.qdoc
+++ b/src/gui/painting/qrgba64.qdoc
@@ -35,7 +35,7 @@
QRgba64 is a 64-bit data-structure containing four 16-bit color channels: Red, green, blue and alpha.
- QRgba64 can be used a replacement for QRgb when higher precision is needed. In particular a
+ QRgba64 can be used as a replacement for QRgb when higher precision is needed. In particular a
premultiplied QRgba64 can operate on unpremultiplied QRgb without loss of precision except
for alpha 0.
diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h
index adceda2210..b7e4d4d905 100644
--- a/src/gui/painting/qrgba64_p.h
+++ b/src/gui/painting/qrgba64_p.h
@@ -51,10 +51,11 @@
// We mean it.
//
+#include "qrgba64.h"
+#include "qdrawhelper_p.h"
+
+#include <QtCore/private/qsimd_p.h>
#include <QtGui/private/qtguiglobal_p.h>
-#include <QtGui/qrgba64.h>
-#include <QtGui/private/qdrawhelper_p.h>
-#include <private/qsimd_p.h>
QT_BEGIN_NAMESPACE
@@ -63,14 +64,6 @@ inline QRgba64 combineAlpha256(QRgba64 rgba64, uint alpha256)
return QRgba64::fromRgba64(rgba64.red(), rgba64.green(), rgba64.blue(), (rgba64.alpha() * alpha256) >> 8);
}
-inline QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
-{
- return QRgba64::fromRgba64((rgba64.red() * alpha256) >> 8,
- (rgba64.green() * alpha256) >> 8,
- (rgba64.blue() * alpha256) >> 8,
- (rgba64.alpha() * alpha256) >> 8);
-}
-
inline QRgba64 multiplyAlpha65535(QRgba64 rgba64, uint alpha65535)
{
return QRgba64::fromRgba64(qt_div_65535(rgba64.red() * alpha65535),
@@ -125,11 +118,6 @@ inline T multiplyAlpha255(T rgba64, uint alpha255)
#endif
}
-inline QRgba64 interpolate256(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
-{
- return QRgba64::fromRgba64(multiplyAlpha256(x, alpha1) + multiplyAlpha256(y, alpha2));
-}
-
inline QRgba64 interpolate255(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
{
return QRgba64::fromRgba64(multiplyAlpha255(x, alpha1) + multiplyAlpha255(y, alpha2));
@@ -159,7 +147,7 @@ Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, uint alpha1, __m128i y, uint
{
return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
}
-// alpha2 below is const-ref because otherwise MSVC2013 complains that it can't 16-byte align the argument.
+// alpha2 below is const-ref because otherwise MSVC2015 complains that it can't 16-byte align the argument.
Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, __m128i alpha1, __m128i y, const __m128i &alpha2)
{
return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
@@ -185,7 +173,8 @@ inline QRgba64 addWithSaturation(QRgba64 a, QRgba64 b)
qMin(a.alpha() + b.alpha(), 65535));
}
-#if defined __SSE2__
+#if QT_COMPILER_SUPPORTS_HERE(SSE2)
+QT_FUNCTION_TARGET(SSE2)
Q_ALWAYS_INLINE uint toArgb32(__m128i v)
{
v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
@@ -239,20 +228,6 @@ inline uint toRgba8888(QRgba64 rgba64)
#endif
}
-#if defined(__SSE2__)
-Q_ALWAYS_INLINE __m128i addWithSaturation(__m128i a, __m128i b)
-{
- return _mm_adds_epu16(a, b);
-}
-#endif
-
-#if defined(__ARM_NEON__)
-Q_ALWAYS_INLINE uint16x4_t addWithSaturation(uint16x4_t a, uint16x4_t b)
-{
- return vqmovn_u32(vaddl_u16(a, b));
-}
-#endif
-
inline QRgba64 rgbBlend(QRgba64 d, QRgba64 s, uint rgbAlpha)
{
QRgba64 blend;
diff --git a/src/gui/painting/qt_attribution.json b/src/gui/painting/qt_attribution.json
index 7eb7db4876..9d3debc1b9 100644
--- a/src/gui/painting/qt_attribution.json
+++ b/src/gui/painting/qt_attribution.json
@@ -27,5 +27,19 @@
"Copyright": "Copyright (C) 2004, 2005 Daniel M. Duley.
(C) Carsten Haitzler and various contributors.
(C) Willem Monsuwe <willem@stack.nl>"
+ },
+ {
+ "Id": "webgradients",
+ "Name": "WebGradients",
+ "QDocModule": "qtgui",
+ "QtUsage": "Used in Qt GUI to provide presets for QGradient.",
+ "Files": "webgradients.css",
+
+ "Description": "WebGradients is a free collection of 180 linear gradients.",
+ "Homepage": "https://webgradients.com/",
+ "License": "MIT License",
+ "LicenseId": "MIT",
+ "LicenseFile": "WEBGRADIENTS_LICENSE.txt",
+ "Copyright": "Copyright (c) 2017 itmeo"
}
]
diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp
index 6d57eba123..9be3eeaffd 100644
--- a/src/gui/painting/qtriangulator.cpp
+++ b/src/gui/painting/qtriangulator.cpp
@@ -472,7 +472,7 @@ class QInt64Set
{
public:
inline QInt64Set(int capacity = 64);
- inline ~QInt64Set() {if (m_array) delete[] m_array;}
+ inline ~QInt64Set() {delete[] m_array;}
inline bool isValid() const {return m_array;}
void insert(quint64 key);
bool contains(quint64 key) const;
@@ -493,10 +493,7 @@ inline QInt64Set::QInt64Set(int capacity)
{
m_capacity = primeForCount(capacity);
m_array = new quint64[m_capacity];
- if (m_array)
- clear();
- else
- m_capacity = 0;
+ clear();
}
bool QInt64Set::rehash(int capacity)
@@ -506,28 +503,19 @@ bool QInt64Set::rehash(int capacity)
m_capacity = capacity;
m_array = new quint64[m_capacity];
- if (m_array) {
- clear();
- if (oldArray) {
- for (int i = 0; i < oldCapacity; ++i) {
- if (oldArray[i] != UNUSED)
- insert(oldArray[i]);
- }
- delete[] oldArray;
- }
- return true;
- } else {
- m_capacity = oldCapacity;
- m_array = oldArray;
- return false;
+ clear();
+ for (int i = 0; i < oldCapacity; ++i) {
+ if (oldArray[i] != UNUSED)
+ insert(oldArray[i]);
}
+ delete[] oldArray;
+ return true;
}
void QInt64Set::insert(quint64 key)
{
if (m_count > 3 * m_capacity / 4)
rehash(primeForCount(2 * m_capacity));
- Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated.");
int index = int(key % m_capacity);
for (int i = 0; i < m_capacity; ++i) {
index += i;
@@ -546,7 +534,6 @@ void QInt64Set::insert(quint64 key)
bool QInt64Set::contains(quint64 key) const
{
- Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated.");
int index = int(key % m_capacity);
for (int i = 0; i < m_capacity; ++i) {
index += i;
@@ -562,7 +549,6 @@ bool QInt64Set::contains(quint64 key) const
inline void QInt64Set::clear()
{
- Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated.");
for (int i = 0; i < m_capacity; ++i)
m_array[i] = UNUSED;
m_count = 0;
diff --git a/src/gui/painting/webgradients.binaryjson b/src/gui/painting/webgradients.binaryjson
new file mode 100644
index 0000000000..75edd487be
--- /dev/null
+++ b/src/gui/painting/webgradients.binaryjson
Binary files differ
diff --git a/src/gui/painting/webgradients.css b/src/gui/painting/webgradients.css
new file mode 100644
index 0000000000..870866bb46
--- /dev/null
+++ b/src/gui/painting/webgradients.css
@@ -0,0 +1,909 @@
+/*001 Warm Flame*/
+.warm_flame{
+ background-image: linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%);
+}
+
+/*002 Night Fade*/
+.night_fade{
+ background-image: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%);
+}
+
+/*003 Spring Warmth*/
+.spring_warmth{
+ background-image: linear-gradient(to top, #fad0c4 0%, #fad0c4 1%, #ffd1ff 100%);
+}
+
+/*004 Juicy Peach*/
+.juicy_peach{
+ background-image: linear-gradient(to right, #ffecd2 0%, #fcb69f 100%);
+}
+
+/*005 Young Passion*/
+.young_passion{
+ background-image: linear-gradient(to right, #ff8177 0%, #ff867a 0%, #ff8c7f 21%, #f99185 52%, #cf556c 78%, #b12a5b 100%);
+}
+
+/*006 Lady Lips*/
+.lady_lips{
+ background-image: linear-gradient(to top, #ff9a9e 0%, #fecfef 99%, #fecfef 100%);
+}
+
+/*007 Sunny Morning*/
+.sunny_morning{
+ background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%);
+}
+
+/*008 Rainy Ashville*/
+.rainy_ashville{
+ background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%);
+}
+
+/*009 Frozen Dreams*/
+.frozen_dreams{
+ background-image: linear-gradient(to top, #fdcbf1 0%, #fdcbf1 1%, #e6dee9 100%);
+}
+
+/*010 Winter Neva*/
+.winter_neva{
+ background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
+}
+
+/*011 Dusty Grass*/
+.dusty_grass{
+ background-image: linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%);
+}
+
+/*012 Tempting Azure*/
+.tempting_azure{
+ background-image: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
+}
+
+/*013 Heavy Rain*/
+.heavy_rain{
+ background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
+}
+
+/*014 Amy Crisp*/
+.amy_crisp{
+ background-image: linear-gradient(120deg, #a6c0fe 0%, #f68084 100%);
+}
+
+/*015 Mean Fruit*/
+.mean_fruit{
+ background-image: linear-gradient(120deg, #fccb90 0%, #d57eeb 100%);
+}
+
+/*016 Deep Blue*/
+.deep_blue{
+ background-image: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%);
+}
+
+/*017 Ripe Malinka*/
+.ripe_malinka{
+ background-image: linear-gradient(120deg, #f093fb 0%, #f5576c 100%);
+}
+
+/*018 Cloudy Knoxville*/
+.cloudy_knoxville{
+ background-image: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
+}
+
+/*019 Malibu Beach*/
+.malibu_beach{
+ background-image: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
+}
+
+/*020 New Life*/
+.new_life{
+ background-image: linear-gradient(to right, #43e97b 0%, #38f9d7 100%);
+}
+
+/*021 True Sunset*/
+.true_sunset{
+ background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%);
+}
+
+/*022 Morpheus Den*/
+.morpheus_den{
+ background-image: linear-gradient(to top, #30cfd0 0%, #330867 100%);
+}
+
+/*023 Rare Wind*/
+.rare_wind{
+ background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
+}
+
+/*024 Near Moon*/
+.near_moon{
+ background-image: linear-gradient(to top, #5ee7df 0%, #b490ca 100%);
+}
+
+/*025 Wild Apple*/
+.wild_apple{
+ background-image: linear-gradient(to top, #d299c2 0%, #fef9d7 100%);
+}
+
+/*026 Saint Petersburg*/
+.saint_petersburg{
+ background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+}
+
+/*027 Arielle's Smile*/
+.arielles_smile{
+ background-image: radial-gradient(circle 248px at center, #16d9e3 0%, #30c7ec 47%, #46aef7 100%);
+}
+
+/*028 Plum Plate*/
+.plum_plate{
+ background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+/*029 Everlasting Sky*/
+.everlasting_sky{
+ background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
+}
+
+/*030 Happy Fisher*/
+.happy_fisher{
+ background-image: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%);
+}
+
+/*031 Blessing*/
+.blessing{
+ background-image: linear-gradient(to top, #fddb92 0%, #d1fdff 100%);
+}
+
+/*032 Sharpeye Eagle*/
+.sharpeye_eagle{
+ background-image: linear-gradient(to top, #9890e3 0%, #b1f4cf 100%);
+}
+
+/*033 Ladoga Bottom*/
+.ladoga_bottom{
+ background-image: linear-gradient(to top, #ebc0fd 0%, #d9ded8 100%);
+}
+
+/*034 Lemon Gate*/
+.lemon_gate{
+ background-image: linear-gradient(to top, #96fbc4 0%, #f9f586 100%);
+}
+
+/*035 Itmeo Branding*/
+.itmeo_branding{
+ background-image: linear-gradient(180deg, #2af598 0%, #009efd 100%);
+}
+
+/*036 Zeus Miracle*/
+.zeus_miracle{
+ background-image: linear-gradient(to top, #cd9cf2 0%, #f6f3ff 100%);
+}
+
+/*037 Old Hat*/
+.old_hat{
+ background-image: linear-gradient(to right, #e4afcb 0%, #b8cbb8 0%, #b8cbb8 0%, #e2c58b 30%, #c2ce9c 64%, #7edbdc 100%);
+}
+
+/*038 Star Wine*/
+.star_wine{
+ background-image: linear-gradient(to right, #b8cbb8 0%, #b8cbb8 0%, #b465da 0%, #cf6cc9 33%, #ee609c 66%, #ee609c 100%);
+}
+
+/*039 Deep Blue*/
+.deep_blue{
+ background-image: linear-gradient(to right, #6a11cb 0%, #2575fc 100%);
+}
+
+/*040 Coup de Grace*/
+.coup_de_grace{
+ background: #DCD9D4 linear-gradient(to bottom, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%), radial-gradient(at 50% 0%, rgba(255, 255, 255, 0.10) 0%, rgba(0, 0, 0, 0.50) 50%);
+ background-blend-mode: soft-light,screen;
+}
+
+/*041 Happy Acid*/
+.happy_acid{
+ background-image: linear-gradient(to top, #37ecba 0%, #72afd3 100%);
+}
+
+/*042 Awesome Pine*/
+.awesome_pine{
+ background-image: linear-gradient(to top, #ebbba7 0%, #cfc7f8 100%);
+}
+
+/*043 New York*/
+.new_york{
+ background-image: linear-gradient(to top, #fff1eb 0%, #ace0f9 100%);
+}
+
+/*044 Shy Rainbow*/
+.shy_rainbow{
+ background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%);
+}
+
+/*045 Loon Crest*/
+.loon_crest{
+ background: linear-gradient(to bottom, rgba(255,255,255,0.15) 0%, rgba(0,0,0,0.15) 100%), radial-gradient(at top center, rgba(255,255,255,0.40) 0%, rgba(0,0,0,0.40) 120%) #989898;
+ background-blend-mode: multiply,multiply;
+}
+
+/*046 Mixed Hopes*/
+.mixed_hopes{
+ background-image: linear-gradient(to top, #c471f5 0%, #fa71cd 100%);
+}
+
+/*047 Fly High*/
+.fly_high{
+ background-image: linear-gradient(to top, #48c6ef 0%, #6f86d6 100%);
+}
+
+/*048 Strong Bliss*/
+.strong_bliss{
+ background-image: linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%);
+}
+
+/*049 Fresh Milk*/
+.fresh_milk{
+ background-image: linear-gradient(to top, #feada6 0%, #f5efef 100%);
+}
+
+/*050 Snow Again*/
+.snow_again{
+ background-image: linear-gradient(to top, #e6e9f0 0%, #eef1f5 100%);
+}
+
+/*051 February Ink*/
+.february_ink{
+ background-image: linear-gradient(to top, #accbee 0%, #e7f0fd 100%);
+}
+
+/*052 Kind Steel*/
+.kind_steel{
+ background-image: linear-gradient(-20deg, #e9defa 0%, #fbfcdb 100%);
+}
+
+/*053 Soft Grass*/
+.soft_grass{
+ background-image: linear-gradient(to top, #c1dfc4 0%, #deecdd 100%);
+}
+
+/*054 Grown Early*/
+.grown_early{
+ background-image: linear-gradient(to top, #0ba360 0%, #3cba92 100%);
+}
+
+/*055 Sharp Blues*/
+.sharp_blues{
+ background-image: linear-gradient(to top, #00c6fb 0%, #005bea 100%);
+}
+
+/*056 Shady Water*/
+.shady_water{
+ background-image: linear-gradient(to right, #74ebd5 0%, #9face6 100%);
+}
+
+/*057 Dirty Beauty*/
+.dirty_beauty{
+ background-image: linear-gradient(to top, #6a85b6 0%, #bac8e0 100%);
+}
+
+/*058 Great Whale*/
+.great_whale{
+ background-image: linear-gradient(to top, #a3bded 0%, #6991c7 100%);
+}
+
+/*059 Teen Notebook*/
+.teen_notebook{
+ background-image: linear-gradient(to top, #9795f0 0%, #fbc8d4 100%);
+}
+
+/*060 Polite Rumors*/
+.polite_rumors{
+ background-image: linear-gradient(to top, #a7a6cb 0%, #8989ba 52%, #8989ba 100%);
+}
+
+/*061 Sweet Period*/
+.sweet_period{
+ background-image: linear-gradient(to top, #3f51b1 0%, #5a55ae 13%, #7b5fac 25%, #8f6aae 38%, #a86aa4 50%, #cc6b8e 62%, #f18271 75%, #f3a469 87%, #f7c978 100%);
+}
+
+/*062 Wide Matrix*/
+.wide_matrix{
+ background-image: linear-gradient(to top, #fcc5e4 0%, #fda34b 15%, #ff7882 35%, #c8699e 52%, #7046aa 71%, #0c1db8 87%, #020f75 100%);
+}
+
+/*063 Soft Cherish*/
+.soft_cherish{
+ background-image: linear-gradient(to top, #dbdcd7 0%, #dddcd7 24%, #e2c9cc 30%, #e7627d 46%, #b8235a 59%, #801357 71%, #3d1635 84%, #1c1a27 100%);
+}
+
+/*064 Red Salvation*/
+.red_salvation{
+ background-image: linear-gradient(to top, #f43b47 0%, #453a94 100%);
+}
+
+/*065 Burning Spring*/
+.burning_spring{
+ background-image: linear-gradient(to top, #4fb576 0%, #44c489 30%, #28a9ae 46%, #28a2b7 59%, #4c7788 71%, #6c4f63 86%, #432c39 100%);
+}
+
+/*066 Night Party*/
+.night_party{
+ background-image: linear-gradient(to top, #0250c5 0%, #d43f8d 100%);
+}
+
+/*067 Sky Glider*/
+.sky_glider{
+ background-image: linear-gradient(to top, #88d3ce 0%, #6e45e2 100%);
+}
+
+/*068 Heaven Peach*/
+.heaven_peach{
+ background-image: linear-gradient(to top, #d9afd9 0%, #97d9e1 100%);
+}
+
+/*069 Purple Division*/
+.purple_division{
+ background-image: linear-gradient(to top, #7028e4 0%, #e5b2ca 100%);
+}
+
+/*070 Aqua Splash*/
+.aqua_splash{
+ background-image: linear-gradient(15deg, #13547a 0%, #80d0c7 100%);
+}
+
+/*071 Above Clouds*/
+.above_clouds{
+ background-image: linear-gradient(to left, #BDBBBE 0%, #9D9EA3 100%), radial-gradient(88% 271%, rgba(255, 255, 255, 0.25) 0%, rgba(254, 254, 254, 0.25) 1%, rgba(0, 0, 0, 0.25) 100%), radial-gradient(50% 100%, rgba(255, 255, 255, 0.30) 0%, rgba(0, 0, 0, 0.30) 100%);
+ background-blend-mode: normal, lighten, soft-light;
+}
+
+/*072 Spiky Naga*/
+.spiky_naga{
+ background-image: linear-gradient(to top, #505285 0%, #585e92 12%, #65689f 25%, #7474b0 37%, #7e7ebb 50%, #8389c7 62%, #9795d4 75%, #a2a1dc 87%, #b5aee4 100%);
+}
+
+/*073 Love Kiss*/
+.love_kiss{
+ background-image: linear-gradient(to top, #ff0844 0%, #ffb199 100%);
+}
+
+/*074 Sharp Glass*/
+.sharp_glass{
+ background: #C9CCD3 linear-gradient(-180deg, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%);
+ background-blend-mode: lighten;
+}
+
+/*075 Clean Mirror*/
+.clean_mirror{
+ background-image: linear-gradient(45deg, #93a5cf 0%, #e4efe9 100%);
+}
+
+/*076 Premium Dark*/
+.premium_dark{
+ background-image: linear-gradient(to right, #434343 0%, black 100%);
+}
+
+/*077 Cold Evening*/
+.cold_evening{
+ background-image: linear-gradient(to top, #0c3483 0%, #a2b6df 100%, #6b8cce 100%, #a2b6df 100%);
+}
+
+/*078 Cochiti Lake*/
+.cochiti_lake{
+ background-image: linear-gradient(45deg, #93a5cf 0%, #e4efe9 100%);
+}
+
+/*079 Summer Games*/
+.summer_games{
+ background-image: linear-gradient(to right, #92fe9d 0%, #00c9ff 100%);
+}
+
+/*080 Passionate Bed*/
+.passionate_bed{
+ background-image: linear-gradient(to right, #ff758c 0%, #ff7eb3 100%);
+}
+
+/*081 Mountain Rock*/
+.mountain_rock{
+ background-image: linear-gradient(to right, #868f96 0%, #596164 100%);
+}
+
+/*082 Desert Hump*/
+.desert_hump{
+ background-image: linear-gradient(to top, #c79081 0%, #dfa579 100%);
+}
+
+/*083 Jungle Day*/
+.jungle_day{
+ background-image: linear-gradient(45deg, #8baaaa 0%, #ae8b9c 100%);
+}
+
+/*084 Phoenix Start*/
+.phoenix_start{
+ background-image: linear-gradient(to right, #f83600 0%, #f9d423 100%);
+}
+
+/*085 October Silence*/
+.october_silence{
+ background-image: linear-gradient(-20deg, #b721ff 0%, #21d4fd 100%);
+}
+
+/*086 Faraway River*/
+.faraway_river{
+ background-image: linear-gradient(-20deg, #6e45e2 0%, #88d3ce 100%);
+}
+
+/*087 Alchemist Lab*/
+.alchemist_lab{
+ background-image: linear-gradient(-20deg, #d558c8 0%, #24d292 100%);
+}
+
+/*088 Over Sun*/
+.over_sun{
+ background-image: linear-gradient(60deg, #abecd6 0%, #fbed96 100%);
+}
+
+/*089 Premium White*/
+.premium_white{
+ background-image: linear-gradient(to top, #d5d4d0 0%, #d5d4d0 1%, #eeeeec 31%, #efeeec 75%, #e9e9e7 100%);
+}
+
+/*090 Mars Party*/
+.mars_party{
+ background-image: linear-gradient(to top, #5f72bd 0%, #9b23ea 100%);
+}
+
+/*091 Eternal Constance*/
+.eternal_constance{
+ background-image: linear-gradient(to top, #09203f 0%, #537895 100%);
+}
+
+/*092 Japan Blush*/
+.japan_blush{
+ background-image: linear-gradient(-20deg, #ddd6f3 0%, #faaca8 100%, #faaca8 100%);
+}
+
+/*093 Smiling Rain*/
+.smiling_rain{
+ background-image: linear-gradient(-20deg, #dcb0ed 0%, #99c99c 100%);
+}
+
+/*094 Cloudy Apple*/
+.cloudy_apple{
+ background-image: linear-gradient(to top, #f3e7e9 0%, #e3eeff 99%, #e3eeff 100%);
+}
+
+/*095 Big Mango*/
+.big_mango{
+ background-image: linear-gradient(to top, #c71d6f 0%, #d09693 100%);
+}
+
+/*096 Healthy Water*/
+.healthy_water{
+ background-image: linear-gradient(60deg, #96deda 0%, #50c9c3 100%);
+}
+
+/*097 Amour Amour*/
+.amour_amour{
+ background-image: linear-gradient(to top, #f77062 0%, #fe5196 100%);
+}
+
+/*098 Risky Concrete*/
+.risky_concrete{
+ background-image: linear-gradient(to top, #c4c5c7 0%, #dcdddf 52%, #ebebeb 100%);
+}
+
+/*099 Strong Stick*/
+.strong_stick{
+ background-image: linear-gradient(to right, #a8caba 0%, #5d4157 100%);
+}
+
+/*100 Vicious Stance*/
+.vicious_stance{
+ background-image: linear-gradient(60deg, #29323c 0%, #485563 100%);
+}
+
+/*101 Palo Alto*/
+.palo_alto{
+ background-image: linear-gradient(-60deg, #16a085 0%, #f4d03f 100%);
+}
+
+/*102 Happy Memories*/
+.happy_memories{
+ background-image: linear-gradient(-60deg, #ff5858 0%, #f09819 100%);
+}
+
+/*103 Midnight Bloom*/
+.midnight_bloom{
+ background-image: linear-gradient(-20deg, #2b5876 0%, #4e4376 100%);
+}
+
+/*104 Crystalline*/
+.crystalline{
+ background-image: linear-gradient(-20deg, #00cdac 0%, #8ddad5 100%);
+}
+
+/*105 Raccoon Back*/
+.raccoon_back{
+ background: linear-gradient(-180deg, #BCC5CE 0%, #929EAD 98%), radial-gradient(at top left, rgba(255,255,255,0.30) 0%, rgba(0,0,0,0.30) 100%);
+ background-blend-mode: screen;
+}
+
+/*106 Party Bliss*/
+.party_bliss{
+ background-image: linear-gradient(to top, #4481eb 0%, #04befe 100%);
+}
+
+/*107 Confident Cloud*/
+.confident_cloud{
+ background-image: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%);
+}
+
+/*108 Le Cocktail*/
+.le_cocktail{
+ background-image: linear-gradient(45deg, #874da2 0%, #c43a30 100%);
+}
+
+/*109 River City*/
+.river_city{
+ background-image: linear-gradient(to top, #4481eb 0%, #04befe 100%);
+}
+
+/*110 Frozen Berry*/
+.frozen_berry{
+ background-image: linear-gradient(to top, #e8198b 0%, #c7eafd 100%);
+}
+
+/*111 Elegance*/
+.elegance{
+ background-image: radial-gradient(73% 147%, #EADFDF 59%, #ECE2DF 100%), radial-gradient(91% 146%, rgba(255,255,255,0.50) 47%, rgba(0,0,0,0.50) 100%);
+ background-blend-mode: screen;
+}
+
+/*112 Child Care*/
+.child_care{
+ background-image: linear-gradient(-20deg, #f794a4 0%, #fdd6bd 100%);
+}
+
+/*113 Flying Lemon*/
+.flying_lemon{
+ background-image: linear-gradient(60deg, #64b3f4 0%, #c2e59c 100%);
+}
+
+/*114 New Retrowave*/
+.new_retrowave{
+ background-image: linear-gradient(to top, #3b41c5 0%, #a981bb 49%, #ffc8a9 100%);
+}
+
+/*115 Hidden Jaguar*/
+.hidden_jaguar{
+ background-image: linear-gradient(to top, #0fd850 0%, #f9f047 100%);
+}
+
+/*116 Above The Sky*/
+.above_the_sky{
+ background-image: linear-gradient(to top, lightgrey 0%, lightgrey 1%, #e0e0e0 26%, #efefef 48%, #d9d9d9 75%, #bcbcbc 100%);
+}
+
+/*117 Nega*/
+.nega{
+ background-image: linear-gradient(45deg, #ee9ca7 0%, #ffdde1 100%);
+}
+
+/*118 Dense Water*/
+.dense_water{
+ background-image: linear-gradient(to right, #3ab5b0 0%, #3d99be 31%, #56317a 100%);
+}
+
+/*119 Chemic Aqua*/
+.chemic_aqua{
+ background: #CDDCDC radial-gradient(at 50% 100%, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%), linear-gradient(to bottom, rgba(255, 255, 255, 0.25) 0%, rgba(0, 0, 0, 0.25) 100%);
+ background-blend-mode: screen, overlay;
+}
+
+/*120 Seashore*/
+.seashore{
+ background-image: linear-gradient(to top, #209cff 0%, #68e0cf 100%);
+}
+
+/*121 Marble Wall*/
+.marble_wall{
+ background-image: linear-gradient(to top, #bdc2e8 0%, #bdc2e8 1%, #e6dee9 100%);
+}
+
+/*122 Cheerful Caramel*/
+.cheerful_caramel{
+ background-image: linear-gradient(to top, #e6b980 0%, #eacda3 100%);
+}
+
+/*123 Night Sky*/
+.night_sky{
+ background-image: linear-gradient(to top, #1e3c72 0%, #1e3c72 1%, #2a5298 100%);
+}
+
+/*124 Magic Lake*/
+.magic_lake{
+ background-image: linear-gradient(to top, #d5dee7 0%, #ffafbd 0%, #c9ffbf 100%);
+}
+
+/*125 Young Grass*/
+.young_grass{
+ background-image: linear-gradient(to top, #9be15d 0%, #00e3ae 100%);
+}
+
+/*126 Colorful Peach*/
+.colorful_peach{
+ background-image: linear-gradient(to right, #ed6ea0 0%, #ec8c69 100%);
+}
+
+/*127 Gentle Care*/
+.gentle_care{
+ background-image: linear-gradient(to right, #ffc3a0 0%, #ffafbd 100%);
+}
+
+/*128 Plum Bath*/
+.plum_bath{
+ background-image: linear-gradient(to top, #cc208e 0%, #6713d2 100%);
+}
+
+/*129 Happy Unicorn*/
+.happy_unicorn{
+ background-image: linear-gradient(to top, #b3ffab 0%, #12fff7 100%);
+}
+
+/*130 Full Metal*/
+.full_metal{
+ background: linear-gradient(to bottom, #D5DEE7 0%, #E8EBF2 50%, #E2E7ED 100%), linear-gradient(to bottom, rgba(0,0,0,0.02) 50%, rgba(255,255,255,0.02) 61%, rgba(0,0,0,0.02) 73%), linear-gradient(33deg, rgba(255,255,255,0.20) 0%, rgba(0,0,0,0.20) 100%);
+ background-blend-mode: normal,color-burn;
+}
+
+/*131 African Field*/
+.african_field{
+ background-image: linear-gradient(to top, #65bd60 0%, #5ac1a8 25%, #3ec6ed 50%, #b7ddb7 75%, #fef381 100%);
+}
+
+/*132 Solid Stone*/
+.solid_stone{
+ background-image: linear-gradient(to right, #243949 0%, #517fa4 100%);
+}
+
+/*133 Orange Juice*/
+.orange_juice{
+ background-image: linear-gradient(-20deg, #fc6076 0%, #ff9a44 100%);
+}
+
+/*134 Glass Water*/
+.glass_water{
+ background-image: linear-gradient(to top, #dfe9f3 0%, white 100%);
+}
+
+/*135 Slick Carbon*/
+.slick_carbon{
+ background: linear-gradient(to bottom, #323232 0%, #3F3F3F 40%, #1C1C1C 150%), linear-gradient(to top, rgba(255,255,255,0.40) 0%, rgba(0,0,0,0.25) 200%);
+ background-blend-mode: multiply;
+}
+
+/*136 North Miracle*/
+.north_miracle{
+ background-image: linear-gradient(to right, #00dbde 0%, #fc00ff 100%);
+}
+
+/*137 Fruit Blend*/
+.fruit_blend{
+ background-image: linear-gradient(to right, #f9d423 0%, #ff4e50 100%);
+}
+
+/*138 Millennium Pine*/
+.millennium_pine{
+ background-image: linear-gradient(to top, #50cc7f 0%, #f5d100 100%);
+}
+
+/*139 High Flight*/
+.high_flight{
+ background-image: linear-gradient(to right, #0acffe 0%, #495aff 100%);
+}
+
+/*140 Mole Hall*/
+.mole_hall{
+ background-image: linear-gradient(-20deg, #616161 0%, #9bc5c3 100%);
+}
+
+/*141 Earl Gray*/
+.earl_gray{
+ background: #E4E4E1 radial-gradient(at top center, rgba(255, 255, 255, 0.03) 0%, rgba(0, 0, 0, 0.03) 100%), linear-gradient(to top, rgba(255, 255, 255, 0.1) 0%, rgba(143, 152, 157, 0.60) 100%);
+ background-blend-mode: normal, multiply;
+}
+
+/*142 Space Shift*/
+.space_shift{
+ background-image: linear-gradient(60deg, #3d3393 0%, #2b76b9 37%, #2cacd1 65%, #35eb93 100%);
+}
+
+/*143 Forest Inei*/
+.forest_inei{
+ background-image: linear-gradient(to top, #df89b5 0%, #bfd9fe 100%);
+}
+
+/*144 Royal Garden*/
+.royal_garden{
+ background-image: linear-gradient(to right, #ed6ea0 0%, #ec8c69 100%);
+}
+
+/*145 Rich Metal*/
+.rich_metal{
+ background-image: linear-gradient(to right, #d7d2cc 0%, #304352 100%);
+}
+
+/*146 Juicy Cake*/
+.juicy_cake{
+ background-image: linear-gradient(to top, #e14fad 0%, #f9d423 100%);
+}
+
+/*147 Smart Indigo*/
+.smart_indigo{
+ background-image: linear-gradient(to top, #b224ef 0%, #7579ff 100%);
+}
+
+/*148 Sand Strike*/
+.sand_strike{
+ background-image: linear-gradient(to right, #c1c161 0%, #c1c161 0%, #d4d4b1 100%);
+}
+
+/*149 Norse Beauty*/
+.norse_beauty{
+ background-image: linear-gradient(to right, #ec77ab 0%, #7873f5 100%);
+}
+
+/*150 Aqua Guidance*/
+.aqua_guidance{
+ background-image: linear-gradient(to top, #007adf 0%, #00ecbc 100%);
+}
+
+/*151 Sun Veggie*/
+.sun_veggie{
+ background-image: linear-gradient(-225deg, #20E2D7 0%, #F9FEA5 100%);
+}
+
+/*152 Sea Lord*/
+.sea_lord{
+ background-image: linear-gradient(-225deg, #2CD8D5 0%, #C5C1FF 56%, #FFBAC3 100%);
+}
+
+/*153 Black Sea*/
+.black_sea{
+ background-image: linear-gradient(-225deg, #2CD8D5 0%, #6B8DD6 48%, #8E37D7 100%);
+}
+
+/*154 Grass Shampoo*/
+.grass_shampoo{
+ background-image: linear-gradient(-225deg, #DFFFCD 0%, #90F9C4 48%, #39F3BB 100%);
+}
+
+/*155 Landing Aircraft*/
+.landing_aircraft{
+ background-image: linear-gradient(-225deg, #5D9FFF 0%, #B8DCFF 48%, #6BBBFF 100%);
+}
+
+/*156 Witch Dance*/
+.witch_dance{
+ background-image: linear-gradient(-225deg, #A8BFFF 0%, #884D80 100%);
+}
+
+/*157 Sleepless Night*/
+.sleepless_night{
+ background-image: linear-gradient(-225deg, #5271C4 0%, #B19FFF 48%, #ECA1FE 100%);
+}
+
+/*158 Angel Care*/
+.angel_care{
+ background-image: linear-gradient(-225deg, #FFE29F 0%, #FFA99F 48%, #FF719A 100%);
+}
+
+/*159 Crystal River*/
+.crystal_river{
+ background-image: linear-gradient(-225deg, #22E1FF 0%, #1D8FE1 48%, #625EB1 100%);
+}
+
+/*160 Soft Lipstick*/
+.soft_lipstick{
+ background-image: linear-gradient(-225deg, #B6CEE8 0%, #F578DC 100%);
+}
+
+/*161 Salt Mountain*/
+.salt_mountain{
+ background-image: linear-gradient(-225deg, #FFFEFF 0%, #D7FFFE 100%);
+}
+
+/*162 Perfect White*/
+.perfect_white{
+ background-image: linear-gradient(-225deg, #E3FDF5 0%, #FFE6FA 100%);
+}
+
+/*163 Fresh Oasis*/
+.fresh_oasis{
+ background-image: linear-gradient(-225deg, #7DE2FC 0%, #B9B6E5 100%);
+}
+
+/*164 Strict November*/
+.strict_november{
+ background-image: linear-gradient(-225deg, #CBBACC 0%, #2580B3 100%);
+}
+
+/*165 Morning Salad*/
+.morning_salad{
+ background-image: linear-gradient(-225deg, #B7F8DB 0%, #50A7C2 100%);
+}
+
+/*166 Deep Relief*/
+.deep_relief{
+ background-image: linear-gradient(-225deg, #7085B6 0%, #87A7D9 50%, #DEF3F8 100%);
+}
+
+/*167 Sea Strike*/
+.sea_strike{
+ background-image: linear-gradient(-225deg, #77FFD2 0%, #6297DB 48%, #1EECFF 100%);
+}
+
+/*168 Night Call*/
+.night_call{
+ background-image: linear-gradient(-225deg, #AC32E4 0%, #7918F2 48%, #4801FF 100%);
+}
+
+/*169 Supreme Sky*/
+.supreme_sky{
+ background-image: linear-gradient(-225deg, #D4FFEC 0%, #57F2CC 48%, #4596FB 100%);
+}
+
+/*170 Light Blue*/
+.light_blue{
+ background-image: linear-gradient(-225deg, #9EFBD3 0%, #57E9F2 48%, #45D4FB 100%);
+}
+
+/*171 Mind Crawl*/
+.mind_crawl{
+ background-image: linear-gradient(-225deg, #473B7B 0%, #3584A7 51%, #30D2BE 100%);
+}
+
+/*172 Lily Meadow*/
+.lily_meadow{
+ background-image: linear-gradient(-225deg, #65379B 0%, #886AEA 53%, #6457C6 100%);
+}
+
+/*173 Sugar Lollipop*/
+.sugar_lollipop{
+ background-image: linear-gradient(-225deg, #A445B2 0%, #D41872 52%, #FF0066 100%);
+}
+
+/*174 Sweet Dessert*/
+.sweet_dessert{
+ background-image: linear-gradient(-225deg, #7742B2 0%, #F180FF 52%, #FD8BD9 100%);
+}
+
+/*175 Magic Ray*/
+.magic_ray{
+ background-image: linear-gradient(-225deg, #FF3CAC 0%, #562B7C 52%, #2B86C5 100%);
+}
+
+/*176 Teen Party*/
+.teen_party{
+ background-image: linear-gradient(-225deg, #FF057C 0%, #8D0B93 50%, #321575 100%);
+}
+
+/*177 Frozen Heat*/
+.frozen_heat{
+ background-image: linear-gradient(-225deg, #FF057C 0%, #7C64D5 48%, #4CC3FF 100%);
+}
+
+/*178 Gagarin View*/
+.gagarin_view{
+ background-image: linear-gradient(-225deg, #69EACB 0%, #EACCF8 48%, #6654F1 100%);
+}
+
+/*179 Fabled Sunset*/
+.fabled_sunset{
+ background-image: linear-gradient(-225deg, #231557 0%, #44107A 29%, #FF1361 67%, #FFF800 100%);
+}
+
+/*180 Perfect Blue*/
+.perfect_blue{
+ background-image: linear-gradient(-225deg, #3D4E81 0%, #5753C9 48%, #6E7FF3 100%);
+}
diff --git a/src/gui/qtgui.tracepoints b/src/gui/qtgui.tracepoints
index aa8a8ede57..aed6c35c03 100644
--- a/src/gui/qtgui.tracepoints
+++ b/src/gui/qtgui.tracepoints
@@ -1,8 +1,13 @@
-qfontdatabase_addapplicationfont(const QString &filename)
-qfontdatabase_load(const QString &family, int pointSize)
-qfontdatabase_loadengine(const QString &family, int pointSize)
-qfontdatabaseprivate_addappfont(const QString &fileName)
-qguiapplicationprivate_init_entry()
-qguiapplicationprivate_init_exit()
-qguiapplicationprivate_processwsevents_entry(int type)
-qguiapplicationprivate_processwsevents_exit(int type)
+QGuiApplicationPrivate_init_entry()
+QGuiApplicationPrivate_init_exit()
+
+QGuiApplicationPrivate_processWindowSystemEvent_entry(int type)
+QGuiApplicationPrivate_processWindowSystemEvent_exit(int type)
+
+QFontDatabase_addApplicationFont(const QString &filename)
+QFontDatabase_load(const QString &family, int pointSize)
+QFontDatabase_loadEngine(const QString &family, int pointSize)
+QFontDatabasePrivate_addAppFont(const QString &fileName)
+
+QImageReader_read_before_reading(QImageReader *reader, const QString &filename)
+QImageReader_read_after_reading(QImageReader *reader, bool result)
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index 64adeaa260..325fd26a31 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -345,13 +345,6 @@ static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
{ "none", StyleFeature_None }
};
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-static bool operator<(const QCssKnownValue &prop1, const QCssKnownValue &prop2)
-{
- return QString::compare(QString::fromLatin1(prop1.name), QLatin1String(prop2.name), Qt::CaseInsensitive) < 0;
-}
-#endif
-
static bool operator<(const QString &name, const QCssKnownValue &prop)
{
return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
@@ -2714,8 +2707,10 @@ bool Parser::parseFunction(QString *name, QString *args)
{
*name = lexem();
name->chop(1);
+ // until(RPAREN) needs FUNCTION token at index-1 to work properly
+ int start = index;
skipSpace();
- const int start = index;
+ std::swap(start, index);
if (!until(RPAREN)) return false;
for (int i = start; i < index - 1; ++i)
args->append(symbols.at(i).lexem());
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index f7462b65c8..580a09427c 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -2703,18 +2703,6 @@ static const int slow_timeout = 300000; // 5m
const uint QFontCache::min_cost = 4*1024; // 4mb
-#ifdef QT_NO_THREAD
-Q_GLOBAL_STATIC(QFontCache, theFontCache)
-
-QFontCache *QFontCache::instance()
-{
- return theFontCache();
-}
-
-void QFontCache::cleanup()
-{
-}
-#else
Q_GLOBAL_STATIC(QThreadStorage<QFontCache *>, theFontCache)
QFontCache *QFontCache::instance()
@@ -2736,7 +2724,6 @@ void QFontCache::cleanup()
if (cache && cache->hasLocalData())
cache->setLocalData(0);
}
-#endif // QT_NO_THREAD
QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(1);
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index a4e5130937..196eebb353 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -689,7 +689,6 @@ static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDe
if (!multi)
fontDef->style = desc.style->key.style;
fontDef->fixedPitch = desc.family->fixedPitch;
- fontDef->stretch = desc.style->key.stretch;
fontDef->ignorePitch = false;
}
@@ -1010,7 +1009,7 @@ QFontEngine *loadEngine(int script, const QFontDef &request,
QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size);
if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) {
- Q_TRACE(qfontdatabase_loadengine, request.family, request.pointSize);
+ Q_TRACE(QFontDatabase_loadEngine, request.family, request.pointSize);
QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script));
@@ -2442,7 +2441,7 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &
font.data = fontData;
font.fileName = fileName;
- Q_TRACE(qfontdatabaseprivate_addappfont, fileName);
+ Q_TRACE(QFontDatabasePrivate_addAppFont, fileName);
int i;
for (i = 0; i < applicationFonts.count(); ++i)
@@ -2500,7 +2499,7 @@ int QFontDatabase::addApplicationFont(const QString &fileName)
if (!f.open(QIODevice::ReadOnly))
return -1;
- Q_TRACE(qfontdatabase_addapplicationfont, fileName);
+ Q_TRACE(QFontDatabase_addApplicationFont, fileName);
data = f.readAll();
}
@@ -2800,7 +2799,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
QFontEngine *fe = nullptr;
- Q_TRACE(qfontdatabase_load, req.family, req.pointSize);
+ Q_TRACE(QFontDatabase_load, req.family, req.pointSize);
req.fallBackFamilies = fallBackFamilies;
if (!req.fallBackFamilies.isEmpty())
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 80fd288c04..9b0b0ec0d5 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -1327,7 +1327,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
resolveTable:
*isSymbolFont = (symbolTable > -1);
- quint32 unicode_table;
+ quint32 unicode_table = 0;
if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
return 0;
diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp
index 2f90754274..0026e3edfb 100644
--- a/src/gui/text/qstatictext.cpp
+++ b/src/gui/text/qstatictext.cpp
@@ -39,6 +39,7 @@
#include "qstatictext.h"
#include "qstatictext_p.h"
+#include <qmath.h>
#include <private/qtextengine_p.h>
#include <private/qfontengine_p.h>
#include <qabstracttextdocumentlayout.h>
@@ -611,22 +612,22 @@ void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p,
textLayout.setTextOption(textOption);
textLayout.setCacheEnabled(true);
- qreal leading = QFontMetricsF(font).leading();
- qreal height = -leading;
-
+ qreal height = 0;
textLayout.beginLayout();
while (1) {
QTextLine line = textLayout.createLine();
if (!line.isValid())
break;
+ line.setLeadingIncluded(true);
if (textWidth >= 0.0)
line.setLineWidth(textWidth);
else
line.setLineWidth(QFIXED_MAX);
- height += leading;
line.setPosition(QPointF(0.0, height));
height += line.height();
+ if (line.leading() < 0)
+ height += qCeil(line.leading());
}
textLayout.endLayout();
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index f32c31d18e..af8fcf369c 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -80,7 +80,8 @@ QTextCursorPrivate::QTextCursorPrivate(const QTextCursorPrivate &rhs)
visualNavigation = rhs.visualNavigation;
keepPositionOnInsert = rhs.keepPositionOnInsert;
changed = rhs.changed;
- priv->addCursor(this);
+ if (priv != nullptr)
+ priv->addCursor(this);
}
QTextCursorPrivate::~QTextCursorPrivate()
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 613ac8fa6c..6ca4bc8209 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -77,6 +77,18 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n);
+
+/*!
+ Returns \c true if the string \a text is likely to be rich text;
+ otherwise returns \c false.
+
+ This function uses a fast and therefore simple heuristic. It
+ mainly checks whether there is something that looks like a tag
+ before the first line break. Although the result may be correct
+ for common cases, there is no guarantee.
+
+ This function is defined in the \c <QTextDocument> header file.
+*/
bool Qt::mightBeRichText(const QString& text)
{
if (text.isEmpty())
@@ -135,6 +147,16 @@ bool Qt::mightBeRichText(const QString& text)
return false;
}
+/*!
+ Converts the plain text string \a plain to an HTML-formatted
+ paragraph while preserving most of its look.
+
+ \a mode defines how whitespace is handled.
+
+ This function is defined in the \c <QTextDocument> header file.
+
+ \sa escape(), mightBeRichText()
+*/
QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
{
int col = 0;
@@ -183,6 +205,12 @@ QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
return rich;
}
+/*!
+ \fn QTextCodec *Qt::codecForHtml(const QByteArray &ba)
+ \internal
+
+ This function is defined in the \c <QTextDocument> header file.
+*/
#if QT_CONFIG(textcodec)
QTextCodec *Qt::codecForHtml(const QByteArray &ba)
{
@@ -1991,7 +2019,6 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
int fromPage = pd->fromPage;
int toPage = pd->toPage;
- bool ascending = true;
if (fromPage == 0 && toPage == 0) {
fromPage = 1;
@@ -2007,6 +2034,7 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
return;
}
+// bool ascending = true;
// if (printer->pageOrder() == QPrinter::LastPageFirst) {
// int tmp = fromPage;
// fromPage = toPage;
@@ -2020,12 +2048,7 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
if (page == toPage)
break;
-
- if (ascending)
- ++page;
- else
- --page;
-
+ ++page;
if (!printer->newPage())
return;
}
@@ -2910,7 +2933,11 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)
html += QLatin1Char('>');
html += QLatin1String("<pre");
} else if (!list) {
- html += QLatin1String("<p");
+ int headingLevel = blockFormat.headingLevel();
+ if (headingLevel > 0 && headingLevel <= 6)
+ html += QLatin1String("<h") + QString::number(headingLevel);
+ else
+ html += QLatin1String("<p");
}
emitBlockAttributes(block);
@@ -2933,8 +2960,13 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)
html += QLatin1String("</pre>");
else if (list)
html += QLatin1String("</li>");
- else
- html += QLatin1String("</p>");
+ else {
+ int headingLevel = blockFormat.headingLevel();
+ if (headingLevel > 0 && headingLevel <= 6)
+ html += QLatin1String("</h") + QString::number(headingLevel) + QLatin1Char('>');
+ else
+ html += QLatin1String("</p>");
+ }
if (list) {
if (list->itemNumber(block) == list->count() - 1) { // last item? close list
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index 140ed628b7..c9b22e053b 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -70,17 +70,15 @@ class QTextCursor;
template<typename T> class QVector;
-#ifndef Q_CLANG_QDOC
namespace Qt
{
Q_GUI_EXPORT bool mightBeRichText(const QString&);
Q_GUI_EXPORT QString convertFromPlainText(const QString &plain, WhiteSpaceMode mode = WhiteSpacePre);
-#if QT_CONFIG(textcodec)
+#if QT_CONFIG(textcodec) || defined(Q_CLANG_QDOC)
Q_GUI_EXPORT QTextCodec *codecForHtml(const QByteArray &ba);
#endif
}
-#endif
class Q_GUI_EXPORT QAbstractUndoItem
{
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
index 0c2cb6f87f..e7eaa54a45 100644
--- a/src/gui/text/qtextdocumentfragment.cpp
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -419,7 +419,7 @@ static QTextListFormat::Style nextListStyle(QTextListFormat::Style style)
}
QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider)
- : indent(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode)
+ : indent(0), headingLevel(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode)
{
cursor = QTextCursor(doc);
wsm = QTextHtmlParserNode::WhiteSpaceNormal;
@@ -723,9 +723,9 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
cursor.insertImage(fmt, QTextFrameFormat::Position(currentNode->cssFloat));
- cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
cursor.mergeCharFormat(currentNode->charFormat);
- cursor.movePosition(QTextCursor::Right);
+ cursor.movePosition(QTextCursor::NextCharacter);
compressNextWhitespace = CollapseWhiteSpace;
hasBlock = false;
@@ -746,8 +746,28 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
return ContinueWithNextNode;
}
+ case Html_h1:
+ headingLevel = 1;
+ break;
+ case Html_h2:
+ headingLevel = 2;
+ break;
+ case Html_h3:
+ headingLevel = 3;
+ break;
+ case Html_h4:
+ headingLevel = 4;
+ break;
+ case Html_h5:
+ headingLevel = 5;
+ break;
+ case Html_h6:
+ headingLevel = 6;
+ break;
+
default: break;
}
+
return ContinueWithCurrentNode;
}
@@ -831,6 +851,15 @@ bool QTextHtmlImporter::closeTag()
}
}
break;
+ case Html_h1:
+ case Html_h2:
+ case Html_h3:
+ case Html_h4:
+ case Html_h5:
+ case Html_h6:
+ headingLevel = 0;
+ blockTagClosed = true;
+ break;
default:
if (closedNode->isBlock())
blockTagClosed = true;
@@ -1092,6 +1121,11 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode()
modifiedBlockFormat = true;
}
+ if (headingLevel) {
+ block.setHeadingLevel(headingLevel);
+ modifiedBlockFormat = true;
+ }
+
if (currentNode->blockFormat.propertyCount() > 0) {
modifiedBlockFormat = true;
block.merge(currentNode->blockFormat);
diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h
index e8699545f7..02a6a429fa 100644
--- a/src/gui/text/qtextdocumentfragment_p.h
+++ b/src/gui/text/qtextdocumentfragment_p.h
@@ -152,6 +152,7 @@ private:
friend class QTypeInfo<List>;
QVector<List> lists;
int indent;
+ int headingLevel;
// insert a named anchor the next time we emit a char format,
// either in a block or in regular text
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index 2957b8d5c0..a9227f0171 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -399,26 +399,6 @@ static bool operator<(const QCheckPoint &checkPoint, int pos)
return checkPoint.positionInFrame < pos;
}
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-//The STL implementation of MSVC 2008 requires the definitions
-
-static bool operator<(const QCheckPoint &checkPoint1, const QCheckPoint &checkPoint2)
-{
- return checkPoint1.y < checkPoint2.y;
-}
-
-static bool operator<(QFixed y, const QCheckPoint &checkPoint)
-{
- return y < checkPoint.y;
-}
-
-static bool operator<(int pos, const QCheckPoint &checkPoint)
-{
- return pos < checkPoint.positionInFrame;
-}
-
-#endif
-
static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, const QPointF &origin, const QRectF &gradientRect = QRectF())
{
p->save();
@@ -2569,12 +2549,14 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
}
static inline void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling,
- QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight)
+ QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight, QFixed *lineBottom)
{
- *lineHeight = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling));
+ qreal rawHeight = qCeil(line.ascent() + line.descent() + line.leading());
+ *lineHeight = QFixed::fromReal(blockFormat.lineHeight(rawHeight, scaling));
+ *lineBottom = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling));
if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight || blockFormat.lineHeightType() == QTextBlockFormat::MinimumHeight) {
- *lineBreakHeight = *lineHeight;
+ *lineBreakHeight = *lineBottom;
if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight)
*lineAdjustment = QFixed::fromReal(line.ascent() + qMax(line.leading(), qreal(0.0))) - ((*lineHeight * 4) / 5);
else
@@ -2652,6 +2634,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
const QFixed cy = layoutStruct->y;
const QFixed l = layoutStruct->x_left + totalLeftMargin;
const QFixed r = layoutStruct->x_right - totalRightMargin;
+ QFixed bottom;
tl->beginLayout();
bool firstLine = true;
@@ -2721,10 +2704,10 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
}
- QFixed lineBreakHeight, lineHeight, lineAdjustment;
+ QFixed lineBreakHeight, lineHeight, lineAdjustment, lineBottom;
qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
- getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
+ getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight, &lineBottom);
if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom) {
layoutStruct->newPage();
@@ -2739,6 +2722,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
}
line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy - lineAdjustment).toReal()));
+ bottom = layoutStruct->y + lineBottom;
layoutStruct->y += lineHeight;
layoutStruct->contentsWidth
= qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
@@ -2750,27 +2734,31 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
}
layoutStruct->pendingFloats.clear();
}
+ layoutStruct->y = qMax(layoutStruct->y, bottom);
tl->endLayout();
} else {
const int cnt = tl->lineCount();
+ QFixed bottom;
for (int i = 0; i < cnt; ++i) {
LDEBUG << "going to move text line" << i;
QTextLine line = tl->lineAt(i);
layoutStruct->contentsWidth
= qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
- QFixed lineBreakHeight, lineHeight, lineAdjustment;
+ QFixed lineBreakHeight, lineHeight, lineAdjustment, lineBottom;
qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
- getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
+ getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight, &lineBottom);
if (layoutStruct->pageHeight != QFIXED_MAX) {
if (layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom)
layoutStruct->newPage();
line.setPosition(QPointF(line.position().x(), (layoutStruct->y - lineAdjustment).toReal() - tl->position().y()));
}
+ bottom = layoutStruct->y + lineBottom;
layoutStruct->y += lineHeight;
}
+ layoutStruct->y = qMax(layoutStruct->y, bottom);
if (layoutStruct->updateRect.isValid()
&& blockLength > 1) {
if (layoutFrom >= blockPosition + blockLength) {
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 87dc63d59c..4e9b00f9c9 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -597,7 +597,13 @@ struct QBidiAlgorithm {
} else if (current == QChar::DirBN) {
current = last;
} else {
- Q_ASSERT(current != QChar::DirLRE && current != QChar::DirRLE && current != QChar::DirLRO && current != QChar::DirRLO && current != QChar::DirPDF); // there shouldn't be any explicit embedding marks here
+ // there shouldn't be any explicit embedding marks here
+ Q_ASSERT(current != QChar::DirLRE);
+ Q_ASSERT(current != QChar::DirRLE);
+ Q_ASSERT(current != QChar::DirLRO);
+ Q_ASSERT(current != QChar::DirRLO);
+ Q_ASSERT(current != QChar::DirPDF);
+
last = current;
}
@@ -1939,7 +1945,7 @@ const QCharAttributes *QTextEngine::attributes() const
QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems(layoutData->items.size());
for (int i = 0; i < layoutData->items.size(); ++i) {
- const QScriptItem &si = layoutData->items[i];
+ const QScriptItem &si = layoutData->items.at(i);
scriptItems[i].position = si.position;
scriptItems[i].script = si.analysis.script;
}
@@ -1956,14 +1962,14 @@ const QCharAttributes *QTextEngine::attributes() const
void QTextEngine::shape(int item) const
{
- if (layoutData->items[item].analysis.flags == QScriptAnalysis::Object) {
+ if (layoutData->items.at(item).analysis.flags == QScriptAnalysis::Object) {
ensureSpace(1);
if (block.docHandle()) {
docLayout()->resizeInlineObject(QTextInlineObject(item, const_cast<QTextEngine *>(this)),
layoutData->items[item].position + block.position(),
format(&layoutData->items[item]));
}
- } else if (layoutData->items[item].analysis.flags == QScriptAnalysis::Tab) {
+ } else if (layoutData->items.at(item).analysis.flags == QScriptAnalysis::Tab) {
// set up at least the ascent/descent/leading of the script item for the tab
fontEngine(layoutData->items[item],
&layoutData->items[item].ascent,
@@ -2076,7 +2082,7 @@ void QTextEngine::itemize() const
case QChar::Space:
case QChar::Nbsp:
if (option.flags() & QTextOption::ShowTabsAndSpaces) {
- analysis->flags = QScriptAnalysis::Space;
+ analysis->flags = (*uc == QChar::Space) ? QScriptAnalysis::Space : QScriptAnalysis::Nbsp;
break;
}
Q_FALLTHROUGH();
@@ -2204,9 +2210,9 @@ int QTextEngine::findItem(int strPos, int firstItem) const
int right = layoutData->items.size()-1;
while(left <= right) {
int middle = ((right-left)/2)+left;
- if (strPos > layoutData->items[middle].position)
+ if (strPos > layoutData->items.at(middle).position)
left = middle+1;
- else if(strPos < layoutData->items[middle].position)
+ else if (strPos < layoutData->items.at(middle).position)
right = middle-1;
else {
return middle;
@@ -2598,7 +2604,7 @@ void QTextEngine::justify(const QScriptLine &line)
int end = line.from + (int)line.length + line.trailingSpaces;
if (end == layoutData->string.length())
return; // no justification at end of paragraph
- if (end && layoutData->items[findItem(end-1)].analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
+ if (end && layoutData->items.at(findItem(end - 1)).analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
return; // no justification at the end of an explicitly separated line
}
@@ -2632,13 +2638,13 @@ void QTextEngine::justify(const QScriptLine &line)
// store pointers to the glyph data that could get reallocated by the shaping
// process.
for (int i = 0; i < nItems; ++i) {
- QScriptItem &si = layoutData->items[firstItem + i];
+ const QScriptItem &si = layoutData->items.at(firstItem + i);
if (!si.num_glyphs)
shape(firstItem + i);
}
for (int i = 0; i < nItems; ++i) {
- QScriptItem &si = layoutData->items[firstItem + i];
+ const QScriptItem &si = layoutData->items.at(firstItem + i);
int kashida_type = Justification_Arabic_Normal;
int kashida_pos = -1;
@@ -2934,7 +2940,7 @@ int QTextEngine::formatIndex(const QScriptItem *si) const
if (specialData && !specialData->resolvedFormats.isEmpty()) {
QTextFormatCollection *collection = formatCollection();
Q_ASSERT(collection);
- return collection->indexForFormat(specialData->resolvedFormats.at(si - &layoutData->items[0]));
+ return collection->indexForFormat(specialData->resolvedFormats.at(si - &layoutData->items.at(0)));
}
QTextDocumentPrivate *p = block.docHandle();
@@ -3140,7 +3146,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
if (!attributes)
return QString();
for (int i = 0; i < layoutData->items.size(); ++i) {
- QScriptItem &si = layoutData->items[i];
+ const QScriptItem &si = layoutData->items.at(i);
if (!si.num_glyphs)
shape(i);
@@ -3325,24 +3331,29 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
QList<QTextOption::Tab> tabArray = option.tabs();
if (!tabArray.isEmpty()) {
if (isRightToLeft()) { // rebase the tabArray positions.
- QList<QTextOption::Tab> newTabs;
- newTabs.reserve(tabArray.count());
- QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
- while(iter != tabArray.end()) {
- QTextOption::Tab tab = *iter;
- if (tab.type == QTextOption::LeftTab)
- tab.type = QTextOption::RightTab;
- else if (tab.type == QTextOption::RightTab)
- tab.type = QTextOption::LeftTab;
- newTabs << tab;
- ++iter;
+ auto isLeftOrRightTab = [](const QTextOption::Tab &tab) {
+ return tab.type == QTextOption::LeftTab || tab.type == QTextOption::RightTab;
+ };
+ const auto cbegin = tabArray.cbegin();
+ const auto cend = tabArray.cend();
+ const auto cit = std::find_if(cbegin, cend, isLeftOrRightTab);
+ if (cit != cend) {
+ const int index = std::distance(cbegin, cit);
+ auto iter = tabArray.begin() + index;
+ const auto end = tabArray.end();
+ while (iter != end) {
+ QTextOption::Tab &tab = *iter;
+ if (tab.type == QTextOption::LeftTab)
+ tab.type = QTextOption::RightTab;
+ else if (tab.type == QTextOption::RightTab)
+ tab.type = QTextOption::LeftTab;
+ ++iter;
+ }
}
- tabArray = newTabs;
}
- for (int i = 0; i < tabArray.size(); ++i) {
- QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
+ for (const QTextOption::Tab &tabSpec : qAsConst(tabArray)) {
+ QFixed tab = QFixed::fromReal(tabSpec.position) * dpiScale;
if (tab > x) { // this is the tab we need.
- QTextOption::Tab tabSpec = tabArray[i];
int tabSectionEnd = layoutData->string.count();
if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
// find next tab to calculate the width required.
@@ -3363,7 +3374,7 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
QFixed length;
// Calculate the length of text between this tab and the tabSectionEnd
for (int i=item; i < layoutData->items.count(); i++) {
- QScriptItem &item = layoutData->items[i];
+ const QScriptItem &item = layoutData->items.at(i);
if (item.position > tabSectionEnd || item.position <= si.position)
continue;
shape(i); // first, lets make sure relevant text is already shaped
@@ -3663,11 +3674,12 @@ int QTextEngine::lineNumberForTextPosition(int pos)
return -1;
}
-void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
+std::vector<int> QTextEngine::insertionPointsForLine(int lineNum)
{
QTextLineItemIterator iterator(this, lineNum);
- insertionPoints.reserve(iterator.line.length);
+ std::vector<int> insertionPoints;
+ insertionPoints.reserve(size_t(iterator.line.length));
bool lastLine = lineNum >= lines.size() - 1;
@@ -3685,25 +3697,22 @@ void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoi
insertionPoints.push_back(i);
}
}
+ return insertionPoints;
}
int QTextEngine::endOfLine(int lineNum)
{
- QVector<int> insertionPoints;
- insertionPointsForLine(lineNum, insertionPoints);
-
+ const auto insertionPoints = insertionPointsForLine(lineNum);
if (insertionPoints.size() > 0)
- return insertionPoints.constLast();
+ return insertionPoints.back();
return 0;
}
int QTextEngine::beginningOfLine(int lineNum)
{
- QVector<int> insertionPoints;
- insertionPointsForLine(lineNum, insertionPoints);
-
+ const auto insertionPoints = insertionPointsForLine(lineNum);
if (insertionPoints.size() > 0)
- return insertionPoints.constFirst();
+ return insertionPoints.front();
return 0;
}
@@ -3720,10 +3729,8 @@ int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation
if (lineNum < 0)
return pos;
- QVector<int> insertionPoints;
- insertionPointsForLine(lineNum, insertionPoints);
- int i, max = insertionPoints.size();
- for (i = 0; i < max; i++)
+ const auto insertionPoints = insertionPointsForLine(lineNum);
+ for (size_t i = 0, max = insertionPoints.size(); i < max; ++i)
if (pos == insertionPoints[i]) {
if (moveRight) {
if (i + 1 < max)
@@ -4001,7 +4008,7 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ levels[i] = eng->layoutData->items.at(i + firstItem).analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
eng->shapeLine(line);
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index f9eb055478..e9187ea605 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -74,6 +74,7 @@
#include <private/qunicodetools_p.h>
#include <stdlib.h>
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -142,9 +143,10 @@ struct Q_AUTOTEST_EXPORT QScriptAnalysis
LineOrParagraphSeparator = 4,
Space = 5,
SpaceTabOrObject = Space,
- Tab = 6,
+ Nbsp = 6,
+ Tab = 7,
TabOrObject = Tab,
- Object = 7
+ Object = 8
};
enum BidiFlags {
BidiBN = 1,
@@ -632,7 +634,7 @@ public:
int nextLogicalPosition(int oldPos) const;
int lineNumberForTextPosition(int pos);
int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
- void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints);
+ std::vector<int> insertionPointsForLine(int lineNum);
void resetFontEngineCache();
void enableDelayDecorations(bool enable = true) { delayDecorations = enable; }
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index 08106db6ce..4957da1908 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -514,7 +514,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value BlockFormat The object formats a text block
\value CharFormat The object formats a single character
\value ListFormat The object formats a list
- \omitvalue TableFormat Unused Value, a table's FormatType is FrameFormat.
+ \omitvalue TableFormat \omit Unused Value, a table's FormatType is FrameFormat. \endomit
\value FrameFormat The object formats a frame
\value UserFormat
@@ -556,6 +556,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value LineHeightType
\value BlockNonBreakableLines
\value BlockTrailingHorizontalRulerWidth The width of a horizontal ruler element.
+ \value HeadingLevel The level of a heading, for example 1 corresponds to an HTML H1 tag; otherwise 0.
+ This enum value has been added in Qt 5.12.
Character properties
@@ -2246,6 +2248,34 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
/*!
+ \fn void QTextBlockFormat::setHeadingLevel(int level)
+ \since 5.12
+
+ Sets the paragraph's heading level, where 1 is the highest-level heading
+ type (usually with the largest possible heading font size), and increasing
+ values are progressively deeper into the document (and usually with smaller
+ font sizes). For example when reading an HTML H1 tag, the heading level is
+ set to 1. Setting the heading level does not automatically change the font
+ size; however QTextDocumentFragment::fromHtml() sets both the heading level
+ and the font size simultaneously.
+
+ If the paragraph is not a heading, the level should be set to 0 (the default).
+
+ \sa headingLevel()
+*/
+
+
+/*!
+ \fn int QTextBlockFormat::headingLevel() const
+ \since 5.12
+
+ Returns the paragraph's heading level if it is a heading, or 0 if not.
+
+ \sa setHeadingLevel()
+*/
+
+
+/*!
\fn void QTextBlockFormat::setLineHeight(qreal height, int heightType)
\since 4.8
@@ -3047,7 +3077,8 @@ QTextTableFormat::QTextTableFormat(const QTextFormat &fmt)
REPLACEMENT CHARACTER) which has an associated QTextImageFormat. The
image format specifies a name with setName() that is used to
locate the image. The size of the rectangle that the image will
- occupy is specified using setWidth() and setHeight().
+ occupy is specified in pixels using setWidth() and setHeight().
+ The desired image quality may be set with setQuality().
Images can be supplied in any format for which Qt has an image
reader, so SVG drawings can be included alongside PNG, TIFF and
@@ -3137,6 +3168,28 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
*/
/*!
+ \fn void QTextImageFormat::setQuality(int quality = 100)
+ \since 5.12
+
+ Sets the quality that should be used by exporters when exporting the image. QTextDocumentWriter
+ will export jpg images with the \a quality set here when exporting to ODF files if \a quality is
+ set to a value between 0 and 100. Or it will export png images if \a quality is set to 100
+ (default) or greater.
+
+ \sa quality()
+*/
+
+
+/*!
+ \fn qreal QTextImageFormat::quality() const
+ \since 5.12
+
+ Returns the value set by setQuality().
+
+ \sa setQuality()
+*/
+
+/*!
\fn void QTextCharFormat::setFontCapitalization(QFont::Capitalization capitalization)
\since 4.4
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index 28c3035537..a8e573d5a4 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.h
@@ -175,6 +175,7 @@ public:
LineHeightType = 0x1049,
BlockNonBreakableLines = 0x1050,
BlockTrailingHorizontalRulerWidth = 0x1060,
+ HeadingLevel = 0x1070,
// character properties
FirstFontProperty = 0x1FE0,
@@ -249,6 +250,7 @@ public:
ImageName = 0x5000,
ImageWidth = 0x5010,
ImageHeight = 0x5011,
+ ImageQuality = 0x5014,
// internal
/*
@@ -624,6 +626,11 @@ public:
inline int indent() const
{ return intProperty(BlockIndent); }
+ inline void setHeadingLevel(int alevel)
+ { setProperty(HeadingLevel, alevel); }
+ inline int headingLevel() const
+ { return intProperty(HeadingLevel); }
+
inline void setLineHeight(qreal height, int heightType)
{ setProperty(LineHeight, height); setProperty(LineHeightType, heightType); }
inline qreal lineHeight(qreal scriptLineHeight, qreal scaling) const;
@@ -748,6 +755,10 @@ public:
inline qreal height() const
{ return doubleProperty(ImageHeight); }
+ inline void setQuality(int quality = 100);
+ inline int quality() const
+ { return intProperty(ImageQuality); }
+
protected:
explicit QTextImageFormat(const QTextFormat &format);
friend class QTextFormat;
@@ -764,6 +775,9 @@ inline void QTextImageFormat::setWidth(qreal awidth)
inline void QTextImageFormat::setHeight(qreal aheight)
{ setProperty(ImageHeight, aheight); }
+inline void QTextImageFormat::setQuality(int aquality)
+{ setProperty(ImageQuality, aquality); }
+
class Q_GUI_EXPORT QTextFrameFormat : public QTextFormat
{
public:
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index 0aad65479a..c9a2a33e5a 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -447,13 +447,6 @@ static const QTextHtmlElement elements[Html_NumElements]= {
{ "var", Html_var, QTextHtmlElement::DisplayInline },
};
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-static bool operator<(const QTextHtmlElement &e1, const QTextHtmlElement &e2)
-{
- return QLatin1String(e1.name) < QLatin1String(e2.name);
-}
-#endif
-
static bool operator<(const QString &str, const QTextHtmlElement &e)
{
return str < QLatin1String(e.name);
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 6e824b2ed1..ca6866d836 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -1006,10 +1006,8 @@ static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPo
}
if (lastSelectionWidth > 0) {
- QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
- rect.moveLeft(qFloor(rect.left()));
- rect.moveTop(qFloor(rect.top()));
- region->addRect(rect);
+ const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
+ region->addRect(rect.toAlignedRect());
}
lastSelectionX = selectionX;
@@ -1017,10 +1015,8 @@ static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPo
}
}
if (lastSelectionWidth > 0) {
- QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
- rect.moveLeft(qFloor(rect.left()));
- rect.moveTop(qFloor(rect.top()));
- region->addRect(rect);
+ const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
+ region->addRect(rect.toAlignedRect());
}
}
@@ -2135,7 +2131,7 @@ static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const Q
QBrush bg = chf.background();
if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
- p->fillRect(QRectF(qFloor(r.x()), qFloor(r.y()), r.width(), r.height()), bg);
+ p->fillRect(r.toAlignedRect(), bg);
if (c.style() != Qt::NoBrush) {
p->setPen(QPen(c, 0));
}
@@ -2614,12 +2610,13 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR
QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
}
- if (si.analysis.flags == QScriptAnalysis::Space
+ if ((si.analysis.flags == QScriptAnalysis::Space
+ || si.analysis.flags == QScriptAnalysis::Nbsp)
&& (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
QBrush c = format.foreground();
if (c.style() != Qt::NoBrush)
p->setPen(c.color());
- QChar visualSpace((ushort)0xb7);
+ QChar visualSpace(si.analysis.flags == QScriptAnalysis::Space ? (ushort)0xb7 : (ushort)0xb0);
p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
p->setPen(pen);
}
@@ -2845,9 +2842,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
bool rtl = eng->isRightToLeft();
eng->shapeLine(line);
- QVector<int> insertionPoints;
- if (visual && rtl)
- eng->insertionPointsForLine(lineNum, insertionPoints);
+ const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<int>();
int nchars = 0;
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem;
@@ -2979,7 +2974,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
continue;
}
if (rtl && nchars > 0)
- return insertionPoints[lastLine ? nchars : nchars - 1];
+ return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
}
return eng->positionInLigature(&si, end, x, pos, -1,
cpos == QTextLine::CursorOnCharacter);
@@ -3007,9 +3002,8 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
// character between lines is a space and we want
// to position the cursor to the left of that
// character.
- // ###### breaks with japanese for example
if (this->index < eng->lines.count() - 1)
- --maxPos;
+ maxPos = eng->previousLogicalPosition(maxPos);
pos = qMin(pos, maxPos);
return pos;
diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp
index 1b742cf202..103e0a8222 100644
--- a/src/gui/text/qtextodfwriter.cpp
+++ b/src/gui/text/qtextodfwriter.cpp
@@ -183,6 +183,35 @@ static QString bulletChar(QTextListFormat::Style style)
}
}
+static QString borderStyleName(QTextFrameFormat::BorderStyle style)
+{
+ switch (style) {
+ case QTextFrameFormat::BorderStyle_None:
+ return QString::fromLatin1("none");
+ case QTextFrameFormat::BorderStyle_Dotted:
+ return QString::fromLatin1("dotted");
+ case QTextFrameFormat::BorderStyle_Dashed:
+ return QString::fromLatin1("dashed");
+ case QTextFrameFormat::BorderStyle_Solid:
+ return QString::fromLatin1("solid");
+ case QTextFrameFormat::BorderStyle_Double:
+ return QString::fromLatin1("double");
+ case QTextFrameFormat::BorderStyle_DotDash:
+ return QString::fromLatin1("dashed");
+ case QTextFrameFormat::BorderStyle_DotDotDash:
+ return QString::fromLatin1("dotted");
+ case QTextFrameFormat::BorderStyle_Groove:
+ return QString::fromLatin1("groove");
+ case QTextFrameFormat::BorderStyle_Ridge:
+ return QString::fromLatin1("ridge");
+ case QTextFrameFormat::BorderStyle_Inset:
+ return QString::fromLatin1("inset");
+ case QTextFrameFormat::BorderStyle_Outset:
+ return QString::fromLatin1("outset");
+ }
+ return QString::fromLatin1("");
+}
+
void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame)
{
Q_ASSERT(frame);
@@ -190,8 +219,21 @@ void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *fram
if (table) { // Start a table.
writer.writeStartElement(tableNS, QString::fromLatin1("table"));
- writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column"));
- writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"), QString::number(table->columns()));
+ writer.writeAttribute(tableNS, QString::fromLatin1("style-name"),
+ QString::fromLatin1("Table%1").arg(table->formatIndex()));
+ // check if column widths are set, if so add TableNS line above for all columns and link to style
+ if (m_tableFormatsWithColWidthConstraints.contains(table->formatIndex())) {
+ for (int colit = 0; colit < table->columns(); ++colit) {
+ writer.writeStartElement(tableNS, QString::fromLatin1("table-column"));
+ writer.writeAttribute(tableNS, QString::fromLatin1("style-name"),
+ QString::fromLatin1("Table%1.%2").arg(table->formatIndex()).arg(colit));
+ writer.writeEndElement();
+ }
+ } else {
+ writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column"));
+ writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"),
+ QString::number(table->columns()));
+ }
} else if (frame->document() && frame->document()->rootFrame() != frame) { // start a section
writer.writeStartElement(textNS, QString::fromLatin1("section"));
}
@@ -219,7 +261,15 @@ void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *fram
if (cell.rowSpan() > 1)
writer.writeAttribute(tableNS, QString::fromLatin1("number-rows-spanned"), QString::number(cell.rowSpan()));
if (cell.format().isTableCellFormat()) {
- writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex()));
+ if (m_cellFormatsInTablesWithBorders.contains(cell.tableCellFormatIndex()) ) {
+ // writing table:style-name tag in <table:table-cell> element
+ writer.writeAttribute(tableNS, QString::fromLatin1("style-name"),
+ QString::fromLatin1("TB%1.%2").arg(table->formatIndex())
+ .arg(cell.tableCellFormatIndex()));
+ } else {
+ writer.writeAttribute(tableNS, QString::fromLatin1("style-name"),
+ QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex()));
+ }
}
}
writeBlock(writer, block);
@@ -326,7 +376,10 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
if (i < fragmentText.count()) {
if (character.unicode() == 0x2028) { // soft-return
//if (exportedIndex < i)
- writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
+ writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
+ // adding tab before line-break, so last line in justified paragraph
+ // will not stretch to the end
+ writer.writeEmptyElement(textNS, QString::fromLatin1("tab"));
writer.writeEmptyElement(textNS, QString::fromLatin1("line-break"));
exportedIndex = i+1;
continue;
@@ -367,7 +420,6 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF
QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
writer.writeAttribute(drawNS, QString::fromLatin1("name"), imageFormat.name());
- // vvv Copy pasted mostly from Qt =================
QImage image;
QString name = imageFormat.name();
if (name.startsWith(QLatin1String(":/"))) // auto-detect resources
@@ -387,26 +439,33 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF
}
}
- // ^^^ Copy pasted mostly from Qt =================
if (! image.isNull()) {
QBuffer imageBytes;
- QImageWriter imageWriter(&imageBytes, "png");
- imageWriter.write(image);
QString filename = m_strategy->createUniqueImageName();
- m_strategy->addFile(filename, QString::fromLatin1("image/png"), imageBytes.data());
-
+ int imgQuality = imageFormat.quality();
+ if (imgQuality >= 100 || imgQuality < 0 || image.hasAlphaChannel()) {
+ QImageWriter imageWriter(&imageBytes, "png");
+ imageWriter.write(image);
+ m_strategy->addFile(filename, QString::fromLatin1("image/png"), imageBytes.data());
+ } else {
+ // Write images without alpha channel as jpg with quality set by QTextImageFormat
+ QImageWriter imageWriter(&imageBytes, "jpg");
+ imageWriter.setQuality(imgQuality);
+ imageWriter.write(image);
+ m_strategy->addFile(filename, QString::fromLatin1("image/jpg"), imageBytes.data());
+ }
// get the width/height from the format.
- qreal width = (imageFormat.hasProperty(QTextFormat::ImageWidth)) ? imageFormat.width() : image.width();
+ qreal width = imageFormat.hasProperty(QTextFormat::ImageWidth)
+ ? imageFormat.width() : image.width();
writer.writeAttribute(svgNS, QString::fromLatin1("width"), pixelToPoint(width));
- qreal height = (imageFormat.hasProperty(QTextFormat::ImageHeight)) ? imageFormat.height() : image.height();
+ qreal height = imageFormat.hasProperty(QTextFormat::ImageHeight)
+ ? imageFormat.height() : image.height();
writer.writeAttribute(svgNS, QString::fromLatin1("height"), pixelToPoint(height));
-
writer.writeStartElement(drawNS, QString::fromLatin1("image"));
writer.writeAttribute(xlinkNS, QString::fromLatin1("href"), filename);
writer.writeEndElement(); // image
}
}
-
writer.writeEndElement(); // frame
}
@@ -421,7 +480,7 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet<int> &for
switch (textFormat.type()) {
case QTextFormat::CharFormat:
if (textFormat.isTableCellFormat())
- writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex);
+ writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex, allStyles);
else
writeCharacterFormat(writer, textFormat.toCharFormat(), formatIndex);
break;
@@ -432,10 +491,15 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet<int> &for
writeListFormat(writer, textFormat.toListFormat(), formatIndex);
break;
case QTextFormat::FrameFormat:
- writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex);
+ if (textFormat.isTableFormat())
+ writeTableFormat(writer, textFormat.toTableFormat(), formatIndex);
+ else
+ writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex);
break;
case QTextFormat::TableFormat:
- ;break;
+ // this case never happens, because TableFormat is a FrameFormat
+ Q_UNREACHABLE();
+ break;
}
}
@@ -449,6 +513,36 @@ void QTextOdfWriter::writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat
writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("paragraph"));
writer.writeStartElement(styleNS, QString::fromLatin1("paragraph-properties"));
+ if (format.hasProperty(QTextBlockFormat::LineHeightType)) {
+ const int blockLineHeightType = format.lineHeightType();
+ const qreal blockLineHeight = format.lineHeight();
+ QString type, value;
+ switch (blockLineHeightType) {
+ case QTextBlockFormat::SingleHeight:
+ type = QString::fromLatin1("line-height");
+ value = QString::fromLatin1("100%");
+ break;
+ case QTextBlockFormat::ProportionalHeight:
+ type = QString::fromLatin1("line-height");
+ value = QString::number(blockLineHeight) + QString::fromLatin1("%");
+ break;
+ case QTextBlockFormat::FixedHeight:
+ type = QString::fromLatin1("line-height");
+ value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
+ break;
+ case QTextBlockFormat::MinimumHeight:
+ type = QString::fromLatin1("line-height-at-least");
+ value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
+ break;
+ case QTextBlockFormat::LineDistanceHeight:
+ type = QString::fromLatin1("line-spacing");
+ value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
+ }
+
+ if (!type.isNull())
+ writer.writeAttribute(styleNS, type, value);
+ }
+
if (format.hasProperty(QTextFormat::BlockAlignment)) {
const Qt::Alignment alignment = format.alignment() & Qt::AlignHorizontal_Mask;
QString value;
@@ -693,14 +787,108 @@ void QTextOdfWriter::writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat
// PageBreakFlags pageBreakPolicy () const
}
-void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const
+void QTextOdfWriter::writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat format, int formatIndex) const
{
+ // start writing table style element
writer.writeStartElement(styleNS, QString::fromLatin1("style"));
- writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"),
+ QString::fromLatin1("Table%1").arg(formatIndex));
writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table"));
writer.writeEmptyElement(styleNS, QString::fromLatin1("table-properties"));
+ if (m_tableFormatsWithBorders.contains(formatIndex)) {
+ // write border format collapsing to table style
+ writer.writeAttribute(tableNS, QString::fromLatin1("border-model"),
+ QString::fromLatin1("collapsing"));
+ }
+ const char* align = nullptr;
+ switch (format.alignment()) {
+ case Qt::AlignLeft:
+ align = "left";
+ break;
+ case Qt::AlignRight:
+ align = "right";
+ break;
+ case Qt::AlignHCenter:
+ align = "center";
+ break;
+ case Qt::AlignJustify:
+ align = "margins";
+ break;
+ }
+ if (align)
+ writer.writeAttribute(tableNS, QString::fromLatin1("align"), QString::fromLatin1(align));
+ if (format.width().rawValue()) {
+ writer.writeAttribute(styleNS, QString::fromLatin1("width"),
+ QString::number(format.width().rawValue()) + QLatin1String("pt"));
+ }
+ writer.writeEndElement();
+ // start writing table-column style element
+ if (format.columnWidthConstraints().size()) {
+ // write table-column-properties for columns with constraints
+ m_tableFormatsWithColWidthConstraints.insert(formatIndex); // needed for linking of columns to styles
+ for (int colit = 0; colit < format.columnWidthConstraints().size(); ++colit) {
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"),
+ QString::fromLatin1("Table%1.%2").arg(formatIndex).arg(colit));
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table-column"));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("table-column-properties"));
+ QString columnWidth;
+ if (format.columnWidthConstraints().at(colit).type() == QTextLength::PercentageLength) {
+ columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue())
+ + QLatin1String("%");
+ } else if (format.columnWidthConstraints().at(colit).type() == QTextLength::FixedLength) {
+ columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue())
+ + QLatin1String("pt");
+ } else {
+ //!! HARD-CODING variableWidth Constraints to 100% / nr constraints
+ columnWidth = QString::number(100 / format.columnWidthConstraints().size())
+ + QLatin1String("%");
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("column-width"), columnWidth);
+ writer.writeEndElement();
+ }
+ }
+}
+
+void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format,
+ int formatIndex, QVector<QTextFormat> &styles) const
+{
+ // check for all table cells here if they are in a table with border
+ if (m_cellFormatsInTablesWithBorders.contains(formatIndex)) {
+ const QVector<int> tableIdVector = m_cellFormatsInTablesWithBorders.value(formatIndex);
+ for (const auto &tableId : tableIdVector) {
+ const auto &tmpStyle = styles.at(tableId);
+ if (tmpStyle.isTableFormat()) {
+ QTextTableFormat tableFormatTmp = tmpStyle.toTableFormat();
+ tableCellStyleElement(writer, formatIndex, format, true, tableId, tableFormatTmp);
+ } else {
+ qDebug("QTextOdfWriter::writeTableCellFormat: ERROR writing table border format");
+ }
+ }
+ }
+ tableCellStyleElement(writer, formatIndex, format, false);
+}
+void QTextOdfWriter::tableCellStyleElement(QXmlStreamWriter &writer, const int &formatIndex,
+ const QTextTableCellFormat &format,
+ bool hasBorder, int tableId,
+ const QTextTableFormat tableFormatTmp) const {
+ writer.writeStartElement(styleNS, QString::fromLatin1("style"));
+ if (hasBorder) {
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"),
+ QString::fromLatin1("TB%1.%2").arg(tableId).arg(formatIndex));
+ } else {
+ writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex));
+ }
+ writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table-cell"));
+ writer.writeEmptyElement(styleNS, QString::fromLatin1("table-cell-properties"));
+ if (hasBorder) {
+ writer.writeAttribute(foNS, QString::fromLatin1("border"),
+ pixelToPoint(tableFormatTmp.border()) + QLatin1String(" ")
+ + borderStyleName(tableFormatTmp.borderStyle())
+ + QLatin1String(" #000000")); //!! HARD-CODING color black
+ }
qreal padding = format.topPadding();
if (padding > 0 && padding == format.bottomPadding()
&& padding == format.leftPadding() && padding == format.rightPadding()) {
@@ -710,16 +898,19 @@ void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCe
if (padding > 0)
writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(padding));
if (format.bottomPadding() > 0)
- writer.writeAttribute(foNS, QString::fromLatin1("padding-bottom"), pixelToPoint(format.bottomPadding()));
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-bottom"),
+ pixelToPoint(format.bottomPadding()));
if (format.leftPadding() > 0)
- writer.writeAttribute(foNS, QString::fromLatin1("padding-left"), pixelToPoint(format.leftPadding()));
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-left"),
+ pixelToPoint(format.leftPadding()));
if (format.rightPadding() > 0)
- writer.writeAttribute(foNS, QString::fromLatin1("padding-right"), pixelToPoint(format.rightPadding()));
+ writer.writeAttribute(foNS, QString::fromLatin1("padding-right"),
+ pixelToPoint(format.rightPadding()));
}
if (format.hasProperty(QTextFormat::TextVerticalAlignment)) {
QString pos;
- switch (format.verticalAlignment()) {
+ switch (format.verticalAlignment()) { // TODO - review: doesn't handle all cases
case QTextCharFormat::AlignMiddle:
pos = QString::fromLatin1("middle"); break;
case QTextCharFormat::AlignTop:
@@ -736,8 +927,6 @@ void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCe
// ODF just search for style-table-cell-properties-attlist)
// QTextFormat::BackgroundImageUrl
// format.background
- // QTextFormat::FrameBorder
-
writer.writeEndElement(); // style
}
@@ -815,8 +1004,28 @@ bool QTextOdfWriter::writeAll()
const QList<int> copy = formats.toList();
for (auto index : copy) {
QTextObject *object = m_document->objectForFormat(allFormats[index]);
- if (object)
+ if (object) {
formats << object->formatIndex();
+ if (auto *tableobject = qobject_cast<QTextTable *>(object)) {
+ if (tableobject->format().borderStyle()) {
+ int tableID = tableobject->formatIndex();
+ m_tableFormatsWithBorders.insert(tableID);
+ // loop through all rows and cols of table and store cell IDs,
+ // create Hash with cell ID as Key and table IDs as Vector
+ for (int rowindex = 0; rowindex < tableobject->rows(); ++rowindex) {
+ for (int colindex = 0; colindex < tableobject->columns(); ++colindex) {
+ const int cellFormatID = tableobject->cellAt(rowindex, colindex).tableCellFormatIndex();
+ QVector<int> tableIdsTmp;
+ if (m_cellFormatsInTablesWithBorders.contains(cellFormatID))
+ tableIdsTmp = m_cellFormatsInTablesWithBorders.value(cellFormatID);
+ if (!tableIdsTmp.contains(tableID))
+ tableIdsTmp.append(tableID);
+ m_cellFormatsInTablesWithBorders.insert(cellFormatID, tableIdsTmp);
+ }
+ }
+ }
+ }
+ }
}
writeFormats(writer, formats);
diff --git a/src/gui/text/qtextodfwriter_p.h b/src/gui/text/qtextodfwriter_p.h
index d0dd7d2b5c..98a6fdfa96 100644
--- a/src/gui/text/qtextodfwriter_p.h
+++ b/src/gui/text/qtextodfwriter_p.h
@@ -56,8 +56,10 @@
//
#include <QtCore/QXmlStreamWriter>
+#include <QtCore/qhash.h>
#include <QtCore/qset.h>
#include <QtCore/qstack.h>
+#include <QtCore/qvector.h>
#include "qtextdocument_p.h"
#include "qtextdocumentwriter.h"
@@ -94,11 +96,21 @@ public:
void writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const;
void writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const;
void writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const;
- void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const;
+ void writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat format, int formatIndex) const;
+ void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex,
+ QVector<QTextFormat> &styles) const;
void writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame);
void writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment) const;
const QString officeNS, textNS, styleNS, foNS, tableNS, drawNS, xlinkNS, svgNS;
+ const int defaultImageResolution = 11811; // 11811 dots per meter = (about) 300 dpi
+
+protected:
+ void tableCellStyleElement(QXmlStreamWriter &writer, const int &formatIndex,
+ const QTextTableCellFormat &format,
+ bool hasBorder, int tableId = 0,
+ const QTextTableFormat tableFormatTmp = QTextTableFormat()) const;
+
private:
const QTextDocument *m_document;
QIODevice *m_device;
@@ -108,6 +120,10 @@ private:
bool m_createArchive;
QStack<QTextList *> m_listStack;
+
+ QHash<int, QVector<int>> m_cellFormatsInTablesWithBorders;
+ QSet<int> m_tableFormatsWithBorders;
+ mutable QSet<int> m_tableFormatsWithColWidthConstraints;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextoption.cpp b/src/gui/text/qtextoption.cpp
index a3fa0e7351..2c2c05567f 100644
--- a/src/gui/text/qtextoption.cpp
+++ b/src/gui/text/qtextoption.cpp
@@ -307,7 +307,8 @@ QList<QTextOption::Tab> QTextOption::tabs() const
\value IncludeTrailingSpaces When this option is set, QTextLine::naturalTextWidth() and naturalTextRect() will
return a value that includes the width of trailing spaces in the text; otherwise
this width is excluded.
- \value ShowTabsAndSpaces Visualize spaces with little dots, and tabs with little arrows.
+ \value ShowTabsAndSpaces Visualize spaces with little dots, and tabs with little arrows. Non-breaking spaces are
+ shown differently to breaking spaces.
\value ShowLineAndParagraphSeparators Visualize line and paragraph separators with appropriate symbol characters.
\value ShowDocumentTerminator Visualize the end of the document with a section sign. This enum value was added
in Qt 5.7.
diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp
index b68c36fd9e..fc7fbcac12 100644
--- a/src/gui/text/qzip.cpp
+++ b/src/gui/text/qzip.cpp
@@ -391,7 +391,6 @@ struct CentralFileHeader
uchar internal_file_attributes[2];
uchar external_file_attributes[4];
uchar offset_local_header[4];
- LocalFileHeader toLocalHeader() const;
};
Q_DECLARE_TYPEINFO(CentralFileHeader, Q_PRIMITIVE_TYPE);
@@ -541,19 +540,19 @@ public:
void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
};
-LocalFileHeader CentralFileHeader::toLocalHeader() const
+static LocalFileHeader toLocalHeader(const CentralFileHeader &ch)
{
LocalFileHeader h;
writeUInt(h.signature, 0x04034b50);
- copyUShort(h.version_needed, version_needed);
- copyUShort(h.general_purpose_bits, general_purpose_bits);
- copyUShort(h.compression_method, compression_method);
- copyUInt(h.last_mod_file, last_mod_file);
- copyUInt(h.crc_32, crc_32);
- copyUInt(h.compressed_size, compressed_size);
- copyUInt(h.uncompressed_size, uncompressed_size);
- copyUShort(h.file_name_length, file_name_length);
- copyUShort(h.extra_field_length, extra_field_length);
+ copyUShort(h.version_needed, ch.version_needed);
+ copyUShort(h.general_purpose_bits, ch.general_purpose_bits);
+ copyUShort(h.compression_method, ch.compression_method);
+ copyUInt(h.last_mod_file, ch.last_mod_file);
+ copyUInt(h.crc_32, ch.crc_32);
+ copyUInt(h.compressed_size, ch.compressed_size);
+ copyUInt(h.uncompressed_size, ch.uncompressed_size);
+ copyUShort(h.file_name_length, ch.file_name_length);
+ copyUShort(h.extra_field_length, ch.extra_field_length);
return h;
}
@@ -751,7 +750,7 @@ void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
fileHeaders.append(header);
- LocalFileHeader h = header.h.toLocalHeader();
+ LocalFileHeader h = toLocalHeader(header.h);
device->write((const char *)&h, sizeof(LocalFileHeader));
device->write(header.file_name);
device->write(data);
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
index dfd190ddd0..b6eac91478 100644
--- a/src/gui/util/qdesktopservices.cpp
+++ b/src/gui/util/qdesktopservices.cpp
@@ -141,6 +141,10 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
same argument, and it will try to open the URL using the
appropriate mechanism for the user's desktop environment.
+ Combined with platform specific settings, the schemes registered by the
+ openUrl() function can also be exposed to other applications, opening up
+ for application deep linking or a very basic URL-based IPC mechanism.
+
\note Since Qt 5, storageLocation() and displayName() are replaced by functionality
provided by the QStandardPaths class.
@@ -183,12 +187,7 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
\l{https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl}{canOpenURL(_:)}.
For example, the following lines enable URLs with the HTTPS scheme:
- \code
- <key>LSApplicationQueriesSchemes</key>
- <array>
- <string>https</string>
- </array>
- \endcode
+ \snippet code/src_gui_util_qdesktopservices.cpp 3
\sa setUrlHandler()
*/
@@ -245,6 +244,14 @@ bool QDesktopServices::openUrl(const QUrl &url)
The provided method must be implemented as a slot that only accepts a single QUrl
argument.
+ To use this function for receiving data from other apps on iOS you also need to
+ add the custom scheme to the \c CFBundleURLSchemes list in your Info.plist file:
+
+ \snippet code/src_gui_util_qdesktopservices.cpp 4
+
+ For more information, see the Apple Developer Documentation for
+ \l{https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content/communicating_with_other_apps_using_custom_urls?language=objc}{Communicating with Other Apps Using Custom URLs}.
+
If setUrlHandler() is used to set a new handler for a scheme which already
has a handler, the existing handler is simply replaced with the new one.
Since QDesktopServices does not take ownership of handlers, no objects are
@@ -324,14 +331,9 @@ void QDesktopServices::unsetUrlHandler(const QString &scheme)
wasn't called, while in Qt 5 it defaults to the name of the executable.
Therefore, if you still need to access the Qt 4 path (for example for data migration to Qt 5), replace
- \code
- QDesktopServices::storageLocation(QDesktopServices::DataLocation)
- \endcode
+ \snippet code/src_gui_util_qdesktopservices.cpp 5
with
- \code
- QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
- "/data/organization/application"
- \endcode
+ \snippet code/src_gui_util_qdesktopservices.cpp 6
(assuming an organization name and an application name were set).
*/
diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp
new file mode 100644
index 0000000000..7eda4c46fb
--- /dev/null
+++ b/src/gui/util/qktxhandler.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qktxhandler_p.h"
+#include "qtexturefiledata_p.h"
+#include <QtEndian>
+#include <QSize>
+
+//#define KTX_DEBUG
+#ifdef KTX_DEBUG
+#include <QDebug>
+#include <QMetaEnum>
+#include <QOpenGLTexture>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define KTX_IDENTIFIER_LENGTH 12
+static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' };
+static const quint32 platformEndianIdentifier = 0x04030201;
+static const quint32 inversePlatformEndianIdentifier = 0x01020304;
+
+struct KTXHeader {
+ quint8 identifier[KTX_IDENTIFIER_LENGTH]; // Must match ktxIdentifier
+ quint32 endianness; // Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed.
+ quint32 glType;
+ quint32 glTypeSize;
+ quint32 glFormat;
+ quint32 glInternalFormat;
+ quint32 glBaseInternalFormat;
+ quint32 pixelWidth;
+ quint32 pixelHeight;
+ quint32 pixelDepth;
+ quint32 numberOfArrayElements;
+ quint32 numberOfFaces;
+ quint32 numberOfMipmapLevels;
+ quint32 bytesOfKeyValueData;
+};
+
+static const quint32 headerSize = sizeof(KTXHeader);
+
+// Currently unused, declared for future reference
+struct KTXKeyValuePairItem {
+ quint32 keyAndValueByteSize;
+ /*
+ quint8 keyAndValue[keyAndValueByteSize];
+ quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)];
+ */
+};
+
+struct KTXMipmapLevel {
+ quint32 imageSize;
+ /*
+ for each array_element in numberOfArrayElements*
+ for each face in numberOfFaces
+ for each z_slice in pixelDepth*
+ for each row or row_of_blocks in pixelHeight*
+ for each pixel or block_of_pixels in pixelWidth
+ Byte data[format-specific-number-of-bytes]**
+ end
+ end
+ end
+ Byte cubePadding[0-3]
+ end
+ end
+ quint8 mipPadding[3 - ((imageSize + 3) % 4)]
+ */
+};
+
+bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
+{
+ Q_UNUSED(suffix)
+
+ return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0);
+}
+
+QTextureFileData QKtxHandler::read()
+{
+ if (!device())
+ return QTextureFileData();
+
+ QByteArray buf = device()->readAll();
+ const quint32 dataSize = quint32(buf.size());
+ if (dataSize < headerSize || !canRead(QByteArray(), buf)) {
+ qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.constData());
+ if (!checkHeader(*header)) {
+ qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ QTextureFileData texData;
+ texData.setData(buf);
+
+ texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight)));
+ texData.setGLFormat(decode(header->glFormat));
+ texData.setGLInternalFormat(decode(header->glInternalFormat));
+ texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat));
+
+ texData.setNumLevels(decode(header->numberOfMipmapLevels));
+ quint32 offset = headerSize + decode(header->bytesOfKeyValueData);
+ const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file.
+ for (int i = 0; i < maxLevels; i++) {
+ if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read
+ break;
+ const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + offset);
+ quint32 levelLen = decode(level->imageSize);
+ texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i);
+ texData.setDataLength(levelLen, i);
+ offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4));
+ }
+
+ if (!texData.isValid()) {
+ qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ texData.setLogName(logName());
+
+#ifdef KTX_DEBUG
+ qDebug() << "KTX file handler read" << texData;
+#endif
+
+ return texData;
+}
+
+bool QKtxHandler::checkHeader(const KTXHeader &header)
+{
+ if (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier)
+ return false;
+ inverseEndian = (header.endianness == inversePlatformEndianIdentifier);
+#ifdef KTX_DEBUG
+ QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>();
+ QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>();
+ qDebug("Header of %s:", logName().constData());
+ qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType)));
+ qDebug(" glTypeSize: %u", decode(header.glTypeSize));
+ qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), tfme.valueToKey(decode(header.glFormat)));
+ qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), tfme.valueToKey(decode(header.glInternalFormat)));
+ qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), tfme.valueToKey(decode(header.glBaseInternalFormat)));
+ qDebug(" pixelWidth: %u", decode(header.pixelWidth));
+ qDebug(" pixelHeight: %u", decode(header.pixelHeight));
+ qDebug(" pixelDepth: %u", decode(header.pixelDepth));
+ qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements));
+ qDebug(" numberOfFaces: %u", decode(header.numberOfFaces));
+ qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels));
+ qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData));
+#endif
+ return ((decode(header.glType) == 0) &&
+ (decode(header.glFormat) == 0) &&
+ (decode(header.pixelDepth) == 0) &&
+ (decode(header.numberOfFaces) == 1));
+}
+
+quint32 QKtxHandler::decode(quint32 val)
+{
+ return inverseEndian ? qbswap<quint32>(val) : val;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimage_avx2.cpp b/src/gui/util/qktxhandler_p.h
index 0519f17c5d..19f7b0e79a 100644
--- a/src/gui/image/qimage_avx2.cpp
+++ b/src/gui/util/qktxhandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -37,31 +37,42 @@
**
****************************************************************************/
-#include <qimage.h>
-#include <private/qdrawhelper_p.h>
-#include <private/qimage_p.h>
-#include <private/qsimd_p.h>
+#ifndef QKTXHANDLER_H
+#define QKTXHANDLER_H
-#ifdef QT_COMPILER_SUPPORTS_AVX2
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtexturefilehandler_p.h"
QT_BEGIN_NAMESPACE
-void convert_ARGB_to_ARGB_PM_avx2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+struct KTXHeader;
+
+class QKtxHandler : public QTextureFileHandler
{
- Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888);
- Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied);
- Q_ASSERT(src->width == dest->width);
- Q_ASSERT(src->height == dest->height);
+public:
+ using QTextureFileHandler::QTextureFileHandler;
+
+ static bool canRead(const QByteArray &suffix, const QByteArray &block);
+
+ QTextureFileData read() override;
+
+private:
+ bool checkHeader(const KTXHeader &header);
+ quint32 decode(quint32 val);
- const uint *src_data = (uint *) src->data;
- uint *dest_data = (uint *) dest->data;
- for (int i = 0; i < src->height; ++i) {
- qt_convertARGB32ToARGB32PM(dest_data, src_data, src->width);
- src_data += src->bytes_per_line >> 2;
- dest_data += dest->bytes_per_line >> 2;
- }
-}
+ bool inverseEndian = false;
+};
QT_END_NAMESPACE
-#endif // QT_COMPILER_SUPPORTS_AVX2
+#endif // QKTXHANDLER_H
diff --git a/src/gui/util/qpkmhandler.cpp b/src/gui/util/qpkmhandler.cpp
new file mode 100644
index 0000000000..e0c3b75efe
--- /dev/null
+++ b/src/gui/util/qpkmhandler.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpkmhandler_p.h"
+#include "qtexturefiledata_p.h"
+
+#include <QFile>
+#include <QDebug>
+#include <QSize>
+#include <qendian.h>
+
+//#define ETC_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+static const int headerSize = 16;
+
+struct PkmType
+{
+ quint32 glFormat;
+ quint32 bytesPerBlock;
+};
+
+static PkmType typeMap[5] = {
+ { 0x8D64, 8 }, // GL_ETC1_RGB8_OES
+ { 0x9274, 8 }, // GL_COMPRESSED_RGB8_ETC2
+ { 0, 0 }, // unused (obsolete)
+ { 0x9278, 16}, // GL_COMPRESSED_RGBA8_ETC2_EAC
+ { 0x9276, 8 } // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
+};
+
+bool QPkmHandler::canRead(const QByteArray &suffix, const QByteArray &block)
+{
+ Q_UNUSED(suffix)
+
+ return block.startsWith("PKM ");
+}
+
+QTextureFileData QPkmHandler::read()
+{
+ QTextureFileData texData;
+
+ if (!device())
+ return texData;
+
+ QByteArray fileData = device()->readAll();
+ if (fileData.size() < headerSize || !canRead(QByteArray(), fileData)) {
+ qCDebug(lcQtGuiTextureIO, "Invalid PKM file %s", logName().constData());
+ return QTextureFileData();
+ }
+ texData.setData(fileData);
+
+ const char *rawData = fileData.constData();
+
+ // ignore version (rawData + 4 & 5)
+
+ // texture type
+ quint16 type = qFromBigEndian<quint16>(rawData + 6);
+ if (type >= sizeof(typeMap)/sizeof(typeMap[0])) {
+ qCDebug(lcQtGuiTextureIO, "Unknown compression format in PKM file %s", logName().constData());
+ return QTextureFileData();
+ }
+ texData.setGLFormat(0); // 0 for compressed textures
+ texData.setGLInternalFormat(typeMap[type].glFormat);
+ //### setBaseInternalFormat
+
+ // texture size
+ texData.setNumLevels(1);
+ const int bpb = typeMap[type].bytesPerBlock;
+ QSize paddedSize(qFromBigEndian<quint16>(rawData + 8), qFromBigEndian<quint16>(rawData + 10));
+ texData.setDataLength((paddedSize.width() / 4) * (paddedSize.height() / 4) * bpb);
+ QSize texSize(qFromBigEndian<quint16>(rawData + 12), qFromBigEndian<quint16>(rawData + 14));
+ texData.setSize(texSize);
+
+ texData.setDataOffset(headerSize);
+
+ if (!texData.isValid()) {
+ qCDebug(lcQtGuiTextureIO, "Invalid values in header of PKM file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ texData.setLogName(logName());
+
+#ifdef ETC_DEBUG
+ qDebug() << "PKM file handler read" << texData;
+#endif
+ return texData;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qpkmhandler_p.h b/src/gui/util/qpkmhandler_p.h
new file mode 100644
index 0000000000..2f7618bc53
--- /dev/null
+++ b/src/gui/util/qpkmhandler_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPKMHANDLER_H
+#define QPKMHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtexturefilehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPkmHandler : public QTextureFileHandler
+{
+public:
+ using QTextureFileHandler::QTextureFileHandler;
+
+ static bool canRead(const QByteArray &suffix, const QByteArray &block);
+
+ QTextureFileData read() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPKMHANDLER_H
diff --git a/src/gui/util/qshadergraphloader.cpp b/src/gui/util/qshadergraphloader.cpp
index 8d92c73a5a..99a9f7869e 100644
--- a/src/gui/util/qshadergraphloader.cpp
+++ b/src/gui/util/qshadergraphloader.cpp
@@ -39,6 +39,8 @@
#include "qshadergraphloader_p.h"
+#include "qshadernodesloader_p.h"
+
#include <QtCore/qdebug.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qjsonarray.h>
@@ -129,6 +131,19 @@ void QShaderGraphLoader::load()
bool hasError = false;
+ const auto prototypesValue = root.value(QStringLiteral("prototypes"));
+ if (!prototypesValue.isUndefined()) {
+ if (prototypesValue.isObject()) {
+ QShaderNodesLoader loader;
+ loader.load(prototypesValue.toObject());
+ m_prototypes.unite(loader.nodes());
+ } else {
+ qWarning() << "Invalid prototypes property, should be an object";
+ m_status = Error;
+ return;
+ }
+ }
+
const auto nodes = nodesValue.toArray();
for (const auto &nodeValue : nodes) {
if (!nodeValue.isObject()) {
diff --git a/src/gui/util/qshadernodesloader.cpp b/src/gui/util/qshadernodesloader.cpp
index db34b6d44d..692653ee44 100644
--- a/src/gui/util/qshadernodesloader.cpp
+++ b/src/gui/util/qshadernodesloader.cpp
@@ -99,11 +99,15 @@ void QShaderNodesLoader::load()
}
const auto root = document.object();
+ load(root);
+}
+void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
+{
bool hasError = false;
- for (const auto &property : root.keys()) {
- const auto nodeValue = root.value(property);
+ for (const auto &property : prototypesObject.keys()) {
+ const auto nodeValue = prototypesObject.value(property);
if (!nodeValue.isObject()) {
qWarning() << "Invalid node found";
hasError = true;
diff --git a/src/gui/util/qshadernodesloader_p.h b/src/gui/util/qshadernodesloader_p.h
index 2696e958b6..0bec871857 100644
--- a/src/gui/util/qshadernodesloader_p.h
+++ b/src/gui/util/qshadernodesloader_p.h
@@ -78,6 +78,7 @@ public:
Q_GUI_EXPORT void setDevice(QIODevice *device) Q_DECL_NOTHROW;
Q_GUI_EXPORT void load();
+ Q_GUI_EXPORT void load(const QJsonObject &prototypesObject);
private:
Status m_status;
diff --git a/src/gui/util/qtexturefiledata.cpp b/src/gui/util/qtexturefiledata.cpp
new file mode 100644
index 0000000000..ebf46f8e4e
--- /dev/null
+++ b/src/gui/util/qtexturefiledata.cpp
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexturefiledata_p.h"
+#include <QMetaEnum>
+#include <QSize>
+#if QT_CONFIG(opengl)
+#include <QOpenGLTexture>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQtGuiTextureIO, "qt.gui.textureio");
+
+class QTextureFileDataPrivate : public QSharedData
+{
+public:
+ QTextureFileDataPrivate()
+ {
+ }
+
+ QTextureFileDataPrivate(const QTextureFileDataPrivate &other)
+ : QSharedData(other),
+ logName(other.logName),
+ data(other.data),
+ offsets(other.offsets),
+ lengths(other.lengths),
+ size(other.size),
+ format(other.format)
+ {
+ }
+
+ ~QTextureFileDataPrivate()
+ {
+ }
+
+ void ensureLevels(int num, bool force = false)
+ {
+ const int newSize = force ? num : qMax(offsets.size(), num);
+ offsets.resize(newSize);
+ lengths.resize(newSize);
+ }
+
+ QByteArray logName;
+ QByteArray data;
+ QVector<int> offsets;
+ QVector<int> lengths;
+ QSize size;
+ quint32 format = 0;
+ quint32 internalFormat = 0;
+ quint32 baseInternalFormat = 0;
+};
+
+
+
+QTextureFileData::QTextureFileData()
+{
+}
+
+QTextureFileData::QTextureFileData(const QTextureFileData &other)
+ : d(other.d)
+{
+}
+
+QTextureFileData &QTextureFileData::operator=(const QTextureFileData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+QTextureFileData::~QTextureFileData()
+{
+}
+
+bool QTextureFileData::isNull() const
+{
+ return !d;
+}
+
+bool QTextureFileData::isValid() const
+{
+ if (!d)
+ return false;
+
+ if (d->data.isEmpty() || d->size.isEmpty() || (!d->format && !d->internalFormat))
+ return false;
+
+ const int numChunks = d->offsets.size();
+ if (numChunks == 0 || (d->lengths.size() != numChunks))
+ return false;
+
+ const qint64 sz = d->data.size();
+ for (int i = 0; i < numChunks; i++) {
+ qint64 offi = d->offsets.at(i);
+ qint64 leni = d->lengths.at(i);
+ if (offi < 0 || offi >= sz || leni <= 0 || (offi + leni > sz))
+ return false;
+ }
+ return true;
+}
+
+void QTextureFileData::clear()
+{
+ d = nullptr;
+}
+
+QByteArray QTextureFileData::data() const
+{
+ return d ? d->data : QByteArray();
+}
+
+void QTextureFileData::setData(const QByteArray &data)
+{
+ if (!d.constData()) //### uh think about this design, this is the only way to create; should be constructor instead at least
+ d = new QTextureFileDataPrivate;
+
+ d->data = data;
+}
+
+int QTextureFileData::dataOffset(int level) const
+{
+ return (d && d->offsets.size() > level) ? d->offsets.at(level) : 0;
+}
+
+void QTextureFileData::setDataOffset(int offset, int level)
+{
+ if (d.constData() && level >= 0) {
+ d->ensureLevels(level + 1);
+ d->offsets[level] = offset;
+ }
+}
+
+int QTextureFileData::dataLength(int level) const
+{
+ return (d && d->lengths.size() > level) ? d->lengths.at(level) : 0;
+}
+
+void QTextureFileData::setDataLength(int length, int level)
+{
+ if (d.constData() && level >= 0) {
+ d->ensureLevels(level + 1);
+ d->lengths[level] = length;
+ }
+}
+
+int QTextureFileData::numLevels() const
+{
+ return d ? d->offsets.size() : 0;
+}
+
+void QTextureFileData::setNumLevels(int num)
+{
+ if (d && num >= 0)
+ d->ensureLevels(num, true);
+}
+
+QSize QTextureFileData::size() const
+{
+ return d ? d->size : QSize();
+}
+
+void QTextureFileData::setSize(const QSize &size)
+{
+ if (d.constData())
+ d->size = size;
+}
+
+quint32 QTextureFileData::glFormat() const
+{
+ return d ? d->format : 0;
+}
+
+void QTextureFileData::setGLFormat(quint32 format)
+{
+ if (d.constData())
+ d->format = format;
+}
+
+quint32 QTextureFileData::glInternalFormat() const
+{
+ return d ? d->internalFormat : 0;
+}
+
+void QTextureFileData::setGLInternalFormat(quint32 format)
+{
+ if (d.constData())
+ d->internalFormat = format;
+}
+
+quint32 QTextureFileData::glBaseInternalFormat() const
+{
+ return d ? d->baseInternalFormat : 0;
+}
+
+void QTextureFileData::setGLBaseInternalFormat(quint32 format)
+{
+ if (d.constData())
+ d->baseInternalFormat = format;
+}
+
+QByteArray QTextureFileData::logName() const
+{
+ return d ? d->logName : QByteArray();
+}
+
+void QTextureFileData::setLogName(const QByteArray &name)
+{
+ if (d.constData())
+ d->logName = name;
+}
+
+static QByteArray glFormatName(quint32 fmt)
+{
+ const char *id = 0;
+#if QT_CONFIG(opengl)
+ id = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>().valueToKey(fmt);
+#endif
+ QByteArray res(id ? id : "(?)");
+ res += " [0x" + QByteArray::number(fmt, 16).rightJustified(4, '0') + ']';
+ return res;
+}
+
+QDebug operator<<(QDebug dbg, const QTextureFileData &d)
+{
+ QDebugStateSaver saver(dbg);
+
+ dbg.nospace() << "QTextureFileData(";
+ if (!d.isNull()) {
+ dbg.space() << d.logName() << d.size();
+ dbg << "glFormat:" << glFormatName(d.glFormat());
+ dbg << "glInternalFormat:" << glFormatName(d.glInternalFormat());
+ dbg << "glBaseInternalFormat:" << glFormatName(d.glBaseInternalFormat());
+ dbg.nospace() << "Levels: " << d.numLevels();
+ if (!d.isValid())
+ dbg << " {Invalid}";
+ dbg << ")";
+ } else {
+ dbg << "null)";
+ }
+
+ return dbg;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qtexturefiledata_p.h b/src/gui/util/qtexturefiledata_p.h
new file mode 100644
index 0000000000..2df23de33c
--- /dev/null
+++ b/src/gui/util/qtexturefiledata_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTUREFILEDATA_P_H
+#define QTEXTUREFILEDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QSharedDataPointer>
+#include <QLoggingCategory>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQtGuiTextureIO)
+
+class QTextureFileDataPrivate;
+
+class Q_GUI_EXPORT QTextureFileData
+{
+public:
+ QTextureFileData();
+ QTextureFileData(const QTextureFileData &other);
+ QTextureFileData &operator=(const QTextureFileData &other);
+ ~QTextureFileData();
+
+ bool isNull() const;
+ bool isValid() const;
+
+ void clear();
+
+ QByteArray data() const;
+ void setData(const QByteArray &data);
+
+ int dataOffset(int level = 0) const;
+ void setDataOffset(int offset, int level = 0);
+
+ int dataLength(int level = 0) const;
+ void setDataLength(int length, int level = 0);
+
+ int numLevels() const;
+ void setNumLevels(int num);
+
+ QSize size() const;
+ void setSize(const QSize &size);
+
+ quint32 glFormat() const;
+ void setGLFormat(quint32 format);
+
+ quint32 glInternalFormat() const;
+ void setGLInternalFormat(quint32 format);
+
+ quint32 glBaseInternalFormat() const;
+ void setGLBaseInternalFormat(quint32 format);
+
+ QByteArray logName() const;
+ void setLogName(const QByteArray &name);
+
+private:
+ QSharedDataPointer<QTextureFileDataPrivate> d;
+};
+
+Q_DECLARE_TYPEINFO(QTextureFileData, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QTextureFileData &d);
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTLAYOUTSTYLEINFO_P_H
diff --git a/src/gui/util/qtexturefilehandler_p.h b/src/gui/util/qtexturefilehandler_p.h
new file mode 100644
index 0000000000..b808d3e7db
--- /dev/null
+++ b/src/gui/util/qtexturefilehandler_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTUREFILEHANDLER_P_H
+#define QTEXTUREFILEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtexturefiledata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextureFileHandler
+{
+public:
+ QTextureFileHandler(QIODevice *device, const QByteArray &logName = QByteArray())
+ : m_device(device)
+ {
+ m_logName = !logName.isEmpty() ? logName : QByteArrayLiteral("(unknown)");
+ }
+ virtual ~QTextureFileHandler() {}
+
+ virtual QTextureFileData read() = 0;
+ QIODevice *device() const { return m_device; }
+ QByteArray logName() const { return m_logName; }
+
+private:
+ QIODevice *m_device = nullptr;
+ QByteArray m_logName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTUREFILEHANDLER_P_H
diff --git a/src/gui/util/qtexturefilereader.cpp b/src/gui/util/qtexturefilereader.cpp
new file mode 100644
index 0000000000..5d4bd600e0
--- /dev/null
+++ b/src/gui/util/qtexturefilereader.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexturefilereader_p.h"
+
+#include "qpkmhandler_p.h"
+#include "qktxhandler_p.h"
+
+#include <QFileInfo>
+
+QT_BEGIN_NAMESPACE
+
+QTextureFileReader::QTextureFileReader(QIODevice *device, const QString &fileName)
+ : m_device(device), m_fileName(fileName)
+{
+}
+
+QTextureFileReader::~QTextureFileReader()
+{
+ delete m_handler;
+}
+
+QTextureFileData QTextureFileReader::read()
+{
+ if (!canRead())
+ return QTextureFileData();
+ return m_handler->read();
+}
+
+bool QTextureFileReader::canRead()
+{
+ if (!checked) {
+ checked = true;
+ if (!init())
+ return false;
+
+ QByteArray headerBlock = m_device->peek(64);
+ QFileInfo fi(m_fileName);
+ QByteArray suffix = fi.suffix().toLower().toLatin1();
+ QByteArray logName = fi.fileName().toUtf8();
+
+ // Currently the handlers are hardcoded; later maybe a list of plugins
+ if (QPkmHandler::canRead(suffix, headerBlock)) {
+ m_handler = new QPkmHandler(m_device, logName);
+ } else if (QKtxHandler::canRead(suffix, headerBlock)) {
+ m_handler = new QKtxHandler(m_device, logName);
+ }
+ // else if OtherHandler::canRead() ...etc.
+ }
+ return (m_handler != nullptr);
+}
+
+QList<QByteArray> QTextureFileReader::supportedFileFormats()
+{
+ // Hardcoded for now
+ return {QByteArrayLiteral("pkm"), QByteArrayLiteral("ktx")};
+}
+
+bool QTextureFileReader::init()
+{
+ if (!m_device)
+ return false;
+ return m_device->isReadable();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/image/qimage_sse4.cpp b/src/gui/util/qtexturefilereader_p.h
index 0e2c2f492e..2ec0b0cc49 100644
--- a/src/gui/image/qimage_sse4.cpp
+++ b/src/gui/util/qtexturefilereader_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -37,40 +37,52 @@
**
****************************************************************************/
-#include <qimage.h>
-#include <private/qdrawhelper_p.h>
-#include <private/qdrawingprimitive_sse2_p.h>
-#include <private/qimage_p.h>
-#include <private/qsimd_p.h>
+#ifndef QTEXTUREFILEREADER_H
+#define QTEXTUREFILEREADER_H
-#ifdef QT_COMPILER_SUPPORTS_SSE4_1
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtexturefiledata_p.h"
+#include <QString>
+#include <QFileInfo>
QT_BEGIN_NAMESPACE
-const uint *QT_FASTCALL convertRGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QVector<QRgb> *, QDitherInfo *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = 0xff000000 | qUnpremultiply_sse4(src[i]);
- return buffer;
-}
+class QIODevice;
+class QTextureFileHandler;
-void convert_ARGB_to_ARGB_PM_sse4(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+class Q_GUI_EXPORT QTextureFileReader
{
- Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888);
- Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied);
- Q_ASSERT(src->width == dest->width);
- Q_ASSERT(src->height == dest->height);
+public:
+ QTextureFileReader(QIODevice *device, const QString &fileName = QString()); //### drop this logname thing?
+ ~QTextureFileReader();
+
+ bool canRead();
+ QTextureFileData read();
- const uint *src_data = (uint *) src->data;
- uint *dest_data = (uint *) dest->data;
- for (int i = 0; i < src->height; ++i) {
- qt_convertARGB32ToARGB32PM(dest_data, src_data, src->width);
- src_data += src->bytes_per_line >> 2;
- dest_data += dest->bytes_per_line >> 2;
- }
-}
+ // TBD access function to params
+ // TBD ask for identified fmt
+
+ static QList<QByteArray> supportedFileFormats();
+
+private:
+ bool init();
+ QIODevice *m_device = nullptr;
+ QString m_fileName;
+ QTextureFileHandler *m_handler = nullptr;
+ bool checked = false;
+};
QT_END_NAMESPACE
-#endif // QT_COMPILER_SUPPORTS_SSE4_1
+
+#endif // QTEXTUREFILEREADER_H
diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp
index 7982ad967e..2237b016e9 100644
--- a/src/gui/util/qvalidator.cpp
+++ b/src/gui/util/qvalidator.cpp
@@ -411,13 +411,15 @@ QValidator::State QIntValidator::validate(QString & input, int&) const
if (buff.isEmpty())
return Intermediate;
- if (b >= 0 && buff.startsWith('-'))
+ const bool startsWithMinus(buff[0] == '-');
+ if (b >= 0 && startsWithMinus)
return Invalid;
- if (t < 0 && buff.startsWith('+'))
+ const bool startsWithPlus(buff[0] == '+');
+ if (t < 0 && startsWithPlus)
return Invalid;
- if (buff.size() == 1 && (buff.at(0) == '+' || buff.at(0) == '-'))
+ if (buff.size() == 1 && (startsWithPlus || startsWithMinus))
return Intermediate;
bool ok;
@@ -433,7 +435,15 @@ QValidator::State QIntValidator::validate(QString & input, int&) const
if (entered >= 0) {
// the -entered < b condition is necessary to allow people to type
// the minus last (e.g. for right-to-left languages)
- return (entered > t && -entered < b) ? Invalid : Intermediate;
+ // The buffLength > tLength condition validates values consisting
+ // of a number of digits equal to or less than the max value as intermediate.
+
+ int buffLength = buff.size();
+ if (startsWithPlus)
+ buffLength--;
+ const int tLength = t != 0 ? static_cast<int>(std::log10(qAbs(t))) + 1 : 1;
+
+ return (entered > t && -entered < b && buffLength > tLength) ? Invalid : Intermediate;
} else {
return (entered < b) ? Invalid : Intermediate;
}
@@ -624,10 +634,10 @@ QDoubleValidator::~QDoubleValidator()
that is within the valid range and is in the correct format.
Returns \l Intermediate if \a input contains a double that is
- outside the range or is in the wrong format; e.g. with too many
- digits after the decimal point or is empty.
+ outside the range or is in the wrong format; e.g. is empty.
- Returns \l Invalid if the \a input is not a double.
+ Returns \l Invalid if the \a input is not a double or with too many
+ digits after the decimal point.
Note: If the valid range consists of just positive doubles (e.g. 0.0 to 100.0)
and \a input is a negative double then \l Invalid is returned. If notation()
@@ -690,8 +700,16 @@ QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QL
if (notation == QDoubleValidator::StandardNotation) {
double max = qMax(qAbs(q->b), qAbs(q->t));
if (max < LLONG_MAX) {
- qlonglong n = pow10(numDigits(qlonglong(max))) - 1;
- if (qAbs(i) > n)
+ qlonglong n = pow10(numDigits(qlonglong(max)));
+ // In order to get the highest possible number in the intermediate
+ // range we need to get 10 to the power of the number of digits
+ // after the decimal's and subtract that from the top number.
+ //
+ // For example, where q->dec == 2 and with a range of 0.0 - 9.0
+ // then the minimum possible number is 0.00 and the maximum
+ // possible is 9.99. Therefore 9.999 and 10.0 should be seen as
+ // invalid.
+ if (qAbs(i) > (n - std::pow(10, -q->dec)))
return QValidator::Invalid;
}
}
@@ -1061,7 +1079,7 @@ void QRegularExpressionValidatorPrivate::setRegularExpression(const QRegularExpr
if (origRe != re) {
usedRe = origRe = re; // copies also the pattern options
- usedRe.setPattern(QLatin1String("\\A(?:") + re.pattern() + QLatin1String(")\\z"));
+ usedRe.setPattern(QRegularExpression::anchoredPattern(re.pattern()));
emit q->regularExpressionChanged(re);
emit q->changed();
}
diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri
index cf3cbee48e..6324642505 100644
--- a/src/gui/util/util.pri
+++ b/src/gui/util/util.pri
@@ -14,7 +14,12 @@ HEADERS += \
util/qshaderlanguage_p.h \
util/qshadernode_p.h \
util/qshadernodeport_p.h \
- util/qshadernodesloader_p.h
+ util/qshadernodesloader_p.h \
+ util/qtexturefiledata_p.h \
+ util/qtexturefilereader_p.h \
+ util/qtexturefilehandler_p.h \
+ util/qpkmhandler_p.h \
+ util/qktxhandler_p.h
SOURCES += \
util/qdesktopservices.cpp \
@@ -29,4 +34,8 @@ SOURCES += \
util/qshaderlanguage.cpp \
util/qshadernode.cpp \
util/qshadernodeport.cpp \
- util/qshadernodesloader.cpp
+ util/qshadernodesloader.cpp \
+ util/qtexturefiledata.cpp \
+ util/qtexturefilereader.cpp \
+ util/qpkmhandler.cpp \
+ util/qktxhandler.cpp
diff --git a/src/gui/vulkan/qvulkanfunctions.cpp b/src/gui/vulkan/qvulkanfunctions.cpp
index c5f9616d20..73dbeb9ab6 100644
--- a/src/gui/vulkan/qvulkanfunctions.cpp
+++ b/src/gui/vulkan/qvulkanfunctions.cpp
@@ -66,16 +66,7 @@ QT_BEGIN_NAMESPACE
The typical usage is the following:
- \code
- void Window::render()
- {
- QVulkanInstance *inst = vulkanInstance();
- QVulkanFunctions *f = inst->functions();
- ...
- VkResult err = f->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
- ...
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkanfunctions.cpp 0
\note Windowing system interface (WSI) specifics and extensions are
excluded. This class only covers core Vulkan commands, with the exception
@@ -118,15 +109,7 @@ QT_BEGIN_NAMESPACE
The typical usage is the following:
- \code
- void Window::render()
- {
- QVulkanInstance *inst = vulkanInstance();
- QVulkanDeviceFunctions *df = inst->deviceFunctions(device);
- VkResult err = df->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
- ...
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkanfunctions.cpp 1
The QVulkanDeviceFunctions object specific to the provided VkDevice is
created when QVulkanInstance::deviceFunctions() is first called with the
diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp
index 8236d9625a..000c2b8caa 100644
--- a/src/gui/vulkan/qvulkaninstance.cpp
+++ b/src/gui/vulkan/qvulkaninstance.cpp
@@ -97,22 +97,7 @@ QT_BEGIN_NAMESPACE
calling QWindow::setVulkanInstance(). Thus a typical application pattern is
the following:
- \code
- int main(int argc, char **argv)
- {
- QGuiApplication app(argc, argv);
-
- QVulkanInstance inst;
- if (!inst.create())
- return 1;
-
- ...
- window->setVulkanInstance(&inst);
- window->show();
-
- return app.exec();
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkaninstance.cpp 0
\section1 Configuration
@@ -138,31 +123,12 @@ QT_BEGIN_NAMESPACE
For example, to enable the standard validation layers, one could do the
following:
- \code
- QVulkanInstance inst;
-
- // Enable validation layer, if supported. Messages go to qDebug by default.
- inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
-
- bool ok = inst.create();
- if (!ok)
- ... // Vulkan not available
- if (!inst.layers().contains("VK_LAYER_LUNARG_standard_validation"))
- ... // validation layer not available
- \endcode
+ \snippet code/src_gui_vulkan_qvulkaninstance.cpp 1
Or, alternatively, to make decisions before attempting to create a Vulkan
instance:
- \code
- QVulkanInstance inst;
-
- if (inst.supportedLayers().contains("VK_LAYER_LUNARG_standard_validation"))
- ...
-
- bool ok = inst.create();
- ...
- \endcode
+ \snippet code/src_gui_vulkan_qvulkaninstance.cpp 2
\section1 Adopting an Existing Instance
@@ -229,62 +195,7 @@ QT_BEGIN_NAMESPACE
The following is the basic outline of creating a Vulkan-capable QWindow:
- \code
- class VulkanWindow : public QWindow
- {
- public:
- VulkanWindow() {
- setSurfaceType(VulkanSurface);
- }
-
- void exposeEvent(QExposeEvent *) {
- if (isExposed()) {
- if (!m_initialized) {
- m_initialized = true;
- // initialize device, swapchain, etc.
- QVulkanInstance *inst = vulkanInstance();
- QVulkanFunctions *f = inst->functions();
- uint32_t devCount = 0;
- f->vkEnumeratePhysicalDevices(inst->vkInstance(), &devCount, nullptr);
- ...
- // build the first frame
- render();
- }
- }
- }
-
- bool event(QEvent *e) {
- if (e->type == QEvent::UpdateRequest)
- render();
- return QWindow::event(e);
- }
-
- void render() {
- ...
- requestUpdate(); // render continuously
- }
-
- private:
- bool m_initialized = false;
- };
-
- int main(int argc, char **argv)
- {
- QGuiApplication app(argc, argv);
-
- QVulkanInstance inst;
- if (!inst.create()) {
- qWarning("Vulkan not available");
- return 1;
- }
-
- VulkanWindow window;
- window.showMaximized();
-
- return app.exec();
-
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkaninstance.cpp 3
\note In addition to expose, a well-behaving window implementation will
also have to take care of additional events like resize and
diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp
index e45a16170e..6d12377a60 100644
--- a/src/gui/vulkan/qvulkanwindow.cpp
+++ b/src/gui/vulkan/qvulkanwindow.cpp
@@ -74,60 +74,7 @@ Q_LOGGING_CATEGORY(lcGuiVk, "qt.vulkan")
A typical application using QVulkanWindow may look like the following:
- \code
- class VulkanRenderer : public QVulkanWindowRenderer
- {
- public:
- VulkanRenderer(QVulkanWindow *w) : m_window(w) { }
-
- void initResources() override
- {
- m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
- ...
- }
- void initSwapChainResources() override { ... }
- void releaseSwapChainResources() override { ... }
- void releaseResources() override { ... }
-
- void startNextFrame() override
- {
- VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
- ...
- m_devFuncs->vkCmdBeginRenderPass(...);
- ...
- m_window->frameReady();
- }
-
- private:
- QVulkanWindow *m_window;
- QVulkanDeviceFunctions *m_devFuncs;
- };
-
- class VulkanWindow : public QVulkanWindow
- {
- public:
- QVulkanWindowRenderer *createRenderer() override {
- return new VulkanRenderer(this);
- }
- };
-
- int main(int argc, char *argv[])
- {
- QGuiApplication app(argc, argv);
-
- QVulkanInstance inst;
- // enable the standard validation layers, when available
- inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
- if (!inst.create())
- qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
-
- VulkanWindow w;
- w.setVulkanInstance(&inst);
- w.showMaximized();
-
- return app.exec();
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkanwindow.cpp 0
As it can be seen in the example, the main patterns in QVulkanWindow usage are:
@@ -989,7 +936,7 @@ bool QVulkanWindowPrivate::createDefaultRenderPass()
attDesc[1].samples = sampleCount;
attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
@@ -999,7 +946,7 @@ bool QVulkanWindowPrivate::createDefaultRenderPass()
attDesc[2].format = colorFormat;
attDesc[2].samples = sampleCount;
attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@@ -2164,8 +2111,8 @@ void QVulkanWindowPrivate::addReadback()
barrier.image = frameGrabImage;
devFuncs->vkCmdPipelineBarrier(image.cmdBuf,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT,
0, 0, nullptr, 0, nullptr,
1, &barrier);
}
@@ -2298,6 +2245,11 @@ uint32_t QVulkanWindow::hostVisibleMemoryIndex() const
\note Calling this function is only valid from the invocation of
QVulkanWindowRenderer::initResources() up until
QVulkanWindowRenderer::releaseResources().
+
+ \note It is not guaranteed that this memory type is always suitable. The
+ correct, cross-implementation solution - especially for device local images
+ - is to manually pick a memory type after checking the mask returned from
+ \c{vkGetImageMemoryRequirements}.
*/
uint32_t QVulkanWindow::deviceLocalMemoryIndex() const
{
@@ -2434,18 +2386,7 @@ VkFramebuffer QVulkanWindow::currentFramebuffer() const
concurrentFrameCount(). Such arrays can then be indexed by the value
returned from this function.
- \code
- class Renderer {
- ...
- VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
- };
-
- void Renderer::startNextFrame()
- {
- VkDescriptorBufferInfo &uniformBufInfo(m_uniformBufInfo[m_window->currentFrame()]);
- ...
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkanwindow.cpp 1
\note This function must only be called from within startNextFrame() and, in
case of asynchronous command generation, up until the call to frameReady().
@@ -2472,20 +2413,7 @@ int QVulkanWindow::currentFrame() const
\note The value is constant for the entire lifetime of the QVulkanWindow.
- \code
- class Renderer {
- ...
- VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
- };
-
- void Renderer::startNextFrame()
- {
- const int count = m_window->concurrentFrameCount();
- for (int i = 0; i < count; ++i)
- m_uniformBufInfo[i] = ...
- ...
- }
- \endcode
+ \snippet code/src_gui_vulkan_qvulkanwindow.cpp 2
\sa currentFrame()
*/