summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/configure.json91
-rw-r--r--src/gui/doc/qtgui.qdocconf3
-rw-r--r--src/gui/doc/snippets/picture/picture.cpp79
-rw-r--r--src/gui/doc/src/includes/qt-colors.qdocinc86
-rw-r--r--src/gui/doc/src/includes/svg-colors.qdocinc594
-rw-r--r--src/gui/doc/src/richtext.qdoc69
-rw-r--r--src/gui/image/image.pri21
-rw-r--r--src/gui/image/qimage.cpp17
-rw-r--r--src/gui/image/qimage.h1
-rw-r--r--src/gui/image/qimage_conversions.cpp222
-rw-r--r--src/gui/image/qimage_p.h1
-rw-r--r--src/gui/image/qimage_ssse3.cpp7
-rw-r--r--src/gui/image/qpicture.cpp832
-rw-r--r--src/gui/image/qpicture.h78
-rw-r--r--src/gui/image/qpictureformatplugin.cpp135
-rw-r--r--src/gui/image/qpictureformatplugin.h76
-rw-r--r--src/gui/image/qpixmap_win.cpp3
-rw-r--r--src/gui/image/qpnghandler.cpp2
-rw-r--r--src/gui/kernel/qevent.cpp73
-rw-r--r--src/gui/kernel/qevent.h3
-rw-r--r--src/gui/kernel/qguiapplication.cpp52
-rw-r--r--src/gui/kernel/qguiapplication.h3
-rw-r--r--src/gui/kernel/qguiapplication_p.h1
-rw-r--r--src/gui/kernel/qguivariant.cpp1
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp337
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h43
-rw-r--r--src/gui/kernel/qopenglcontext.cpp3
-rw-r--r--src/gui/kernel/qplatformintegration.cpp3
-rw-r--r--src/gui/kernel/qplatformintegration.h3
-rw-r--r--src/gui/kernel/qplatformscreen.cpp22
-rw-r--r--src/gui/kernel/qplatformscreen.h3
-rw-r--r--src/gui/kernel/qscreen.cpp13
-rw-r--r--src/gui/kernel/qsurface.cpp6
-rw-r--r--src/gui/kernel/qsurfaceformat.cpp12
-rw-r--r--src/gui/kernel/qtouchdevice.cpp32
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp4
-rw-r--r--src/gui/opengl/qopenglprogrambinarycache.cpp3
-rw-r--r--src/gui/opengl/qopengltextureuploader.cpp18
-rw-r--r--src/gui/painting/painting.pri42
-rw-r--r--src/gui/painting/qbezier.cpp34
-rw-r--r--src/gui/painting/qbezier_p.h40
-rw-r--r--src/gui/painting/qcolor.cpp81
-rw-r--r--src/gui/painting/qcolor.h187
-rw-r--r--src/gui/painting/qcolorspace.cpp74
-rw-r--r--src/gui/painting/qcolorspace.h16
-rw-r--r--src/gui/painting/qcolorspace_p.h15
-rw-r--r--src/gui/painting/qcolortransform.h1
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp24
-rw-r--r--src/gui/painting/qdrawhelper.cpp30
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp35
-rw-r--r--src/gui/painting/qpainterpath.cpp21
-rw-r--r--src/gui/painting/qstroker_p.h2
-rw-r--r--src/gui/platform/wasm/qwasmlocalfileaccess.cpp37
-rw-r--r--src/gui/platform/wasm/qwasmlocalfileaccess_p.h2
-rw-r--r--src/gui/rhi/qrhi.cpp214
-rw-r--r--src/gui/rhi/qrhi_p.h7
-rw-r--r--src/gui/rhi/qrhi_p_p.h4
-rw-r--r--src/gui/rhi/qrhid3d11.cpp205
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h12
-rw-r--r--src/gui/rhi/qrhigles2.cpp179
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h25
-rw-r--r--src/gui/rhi/qrhimetal.mm15
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h4
-rw-r--r--src/gui/rhi/qrhinull.cpp15
-rw-r--r--src/gui/rhi/qrhinull_p_p.h4
-rw-r--r--src/gui/rhi/qrhiprofiler.cpp9
-rw-r--r--src/gui/rhi/qrhivulkan.cpp532
-rw-r--r--src/gui/rhi/qrhivulkan_p.h5
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h57
-rw-r--r--src/gui/rhi/qshader.cpp12
-rw-r--r--src/gui/rhi/qshaderdescription.cpp18
-rw-r--r--src/gui/text/AGLFN_LICENSE.txt26
-rw-r--r--src/gui/text/qcssparser.cpp22
-rw-r--r--src/gui/text/qcssparser_p.h3
-rw-r--r--src/gui/text/qfont.cpp8
-rw-r--r--src/gui/text/qfont_p.h10
-rw-r--r--src/gui/text/qfontengine.cpp25
-rw-r--r--src/gui/text/qfontengine_p.h23
-rw-r--r--src/gui/text/qfontmetrics.cpp27
-rw-r--r--src/gui/text/qfontmetrics.h4
-rw-r--r--src/gui/text/qt_attribution.json17
-rw-r--r--src/gui/text/qtextdocument.cpp80
-rw-r--r--src/gui/text/qtextdocument_p.cpp3
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp35
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp931
-rw-r--r--src/gui/text/qtextformat.cpp279
-rw-r--r--src/gui/text/qtextformat.h110
-rw-r--r--src/gui/text/qtexthtmlparser.cpp43
-rw-r--r--src/gui/text/qtexthtmlparser_p.h9
-rw-r--r--src/gui/text/qtextlayout.h4
90 files changed, 4388 insertions, 2181 deletions
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 9d01f54995..d5cbe6f78a 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -1,7 +1,7 @@
{
"module": "gui",
"depends": [
- "core",
+ "core-private",
"network"
],
"testDir": "../../config.tests",
@@ -41,11 +41,10 @@
"sm": { "type": "boolean", "name": "sessionmanager" },
"tslib": "boolean",
"vulkan": "boolean",
- "xcb": { "type": "enum", "values": [ "no", "yes", "qt", "system" ] },
+ "xcb": "boolean",
+ "bundled-xcb-xinput": "boolean",
"xcb-native-painting": "boolean",
"xcb-xlib": "boolean",
- "xcb-xinput": "boolean",
- "xkb": "boolean",
"xkbcommon": "boolean"
}
},
@@ -61,6 +60,7 @@
"export": "",
"headers": ["bcm_host.h"],
"sources": [
+ { "type": "pkgConfig", "args": "bcm_host" },
{ "type": "makeSpec", "spec": "BCM_HOST" }
]
},
@@ -572,18 +572,22 @@
]
},
"xcb": {
- "label": "XCB >= 1.9",
+ "label": "XCB >= 1.11",
"test": {
"main": [
"int primaryScreen = 0;",
"(void)xcb_connect(\"\", &primaryScreen);",
- "// This won't compile unless libxcb >= 1.9 which defines XCB_CONN_CLOSED_INVALID_SCREEN.",
- "int xcbScreenError = XCB_CONN_CLOSED_INVALID_SCREEN;"
+ "/* XCB_PACKED define was added in libxcb 1.11 */",
+ "#ifdef XCB_PACKED",
+ " return 0;",
+ "#else",
+ " return -1;",
+ "#endif"
]
},
"headers": "xcb/xcb.h",
"sources": [
- { "type": "pkgConfig", "args": "xcb >= 1.9" },
+ { "type": "pkgConfig", "args": "xcb >= 1.11" },
"-lxcb"
]
},
@@ -690,21 +694,10 @@
"use": "xcb xlib"
},
"xcb_xkb": {
- "label": "XCB XKB >= 1.10",
- "test": {
- "head": [
- "// xkb.h is using a variable called 'explicit', which is a reserved keyword in C++",
- "#define explicit dont_use_cxx_explicit"
- ],
- "tail": "#undef explicit",
- "main": [
- "// This takes more arguments in xcb-xkb < 1.10.",
- "xcb_xkb_get_kbd_by_name_unchecked(NULL, 0, 0, 0, 0);"
- ]
- },
+ "label": "XCB XKB",
"headers": "xcb/xkb.h",
"sources": [
- { "type": "pkgConfig", "args": "xcb-xkb >= 1.10" },
+ { "type": "pkgConfig", "args": "xcb-xkb" },
"-lxcb-xkb"
],
"use": "xcb"
@@ -768,7 +761,7 @@
"xkbcommon_x11": {
"label": "xkbcommon-x11",
"test": {
- "main": "xkb_x11_get_core_keyboard_device_id(nullptr);"
+ "main": "xkb_x11_setup_xkb_extension_flags flag = XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS;"
},
"headers": [ "xkbcommon/xkbcommon-x11.h" ],
"sources": [
@@ -1055,6 +1048,11 @@
"label": "XCB (extensions)",
"type": "compile",
"test": {
+ "head": [
+ "// xkb.h is using a variable called 'explicit', which is a reserved keyword in C++",
+ "#define explicit dont_use_cxx_explicit"
+ ],
+ "tail": "#undef explicit",
"include": [
"xcb/xcb.h",
"xcb/xcb_image.h",
@@ -1067,7 +1065,8 @@
"xcb/xfixes.h",
"xcb/xinerama.h",
"xcb/xcb_icccm.h",
- "xcb/xcb_renderutil.h"
+ "xcb/xcb_renderutil.h",
+ "xcb/xkb.h"
],
"main": [
"int primaryScreen = 0;",
@@ -1081,10 +1080,13 @@
" xcb_render_query_pict_formats_reply(c, formatsCookie, &error);",
"/* RENDERUTIL: xcb_renderutil.h include won't compile unless version >= 0.3.9 */",
- "xcb_render_util_find_standard_format(nullptr, XCB_PICT_STANDARD_ARGB_32);"
+ "xcb_render_util_find_standard_format(nullptr, XCB_PICT_STANDARD_ARGB_32);",
+
+ "/* XKB: This takes more arguments in xcb-xkb < 1.11 */",
+ "xcb_xkb_get_kbd_by_name_replies_key_names_value_list_sizeof(nullptr, 0, 0, 0, 0, 0, 0, 0, 0);"
]
},
- "use": "xcb_icccm xcb_image xcb_keysyms xcb_randr xcb_render xcb_renderutil xcb_shape xcb_shm xcb_sync xcb_xfixes xcb_xinerama xcb"
+ "use": "xcb_icccm xcb_image xcb_keysyms xcb_randr xcb_render xcb_renderutil xcb_shape xcb_shm xcb_sync xcb_xfixes xcb_xinerama xcb_xkb xcb"
},
"x11prefix": {
"label": "X11 prefix",
@@ -1373,7 +1375,7 @@
},
"egl": {
"label": "EGL",
- "condition": "(features.opengl || features.openvg) && (features.angle || libs.egl)",
+ "condition": "(features.opengl || features.openvg) && (features.angle || libs.egl) && (features.dlopen || !config.unix)",
"output": [ "privateFeature", "feature" ]
},
"egl_x11": {
@@ -1512,16 +1514,7 @@
"label": "XCB",
"section": "Platform plugins",
"autoDetect": "!config.darwin",
- "enable": "input.xcb == 'system' || input.xcb == 'qt' || input.xcb == 'yes'",
- "condition": "features.thread && features.xkbcommon && libs.xcb",
- "output": [ "privateFeature" ]
- },
- "system-xcb": {
- "label": "Using system-provided XCB libraries",
- "enable": "input.xcb == 'system'",
- "disable": "input.xcb == 'qt'",
- "autoDetect": "!config.darwin",
- "condition": "features.xcb && tests.xcb_syslibs",
+ "condition": "features.thread && libs.xcb && tests.xcb_syslibs && features.xkbcommon-x11",
"output": [ "privateFeature" ]
},
"x11-prefix": {
@@ -1560,12 +1553,6 @@
"condition": "features.xcb-native-painting",
"output": [ "privateFeature" ]
},
- "xkb": {
- "label": "XCB XKB",
- "emitIf": "features.xcb",
- "condition": "(!features.system-xcb || libs.xcb_xkb) && libs.xkbcommon_x11",
- "output": [ "privateFeature" ]
- },
"xcb-xlib": {
"label": "XCB Xlib",
"condition": "features.xlib && libs.xcb_xlib",
@@ -1577,10 +1564,12 @@
"condition": "features.sessionmanager && libs.x11sm",
"output": [ "privateFeature" ]
},
- "xcb-xinput": {
- "label": "XCB XInput",
+ "system-xcb-xinput": {
+ "label": "Using system-provided xcb-xinput",
"emitIf": "features.xcb",
- "condition": "!features.system-xcb || libs.xcb_xinput",
+ "disable": "input.bundled-xcb-xinput == 'yes'",
+ "enable": "input.bundled-xcb-xinput == 'no'",
+ "condition": "libs.xcb_xinput",
"output": [ "privateFeature" ]
},
"xkbcommon": {
@@ -1588,6 +1577,11 @@
"condition": "libs.xkbcommon",
"output": [ "privateFeature" ]
},
+ "xkbcommon-x11": {
+ "label": "xkbcommon-x11",
+ "condition": "features.xkbcommon && libs.xkbcommon_x11",
+ "output": [ "privateFeature" ]
+ },
"xlib": {
"label": "XLib",
"autoDetect": "!config.darwin || features.xcb",
@@ -1829,7 +1823,7 @@
{
"type": "error",
"condition": "input.xcb != '' && input.xcb != 'no' && input.xkbcommon == 'no'",
- "message": "XCB plugin requires xkbcommon, but -no-xkbcommon was provided."
+ "message": "XCB plugin requires xkbcommon and xkbcommon-x11, but -no-xkbcommon was provided."
}
],
@@ -1943,7 +1937,8 @@
"entries": [
"xlib",
"xcb-xlib",
- "egl_x11"
+ "egl_x11",
+ "xkbcommon-x11"
]
}
]
@@ -1976,7 +1971,7 @@
"section": "XCB",
"condition": "features.xcb",
"entries": [
- "system-xcb", "xkb", "xcb-xinput", "xcb-native-painting",
+ "system-xcb-xinput", "xcb-native-painting",
{
"section": "GL integrations",
"entries": [
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index 049b9ef179..76dd6d7ea1 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -67,3 +67,6 @@ navigation.cppclassespage = "Qt GUI C++ Classes"
# Ignore warnings about undocumented enum values for the QGradient presets
spurious += "Undocumented enum item '.*' in QGradient::Preset"
+
+# \svgcolor {#ffdead}
+macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>"
diff --git a/src/gui/doc/snippets/picture/picture.cpp b/src/gui/doc/snippets/picture/picture.cpp
index 863476fdbf..807c263b19 100644
--- a/src/gui/doc/snippets/picture/picture.cpp
+++ b/src/gui/doc/snippets/picture/picture.cpp
@@ -80,82 +80,3 @@ int main()
painter.end(); // painting done
//! [1]
}
-
- QPicture myPicture;
- {
- // FORMATS
-//! [2]
- const QStringList list = QPicture::inputFormatList();
- for (const QString &string : list)
- myProcessing(string);
-//! [2]
- }
-
- {
- // OUTPUT
-//! [3]
- const QStringList list = QPicture::outputFormatList();
- for (const QString &string : list)
- myProcessing(string);
-//! [3]
- }
-
- {
- // PIC READ
-//! [4]
- QPictureIO iio;
- QPixmap pixmap;
- iio.setFileName("vegeburger.pic");
- if (iio.read()) { // OK
- QPicture picture = iio.picture();
- QPainter painter(&pixmap);
- painter.drawPicture(0, 0, picture);
- }
-//! [4]
- }
-
- {
- QPixmap pixmap;
- // PIC WRITE
-//! [5]
- QPictureIO iio;
- QPicture picture;
- QPainter painter(&picture);
- painter.drawPixmap(0, 0, pixmap);
- iio.setPicture(picture);
- iio.setFileName("vegeburger.pic");
- iio.setFormat("PIC");
- if (iio.write())
- return true; // returned true if written successfully
-//! [5]
- }
-
-}
-
-// SVG READ
-//! [6]
-void readSVG(QPictureIO *picture)
-{
- // read the picture using the picture->ioDevice()
-}
-//! [6]
-
-// SVG WRITE
-//! [7]
-void writeSVG(QPictureIO *picture)
-{
- // write the picture using the picture->ioDevice()
-}
-//! [7]
-
-// USE SVG
-void foo() {
-
-//! [8]
- // add the SVG picture handler
- // ...
-//! [8]
- QPictureIO::defineIOHandler("SVG", 0, 0, readSVG, writeSVG);
- // ...
-
-}
diff --git a/src/gui/doc/src/includes/qt-colors.qdocinc b/src/gui/doc/src/includes/qt-colors.qdocinc
new file mode 100644
index 0000000000..4c082323b6
--- /dev/null
+++ b/src/gui/doc/src/includes/qt-colors.qdocinc
@@ -0,0 +1,86 @@
+\table
+\header
+ \li Name
+ \li Hex
+ \li Color
+\row
+ \li Color0
+ \li #000000
+ \li \svgcolor {#000000}
+\row
+ \li Color1
+ \li #ffffff
+ \li \svgcolor {#ffffff}
+\row
+ \li Black
+ \li #000000
+ \li \svgcolor {#000000}
+\row
+ \li White
+ \li #ffffff
+ \li \svgcolor {#ffffff}
+\row
+ \li DarkGray
+ \li #808080
+ \li \svgcolor {#808080}
+\row
+ \li Gray
+ \li #a0a0a4
+ \li \svgcolor {#a0a0a4}
+\row
+ \li LightGray
+ \li #c0c0c0
+ \li \svgcolor {#c0c0c0}
+\row
+ \li Red
+ \li #ff0000
+ \li \svgcolor {#ff0000}
+\row
+ \li Green
+ \li #00ff00
+ \li \svgcolor {#00ff00}
+\row
+ \li Blue
+ \li #0000ff
+ \li \svgcolor {#0000ff}
+\row
+ \li Cyan
+ \li #00ffff
+ \li \svgcolor {#00ffff}
+\row
+ \li Magenta
+ \li #ff00ff
+ \li \svgcolor {#ff00ff}
+\row
+ \li Yellow
+ \li #ffff00
+ \li \svgcolor {#ffff00}
+\row
+ \li DarkRed
+ \li #800000
+ \li \svgcolor {#800000}
+\row
+ \li DarkGreen
+ \li #008000
+ \li \svgcolor {#008000}
+\row
+ \li DarkBlue
+ \li #000080
+ \li \svgcolor {#000080}
+\row
+ \li DarkCyan
+ \li #008080
+ \li \svgcolor {#008080}
+\row
+ \li DarkMagenta
+ \li #800080
+ \li \svgcolor {#800080}
+\row
+ \li DarkYellow
+ \li #808000
+ \li \svgcolor {#808000}
+\row
+ \li Transparent
+ \li #00000000
+ \li (transparent)
+\endtable
diff --git a/src/gui/doc/src/includes/svg-colors.qdocinc b/src/gui/doc/src/includes/svg-colors.qdocinc
new file mode 100644
index 0000000000..4e5fb56d5e
--- /dev/null
+++ b/src/gui/doc/src/includes/svg-colors.qdocinc
@@ -0,0 +1,594 @@
+\table
+\header
+ \li Name
+ \li Hex
+ \li Color
+\row
+ \li aliceblue
+ \li #f0f8ff
+ \li \svgcolor {#f0f8ff}
+\row
+ \li antiquewhite
+ \li #faebd7
+ \li \svgcolor {#faebd7}
+\row
+ \li aqua
+ \li #00ffff
+ \li \svgcolor {#00ffff}
+\row
+ \li aquamarine
+ \li #7fffd4
+ \li \svgcolor {#7fffd4}
+\row
+ \li azure
+ \li #f0ffff
+ \li \svgcolor {#f0ffff}
+\row
+ \li beige
+ \li #f5f5dc
+ \li \svgcolor {#f5f5dc}
+\row
+ \li bisque
+ \li #ffe4c4
+ \li \svgcolor {#ffe4c4}
+\row
+ \li black
+ \li #000000
+ \li \svgcolor {#000000}
+\row
+ \li blanchedalmond
+ \li #ffebcd
+ \li \svgcolor {#ffebcd}
+\row
+ \li blue
+ \li #0000ff
+ \li \svgcolor {#0000ff}
+\row
+ \li blueviolet
+ \li #8a2be2
+ \li \svgcolor {#8a2be2}
+\row
+ \li brown
+ \li #a52a2a
+ \li \svgcolor {#a52a2a}
+\row
+ \li burlywood
+ \li #deb887
+ \li \svgcolor {#deb887}
+\row
+ \li cadetblue
+ \li #5f9ea0
+ \li \svgcolor {#5f9ea0}
+\row
+ \li chartreuse
+ \li #7fff00
+ \li \svgcolor {#7fff00}
+\row
+ \li chocolate
+ \li #d2691e
+ \li \svgcolor {#d2691e}
+\row
+ \li coral
+ \li #ff7f50
+ \li \svgcolor {#ff7f50}
+\row
+ \li cornflowerblue
+ \li #6495ed
+ \li \svgcolor {#6495ed}
+\row
+ \li cornsilk
+ \li #fff8dc
+ \li \svgcolor {#fff8dc}
+\row
+ \li crimson
+ \li #dc143c
+ \li \svgcolor {#dc143c}
+\row
+ \li cyan
+ \li #00ffff
+ \li \svgcolor {#00ffff}
+\row
+ \li darkblue
+ \li #00008b
+ \li \svgcolor {#00008b}
+\row
+ \li darkcyan
+ \li #008b8b
+ \li \svgcolor {#008b8b}
+\row
+ \li darkgoldenrod
+ \li #b8860b
+ \li \svgcolor {#b8860b}
+\row
+ \li darkgray
+ \li #a9a9a9
+ \li \svgcolor {#a9a9a9}
+\row
+ \li darkgreen
+ \li #006400
+ \li \svgcolor {#006400}
+\row
+ \li darkgrey
+ \li #a9a9a9
+ \li \svgcolor {#a9a9a9}
+\row
+ \li darkkhaki
+ \li #bdb76b
+ \li \svgcolor {#bdb76b}
+\row
+ \li darkmagenta
+ \li #8b008b
+ \li \svgcolor {#8b008b}
+\row
+ \li darkolivegreen
+ \li #556b2f
+ \li \svgcolor {#556b2f}
+\row
+ \li darkorange
+ \li #ff8c00
+ \li \svgcolor {#ff8c00}
+\row
+ \li darkorchid
+ \li #9932cc
+ \li \svgcolor {#9932cc}
+\row
+ \li darkred
+ \li #8b0000
+ \li \svgcolor {#8b0000}
+\row
+ \li darksalmon
+ \li #e9967a
+ \li \svgcolor {#e9967a}
+\row
+ \li darkseagreen
+ \li #8fbc8f
+ \li \svgcolor {#8fbc8f}
+\row
+ \li darkslateblue
+ \li #483d8b
+ \li \svgcolor {#483d8b}
+\row
+ \li darkslategray
+ \li #2f4f4f
+ \li \svgcolor {#2f4f4f}
+\row
+ \li darkslategrey
+ \li #2f4f4f
+ \li \svgcolor {#2f4f4f}
+\row
+ \li darkturquoise
+ \li #00ced1
+ \li \svgcolor {#00ced1}
+\row
+ \li darkviolet
+ \li #9400d3
+ \li \svgcolor {#9400d3}
+\row
+ \li deeppink
+ \li #ff1493
+ \li \svgcolor {#ff1493}
+\row
+ \li deepskyblue
+ \li #00bfff
+ \li \svgcolor {#00bfff}
+\row
+ \li dimgray
+ \li #696969
+ \li \svgcolor {#696969}
+\row
+ \li dimgrey
+ \li #696969
+ \li \svgcolor {#696969}
+\row
+ \li dodgerblue
+ \li #1e90ff
+ \li \svgcolor {#1e90ff}
+\row
+ \li firebrick
+ \li #b22222
+ \li \svgcolor {#b22222}
+\row
+ \li floralwhite
+ \li #fffaf0
+ \li \svgcolor {#fffaf0}
+\row
+ \li forestgreen
+ \li #228b22
+ \li \svgcolor {#228b22}
+\row
+ \li fuchsia
+ \li #ff00ff
+ \li \svgcolor {#ff00ff}
+\row
+ \li gainsboro
+ \li #dcdcdc
+ \li \svgcolor {#dcdcdc}
+\row
+ \li ghostwhite
+ \li #f8f8ff
+ \li \svgcolor {#f8f8ff}
+\row
+ \li gold
+ \li #ffd700
+ \li \svgcolor {#ffd700}
+\row
+ \li goldenrod
+ \li #daa520
+ \li \svgcolor {#daa520}
+\row
+ \li gray
+ \li #808080
+ \li \svgcolor {#808080}
+\row
+ \li grey
+ \li #808080
+ \li \svgcolor {#808080}
+\row
+ \li green
+ \li #008000
+ \li \svgcolor {#008000}
+\row
+ \li greenyellow
+ \li #adff2f
+ \li \svgcolor {#adff2f}
+\row
+ \li honeydew
+ \li #f0fff0
+ \li \svgcolor {#f0fff0}
+\row
+ \li hotpink
+ \li #ff69b4
+ \li \svgcolor {#ff69b4}
+\row
+ \li indianred
+ \li #cd5c5c
+ \li \svgcolor {#cd5c5c}
+\row
+ \li indigo
+ \li #4b0082
+ \li \svgcolor {#4b0082}
+\row
+ \li ivory
+ \li #fffff0
+ \li \svgcolor {#fffff0}
+\row
+ \li khaki
+ \li #f0e68c
+ \li \svgcolor {#f0e68c}
+\row
+ \li lavender
+ \li #e6e6fa
+ \li \svgcolor {#e6e6fa}
+\row
+ \li lavenderblush
+ \li #fff0f5
+ \li \svgcolor {#fff0f5}
+\row
+ \li lawngreen
+ \li #7cfc00
+ \li \svgcolor {#7cfc00}
+\row
+ \li lemonchiffon
+ \li #fffacd
+ \li \svgcolor {#fffacd}
+\row
+ \li lightblue
+ \li #add8e6
+ \li \svgcolor {#add8e6}
+\row
+ \li lightcoral
+ \li #f08080
+ \li \svgcolor {#f08080}
+\row
+ \li lightcyan
+ \li #e0ffff
+ \li \svgcolor {#e0ffff}
+\row
+ \li lightgoldenrodyellow
+ \li #fafad2
+ \li \svgcolor {#fafad2}
+\row
+ \li lightgray
+ \li #d3d3d3
+ \li \svgcolor {#d3d3d3}
+\row
+ \li lightgreen
+ \li #90ee90
+ \li \svgcolor {#90ee90}
+\row
+ \li lightgrey
+ \li #d3d3d3
+ \li \svgcolor {#d3d3d3}
+\row
+ \li lightpink
+ \li #ffb6c1
+ \li \svgcolor {#ffb6c1}
+\row
+ \li lightsalmon
+ \li #ffa07a
+ \li \svgcolor {#ffa07a}
+\row
+ \li lightseagreen
+ \li #20b2aa
+ \li \svgcolor {#20b2aa}
+\row
+ \li lightskyblue
+ \li #87cefa
+ \li \svgcolor {#87cefa}
+\row
+ \li lightslategray
+ \li #778899
+ \li \svgcolor {#778899}
+\row
+ \li lightslategrey
+ \li #778899
+ \li \svgcolor {#778899}
+\row
+ \li lightsteelblue
+ \li #b0c4de
+ \li \svgcolor {#b0c4de}
+\row
+ \li lightyellow
+ \li #ffffe0
+ \li \svgcolor {#ffffe0}
+\row
+ \li lime
+ \li #00ff00
+ \li \svgcolor {#00ff00}
+\row
+ \li limegreen
+ \li #32cd32
+ \li \svgcolor {#32cd32}
+\row
+ \li linen
+ \li #faf0e6
+ \li \svgcolor {#faf0e6}
+\row
+ \li magenta
+ \li #ff00ff
+ \li \svgcolor {#ff00ff}
+\row
+ \li maroon
+ \li #800000
+ \li \svgcolor {#800000}
+\row
+ \li mediumaquamarine
+ \li #66cdaa
+ \li \svgcolor {#66cdaa}
+\row
+ \li mediumblue
+ \li #0000cd
+ \li \svgcolor {#0000cd}
+\row
+ \li mediumorchid
+ \li #ba55d3
+ \li \svgcolor {#ba55d3}
+\row
+ \li mediumpurple
+ \li #9370db
+ \li \svgcolor {#9370db}
+\row
+ \li mediumseagreen
+ \li #3cb371
+ \li \svgcolor {#3cb371}
+\row
+ \li mediumslateblue
+ \li #7b68ee
+ \li \svgcolor {#7b68ee}
+\row
+ \li mediumspringgreen
+ \li #00fa9a
+ \li \svgcolor {#00fa9a}
+\row
+ \li mediumturquoise
+ \li #48d1cc
+ \li \svgcolor {#48d1cc}
+\row
+ \li mediumvioletred
+ \li #c71585
+ \li \svgcolor {#c71585}
+\row
+ \li midnightblue
+ \li #191970
+ \li \svgcolor {#191970}
+\row
+ \li mintcream
+ \li #f5fffa
+ \li \svgcolor {#f5fffa}
+\row
+ \li mistyrose
+ \li #ffe4e1
+ \li \svgcolor {#ffe4e1}
+\row
+ \li moccasin
+ \li #ffe4b5
+ \li \svgcolor {#ffe4b5}
+\row
+ \li navajowhite
+ \li #ffdead
+ \li \svgcolor {#ffdead}
+\row
+ \li navy
+ \li #000080
+ \li \svgcolor {#000080}
+\row
+ \li oldlace
+ \li #fdf5e6
+ \li \svgcolor {#fdf5e6}
+\row
+ \li olive
+ \li #808000
+ \li \svgcolor {#808000}
+\row
+ \li olivedrab
+ \li #6b8e23
+ \li \svgcolor {#6b8e23}
+\row
+ \li orange
+ \li #ffa500
+ \li \svgcolor {#ffa500}
+\row
+ \li orangered
+ \li #ff4500
+ \li \svgcolor {#ff4500}
+\row
+ \li orchid
+ \li #da70d6
+ \li \svgcolor {#da70d6}
+\row
+ \li palegoldenrod
+ \li #eee8aa
+ \li \svgcolor {#eee8aa}
+\row
+ \li palegreen
+ \li #98fb98
+ \li \svgcolor {#98fb98}
+\row
+ \li paleturquoise
+ \li #afeeee
+ \li \svgcolor {#afeeee}
+\row
+ \li palevioletred
+ \li #db7093
+ \li \svgcolor {#db7093}
+\row
+ \li papayawhip
+ \li #ffefd5
+ \li \svgcolor {#ffefd5}
+\row
+ \li peachpuff
+ \li #ffdab9
+ \li \svgcolor {#ffdab9}
+\row
+ \li peru
+ \li #cd853f
+ \li \svgcolor {#cd853f}
+\row
+ \li pink
+ \li #ffc0cb
+ \li \svgcolor {#ffc0cb}
+\row
+ \li plum
+ \li #dda0dd
+ \li \svgcolor {#dda0dd}
+\row
+ \li powderblue
+ \li #b0e0e6
+ \li \svgcolor {#b0e0e6}
+\row
+ \li purple
+ \li #800080
+ \li \svgcolor {#800080}
+\row
+ \li red
+ \li #ff0000
+ \li \svgcolor {#ff0000}
+\row
+ \li rosybrown
+ \li #bc8f8f
+ \li \svgcolor {#bc8f8f}
+\row
+ \li royalblue
+ \li #4169e1
+ \li \svgcolor {#4169e1}
+\row
+ \li saddlebrown
+ \li #8b4513
+ \li \svgcolor {#8b4513}
+\row
+ \li salmon
+ \li #fa8072
+ \li \svgcolor {#fa8072}
+\row
+ \li sandybrown
+ \li #f4a460
+ \li \svgcolor {#f4a460}
+\row
+ \li seagreen
+ \li #2e8b57
+ \li \svgcolor {#2e8b57}
+\row
+ \li seashell
+ \li #fff5ee
+ \li \svgcolor {#fff5ee}
+\row
+ \li sienna
+ \li #a0522d
+ \li \svgcolor {#a0522d}
+\row
+ \li silver
+ \li #c0c0c0
+ \li \svgcolor {#c0c0c0}
+\row
+ \li skyblue
+ \li #87ceeb
+ \li \svgcolor {#87ceeb}
+\row
+ \li slateblue
+ \li #6a5acd
+ \li \svgcolor {#6a5acd}
+\row
+ \li slategray
+ \li #708090
+ \li \svgcolor {#708090}
+\row
+ \li slategrey
+ \li #708090
+ \li \svgcolor {#708090}
+\row
+ \li snow
+ \li #fffafa
+ \li \svgcolor {#fffafa}
+\row
+ \li springgreen
+ \li #00ff7f
+ \li \svgcolor {#00ff7f}
+\row
+ \li steelblue
+ \li #4682b4
+ \li \svgcolor {#4682b4}
+\row
+ \li tan
+ \li #d2b48c
+ \li \svgcolor {#d2b48c}
+\row
+ \li teal
+ \li #008080
+ \li \svgcolor {#008080}
+\row
+ \li thistle
+ \li #d8bfd8
+ \li \svgcolor {#d8bfd8}
+\row
+ \li tomato
+ \li #ff6347
+ \li \svgcolor {#ff6347}
+\row
+ \li turquoise
+ \li #40e0d0
+ \li \svgcolor {#40e0d0}
+\row
+ \li violet
+ \li #ee82ee
+ \li \svgcolor {#ee82ee}
+\row
+ \li wheat
+ \li #f5deb3
+ \li \svgcolor {#f5deb3}
+\row
+ \li white
+ \li #ffffff
+ \li \svgcolor {#ffffff}
+\row
+ \li whitesmoke
+ \li #f5f5f5
+ \li \svgcolor {#f5f5f5}
+\row
+ \li yellow
+ \li #ffff00
+ \li \svgcolor {#ffff00}
+\row
+ \li yellowgreen
+ \li #9acd32
+ \li \svgcolor {#9acd32}
+\endtable
diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc
index a8ba076e3d..24fd4240b3 100644
--- a/src/gui/doc/src/richtext.qdoc
+++ b/src/gui/doc/src/richtext.qdoc
@@ -1175,12 +1175,75 @@
\row \li \c vertical-align
\li baseline | sub | super | middle | top | bottom
\li Vertical text alignment. For vertical alignment in text table cells only middle, top, and bottom apply.
+ \row \li \c border-collapse
+ \li collapse | separate
+ \li Border Collapse mode for text tables. If set to collapse, cell-spacing will not be applied.
\row \li \c border-color
\li <color>
- \li Border color for text tables.
+ \li Border color for text tables and table cells.
+ \row \li \c border-top-color
+ \li <color>
+ \li Top border color for table cells.
+ \row \li \c border-bottom-color
+ \li <color>
+ \li Bottom border color for table cells.
+ \row \li \c border-left-color
+ \li <color>
+ \li Left border color for table cells.
+ \row \li \c border-right-color
+ \li <color>
+ \li Right border color for table cells.
\row \li \c border-style
\li none | dotted | dashed | dot-dash | dot-dot-dash | solid | double | groove | ridge | inset | outset
- \li Border style for text tables.
+ \li Border style for text tables and table cells.
+ \row \li \c border-top-style
+ \li <color>
+ \li Top border style for table cells.
+ \row \li \c border-bottom-style
+ \li <color>
+ \li Bottom border style for table cells.
+ \row \li \c border-left-style
+ \li <color>
+ \li Left border style for table cells.
+ \row \li \c border-right-style
+ \li <color>
+ \li Right border style for table cells.
+ \row \li \c border-width
+ \li <width>px
+ \li Width of table or cell border
+ \row \li \c border-top-width
+ \li <length>px
+ \li Top border width for table cells.
+ \row \li \c border-bottom-width
+ \li <length>px
+ \li Bottom border width for table cells.
+ \row \li \c border-left-width
+ \li <length>px
+ \li Left border width for table cells.
+ \row \li \c border-right-width
+ \li <length>px
+ \li Right border width for table cells.
+ \row \li \c border-top
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting top border width, style and color
+ \row \li \c border-bottom
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting bottom border width, style and color
+ \row \li \c border-left
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting left border width, style and color
+ \row \li \c border-right
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting right border width, style and color
+ \row \li \c border-top
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting top border width, style and color
+ \row \li \c border-bottom
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting bottom border width, style and color
+ \row \li \c border
+ \li <width>px <border-style> <border-color>
+ \li Shorthand for setting all four border's width, style and color
\row \li \c background
\li [ <'background-color'> || <'background-image'> ]
\li Background shorthand property
@@ -1190,7 +1253,7 @@
\row \li \c page-break-after
\li [ auto | always ]
\li Make it possible to enforce a page break after the paragraph/table
- \row \li float
+ \row \li \c float
\li [ left | right | none ]
\li Specifies where an image or a text will be placed in another element. Note that the \c float property is
only supported for tables and images.
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri
index b482a6e142..085ec90c4b 100644
--- a/src/gui/image/image.pri
+++ b/src/gui/image/image.pri
@@ -14,7 +14,6 @@ HEADERS += \
image/qpaintengine_pic_p.h \
image/qpicture.h \
image/qpicture_p.h \
- image/qpictureformatplugin.h \
image/qpixmap.h \
image/qpixmap_raster_p.h \
image/qpixmap_blitter_p.h \
@@ -38,7 +37,6 @@ SOURCES += \
image/qimagewriter.cpp \
image/qpaintengine_pic.cpp \
image/qpicture.cpp \
- image/qpictureformatplugin.cpp \
image/qpixmap.cpp \
image/qpixmapcache.cpp \
image/qplatformpixmap.cpp \
@@ -79,7 +77,18 @@ qtConfig(png) {
}
# SIMD
-SSSE3_SOURCES += image/qimage_ssse3.cpp
-NEON_SOURCES += image/qimage_neon.cpp
-MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp
-MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S
+!android {
+ SSSE3_SOURCES += image/qimage_ssse3.cpp
+ NEON_SOURCES += image/qimage_neon.cpp
+ MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp
+ MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S
+} else {
+ # see https://developer.android.com/ndk/guides/abis
+ arm64-v8a {
+ SOURCES += image/qimage_neon.cpp
+ }
+ x86 | x86_64 {
+ DEFINES += QT_COMPILER_SUPPORTS_SSE2 QT_COMPILER_SUPPORTS_SSE3 QT_COMPILER_SUPPORTS_SSSE3
+ SOURCES += image/qimage_ssse3.cpp
+ }
+}
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index cd2fe5bc10..b7f67cd7ef 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -287,6 +287,7 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_RGB555:
case QImage::Format_RGB666:
case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
case QImage::Format_RGBX8888:
case QImage::Format_BGR30:
case QImage::Format_RGB30:
@@ -716,10 +717,11 @@ bool QImageData::checkForAlphaPixels() const
\value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5)
\value Format_Grayscale16 The image is stored using an 16-bit grayscale format. (added in Qt 5.13)
\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)
+ This is the same as the Format_RGBA64 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)
+ \value Format_BGR888 The image is stored using a 24-bit BGR format. (added in Qt 5.14)
\note Drawing into a QImage with QImage::Format_Indexed8 is not
supported.
@@ -5550,6 +5552,19 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
/*INTERPRETATION*/ QPixelFormat::UnsignedShort,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_BGR888:
+ QPixelFormat(QPixelFormat::BGR,
+ /*RED*/ 8,
+ /*GREEN*/ 8,
+ /*BLUE*/ 8,
+ /*FOURTH*/ 0,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 0,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
+ /*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 3f0d900a70..c657d6067f 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -131,6 +131,7 @@ public:
Format_RGBA64,
Format_RGBA64_Premultiplied,
Format_Grayscale16,
+ Format_BGR888,
#ifndef Q_QDOC
NImageFormats
#endif
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 837ac88470..539bac222a 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -429,8 +429,8 @@ typedef void (QT_FASTCALL *Rgb888ToRgbConverter)(quint32 *dst, const uchar *src,
template <bool rgbx>
static void convert_RGB888_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
- Q_ASSERT(src->format == QImage::Format_RGB888);
- if (rgbx)
+ Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888);
+ if (rgbx ^ (src->format == QImage::Format_BGR888))
Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied);
else
Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
@@ -1421,6 +1421,69 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt
}
}
+static void convert_RGB888_to_BGR888(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888);
+ Q_ASSERT(dest->format == QImage::Format_RGB888 || dest->format == QImage::Format_BGR888);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const qsizetype sbpl = src->bytes_per_line;
+ const qsizetype dbpl = dest->bytes_per_line;
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+
+ for (int i = 0; i < src->height; ++i) {
+ int pixel = 0;
+ // Handle 4 pixels (12 bytes) at a time
+ for (; pixel + 3 < src->width; pixel += 4) {
+ const uchar *src = src_data + pixel * 3;
+ quint32 *dest_packed = (quint32 *) (dest_data + pixel * 3);
+ dest_packed[0] = (src[5] << 24) | (src[0] << 16) | (src[1] << 8) | (src[2] << 0);
+ dest_packed[1] = (src[7] << 24) | (src[8] << 16) | (src[3] << 8) | (src[4] << 0);
+ dest_packed[2] = (src[9] << 24) | (src[10] << 16) | (src[11] << 8) | (src[6] << 0);
+ }
+
+ // epilog: handle left over pixels
+ for (; pixel < src->width; ++pixel) {
+ dest_data[pixel * 3 + 0] = src_data[pixel * 3 + 2];
+ dest_data[pixel * 3 + 1] = src_data[pixel * 3 + 1];
+ dest_data[pixel * 3 + 2] = src_data[pixel * 3 + 0];
+ }
+
+ src_data += sbpl;
+ dest_data += dbpl;
+ }
+}
+
+static bool convert_RGB888_to_BGR888_inplace(QImageData *data, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format == QImage::Format_RGB888 || data->format == QImage::Format_BGR888);
+
+ const qsizetype bpl = data->bytes_per_line;
+ uchar *line_data = data->data;
+
+ for (int i = 0; i < data->height; ++i) {
+ for (int j = 0; j < data->width; ++j)
+ qSwap(line_data[j * 3 + 0], line_data[j * 3 + 2]);
+ line_data += bpl;
+ }
+
+ switch (data->format) {
+ case QImage::Format_RGB888:
+ data->format = QImage::Format_BGR888;
+ break;
+ case QImage::Format_BGR888:
+ data->format = QImage::Format_RGB888;
+ break;
+ default:
+ Q_UNREACHABLE();
+ data->format = QImage::Format_Invalid;
+ return false;
+ }
+ return true;
+}
+
static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format)
{
QVector<QRgb> colorTable = ctbl;
@@ -2265,7 +2328,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, 0, 0, 0, 0, 0, 0, 0, 0
},
{
0,
@@ -2286,7 +2349,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, 0, 0, 0, 0, 0, 0
}, // Format_Mono
{
@@ -2308,7 +2371,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, 0, 0, 0, 0, 0, 0
}, // Format_MonoLSB
{
@@ -2333,7 +2396,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, 0
+ 0, 0, 0, 0, 0
}, // Format_Indexed8
{
@@ -2361,7 +2424,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_RGB_to_RGB30<PixelOrderRGB, false>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGB32
{
@@ -2391,7 +2454,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0, 0,
0,
convert_ARGB32_to_RGBA64<false>,
- 0, 0
+ 0, 0, 0
}, // Format_ARGB32
{
@@ -2416,7 +2479,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_ARGB_to_RGBA,
0, 0, 0, 0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_ARGB32_Premultiplied
{
@@ -2438,7 +2501,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, 0, 0, 0, 0, 0, 0
}, // Format_RGB16
{
@@ -2460,7 +2523,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, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8565_Premultiplied
{
@@ -2482,7 +2545,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, 0, 0, 0, 0, 0, 0
}, // Format_RGB666
{
@@ -2504,7 +2567,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, 0, 0, 0, 0, 0, 0
}, // Format_ARGB6666_Premultiplied
{
@@ -2526,7 +2589,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, 0, 0, 0, 0, 0, 0
}, // Format_RGB555
{
@@ -2548,7 +2611,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, 0, 0, 0, 0, 0, 0
}, // Format_ARGB8555_Premultiplied
{
@@ -2565,13 +2628,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0,
0,
0,
- 0,
+ 0, // self
0,
0,
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, 0, 0, 0, 0, 0,
+ convert_RGB888_to_BGR888,
}, // Format_RGB888
{
@@ -2593,7 +2657,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, 0, 0, 0, 0, 0, 0
}, // Format_RGB444
{
@@ -2614,7 +2678,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, 0, 0, 0, 0, 0, 0
}, // Format_ARGB4444_Premultiplied
{
0,
@@ -2641,7 +2705,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_RGB_to_RGB30<PixelOrderRGB, true>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGBX8888
{
0,
@@ -2670,7 +2734,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
0, 0,
0,
convert_ARGB32_to_RGBA64<true>,
- 0, 0
+ 0, 0, 0
}, // Format_RGBA8888
{
@@ -2692,7 +2756,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, 0, 0, 0, 0
}, // Format_RGBA8888_Premultiplied
{
@@ -2720,7 +2784,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_BGR30_to_RGB30,
convert_BGR30_to_RGB30,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_BGR30
{
0,
@@ -2747,7 +2811,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_A2RGB30_PM_to_RGB30<true>,
convert_BGR30_to_RGB30,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_A2BGR30_Premultiplied
{
0,
@@ -2773,7 +2837,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_BGR30_to_RGB30,
0,
convert_passthrough,
- 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0
}, // Format_RGB30
{
0,
@@ -2800,7 +2864,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_A2RGB30_PM_to_RGB30<false>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_A2RGB30_Premultiplied
{
0,
@@ -2820,7 +2884,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, 0, 0, 0, 0, 0, 0
}, // Format_Alpha8
{
0,
@@ -2840,14 +2904,15 @@ 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, 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,
- convert_RGBA64_to_gray16
+ convert_RGBA64_to_gray16,
+ 0
}, // Format_RGBX64
{
0,
@@ -2874,6 +2939,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_RGBA64_to_RGBx64,
0, // self
convert_RGBA64_to_RGBA64PM,
+ 0,
0
}, // Format_RGBA64
{
@@ -2901,7 +2967,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_RGBA64PM_to_RGBA64<true>,
convert_RGBA64PM_to_RGBA64<false>,
0, // self
- convert_RGBA64_to_gray16
+ convert_RGBA64_to_gray16,
+ 0
}, // Format_RGBA64_Premultiplied
{
0,
@@ -2931,20 +2998,46 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat
convert_gray16_to_RGBA64,
convert_gray16_to_RGBA64,
convert_gray16_to_RGBA64,
- 0 // self
+ 0, // self
+ 0
}, // Format_Grayscale16
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, 0, 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ convert_RGB888_to_BGR888,
+ 0,
+ 0,
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ convert_RGB888_to_RGB<false>,
+ convert_RGB888_to_RGB<false>,
+ convert_RGB888_to_RGB<false>,
+#else
+ 0, 0, 0,
+#endif
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // self
+ }, // Format_BGR888
};
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, 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, 0, 0, 0, 0, 0, 0
}, // Format_MonoLSB
{
0,
@@ -2968,7 +3061,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, 0
+ 0, 0, 0, 0, 0
}, // Format_Indexed8
{
0,
@@ -2995,7 +3088,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGB32
{
0,
@@ -3022,7 +3115,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_ARGB32
{
0,
@@ -3046,34 +3139,35 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0,
+ convert_RGB888_to_BGR888_inplace
}, // 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, 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, 0, 0, 0, 0, 0, 0
}, // Format_ARGB4444_Premultiplied
{
0,
@@ -3100,7 +3194,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGBX8888
{
0,
@@ -3127,7 +3221,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>,
0,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_RGBA8888
{
0,
@@ -3149,7 +3243,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, 0, 0, 0, 0, 0, 0
}, // Format_RGBA8888_Premultiplied
{
0,
@@ -3176,7 +3270,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_BGR30_to_RGB30_inplace,
convert_BGR30_to_A2RGB30_inplace,
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_BGR30
{
0,
@@ -3202,7 +3296,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0, // self
convert_A2RGB30_PM_to_RGB30_inplace<true>,
convert_BGR30_to_RGB30_inplace,
- 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0
}, // Format_A2BGR30_Premultiplied
{
0,
@@ -3228,7 +3322,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, 0, 0, 0, 0, 0, 0
}, // Format_RGB30
{
0,
@@ -3255,7 +3349,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
convert_A2RGB30_PM_to_RGB30_inplace<false>,
0, // self
0, 0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_A2RGB30_Premultiplied
{
0,
@@ -3280,7 +3374,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0, 0, 0, 0,
0, // self
0,
- 0, 0, 0, 0
+ 0, 0, 0, 0, 0
}, // Format_Alpha8
{
0,
@@ -3305,32 +3399,37 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma
0, 0, 0, 0,
0,
0, // self
- 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_inplace<QImage::Format_RGBA64>,
convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>,
- 0
+ 0, 0
}, // 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,
- 0
+ 0, 0
}, // 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
- 0
+ 0, 0
}, // Format_RGBA64_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, 0, 0, 0, 0, 0, 0
}, // Format_Grayscale16
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ convert_RGB888_to_BGR888_inplace,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, // Format_BGR888
};
static void qInitImageConversions()
@@ -3341,6 +3440,9 @@ static void qInitImageConversions()
qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3;
qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3;
qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
+ qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB32_ssse3;
+ qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB32_ssse3;
+ qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
}
#endif
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index 1cd0e8e0aa..2b1720580a 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -207,6 +207,7 @@ inline int qt_depthForFormat(QImage::Format format)
case QImage::Format_ARGB8565_Premultiplied:
case QImage::Format_ARGB8555_Premultiplied:
case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
depth = 24;
break;
case QImage::Format_RGBX64:
diff --git a/src/gui/image/qimage_ssse3.cpp b/src/gui/image/qimage_ssse3.cpp
index 9cdfba20e3..fb81a1a6c3 100644
--- a/src/gui/image/qimage_ssse3.cpp
+++ b/src/gui/image/qimage_ssse3.cpp
@@ -121,8 +121,11 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, con
void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
- Q_ASSERT(src->format == QImage::Format_RGB888);
- Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888);
+ if (src->format == QImage::Format_BGR888)
+ Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied);
+ else
+ Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
Q_ASSERT(src->width == dest->width);
Q_ASSERT(src->height == dest->height);
diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp
index 8548f1857e..84de91353a 100644
--- a/src/gui/image/qpicture.cpp
+++ b/src/gui/image/qpicture.cpp
@@ -263,14 +263,14 @@ void QPicture::setData(const char* data, uint size)
\sa save()
*/
-bool QPicture::load(const QString &fileName, const char *format)
+bool QPicture::load(const QString &fileName)
{
QFile f(fileName);
if (!f.open(QIODevice::ReadOnly)) {
operator=(QPicture());
return false;
}
- return load(&f, format);
+ return load(&f);
}
/*!
@@ -279,21 +279,8 @@ bool QPicture::load(const QString &fileName, const char *format)
\a dev is the device to use for loading.
*/
-bool QPicture::load(QIODevice *dev, const char *format)
+bool QPicture::load(QIODevice *dev)
{
- if(format) {
-#ifndef QT_NO_PICTUREIO
- QPictureIO io(dev, format);
- if (io.read()) {
- operator=(io.picture());
- return true;
- }
-#endif
- qWarning("QPicture::load: No such picture format: %s", format);
- operator=(QPicture());
- return false;
- }
-
detach();
QByteArray a = dev->readAll();
@@ -311,7 +298,7 @@ bool QPicture::load(QIODevice *dev, const char *format)
\sa load()
*/
-bool QPicture::save(const QString &fileName, const char *format)
+bool QPicture::save(const QString &fileName)
{
if (paintingActive()) {
qWarning("QPicture::save: still being painted on. "
@@ -319,27 +306,10 @@ bool QPicture::save(const QString &fileName, const char *format)
return false;
}
-
- if(format) {
-#ifndef QT_NO_PICTUREIO
- QPictureIO io(fileName, format);
- bool result = io.write();
- if (result) {
- operator=(io.picture());
- } else if (format)
-#else
- bool result = false;
-#endif
- {
- qWarning("QPicture::save: No such picture format: %s", format);
- }
- return result;
- }
-
QFile f(fileName);
if (!f.open(QIODevice::WriteOnly))
return false;
- return save(&f, format);
+ return save(&f);
}
/*!
@@ -348,7 +318,7 @@ bool QPicture::save(const QString &fileName, const char *format)
\a dev is the device to use for saving.
*/
-bool QPicture::save(QIODevice *dev, const char *format)
+bool QPicture::save(QIODevice *dev)
{
if (paintingActive()) {
qWarning("QPicture::save: still being painted on. "
@@ -356,22 +326,6 @@ bool QPicture::save(QIODevice *dev, const char *format)
return false;
}
- if(format) {
-#ifndef QT_NO_PICTUREIO
- QPictureIO io(dev, format);
- bool result = io.write();
- if (result) {
- operator=(io.picture());
- } else if (format)
-#else
- bool result = false;
-#endif
- {
- qWarning("QPicture::save: No such picture format: %s", format);
- }
- return result;
- }
-
dev->write(d_func()->pictb.buffer(), d_func()->pictb.buffer().size());
return true;
}
@@ -1188,780 +1142,6 @@ QDataStream &operator>>(QDataStream &s, QPicture &r)
}
#endif // QT_NO_DATASTREAM
-
-#ifndef QT_NO_PICTUREIO
-
-QT_BEGIN_INCLUDE_NAMESPACE
-#include "qregexp.h"
-#include "qpictureformatplugin.h"
-QT_END_INCLUDE_NAMESPACE
-
-#if QT_DEPRECATED_SINCE(5, 10)
-/*!
- \obsolete
-
- Returns a string that specifies the picture format of the file \a
- fileName, or \nullptr if the file cannot be read or if the format
- is not recognized.
-
- \sa load(), save()
-*/
-
-const char* QPicture::pictureFormat(const QString &fileName)
-{
- const QByteArray format = QPictureIO::pictureFormat(fileName);
- // This function returns a const char * from a QByteArray.
- // Double check that the QByteArray is not detached, otherwise
- // we would return a dangling pointer.
- Q_ASSERT(!format.isDetached());
- return format;
-}
-
-/*!
- \obsolete
-
- Returns a list of picture formats that are supported for picture
- input.
-
- \sa outputFormats(), inputFormatList(), QPictureIO
-*/
-QList<QByteArray> QPicture::inputFormats()
-{
- return QPictureIO::inputFormats();
-}
-
-static QStringList qToStringList(const QList<QByteArray> &arr)
-{
- QStringList list;
- const int count = arr.count();
- list.reserve(count);
- for (int i = 0; i < count; ++i)
- list.append(QString::fromLatin1(arr.at(i)));
- return list;
-}
-
-/*!
- \obsolete
-
- Returns a list of picture formats that are supported for picture
- input.
-
- Note that if you want to iterate over the list, you should iterate
- over a copy, e.g.
- \snippet picture/picture.cpp 2
-
- \sa outputFormatList(), inputFormats(), QPictureIO
-*/
-QStringList QPicture::inputFormatList()
-{
- return qToStringList(QPictureIO::inputFormats());
-}
-
-
-/*!
- \obsolete
-
- Returns a list of picture formats that are supported for picture
- output.
-
- Note that if you want to iterate over the list, you should iterate
- over a copy, e.g.
- \snippet picture/picture.cpp 3
-
- \sa inputFormatList(), outputFormats(), QPictureIO
-*/
-QStringList QPicture::outputFormatList()
-{
- return qToStringList(QPictureIO::outputFormats());
-}
-
-/*!
- \obsolete
-
- Returns a list of picture formats that are supported for picture
- output.
-
- \sa inputFormats(), outputFormatList(), QPictureIO
-*/
-QList<QByteArray> QPicture::outputFormats()
-{
- return QPictureIO::outputFormats();
-}
-#endif // QT_DEPRECATED_SINCE(5, 10)
-
-/*****************************************************************************
- QPictureIO member functions
- *****************************************************************************/
-
-/*!
- \obsolete
-
- \class QPictureIO
-
- \brief The QPictureIO class contains parameters for loading and
- saving pictures.
-
- \ingroup painting
- \ingroup io
- \inmodule QtGui
-
- QPictureIO contains a QIODevice object that is used for picture data
- I/O. The programmer can install new picture file formats in addition
- to those that Qt provides.
-
- You don't normally need to use this class; QPicture::load(),
- QPicture::save().
-
- \sa QPicture, QPixmap, QFile
-*/
-
-struct QPictureIOData
-{
- QPicture pi; // picture
- int iostat; // IO status
- QByteArray frmt; // picture format
- QIODevice *iodev; // IO device
- QString fname; // file name
- QString descr; // picture description
- const char *parameters;
- int quality;
- float gamma;
-};
-
-/*!
- Constructs a QPictureIO object with all parameters set to zero.
-*/
-
-QPictureIO::QPictureIO()
-{
- init();
-}
-
-/*!
- Constructs a QPictureIO object with the I/O device \a ioDevice and a
- \a format tag.
-*/
-
-QPictureIO::QPictureIO(QIODevice *ioDevice, const char *format)
-{
- init();
- d->iodev = ioDevice;
- d->frmt = format;
-}
-
-/*!
- Constructs a QPictureIO object with the file name \a fileName and a
- \a format tag.
-*/
-
-QPictureIO::QPictureIO(const QString &fileName, const char* format)
-{
- init();
- d->frmt = format;
- d->fname = fileName;
-}
-
-/*!
- Contains initialization common to all QPictureIO constructors.
-*/
-
-void QPictureIO::init()
-{
- d = new QPictureIOData();
- d->parameters = 0;
- d->quality = -1; // default quality of the current format
- d->gamma=0.0f;
- d->iostat = 0;
- d->iodev = 0;
-}
-
-/*!
- Destroys the object and all related data.
-*/
-
-QPictureIO::~QPictureIO()
-{
- if (d->parameters)
- delete [] d->parameters;
- delete d;
-}
-
-
-/*****************************************************************************
- QPictureIO picture handler functions
- *****************************************************************************/
-
-class QPictureHandler
-{
-public:
- QPictureHandler(const char *f, const char *h, const QByteArray& fl,
- picture_io_handler r, picture_io_handler w);
- QByteArray format; // picture format
- QRegExp header; // picture header pattern
- enum TMode { Untranslated=0, TranslateIn, TranslateInOut } text_mode;
- picture_io_handler read_picture; // picture read function
- picture_io_handler write_picture; // picture write function
- bool obsolete; // support not "published"
-};
-
-QPictureHandler::QPictureHandler(const char *f, const char *h, const QByteArray& fl,
- picture_io_handler r, picture_io_handler w)
- : format(f), header(QString::fromLatin1(h))
-{
- text_mode = Untranslated;
- if (fl.contains('t'))
- text_mode = TranslateIn;
- else if (fl.contains('T'))
- text_mode = TranslateInOut;
- obsolete = fl.contains('O');
- read_picture = r;
- write_picture = w;
-}
-
-typedef QList<QPictureHandler *> QPHList;
-Q_GLOBAL_STATIC(QPHList, pictureHandlers)
-
-void qt_init_picture_plugins()
-{
- typedef QMultiMap<int, QString> PluginKeyMap;
- typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
-
- static QBasicMutex mutex;
- QMutexLocker locker(&mutex);
- static QFactoryLoader loader(QPictureFormatInterface_iid,
- QStringLiteral("/pictureformats"));
-
- const PluginKeyMap keyMap = loader.keyMap();
- const PluginKeyMapConstIterator cend = keyMap.constEnd();
- for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) {
- if (QPictureFormatPlugin *format = qobject_cast<QPictureFormatPlugin*>(loader.instance(it.key())))
- format->installIOHandler(it.value());
- }
-}
-
-static void cleanup()
-{
- // make sure that picture handlers are delete before plugin manager
- if (QPHList *list = pictureHandlers()) {
- qDeleteAll(*list);
- list->clear();
- }
-}
-
-void qt_init_picture_handlers() // initialize picture handlers
-{
- static QBasicAtomicInt done = Q_BASIC_ATOMIC_INITIALIZER(0);
- if (done.testAndSetRelaxed(0, 1)) {
- qAddPostRoutine(cleanup);
- }
-}
-
-static QPictureHandler *get_picture_handler(const char *format)
-{ // get pointer to handler
- qt_init_picture_handlers();
- qt_init_picture_plugins();
- if (QPHList *list = pictureHandlers()) {
- for (int i = 0; i < list->size(); ++i) {
- if (list->at(i)->format == format)
- return list->at(i);
- }
- }
- return 0; // no such handler
-}
-
-
-/*!
- Defines a picture I/O handler for the picture format called \a
- format, which is recognized using the regular
- expression defined in \a header, read using \a readPicture and
- written using \a writePicture.
-
- \a flags is a string of single-character flags for this format.
- The only flag defined currently is T (upper case), so the only
- legal value for \a flags are "T" and the empty string. The "T"
- flag means that the picture file is a text file, and Qt should treat
- all newline conventions as equivalent. (XPM files and some PPM
- files are text files for example.)
-
- \a format is used to select a handler to write a QPicture; \a header
- is used to select a handler to read an picture file.
-
- If \a readPicture is \nullptr, the QPictureIO will not be able
- to read pictures in \a format. If \a writePicture is \nullptr,
- the QPictureIO will not be able to write pictures in \a format. If
- both are null, the QPictureIO object is valid but useless.
-
- Example:
- \snippet picture/picture.cpp 6
- \codeline
- \snippet picture/picture.cpp 7
- \codeline
- \snippet picture/picture.cpp 8
-
- Before the regular expression test, all the 0 bytes in the file header are
- converted to 1 bytes. This is done because when Qt was ASCII-based, QRegExp
- could not handle 0 bytes in strings.
-
- The regexp is only applied on the first 14 bytes of the file.
-
- (Note that if one handlerIO supports writing a format and another
- supports reading it, Qt supports both reading and writing. If two
- handlers support the same operation, Qt chooses one arbitrarily.)
-*/
-
-void QPictureIO::defineIOHandler(const char *format,
- const char *header,
- const char *flags,
- picture_io_handler readPicture,
- picture_io_handler writePicture)
-{
- qt_init_picture_handlers();
- if (QPHList *list = pictureHandlers()) {
- QPictureHandler *p;
- p = new QPictureHandler(format, header, QByteArray(flags), readPicture, writePicture);
- list->prepend(p);
- }
-}
-
-
-/*****************************************************************************
- QPictureIO normal member functions
- *****************************************************************************/
-
-/*!
- Returns the picture currently set.
-
- \sa setPicture()
-*/
-const QPicture &QPictureIO::picture() const { return d->pi; }
-
-/*!
- Returns the picture's IO status. A non-zero value indicates an
- error, whereas 0 means that the IO operation was successful.
-
- \sa setStatus()
-*/
-int QPictureIO::status() const { return d->iostat; }
-
-/*!
- Returns the picture format string or \nullptr if no format has been
- explicitly set.
-*/
-const char *QPictureIO::format() const { return d->frmt; }
-
-/*!
- Returns the IO device currently set.
-
- \sa setIODevice()
-*/
-QIODevice *QPictureIO::ioDevice() const { return d->iodev; }
-
-/*!
- Returns the file name currently set.
-
- \sa setFileName()
-*/
-QString QPictureIO::fileName() const { return d->fname; }
-
-
-/*!
- Returns the picture description string.
-
- \sa setDescription()
-*/
-QString QPictureIO::description() const { return d->descr; }
-
-/*!
- Sets the picture to \a picture.
-
- \sa picture()
-*/
-void QPictureIO::setPicture(const QPicture &picture)
-{
- d->pi = picture;
-}
-
-/*!
- Sets the picture IO status to \a status. A non-zero value indicates
- an error, whereas 0 means that the IO operation was successful.
-
- \sa status()
-*/
-void QPictureIO::setStatus(int status)
-{
- d->iostat = status;
-}
-
-/*!
- Sets the picture format to \a format for the picture to be read or
- written.
-
- It is necessary to specify a format before writing an picture, but
- it is not necessary to specify a format before reading an picture.
-
- If no format has been set, Qt guesses the picture format before
- reading it. If a format is set the picture will only be read if it
- has that format.
-
- \sa read(), write(), format()
-*/
-void QPictureIO::setFormat(const char *format)
-{
- d->frmt = format;
-}
-
-/*!
- Sets the IO device to be used for reading or writing an picture.
-
- Setting the IO device allows pictures to be read/written to any
- block-oriented QIODevice.
-
- If \a ioDevice is not null, this IO device will override file name
- settings.
-
- \sa setFileName()
-*/
-void QPictureIO::setIODevice(QIODevice *ioDevice)
-{
- d->iodev = ioDevice;
-}
-
-/*!
- Sets the name of the file to read or write an picture from to \a
- fileName.
-
- \sa setIODevice()
-*/
-void QPictureIO::setFileName(const QString &fileName)
-{
- d->fname = fileName;
-}
-
-/*!
- Returns the quality of the written picture, related to the
- compression ratio.
-
- \sa setQuality(), QPicture::save()
-*/
-int QPictureIO::quality() const
-{
- return d->quality;
-}
-
-/*!
- Sets the quality of the written picture to \a q, related to the
- compression ratio.
-
- \a q must be in the range -1..100. Specify 0 to obtain small
- compressed files, 100 for large uncompressed files. (-1 signifies
- the default compression.)
-
- \sa quality(), QPicture::save()
-*/
-
-void QPictureIO::setQuality(int q)
-{
- d->quality = q;
-}
-
-/*!
- Returns the picture's parameters string.
-
- \sa setParameters()
-*/
-
-const char *QPictureIO::parameters() const
-{
- return d->parameters;
-}
-
-/*!
- Sets the picture's parameter string to \a parameters. This is for
- picture handlers that require special parameters.
-
- Although the current picture formats supported by Qt ignore the
- parameters string, it may be used in future extensions or by
- contributions (for example, JPEG).
-
- \sa parameters()
-*/
-
-void QPictureIO::setParameters(const char *parameters)
-{
- if (d->parameters)
- delete [] d->parameters;
- d->parameters = qstrdup(parameters);
-}
-
-/*!
- Sets the gamma value at which the picture will be viewed to \a
- gamma. If the picture format stores a gamma value for which the
- picture is intended to be used, then this setting will be used to
- modify the picture. Setting to 0.0 will disable gamma correction
- (i.e. any specification in the file will be ignored).
-
- The default value is 0.0.
-
- \sa gamma()
-*/
-void QPictureIO::setGamma(float gamma)
-{
- d->gamma=gamma;
-}
-
-/*!
- Returns the gamma value at which the picture will be viewed.
-
- \sa setGamma()
-*/
-float QPictureIO::gamma() const
-{
- return d->gamma;
-}
-
-/*!
- Sets the picture description string for picture handlers that support
- picture descriptions to \a description.
-
- Currently, no picture format supported by Qt uses the description
- string.
-*/
-
-void QPictureIO::setDescription(const QString &description)
-{
- d->descr = description;
-}
-
-
-/*!
- Returns a string that specifies the picture format of the file \a
- fileName, or null if the file cannot be read or if the format is
- not recognized.
-*/
-
-QByteArray QPictureIO::pictureFormat(const QString &fileName)
-{
- QFile file(fileName);
- QByteArray format;
- if (!file.open(QIODevice::ReadOnly))
- return format;
- format = pictureFormat(&file);
- file.close();
- return format;
-}
-
-/*!
- \overload
-
- Returns a string that specifies the picture format of the picture read
- from IO device \a d, or 0 if the device cannot be read or if the
- format is not recognized.
-
- Make sure that \a d is at the right position in the device (for
- example, at the beginning of the file).
-
- \sa QIODevice::pos()
-*/
-
-QByteArray QPictureIO::pictureFormat(QIODevice *d)
-{
- // if you change this change the documentation for defineIOHandler()
- const int buflen = 14;
-
- char buf[buflen];
- char buf2[buflen];
- qt_init_picture_handlers();
- qt_init_picture_plugins();
- int pos = d->pos(); // save position
- int rdlen = d->read(buf, buflen); // read a few bytes
-
- QByteArray format;
- if (rdlen != buflen)
- return format;
-
- memcpy(buf2, buf, buflen);
-
- for (int n = 0; n < rdlen; n++)
- if (buf[n] == '\0')
- buf[n] = '\001';
- if (rdlen > 0) {
- buf[rdlen - 1] = '\0';
- QString bufStr = QString::fromLatin1(buf);
- if (QPHList *list = pictureHandlers()) {
- for (int i = 0; i < list->size(); ++i) {
- if (list->at(i)->header.indexIn(bufStr) != -1) { // try match with headers
- format = list->at(i)->format;
- break;
- }
- }
- }
- }
- d->seek(pos); // restore position
- return format;
-}
-
-/*!
- Returns a sorted list of picture formats that are supported for
- picture input.
-*/
-QList<QByteArray> QPictureIO::inputFormats()
-{
- QList<QByteArray> result;
-
- qt_init_picture_handlers();
- qt_init_picture_plugins();
-
- if (QPHList *list = pictureHandlers()) {
- for (int i = 0; i < list->size(); ++i) {
- QPictureHandler *p = list->at(i);
- if (p->read_picture && !p->obsolete && !result.contains(p->format))
- result.append(p->format);
- }
- }
- std::sort(result.begin(), result.end());
-
- return result;
-}
-
-/*!
- Returns a sorted list of picture formats that are supported for
- picture output.
-*/
-QList<QByteArray> QPictureIO::outputFormats()
-{
- qt_init_picture_handlers();
- qt_init_picture_plugins();
-
- QList<QByteArray> result;
- if (QPHList *list = pictureHandlers()) {
- for (int i = 0; i < list->size(); ++i) {
- QPictureHandler *p = list->at(i);
- if (p->write_picture && !p->obsolete && !result.contains(p->format))
- result.append(p->format);
- }
- }
- return result;
-}
-
-
-
-/*!
- Reads an picture into memory and returns \c true if the picture was
- successfully read; otherwise returns \c false.
-
- Before reading an picture you must set an IO device or a file name.
- If both an IO device and a file name have been set, the IO device
- will be used.
-
- Setting the picture file format string is optional.
-
- Note that this function does \e not set the \l{format()}{format} used to read the picture. If you need that
- information, use the pictureFormat() static functions.
-
- Example:
-
- \snippet picture/picture.cpp 4
-
- \sa setIODevice(), setFileName(), setFormat(), write(), QPixmap::load()
-*/
-bool QPictureIO::read()
-{
- QFile file;
- QByteArray picture_format;
- QPictureHandler *h;
-
- if (d->iodev) { // read from io device
- // ok, already open
- } else if (!d->fname.isEmpty()) { // read from file
- file.setFileName(d->fname);
- if (!file.open(QIODevice::ReadOnly))
- return false; // cannot open file
- d->iodev = &file;
- } else { // no file name or io device
- return false;
- }
- if (d->frmt.isEmpty()) {
- // Try to guess format
- picture_format = pictureFormat(d->iodev); // get picture format
- if (picture_format.isEmpty()) {
- if (file.isOpen()) { // unknown format
- file.close();
- d->iodev = 0;
- }
- return false;
- }
- } else {
- picture_format = d->frmt;
- }
-
- h = get_picture_handler(picture_format);
- if (file.isOpen()) {
-#if !defined(Q_OS_UNIX)
- if (h && h->text_mode) { // reopen in translated mode
- file.close();
- file.open(QIODevice::ReadOnly | QIODevice::Text);
- }
- else
-#endif
- file.seek(0); // position to start
- }
- d->iostat = 1; // assume error
-
- if (h && h->read_picture)
- (*h->read_picture)(this);
-
- if (file.isOpen()) { // picture was read using file
- file.close();
- d->iodev = 0;
- }
- return d->iostat == 0; // picture successfully read?
-}
-
-
-/*!
- Writes an picture to an IO device and returns \c true if the picture was
- successfully written; otherwise returns \c false.
-
- Before writing an picture you must set an IO device or a file name.
- If both an IO device and a file name have been set, the IO device
- will be used.
-
- The picture will be written using the specified picture format.
-
- Example:
- \snippet picture/picture.cpp 5
-
- \sa setIODevice(), setFileName(), setFormat(), read(), QPixmap::save()
-*/
-bool QPictureIO::write()
-{
- if (d->frmt.isEmpty())
- return false;
- QPictureHandler *h = get_picture_handler(d->frmt);
- if (!h || !h->write_picture) {
- qWarning("QPictureIO::write: No such picture format handler: %s",
- format());
- return false;
- }
- QFile file;
- if (!d->iodev && !d->fname.isEmpty()) {
- file.setFileName(d->fname);
- bool translate = h->text_mode==QPictureHandler::TranslateInOut;
- QIODevice::OpenMode fmode = translate ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::OpenMode(QIODevice::WriteOnly);
- if (!file.open(fmode)) // couldn't create file
- return false;
- d->iodev = &file;
- }
- d->iostat = 1;
- (*h->write_picture)(this);
- if (file.isOpen()) { // picture was written using file
- file.close();
- d->iodev = 0;
- }
- return d->iostat == 0; // picture successfully written?
-}
-#endif //QT_NO_PICTUREIO
-
QT_END_NAMESPACE
#endif // QT_NO_PICTURE
diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h
index 189e57b9a3..34860985ed 100644
--- a/src/gui/image/qpicture.h
+++ b/src/gui/image/qpicture.h
@@ -48,7 +48,6 @@
QT_BEGIN_NAMESPACE
-
#ifndef QT_NO_PICTURE
class QPicturePrivate;
@@ -69,10 +68,10 @@ public:
bool play(QPainter *p);
- bool load(QIODevice *dev, const char *format = nullptr);
- bool load(const QString &fileName, const char *format = nullptr);
- bool save(QIODevice *dev, const char *format = nullptr);
- bool save(const QString &fileName, const char *format = nullptr);
+ bool load(QIODevice *dev);
+ bool load(const QString &fileName);
+ bool save(QIODevice *dev);
+ bool save(const QString &fileName);
QRect boundingRect() const;
void setBoundingRect(const QRect &r);
@@ -88,14 +87,6 @@ public:
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QPicture &p);
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QPicture &p);
-#if QT_DEPRECATED_SINCE(5, 10)
- static QT_DEPRECATED const char* pictureFormat(const QString &fileName);
- static QT_DEPRECATED QList<QByteArray> inputFormats();
- static QT_DEPRECATED QList<QByteArray> outputFormats();
- static QT_DEPRECATED QStringList inputFormatList();
- static QT_DEPRECATED QStringList outputFormatList();
-#endif // QT_DEPRECATED_SINCE(5, 10)
-
QPaintEngine *paintEngine() const override;
protected:
@@ -118,67 +109,6 @@ public:
Q_DECLARE_SHARED(QPicture)
-
-#ifndef QT_NO_PICTUREIO
-class QIODevice;
-class QPictureIO;
-typedef void (*picture_io_handler)(QPictureIO *); // picture IO handler
-
-struct QPictureIOData;
-
-class Q_GUI_EXPORT QPictureIO
-{
-public:
- QPictureIO();
- QPictureIO(QIODevice *ioDevice, const char *format);
- QPictureIO(const QString &fileName, const char *format);
- ~QPictureIO();
-
- const QPicture &picture() const;
- int status() const;
- const char *format() const;
- QIODevice *ioDevice() const;
- QString fileName() const;
- int quality() const;
- QString description() const;
- const char *parameters() const;
- float gamma() const;
-
- void setPicture(const QPicture &);
- void setStatus(int);
- void setFormat(const char *);
- void setIODevice(QIODevice *);
- void setFileName(const QString &);
- void setQuality(int);
- void setDescription(const QString &);
- void setParameters(const char *);
- void setGamma(float);
-
- bool read();
- bool write();
-
- static QByteArray pictureFormat(const QString &fileName);
- static QByteArray pictureFormat(QIODevice *);
- static QList<QByteArray> inputFormats();
- static QList<QByteArray> outputFormats();
-
- static void defineIOHandler(const char *format,
- const char *header,
- const char *flags,
- picture_io_handler read_picture,
- picture_io_handler write_picture);
-
-private:
- Q_DISABLE_COPY(QPictureIO)
-
- void init();
-
- QPictureIOData *d;
-};
-
-#endif //QT_NO_PICTUREIO
-
-
/*****************************************************************************
QPicture stream functions
*****************************************************************************/
diff --git a/src/gui/image/qpictureformatplugin.cpp b/src/gui/image/qpictureformatplugin.cpp
deleted file mode 100644
index ef57ad720f..0000000000
--- a/src/gui/image/qpictureformatplugin.cpp
+++ /dev/null
@@ -1,135 +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 "qpictureformatplugin.h"
-#if !defined(QT_NO_PICTURE)
-#include "qpicture.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \obsolete
-
- \class QPictureFormatPlugin
- \brief The QPictureFormatPlugin class provides an abstract base
- for custom picture format plugins.
-
- \ingroup plugins
- \inmodule QtGui
-
- The picture format plugin is a simple plugin interface that makes
- it easy to create custom picture formats that can be used
- transparently by applications.
-
- Writing an picture format plugin is achieved by subclassing this
- base class, reimplementing the pure virtual functions
- loadPicture(), savePicture(), and installIOHandler(), and
- exporting the class with the Q_PLUGIN_METADATA() macro.
-
- The json file containing the metadata should contain one entry
- with the list of picture formats supported by the plugin:
-
- \code
- { "Keys": [ "mypictureformat" ] }
- \endcode
-
- \sa {How to Create Qt Plugins}
-*/
-
-/*!
- \fn bool QPictureFormatPlugin::installIOHandler(const QString &format)
-
- Installs a QPictureIO picture I/O handler for the picture format \a
- format. Returns \c true on success.
-*/
-
-
-/*!
- Constructs an picture format plugin with the given \a parent.
- This is invoked automatically by the moc generated code that exports the plugin.
-*/
-QPictureFormatPlugin::QPictureFormatPlugin(QObject *parent)
- : QObject(parent)
-{
-}
-
-/*!
- Destroys the picture format plugin.
-
- You never have to call this explicitly. Qt destroys a plugin
- automatically when it is no longer used.
-*/
-QPictureFormatPlugin::~QPictureFormatPlugin()
-{
-}
-
-
-/*!
- Loads the picture stored in the file called \a fileName, with the
- given \a format, into *\a picture. Returns \c true on success;
- otherwise returns \c false.
-
- \sa savePicture()
-*/
-bool QPictureFormatPlugin::loadPicture(const QString &format, const QString &fileName, QPicture *picture)
-{
- Q_UNUSED(format)
- Q_UNUSED(fileName)
- Q_UNUSED(picture)
- return false;
-}
-
-/*!
- Saves the given \a picture into the file called \a fileName,
- using the specified \a format. Returns \c true on success; otherwise
- returns \c false.
-
- \sa loadPicture()
-*/
-bool QPictureFormatPlugin::savePicture(const QString &format, const QString &fileName, const QPicture &picture)
-{
- Q_UNUSED(format)
- Q_UNUSED(fileName)
- Q_UNUSED(picture)
- return false;
-}
-
-#endif // QT_NO_PICTURE
-
-QT_END_NAMESPACE
diff --git a/src/gui/image/qpictureformatplugin.h b/src/gui/image/qpictureformatplugin.h
deleted file mode 100644
index 3f59c04d79..0000000000
--- a/src/gui/image/qpictureformatplugin.h
+++ /dev/null
@@ -1,76 +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$
-**
-****************************************************************************/
-
-#ifndef QPICTUREFORMATPLUGIN_H
-#define QPICTUREFORMATPLUGIN_H
-
-#include <QtGui/qtguiglobal.h>
-#include <QtCore/qplugin.h>
-#include <QtCore/qfactoryinterface.h>
-
-QT_BEGIN_NAMESPACE
-
-
-#if !defined(QT_NO_PICTURE)
-
-class QPicture;
-class QImage;
-class QString;
-class QStringList;
-
-#define QPictureFormatInterface_iid "org.qt-project.Qt.QPictureFormatInterface"
-
-class Q_GUI_EXPORT QPictureFormatPlugin : public QObject
-{
- Q_OBJECT
-public:
- explicit QPictureFormatPlugin(QObject *parent = nullptr);
- ~QPictureFormatPlugin();
-
- virtual bool loadPicture(const QString &format, const QString &filename, QPicture *pic);
- virtual bool savePicture(const QString &format, const QString &filename, const QPicture &pic);
- virtual bool installIOHandler(const QString &format) = 0;
-
-};
-
-#endif // QT_NO_PICTURE
-
-QT_END_NAMESPACE
-
-#endif // QPICTUREFORMATPLUGIN_H
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
index 3a2db74098..8aad77b991 100644
--- a/src/gui/image/qpixmap_win.cpp
+++ b/src/gui/image/qpixmap_win.cpp
@@ -281,6 +281,7 @@ Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapForm
}
break;
case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
compression = BI_RGB;
bitCount = 24u;
break;
@@ -368,7 +369,7 @@ static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header,
? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied;
break;
case 24:
- result = QImage::Format_RGB888;
+ result = QImage::Format_BGR888;
break;
case 16:
result = QImage::Format_RGB555;
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index 16d6c25b8b..4ab45337b0 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -1079,6 +1079,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i
if (color_type == PNG_COLOR_TYPE_RGB) {
switch (image.format()) {
case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
break;
case QImage::Format_RGBX8888:
case QImage::Format_RGBX64:
@@ -1131,6 +1132,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
case QImage::Format_RGBX64:
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index ec52791010..8e5945be41 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -535,13 +535,6 @@ Qt::MouseEventFlags QMouseEvent::flags() const
*/
/*!
- \fn QPointF QMouseEvent::posF() const
- \obsolete
-
- Use localPos() instead.
-*/
-
-/*!
\class QHoverEvent
\ingroup events
\inmodule QtGui
@@ -750,12 +743,14 @@ QHoverEvent::~QHoverEvent()
\l inverted always returns false.
*/
+#if QT_DEPRECATED_SINCE(5, 15)
/*!
\fn Qt::Orientation QWheelEvent::orientation() const
\obsolete
Use angleDelta() instead.
*/
+#endif
#if QT_CONFIG(wheelevent)
#if QT_DEPRECATED_SINCE(5, 15)
@@ -936,6 +931,7 @@ QWheelEvent::~QWheelEvent()
\endlist
*/
+#if QT_DEPRECATED_SINCE(5, 15)
/*!
\fn int QWheelEvent::delta() const
\obsolete
@@ -999,6 +995,7 @@ QWheelEvent::~QWheelEvent()
This function has been deprecated, use globalPosition() instead.
*/
+#endif
/*!
\fn Qt::ScrollPhase QWheelEvent::phase() const
@@ -1102,16 +1099,6 @@ QKeyEvent::~QKeyEvent()
}
/*!
- \fn QKeyEvent *QKeyEvent::createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString& text, bool autorep, ushort count)
- \internal
-*/
-
-/*!
- \fn bool QKeyEvent::hasExtendedInfo() const
- \internal
-*/
-
-/*!
\fn quint32 QKeyEvent::nativeScanCode() const
\since 4.2
@@ -2539,7 +2526,7 @@ Qt::MouseButtons QTabletEvent::buttons() const
globalPos() can differ significantly from the current position
QCursor::pos().
- \sa globalX(), globalY(), hiResGlobalPos()
+ \sa globalX(), globalY()
*/
/*!
@@ -2584,15 +2571,6 @@ Qt::MouseButtons QTabletEvent::buttons() const
*/
/*!
- \fn const QPointF &QTabletEvent::hiResGlobalPos() const
-
- The high precision coordinates delivered from the tablet expressed.
- Sub pixeling information is in the fractional part of the QPointF.
-
- \sa globalPos(), hiResGlobalX(), hiResGlobalY()
-*/
-
-/*!
\fn qreal &QTabletEvent::hiResGlobalX() const
The high precision x position of the tablet device.
@@ -2674,10 +2652,10 @@ Qt::MouseButtons QTabletEvent::buttons() const
\sa Qt::NativeGestureType, QGestureEvent
*/
+#if QT_DEPRECATED_SINCE(5, 10)
/*!
\deprecated The QTouchDevice parameter is now required
*/
-#if QT_DEPRECATED_SINCE(5, 10)
QNativeGestureEvent::QNativeGestureEvent(Qt::NativeGestureType type, const QPointF &localPos, const QPointF &windowPos,
const QPointF &screenPos, qreal realValue, ulong sequenceId, quint64 intValue)
: QInputEvent(QEvent::NativeGesture), mGestureType(type),
@@ -2686,10 +2664,6 @@ QNativeGestureEvent::QNativeGestureEvent(Qt::NativeGestureType type, const QPoin
{ }
#endif
-typedef QHash<const QNativeGestureEvent*, const QTouchDevice*> NativeGestureEventDataHash;
-// ### Qt6: move this to a member in QNativeGestureEvent
-Q_GLOBAL_STATIC(NativeGestureEventDataHash, g_nativeGestureEventDataHash)
-
/*!
Constructs a native gesture event of type \a type originating from \a device.
@@ -2704,27 +2678,20 @@ QNativeGestureEvent::QNativeGestureEvent(Qt::NativeGestureType type, const QTouc
const QPointF &screenPos, qreal realValue, ulong sequenceId, quint64 intValue)
: QInputEvent(QEvent::NativeGesture), mGestureType(type),
mLocalPos(localPos), mWindowPos(windowPos), mScreenPos(screenPos), mRealValue(realValue),
- mSequenceId(sequenceId), mIntValue(intValue)
+ mSequenceId(sequenceId), mIntValue(intValue), mDevice(device)
{
- g_nativeGestureEventDataHash->insert(this, device);
}
QNativeGestureEvent::~QNativeGestureEvent()
-{
- g_nativeGestureEventDataHash->remove(this);
-}
+ = default;
/*!
+ \fn const QTouchDevice *QNativeGestureEvent::device() const
\since 5.10
Returns the device.
*/
-const QTouchDevice *QNativeGestureEvent::device() const
-{
- return g_nativeGestureEventDataHash->value(this);
-}
-
/*!
\fn QNativeGestureEvent::gestureType() const
\since 5.2
@@ -4290,18 +4257,6 @@ QWindowStateChangeEvent::~QWindowStateChangeEvent()
QGraphicsItem::acceptTouchEvents()
*/
-/*! \enum QTouchEvent::DeviceType
- \obsolete
-
- This enum represents the type of device that generated a QTouchEvent.
-
- This enum has been deprecated. Use QTouchDevice::DeviceType instead.
- \omitvalue TouchPad
- \omitvalue TouchScreen
-
- \sa QTouchDevice::DeviceType, QTouchDevice::type(), QTouchEvent::device()
-*/
-
/*!
Constructs a QTouchEvent with the given \a eventType, \a device, and
\a touchPoints. The \a touchPointStates and \a modifiers
@@ -4341,16 +4296,6 @@ QTouchEvent::~QTouchEvent()
This is typically a QWidget or a QQuickItem. May be 0 when no specific target is available.
*/
-/*! \fn QTouchEvent::DeviceType QTouchEvent::deviceType() const
- \obsolete
-
- Returns the touch device Type, which is of type \l {QTouchEvent::DeviceType} {DeviceType}.
-
- This function has been deprecated. Use QTouchDevice::type() instead.
-
- \sa QTouchDevice::type(), QTouchEvent::device()
-*/
-
/*! \fn QTouchEvent::TouchPoint::TouchPoint(TouchPoint &&other)
Move-constructs a TouchPoint instance, making it point to the same
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index b73d90529a..10ef01fa1d 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -350,7 +350,7 @@ public:
const QPointF &windowPos() const { return mWindowPos; }
const QPointF &screenPos() const { return mScreenPos; }
- const QTouchDevice *device() const;
+ const QTouchDevice *device() const { return mDevice; }
protected:
Qt::NativeGestureType mGestureType;
@@ -360,6 +360,7 @@ protected:
qreal mRealValue;
ulong mSequenceId;
quint64 mIntValue;
+ const QTouchDevice *mDevice;
};
#endif // QT_NO_GESTURES
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 426f2aeece..432618968f 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -146,6 +146,8 @@ QString QGuiApplicationPrivate::styleOverride;
Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
+Qt::HighDpiScaleFactorRoundingPolicy QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy =
+ Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor;
bool QGuiApplicationPrivate::highDpiScalingUpdated = false;
QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow;
@@ -687,6 +689,8 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::lastCursorPosition = {qInf(), qInf()};
QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr;
QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
+ QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy =
+ Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor;
QGuiApplicationPrivate::highDpiScalingUpdated = false;
QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear();
@@ -1177,14 +1181,18 @@ Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin");
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
- QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'));
+ QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'), QString::SkipEmptyParts);
QStringList platformArguments;
QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath);
for (const auto &pluginArgument : plugins) {
// Split into platform name and arguments
- QStringList arguments = pluginArgument.split(QLatin1Char(':'));
+ QStringList arguments = pluginArgument.split(QLatin1Char(':'), QString::SkipEmptyParts);
+ if (arguments.isEmpty())
+ continue;
const QString name = arguments.takeFirst().toLower();
QString argumentsKey = name;
+ if (name.isEmpty())
+ continue;
argumentsKey[0] = argumentsKey.at(0).toUpper();
arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey));
@@ -3491,6 +3499,46 @@ Qt::ApplicationState QGuiApplication::applicationState()
}
/*!
+ \since 5.14
+
+ Sets the high-DPI scale factor rounding policy for the application. The
+ policy decides how non-integer scale factors (such as Windows 150%) are
+ handled, for applications that have AA_EnableHighDpiScaling enabled.
+
+ The two principal options are whether fractional scale factors should
+ be rounded to an integer or not. Keeping the scale factor as-is will
+ make the user interface size match the OS setting exactly, but may cause
+ painting errors, for example with the Windows style.
+
+ If rounding is wanted, then which type of rounding should be decided
+ next. Mathematically correct rounding is supported but may not give
+ the best visual results: Consider if you want to render 1.5x as 1x
+ ("small UI") or as 2x ("large UI"). See the Qt::HighDpiScaleFactorRoundingPolicy
+ enum for a complete list of all options.
+
+ This function must be called before creating the application object,
+ and can be overridden by setting the QT_SCALE_FACTOR_ROUNDING_POLICY
+ environment variable. The QGuiApplication::highDpiScaleFactorRoundingPolicy()
+ accessor will reflect the environment, if set.
+
+ The default value is Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor.
+*/
+void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy)
+{
+ QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy;
+}
+
+/*!
+ \since 5.14
+
+ Returns the high-DPI scale factor rounding policy.
+*/
+Qt::HighDpiScaleFactorRoundingPolicy QGuiApplication::highDpiScaleFactorRoundingPolicy()
+{
+ return QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy;
+}
+
+/*!
\since 5.2
\fn void QGuiApplication::applicationStateChanged(Qt::ApplicationState state)
diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h
index 5ea72fa0f6..fc74c5299a 100644
--- a/src/gui/kernel/qguiapplication.h
+++ b/src/gui/kernel/qguiapplication.h
@@ -156,6 +156,9 @@ public:
static Qt::ApplicationState applicationState();
+ static void setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy);
+ static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy();
+
static int exec();
bool notify(QObject *, QEvent *) override;
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index afca7579ea..e28607bad6 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -223,6 +223,7 @@ public:
static QWindow *currentMouseWindow;
static QWindow *currentMousePressWindow;
static Qt::ApplicationState applicationState;
+ static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy;
static bool highDpiScalingUpdated;
static QPointer<QWindow> currentDragWindow;
diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp
index 090217187e..edca8d9423 100644
--- a/src/gui/kernel/qguivariant.cpp
+++ b/src/gui/kernel/qguivariant.cpp
@@ -68,7 +68,6 @@
#include "qdebug.h"
#include "qmap.h"
#include "qdatetime.h"
-#include "qeasingcurve.h"
#include "qlist.h"
#include "qstring.h"
#include "qstringlist.h"
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 0fea416404..c031885d5d 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -46,6 +46,9 @@
#include <private/qguiapplication_p.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qmetaobject.h>
+
+#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -53,9 +56,29 @@ Q_LOGGING_CATEGORY(lcScaling, "qt.scaling");
#ifndef QT_NO_HIGHDPISCALING
static const char legacyDevicePixelEnvVar[] = "QT_DEVICE_PIXEL_RATIO";
+static const char legacyAutoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR";
+
+static const char enableHighDpiScalingEnvVar[] = "QT_ENABLE_HIGHDPI_SCALING";
static const char scaleFactorEnvVar[] = "QT_SCALE_FACTOR";
-static const char autoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR";
static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS";
+static const char scaleFactorRoundingPolicyEnvVar[] = "QT_SCALE_FACTOR_ROUNDING_POLICY";
+static const char dpiAdjustmentPolicyEnvVar[] = "QT_DPI_ADJUSTMENT_POLICY";
+static const char usePhysicalDpiEnvVar[] = "QT_USE_PHYSICAL_DPI";
+
+// Per-screen scale factors for named screens set with QT_SCREEN_SCALE_FACTORS
+// are stored here. Use a global hash to keep the factor across screen
+// disconnect/connect cycles where the screen object may be deleted.
+typedef QHash<QString, qreal> QScreenScaleFactorHash;
+Q_GLOBAL_STATIC(QScreenScaleFactorHash, qNamedScreenScaleFactors);
+
+// Reads and interprets the given environment variable as a bool,
+// returns the default value if not set.
+static bool qEnvironmentVariableAsBool(const char *name, bool defaultValue)
+{
+ bool ok = false;
+ int value = qEnvironmentVariableIntValue(name, &ok);
+ return ok ? value > 0 : defaultValue;
+}
static inline qreal initialGlobalScaleFactor()
{
@@ -69,17 +92,24 @@ static inline qreal initialGlobalScaleFactor()
result = f;
}
} else {
+ // Check for deprecated environment variables.
if (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)) {
qWarning("Warning: %s is deprecated. Instead use:\n"
" %s to enable platform plugin controlled per-screen factors.\n"
- " %s to set per-screen factors.\n"
+ " %s to set per-screen DPI.\n"
" %s to set the application global scale factor.",
- legacyDevicePixelEnvVar, autoScreenEnvVar, screenFactorsEnvVar, scaleFactorEnvVar);
+ legacyDevicePixelEnvVar, legacyAutoScreenEnvVar, screenFactorsEnvVar, scaleFactorEnvVar);
int dpr = qEnvironmentVariableIntValue(legacyDevicePixelEnvVar);
if (dpr > 0)
result = dpr;
}
+
+ if (qEnvironmentVariableIsSet(legacyAutoScreenEnvVar)) {
+ qWarning("Warning: %s is deprecated. Instead use:\n"
+ " %s to enable platform plugin controlled per-screen factors.",
+ legacyAutoScreenEnvVar, enableHighDpiScalingEnvVar);
+ }
}
return result;
}
@@ -226,7 +256,6 @@ bool QHighDpiScaling::m_usePixelDensity = false; // use scale factor from platfo
bool QHighDpiScaling::m_pixelDensityScalingActive = false; // pixel density scale factor > 1
bool QHighDpiScaling::m_globalScalingActive = false; // global scale factor is active
bool QHighDpiScaling::m_screenFactorSet = false; // QHighDpiScaling::setScreenFactor has been used
-QDpi QHighDpiScaling::m_logicalDpi = QDpi(-1,-1); // The scaled logical DPI of the primary screen
/*
Initializes the QHighDpiScaling global variables. Called before the
@@ -238,16 +267,214 @@ static inline bool usePixelDensity()
// Determine if we should set a scale factor based on the pixel density
// reported by the platform plugin. There are several enablers and several
// disablers. A single disable may veto all other enablers.
+
+ // First, check of there is an explicit disable.
if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling))
return false;
bool screenEnvValueOk;
- const int screenEnvValue = qEnvironmentVariableIntValue(autoScreenEnvVar, &screenEnvValueOk);
+ const int screenEnvValue = qEnvironmentVariableIntValue(legacyAutoScreenEnvVar, &screenEnvValueOk);
if (screenEnvValueOk && screenEnvValue < 1)
return false;
+ bool enableEnvValueOk;
+ const int enableEnvValue = qEnvironmentVariableIntValue(enableHighDpiScalingEnvVar, &enableEnvValueOk);
+ if (enableEnvValueOk && enableEnvValue < 1)
+ return false;
+
+ // Then return if there was an enable.
return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling)
|| (screenEnvValueOk && screenEnvValue > 0)
- || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) &&
- qgetenv(legacyDevicePixelEnvVar).compare("auto", Qt::CaseInsensitive) == 0);
+ || (enableEnvValueOk && enableEnvValue > 0)
+ || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && qgetenv(legacyDevicePixelEnvVar).toLower() == "auto");
+}
+
+qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen)
+{
+ // Determine if physical DPI should be used
+ static const bool usePhysicalDpi = qEnvironmentVariableAsBool(usePhysicalDpiEnvVar, false);
+
+ // Calculate scale factor beased on platform screen DPI values
+ qreal factor;
+ QDpi platformBaseDpi = screen->logicalBaseDpi();
+ if (usePhysicalDpi) {
+ qreal platformPhysicalDpi = screen->screen()->physicalDotsPerInch();
+ factor = qreal(platformPhysicalDpi) / qreal(platformBaseDpi.first);
+ } else {
+ const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi());
+ factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first);
+ }
+
+ return factor;
+}
+
+template <class EnumType>
+struct EnumLookup
+{
+ const char *name;
+ EnumType value;
+};
+
+template <class EnumType>
+static bool operator==(const EnumLookup<EnumType> &e1, const EnumLookup<EnumType> &e2)
+{
+ return qstricmp(e1.name, e2.name) == 0;
+}
+
+template <class EnumType>
+static QByteArray joinEnumValues(const EnumLookup<EnumType> *i1, const EnumLookup<EnumType> *i2)
+{
+ QByteArray result;
+ for (; i1 < i2; ++i1) {
+ if (!result.isEmpty())
+ result += QByteArrayLiteral(", ");
+ result += i1->name;
+ }
+ return result;
+}
+
+using ScaleFactorRoundingPolicyLookup = EnumLookup<Qt::HighDpiScaleFactorRoundingPolicy>;
+
+static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[] =
+{
+ {"Round", Qt::HighDpiScaleFactorRoundingPolicy::Round},
+ {"Ceil", Qt::HighDpiScaleFactorRoundingPolicy::Ceil},
+ {"Floor", Qt::HighDpiScaleFactorRoundingPolicy::Floor},
+ {"RoundPreferFloor", Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor},
+ {"PassThrough", Qt::HighDpiScaleFactorRoundingPolicy::PassThrough}
+};
+
+static Qt::HighDpiScaleFactorRoundingPolicy
+ lookupScaleFactorRoundingPolicy(const QByteArray &v)
+{
+ auto end = std::end(scaleFactorRoundingPolicyLookup);
+ auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end,
+ ScaleFactorRoundingPolicyLookup{v.constData(), Qt::HighDpiScaleFactorRoundingPolicy::Unset});
+ return it != end ? it->value : Qt::HighDpiScaleFactorRoundingPolicy::Unset;
+}
+
+using DpiAdjustmentPolicyLookup = EnumLookup<QHighDpiScaling::DpiAdjustmentPolicy>;
+
+static const DpiAdjustmentPolicyLookup dpiAdjustmentPolicyLookup[] =
+{
+ {"AdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Enabled},
+ {"DontAdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Disabled},
+ {"AdjustUpOnly", QHighDpiScaling::DpiAdjustmentPolicy::UpOnly}
+};
+
+static QHighDpiScaling::DpiAdjustmentPolicy
+ lookupDpiAdjustmentPolicy(const QByteArray &v)
+{
+ auto end = std::end(dpiAdjustmentPolicyLookup);
+ auto it = std::find(std::begin(dpiAdjustmentPolicyLookup), end,
+ DpiAdjustmentPolicyLookup{v.constData(), QHighDpiScaling::DpiAdjustmentPolicy::Unset});
+ return it != end ? it->value : QHighDpiScaling::DpiAdjustmentPolicy::Unset;
+}
+
+qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
+{
+ // Apply scale factor rounding policy. Using mathematically correct rounding
+ // may not give the most desirable visual results, especially for
+ // critical fractions like .5. In general, rounding down results in visual
+ // sizes that are smaller than the ideal size, and opposite for rounding up.
+ // Rounding down is then preferable since "small UI" is a more acceptable
+ // high-DPI experience than "large UI".
+ static auto scaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::Unset;
+
+ // Determine rounding policy
+ if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
+ // Check environment
+ if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) {
+ QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar);
+ auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
+ if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
+ scaleFactorRoundingPolicy = policyEnumValue;
+ } else {
+ auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
+ std::end(scaleFactorRoundingPolicyLookup));
+ qWarning("Unknown scale factor rounding policy: %s. Supported values are: %s.",
+ policyText.constData(), values.constData());
+ }
+ }
+
+ // Check application object if no environment value was set.
+ if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
+ scaleFactorRoundingPolicy = QGuiApplication::highDpiScaleFactorRoundingPolicy();
+ } else {
+ // Make application setting reflect environment
+ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(scaleFactorRoundingPolicy);
+ }
+ }
+
+ // Apply rounding policy.
+ qreal roundedFactor = rawFactor;
+ switch (scaleFactorRoundingPolicy) {
+ case Qt::HighDpiScaleFactorRoundingPolicy::Round:
+ roundedFactor = qRound(rawFactor);
+ break;
+ case Qt::HighDpiScaleFactorRoundingPolicy::Ceil:
+ roundedFactor = qCeil(rawFactor);
+ break;
+ case Qt::HighDpiScaleFactorRoundingPolicy::Floor:
+ roundedFactor = qFloor(rawFactor);
+ break;
+ case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor:
+ // Round up for .75 and higher. This favors "small UI" over "large UI".
+ roundedFactor = rawFactor - qFloor(rawFactor) < 0.75
+ ? qFloor(rawFactor) : qCeil(rawFactor);
+ break;
+ case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough:
+ case Qt::HighDpiScaleFactorRoundingPolicy::Unset:
+ break;
+ }
+
+ // Don't round down to to zero; clamp the minimum (rounded) factor to 1.
+ // This is not a common case but can happen if a display reports a very
+ // low DPI.
+ if (scaleFactorRoundingPolicy != Qt::HighDpiScaleFactorRoundingPolicy::PassThrough)
+ roundedFactor = qMax(roundedFactor, qreal(1));
+
+ return roundedFactor;
+}
+
+QDpi QHighDpiScaling::effectiveLogicalDpi(const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor)
+{
+ // Apply DPI adjustment policy, if needed. If enabled this will change the
+ // reported logical DPI to account for the difference between the rounded
+ // scale factor and the actual scale factor. The effect is that text size
+ // will be correct for the screen dpi, but may be (slightly) out of sync
+ // with the rest of the UI. The amount of out-of-synch-ness depends on how
+ // well user code handles a non-standard DPI values, but since the
+ // adjustment is small (typically +/- 48 max) this might be OK.
+ static auto dpiAdjustmentPolicy = DpiAdjustmentPolicy::Unset;
+
+ // Determine adjustment policy.
+ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Unset) {
+ if (qEnvironmentVariableIsSet(dpiAdjustmentPolicyEnvVar)) {
+ QByteArray policyText = qgetenv(dpiAdjustmentPolicyEnvVar);
+ auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText);
+ if (policyEnumValue != DpiAdjustmentPolicy::Unset) {
+ dpiAdjustmentPolicy = policyEnumValue;
+ } else {
+ auto values = joinEnumValues(std::begin(dpiAdjustmentPolicyLookup),
+ std::end(dpiAdjustmentPolicyLookup));
+ qWarning("Unknown DPI adjustment policy: %s. Supported values are: %s.",
+ policyText.constData(), values.constData());
+ }
+ }
+ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Unset)
+ dpiAdjustmentPolicy = DpiAdjustmentPolicy::UpOnly;
+ }
+
+ // Apply adjustment policy.
+ const QDpi baseDpi = screen->logicalBaseDpi();
+ const qreal dpiAdjustmentFactor = rawFactor / roundedFactor;
+
+ // Return the base DPI for cases where there is no adjustment
+ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Disabled)
+ return baseDpi;
+ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1)
+ return baseDpi;
+
+ return QDpi(baseDpi.first * dpiAdjustmentFactor, baseDpi.second * dpiAdjustmentFactor);
}
void QHighDpiScaling::initHighDpiScaling()
@@ -260,8 +487,6 @@ void QHighDpiScaling::initHighDpiScaling()
m_pixelDensityScalingActive = false; //set in updateHighDpiScaling below
- // we update m_active in updateHighDpiScaling, but while we create the
- // screens, we have to assume that m_usePixelDensity implies scaling
m_active = m_globalScalingActive || m_usePixelDensity;
}
@@ -283,20 +508,19 @@ void QHighDpiScaling::updateHighDpiScaling()
int i = 0;
const auto specs = qgetenv(screenFactorsEnvVar).split(';');
for (const QByteArray &spec : specs) {
- QScreen *screen = 0;
int equalsPos = spec.lastIndexOf('=');
- double factor = 0;
+ qreal factor = 0;
if (equalsPos > 0) {
// support "name=factor"
QByteArray name = spec.mid(0, equalsPos);
QByteArray f = spec.mid(equalsPos + 1);
bool ok;
factor = f.toDouble(&ok);
- if (ok) {
+ if (ok && factor > 0 ) {
const auto screens = QGuiApplication::screens();
for (QScreen *s : screens) {
if (s->name() == QString::fromLocal8Bit(name)) {
- screen = s;
+ setScreenFactor(s, factor);
break;
}
}
@@ -305,23 +529,15 @@ void QHighDpiScaling::updateHighDpiScaling()
// listing screens in order
bool ok;
factor = spec.toDouble(&ok);
- if (ok && i < QGuiApplication::screens().count())
- screen = QGuiApplication::screens().at(i);
+ if (ok && factor > 0 && i < QGuiApplication::screens().count()) {
+ QScreen *screen = QGuiApplication::screens().at(i);
+ setScreenFactor(screen, factor);
+ }
}
- if (screen)
- setScreenFactor(screen, factor);
++i;
}
}
- m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
-
- QScreen *primaryScreen = QGuiApplication::primaryScreen();
- if (!primaryScreen)
- return;
- QPlatformScreen *platformScreen = primaryScreen->handle();
- qreal sf = screenSubfactor(platformScreen);
- QDpi primaryDpi = platformScreen->logicalDpi();
- m_logicalDpi = QDpi(primaryDpi.first / sf, primaryDpi.second / sf);
+ m_active = m_globalScalingActive || m_usePixelDensity;
}
/*
@@ -353,7 +569,14 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
m_screenFactorSet = true;
m_active = true;
}
- screen->setProperty(scaleFactorProperty, QVariant(factor));
+
+ // Prefer associating the factor with screen name over the object
+ // since the screen object may be deleted on screen disconnects.
+ const QString name = screen->name();
+ if (name.isEmpty())
+ screen->setProperty(scaleFactorProperty, QVariant(factor));
+ else
+ qNamedScreenScaleFactors()->insert(name, factor);
// hack to force re-evaluation of screen geometry
if (screen->handle())
@@ -421,41 +644,55 @@ QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &w
qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
{
qreal factor = qreal(1.0);
- if (screen) {
- if (m_usePixelDensity) {
- qreal pixelDensity = screen->pixelDensity();
-
- // Pixel density reported by the screen is sometimes not precise enough,
- // so recalculate it: divide px (physical pixels) by dp (device-independent pixels)
- // for both width and height, and then use the average if it is different from
- // the one initially reported by the screen
- QRect screenGeometry = screen->geometry();
- qreal wFactor = qreal(screenGeometry.width()) / qRound(screenGeometry.width() / pixelDensity);
- qreal hFactor = qreal(screenGeometry.height()) / qRound(screenGeometry.height() / pixelDensity);
- qreal averageDensity = (wFactor + hFactor) / 2;
- if (!qFuzzyCompare(pixelDensity, averageDensity))
- pixelDensity = averageDensity;
-
- factor *= pixelDensity;
- }
- if (m_screenFactorSet) {
- QVariant screenFactor = screen->screen()->property(scaleFactorProperty);
- if (screenFactor.isValid())
- factor *= screenFactor.toReal();
+ if (!screen)
+ return factor;
+
+ // Unlike the other code where factors are combined by multiplication,
+ // factors from QT_SCREEN_SCALE_FACTORS takes precedence over the factor
+ // computed from platform plugin DPI. The rationale is that the user is
+ // setting the factor to override erroneous DPI values.
+ bool screenPropertyUsed = false;
+ if (m_screenFactorSet) {
+ // Check if there is a factor set on the screen object or associated
+ // with the screen name. These are mutually exclusive, so checking
+ // order is not significant.
+ QVariant byIndex = screen->screen()->property(scaleFactorProperty);
+ auto byNameIt = qNamedScreenScaleFactors()->constFind(screen->name());
+ if (byIndex.isValid()) {
+ screenPropertyUsed = true;
+ factor = byIndex.toReal();
+ } else if (byNameIt != qNamedScreenScaleFactors()->cend()) {
+ screenPropertyUsed = true;
+ factor = *byNameIt;
}
}
+
+ if (!screenPropertyUsed && m_usePixelDensity)
+ factor = roundScaleFactor(rawScaleFactor(screen));
+
return factor;
}
-QDpi QHighDpiScaling::logicalDpi()
+QDpi QHighDpiScaling::logicalDpi(const QScreen *screen)
{
- return m_logicalDpi;
+ // (Note: m_active test is performed at call site.)
+ if (!screen || !screen->handle())
+ return QDpi(96, 96);
+
+ if (!m_usePixelDensity)
+ return QPlatformScreen::overrideDpi(screen->handle()->logicalDpi());
+
+ const qreal scaleFactor = rawScaleFactor(screen->handle());
+ const qreal roundedScaleFactor = roundScaleFactor(scaleFactor);
+ return effectiveLogicalDpi(screen->handle(), scaleFactor, roundedScaleFactor);
}
QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *platformScreen, QPoint *nativePosition)
{
if (!m_active)
return { qreal(1), QPoint() };
+ if (!platformScreen)
+ return { m_factor, QPoint() }; // the global factor
const QPlatformScreen *actualScreen = nativePosition ?
platformScreen->screenForPosition(*nativePosition) : platformScreen;
return { m_factor * screenSubfactor(actualScreen), actualScreen->geometry().topLeft() };
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index 50221926c6..f58944a7d2 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -59,6 +59,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtGui/qregion.h>
#include <QtGui/qscreen.h>
+#include <QtGui/qvector2d.h>
#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE
@@ -71,11 +72,27 @@ typedef QPair<qreal, qreal> QDpi;
#ifndef QT_NO_HIGHDPISCALING
class Q_GUI_EXPORT QHighDpiScaling {
+ Q_GADGET
public:
+ enum class DpiAdjustmentPolicy {
+ Unset,
+ Enabled,
+ Disabled,
+ UpOnly
+ };
+ Q_ENUM(DpiAdjustmentPolicy)
+
+ QHighDpiScaling() = delete;
+ ~QHighDpiScaling() = delete;
+ QHighDpiScaling(const QHighDpiScaling &) = delete;
+ QHighDpiScaling &operator=(const QHighDpiScaling &) = delete;
+ QHighDpiScaling(QHighDpiScaling &&) = delete;
+ QHighDpiScaling &operator=(QHighDpiScaling &&) = delete;
+
static void initHighDpiScaling();
static void updateHighDpiScaling();
static void setGlobalFactor(qreal factor);
- static void setScreenFactor(QScreen *window, qreal factor);
+ static void setScreenFactor(QScreen *screen, qreal factor);
static bool isActive() { return m_active; }
@@ -97,9 +114,12 @@ public:
static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
static QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
- static QDpi logicalDpi();
+ static QDpi logicalDpi(const QScreen *screen);
private:
+ static qreal rawScaleFactor(const QPlatformScreen *screen);
+ static qreal roundScaleFactor(qreal rawFactor);
+ static QDpi effectiveLogicalDpi(const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor);
static qreal screenSubfactor(const QPlatformScreen *screen);
static qreal m_factor;
@@ -117,13 +137,26 @@ private:
namespace QHighDpi {
-template <typename T>
-inline T scale(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0))
+inline qreal scale(qreal value, qreal scaleFactor, QPointF /* origin */ = QPointF(0, 0))
+{
+ return value * scaleFactor;
+}
+
+inline QSize scale(const QSize &value, qreal scaleFactor, QPointF /* origin */ = QPointF(0, 0))
+{
+ return value * scaleFactor;
+}
+
+inline QSizeF scale(const QSizeF &value, qreal scaleFactor, QPointF /* origin */ = QPointF(0, 0))
{
- Q_UNUSED(origin)
return value * scaleFactor;
}
+inline QVector2D scale(const QVector2D &value, qreal scaleFactor, QPointF /* origin */ = QPointF(0, 0))
+{
+ return value * float(scaleFactor);
+}
+
inline QPointF scale(const QPointF &pos, qreal scaleFactor, QPointF origin = QPointF(0, 0))
{
return (pos - origin) * scaleFactor + origin;
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 10adee438b..6f51fe3095 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -976,11 +976,8 @@ 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
}
if (!d->platformGLContext->makeCurrent(surface->surfaceHandle()))
diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp
index 490cfc6178..b3d3db0751 100644
--- a/src/gui/kernel/qplatformintegration.cpp
+++ b/src/gui/kernel/qplatformintegration.cpp
@@ -244,6 +244,9 @@ QPlatformServices *QPlatformIntegration::services() const
\value TopStackedNativeChildWindows The platform supports native child windows via
QWindowContainer without having to punch a transparent hole in the
backingstore. (since 5.10)
+
+ \value OpenGLOnRasterSurface The platform supports making a QOpenGLContext current
+ in combination with a QWindow of type RasterSurface.
*/
/*!
diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h
index b764bd2617..d9f349555a 100644
--- a/src/gui/kernel/qplatformintegration.h
+++ b/src/gui/kernel/qplatformintegration.h
@@ -105,7 +105,8 @@ public:
AllGLFunctionsQueryable,
ApplicationIcon,
SwitchableWidgetComposition,
- TopStackedNativeChildWindows
+ TopStackedNativeChildWindows,
+ OpenGLOnRasterSurface
};
virtual ~QPlatformIntegration() { }
diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp
index 9c5876550a..f3213bf5ea 100644
--- a/src/gui/kernel/qplatformscreen.cpp
+++ b/src/gui/kernel/qplatformscreen.cpp
@@ -193,6 +193,28 @@ QDpi QPlatformScreen::logicalDpi() const
25.4 * s.height() / ps.height());
}
+// Helper function for accessing the platform screen logical dpi
+// which accounts for QT_FONT_DPI.
+QPair<qreal, qreal> QPlatformScreen::overrideDpi(const QPair<qreal, qreal> &in)
+{
+ static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI");
+ return overrideDpi > 0 ? QDpi(overrideDpi, overrideDpi) : in;
+}
+
+/*!
+ Reimplement to return the base logical DPI for the platform. This
+ DPI value should correspond to a standard-DPI (1x) display. The
+ default implementation returns 96.
+
+ QtGui will use this value (together with logicalDpi) to compute
+ the scale factor when high-DPI scaling is enabled:
+ factor = logicalDPI / baseDPI
+*/
+QDpi QPlatformScreen::logicalBaseDpi() const
+{
+ return QDpi(96, 96);
+}
+
/*!
Reimplement this function in subclass to return the device pixel ratio
for the screen. This is the ratio between physical pixels and the
diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h
index b9ecc80320..d7378aed51 100644
--- a/src/gui/kernel/qplatformscreen.h
+++ b/src/gui/kernel/qplatformscreen.h
@@ -115,6 +115,7 @@ public:
virtual QSizeF physicalSize() const;
virtual QDpi logicalDpi() const;
+ virtual QDpi logicalBaseDpi() const;
virtual qreal devicePixelRatio() const;
virtual qreal pixelDensity() const;
@@ -160,6 +161,8 @@ public:
// The platform screen's geometry in device independent coordinates
QRect deviceIndependentGeometry() const;
+ static QDpi overrideDpi(const QDpi &in);
+
protected:
void resizeMaximizedWindows();
diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp
index c79f597414..7adf3db1b8 100644
--- a/src/gui/kernel/qscreen.cpp
+++ b/src/gui/kernel/qscreen.cpp
@@ -84,8 +84,11 @@ void QScreenPrivate::setPlatformScreen(QPlatformScreen *screen)
platformScreen->d_func()->screen = q;
orientation = platformScreen->orientation();
geometry = platformScreen->deviceIndependentGeometry();
- availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), QHighDpiScaling::factor(platformScreen), geometry.topLeft());
- logicalDpi = platformScreen->logicalDpi();
+ availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(),
+ QHighDpiScaling::factor(platformScreen), geometry.topLeft());
+
+ logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi());
+
refreshRate = platformScreen->refreshRate();
// safeguard ourselves against buggy platform behavior...
if (refreshRate < 1.0)
@@ -285,7 +288,7 @@ qreal QScreen::logicalDotsPerInchX() const
{
Q_D(const QScreen);
if (QHighDpiScaling::isActive())
- return QHighDpiScaling::logicalDpi().first;
+ return QHighDpiScaling::logicalDpi(this).first;
return d->logicalDpi.first;
}
@@ -301,7 +304,7 @@ qreal QScreen::logicalDotsPerInchY() const
{
Q_D(const QScreen);
if (QHighDpiScaling::isActive())
- return QHighDpiScaling::logicalDpi().second;
+ return QHighDpiScaling::logicalDpi(this).second;
return d->logicalDpi.second;
}
@@ -320,7 +323,7 @@ qreal QScreen::logicalDotsPerInchY() const
qreal QScreen::logicalDotsPerInch() const
{
Q_D(const QScreen);
- QDpi dpi = QHighDpiScaling::isActive() ? QHighDpiScaling::logicalDpi() : d->logicalDpi;
+ QDpi dpi = QHighDpiScaling::isActive() ? QHighDpiScaling::logicalDpi(this) : d->logicalDpi;
return (dpi.first + dpi.second) * qreal(0.5);
}
diff --git a/src/gui/kernel/qsurface.cpp b/src/gui/kernel/qsurface.cpp
index 415e64b39c..709f28d431 100644
--- a/src/gui/kernel/qsurface.cpp
+++ b/src/gui/kernel/qsurface.cpp
@@ -39,6 +39,8 @@
#include "qsurface.h"
#include "qopenglcontext.h"
+#include <qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
@@ -103,6 +105,10 @@ QT_BEGIN_NAMESPACE
bool QSurface::supportsOpenGL() const
{
SurfaceType type = surfaceType();
+ if (type == RasterSurface) {
+ QPlatformIntegration *integ = QGuiApplicationPrivate::instance()->platformIntegration();
+ return integ->hasCapability(QPlatformIntegration::OpenGLOnRasterSurface);
+ }
return type == OpenGLSurface || type == RasterGLSurface;
}
diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp
index 2e2738ec81..238886220b 100644
--- a/src/gui/kernel/qsurfaceformat.cpp
+++ b/src/gui/kernel/qsurfaceformat.cpp
@@ -554,10 +554,6 @@ int QSurfaceFormat::alphaBufferSize() const
/*!
Set the desired \a size in bits of the red channel of the color buffer.
-
- \note On Mac OSX, be sure to set the buffer size of all color channels,
- otherwise this setting will have no effect. If one of the buffer sizes is not set,
- the current bit-depth of the screen is used.
*/
void QSurfaceFormat::setRedBufferSize(int size)
{
@@ -569,10 +565,6 @@ void QSurfaceFormat::setRedBufferSize(int size)
/*!
Set the desired \a size in bits of the green channel of the color buffer.
-
- \note On Mac OSX, be sure to set the buffer size of all color channels,
- otherwise this setting will have no effect. If one of the buffer sizes is not set,
- the current bit-depth of the screen is used.
*/
void QSurfaceFormat::setGreenBufferSize(int size)
{
@@ -584,10 +576,6 @@ void QSurfaceFormat::setGreenBufferSize(int size)
/*!
Set the desired \a size in bits of the blue channel of the color buffer.
-
- \note On Mac OSX, be sure to set the buffer size of all color channels,
- otherwise this setting will have no effect. If one of the buffer sizes is not set,
- the current bit-depth of the screen is used.
*/
void QSurfaceFormat::setBlueBufferSize(int size)
{
diff --git a/src/gui/kernel/qtouchdevice.cpp b/src/gui/kernel/qtouchdevice.cpp
index 511e92566e..ea187f54aa 100644
--- a/src/gui/kernel/qtouchdevice.cpp
+++ b/src/gui/kernel/qtouchdevice.cpp
@@ -44,6 +44,7 @@
#include <QCoreApplication>
#include <private/qdebug_p.h>
+#include <private/qlocking_p.h>
QT_BEGIN_NAMESPACE
@@ -201,15 +202,20 @@ void QTouchDevice::setName(const QString &name)
d->name = name;
}
-typedef QList<const QTouchDevice *> TouchDevices;
-Q_GLOBAL_STATIC(TouchDevices, deviceList)
static QBasicMutex devicesMutex;
-static void cleanupDevicesList()
+struct TouchDevices {
+ TouchDevices();
+ QList<const QTouchDevice *> list;
+};
+Q_GLOBAL_STATIC(TouchDevices, deviceList)
+
+TouchDevices::TouchDevices()
{
- QMutexLocker lock(&devicesMutex);
- qDeleteAll(*deviceList());
- deviceList()->clear();
+ qAddPostRoutine([]{
+ const auto locker = qt_scoped_lock(devicesMutex);
+ qDeleteAll(qExchange(deviceList->list, {}));
+ });
}
/*!
@@ -223,7 +229,7 @@ static void cleanupDevicesList()
QList<const QTouchDevice *> QTouchDevice::devices()
{
QMutexLocker lock(&devicesMutex);
- return *deviceList();
+ return deviceList->list;
}
/*!
@@ -232,13 +238,13 @@ QList<const QTouchDevice *> QTouchDevice::devices()
bool QTouchDevicePrivate::isRegistered(const QTouchDevice *dev)
{
QMutexLocker locker(&devicesMutex);
- return deviceList()->contains(dev);
+ return deviceList->list.contains(dev);
}
const QTouchDevice *QTouchDevicePrivate::deviceById(quint8 id)
{
QMutexLocker locker(&devicesMutex);
- for (const QTouchDevice *dev : *deviceList())
+ for (const QTouchDevice *dev : qAsConst(deviceList->list))
if (QTouchDevicePrivate::get(const_cast<QTouchDevice *>(dev))->id == id)
return dev;
return nullptr;
@@ -250,9 +256,7 @@ const QTouchDevice *QTouchDevicePrivate::deviceById(quint8 id)
void QTouchDevicePrivate::registerDevice(const QTouchDevice *dev)
{
QMutexLocker lock(&devicesMutex);
- if (deviceList()->isEmpty())
- qAddPostRoutine(cleanupDevicesList);
- deviceList()->append(dev);
+ deviceList->list.append(dev);
}
/*!
@@ -261,9 +265,7 @@ void QTouchDevicePrivate::registerDevice(const QTouchDevice *dev)
void QTouchDevicePrivate::unregisterDevice(const QTouchDevice *dev)
{
QMutexLocker lock(&devicesMutex);
- bool wasRemoved = deviceList()->removeOne(dev);
- if (wasRemoved && deviceList()->isEmpty())
- qRemovePostRoutine(cleanupDevicesList);
+ deviceList->list.removeOne(dev);
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index 4b8cb3646a..40a298226a 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -857,8 +857,8 @@ void QWindowSystemInterface::handleScreenGeometryChange(QScreen *screen, const Q
void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal dpiX, qreal dpiY)
{
- QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *e =
- new QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent(screen, dpiX, dpiY); // ### tja
+ const QDpi effectiveDpi = QPlatformScreen::overrideDpi(QDpi{dpiX, dpiY});
+ auto e = new QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent(screen, effectiveDpi.first, effectiveDpi.second);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
diff --git a/src/gui/opengl/qopenglprogrambinarycache.cpp b/src/gui/opengl/qopenglprogrambinarycache.cpp
index 1f1ac1fd80..8d4d8ed183 100644
--- a/src/gui/opengl/qopenglprogrambinarycache.cpp
+++ b/src/gui/opengl/qopenglprogrambinarycache.cpp
@@ -40,6 +40,7 @@
#include "qopenglprogrambinarycache_p.h"
#include <QOpenGLContext>
#include <QOpenGLExtraFunctions>
+#include <QSysInfo>
#include <QStandardPaths>
#include <QDir>
#include <QSaveFile>
@@ -102,7 +103,7 @@ static inline bool qt_ensureWritableDir(const QString &name)
QOpenGLProgramBinaryCache::QOpenGLProgramBinaryCache()
: m_cacheWritable(false)
{
- const QString subPath = QLatin1String("/qtshadercache/");
+ const QString subPath = QLatin1String("/qtshadercache-") + QSysInfo::buildAbi() + QLatin1Char('/');
const QString sharedCachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
if (!sharedCachePath.isEmpty()) {
m_cacheDir = sharedCachePath + subPath;
diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp
index d9d5403cf3..9e393bc47a 100644
--- a/src/gui/opengl/qopengltextureuploader.cpp
+++ b/src/gui/opengl/qopengltextureuploader.cpp
@@ -65,6 +65,10 @@
#define GL_RGBA16 0x805B
#endif
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
@@ -202,6 +206,20 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag
pixelType = GL_UNSIGNED_BYTE;
targetFormat = QImage::Format_RGB888;
break;
+ case QImage::Format_BGR888:
+ if (isOpenGL12orBetter) {
+ externalFormat = GL_BGR;
+ internalFormat = GL_RGB;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = QImage::Format_BGR888;
+ } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
+ funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED);
+ funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
+ externalFormat = internalFormat = GL_RGB;
+ pixelType = GL_UNSIGNED_BYTE;
+ targetFormat = QImage::Format_BGR888;
+ }
+ break;
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
case QImage::Format_RGBA8888_Premultiplied:
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index 972cf387ff..fcf6488edd 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -135,23 +135,41 @@ gcc:equals(QT_GCC_MAJOR_VERSION, 5) {
NO_PCH_SOURCES += painting/qdrawhelper.cpp
}
-SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
-SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp
-SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \
- painting/qimagescale_sse4.cpp
-ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp
+!android {
+ SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
+ SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp
+ SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \
+ painting/qimagescale_sse4.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
+ NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp
+ NEON_HEADERS += painting/qdrawhelper_neon_p.h
+}
!uikit:!win32:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as
-!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") {
+!android:!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") {
NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S
DEFINES += ENABLE_PIXMAN_DRAWHELPERS
}
-MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp
-MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h
-MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S
-MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S
+!android {
+ MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp
+ MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h
+ MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S
+ MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S
+} else {
+ # see https://developer.android.com/ndk/guides/abis
+ x86 | x86_64 {
+ DEFINES += QT_COMPILER_SUPPORTS_SSE2 QT_COMPILER_SUPPORTS_SSE3 QT_COMPILER_SUPPORTS_SSSE3
+ SOURCES += painting/qdrawhelper_sse2.cpp painting/qdrawhelper_ssse3.cpp
+ }
+ x86_64 {
+ DEFINES += QT_COMPILER_SUPPORTS_SSE4_1 QT_COMPILER_SUPPORTS_SSE4_2
+ SOURCES += painting/qdrawhelper_sse4.cpp painting/qimagescale_sse4.cpp
+ }
+ arm64-v8a {
+ SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp
+ HEADERS += painting/qdrawhelper_neon_p.h
+ }
+}
include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
index 8cda4b4072..9861fffff3 100644
--- a/src/gui/painting/qbezier.cpp
+++ b/src/gui/painting/qbezier.cpp
@@ -47,6 +47,8 @@
#include <private/qnumeric_p.h>
+#include <tuple> // for std::tie()
+
QT_BEGIN_NAMESPACE
//#define QDEBUG_BEZIER
@@ -54,24 +56,6 @@ QT_BEGIN_NAMESPACE
/*!
\internal
*/
-QBezier QBezier::fromPoints(const QPointF &p1, const QPointF &p2,
- const QPointF &p3, const QPointF &p4)
-{
- QBezier b;
- b.x1 = p1.x();
- b.y1 = p1.y();
- b.x2 = p2.x();
- b.y2 = p2.y();
- b.x3 = p3.x();
- b.y3 = p3.y();
- b.x4 = p4.x();
- b.y4 = p4.y();
- return b;
-}
-
-/*!
- \internal
-*/
QPolygonF QBezier::toPolygon(qreal bezier_flattening_threshold) const
{
// flattening is done by splitting the bezier until we can replace the segment by a straight
@@ -146,7 +130,7 @@ void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold
--lvl;
} else {
// split, second half of the polygon goes lower into the stack
- b->split(b+1, b);
+ std::tie(b[1], b[0]) = b->split();
lvl[1] = --lvl[0];
++b;
++lvl;
@@ -184,7 +168,7 @@ void QBezier::addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattenin
--lvl;
} else {
// split, second half of the polygon goes lower into the stack
- b->split(b+1, b);
+ std::tie(b[1], b[0]) = b->split();
lvl[1] = --lvl[0];
++b;
++lvl;
@@ -440,7 +424,7 @@ redo:
o += 2;
--b;
} else {
- b->split(b+1, b);
+ std::tie(b[1], b[0]) = b->split();
++b;
}
}
@@ -482,8 +466,6 @@ qreal QBezier::length(qreal error) const
void QBezier::addIfClose(qreal *length, qreal error) const
{
- QBezier left, right; /* bez poly splits */
-
qreal len = qreal(0.0); /* arc length */
qreal chord; /* chord length */
@@ -494,9 +476,9 @@ void QBezier::addIfClose(qreal *length, qreal error) const
chord = QLineF(QPointF(x1, y1),QPointF(x4, y4)).length();
if((len-chord) > error) {
- split(&left, &right); /* split in two */
- left.addIfClose(length, error); /* try left side */
- right.addIfClose(length, error); /* try right side */
+ const auto halves = split(); /* split in two */
+ halves.first.addIfClose(length, error); /* try left side */
+ halves.second.addIfClose(length, error); /* try right side */
return;
}
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
index f8a91e9ef3..1c49f82416 100644
--- a/src/gui/painting/qbezier_p.h
+++ b/src/gui/painting/qbezier_p.h
@@ -69,7 +69,8 @@ class Q_GUI_EXPORT QBezier
{
public:
static QBezier fromPoints(const QPointF &p1, const QPointF &p2,
- const QPointF &p3, const QPointF &p4);
+ const QPointF &p3, const QPointF &p4)
+ { return {p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), p4.x(), p4.y()}; }
static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d);
@@ -106,7 +107,7 @@ public:
inline QLineF endTangent() const;
inline void parameterSplitLeft(qreal t, QBezier *left);
- inline void split(QBezier *firstHalf, QBezier *secondHalf) const;
+ inline std::pair<QBezier, QBezier> split() const;
int shifted(QBezier *curveSegments, int maxSegmets,
qreal offset, float threshold) const;
@@ -222,28 +223,21 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const
a * y1 + b * y2 + c * y3 + d * y4);
}
-inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
+std::pair<QBezier, QBezier> QBezier::split() const
{
- Q_ASSERT(firstHalf);
- Q_ASSERT(secondHalf);
-
- qreal c = (x2 + x3)*.5;
- firstHalf->x2 = (x1 + x2)*.5;
- secondHalf->x3 = (x3 + x4)*.5;
- firstHalf->x1 = x1;
- secondHalf->x4 = x4;
- firstHalf->x3 = (firstHalf->x2 + c)*.5;
- secondHalf->x2 = (secondHalf->x3 + c)*.5;
- firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
-
- c = (y2 + y3)/2;
- firstHalf->y2 = (y1 + y2)*.5;
- secondHalf->y3 = (y3 + y4)*.5;
- firstHalf->y1 = y1;
- secondHalf->y4 = y4;
- firstHalf->y3 = (firstHalf->y2 + c)*.5;
- secondHalf->y2 = (secondHalf->y3 + c)*.5;
- firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
+ const auto mid = [](QPointF lhs, QPointF rhs) { return (lhs + rhs) * .5; };
+
+ const QPointF mid_12 = mid(pt1(), pt2());
+ const QPointF mid_23 = mid(pt2(), pt3());
+ const QPointF mid_34 = mid(pt3(), pt4());
+ const QPointF mid_12_23 = mid(mid_12, mid_23);
+ const QPointF mid_23_34 = mid(mid_23, mid_34);
+ const QPointF mid_12_23__23_34 = mid(mid_12_23, mid_23_34);
+
+ return {
+ fromPoints(pt1(), mid_12, mid_12_23, mid_12_23__23_34),
+ fromPoints(mid_12_23__23_34, mid_23_34, mid_34, pt4()),
+ };
}
inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 6cbc30e79a..8780cce223 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -147,6 +147,7 @@ static bool get_hex_rgb(const QChar *str, size_t len, QRgba64 *rgb)
#endif
#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
+// keep this is in sync with QColorConstants
static const struct RGBData {
const char name[21];
uint value;
@@ -475,25 +476,35 @@ static QStringList get_colornames()
\section1 Predefined Colors
- There are 20 predefined QColors described by the Qt::GlobalColor enum,
- including black, white, primary and secondary colors, darker versions
- of these colors and three shades of gray. QColor also recognizes a
- variety of color names; the static colorNames() function returns a
- QStringList color names that QColor knows about.
+ There are 20 predefined QColor objects in the \c{QColorConstants}
+ namespace, including black, white, primary and secondary colors,
+ darker versions of these colors, and three shades of gray.
+ Furthermore, the \c{QColorConstants::Svg} namespace defines QColor
+ objects for the standard \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names}.
\image qt-colors.png Qt Colors
- Additionally, the Qt::color0, Qt::color1 and Qt::transparent colors
- are used for special purposes.
+ The \c{QColorConstants::Color0}, \c{QColorConstants::Color1} and
+ \c{QColorConstants::Transparent} colors are used for special
+ purposes.
- Qt::color0 (zero pixel value) and Qt::color1 (non-zero pixel value)
- are special colors for drawing in QBitmaps. Painting with Qt::color0
- sets the bitmap bits to 0 (transparent; i.e., background), and painting
- with Qt::color1 sets the bits to 1 (opaque; i.e., foreground).
+ \c{QColorConstants::Color0} (zero pixel value) and
+ \c{QColorConstants::Color1} (non-zero pixel value) are special
+ colors for drawing in QBitmaps. Painting with
+ \c{QColorConstants::Color0} sets the bitmap bits to 0 (transparent;
+ i.e., background), and painting with c{QColorConstants::Color1}
+ sets the bits to 1 (opaque; i.e., foreground).
- Qt::transparent is used to indicate a transparent pixel. When painting
- with this value, a pixel value will be used that is appropriate for the
- underlying pixel format in use.
+ \c{QColorConstants::Transparent} is used to indicate a transparent
+ pixel. When painting with this value, a pixel value will be used
+ that is appropriate for the underlying pixel format in use.
+
+ For historical reasons, the 20 predefined colors are also available
+ in the Qt::GlobalColor enumeration.
+
+ Finally, QColor recognizes a variety of color names (as strings);
+ the static colorNames() function returns a QStringList color names
+ that QColor knows about.
\section1 The Extended RGB Color Model
@@ -586,7 +597,7 @@ static QStringList get_colornames()
alpha-channel to feature \l {QColor#Alpha-Blended
Drawing}{alpha-blended drawing}.
- \sa QPalette, QBrush
+ \sa QPalette, QBrush, QColorConstants
*/
#define QCOLOR_INT_RANGE_CHECK(fn, var) \
@@ -886,7 +897,8 @@ QString QColor::name(NameFormat format) const
\li #AARRGGBB (Since 5.2)
\li #RRRGGGBBB
\li #RRRRGGGGBBBB
- \li A name from the list of colors defined in the list of \l{http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG color keyword names}
+ \li A name from the list of colors defined in the list of
+ \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names}
provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro".
These color names work on all platforms. Note that these color names are \e not the
same as defined by the Qt::GlobalColor enums, e.g. "green" and Qt::green does not
@@ -3249,4 +3261,41 @@ const uint qt_inv_premul_factor[256] = {
\sa QColor::rgb(), QColor::rgba()
*/
+/*!
+ \namespace QColorConstants
+ \inmodule QtGui
+
+ \brief The QColorConstants namespace contains QColor predefined constants.
+
+ These constants are usable everywhere a QColor object is expected:
+
+ \code
+ painter.setBrush(QColorConstants::Svg::lightblue);
+ \endcode
+
+ Their usage is much cheaper than e.g. passing a string to QColor's constructor,
+ as they don't require any parsing of the string, and always result in a valid
+ QColor object:
+
+ \badcode
+ object.setColor(QColor("lightblue")); // expensive
+ \endcode
+
+ \section1 Qt Colors
+
+ The following colors are defined in the \c{QColorConstants} namespace:
+
+ \include qt-colors.qdocinc
+
+ \section1 SVG Colors
+
+ The following table lists the available
+ \l {http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG colors}.
+ They are available in the \c{QColorConstants::Svg} inner namespace.
+
+ \include svg-colors.qdocinc
+
+ \sa QColor, Qt::GlobalColor
+*/
+
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
index 723b9fce73..f0d7dd23ad 100644
--- a/src/gui/painting/qcolor.h
+++ b/src/gui/painting/qcolor.h
@@ -309,6 +309,12 @@ private:
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &);
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
#endif
+
+#ifdef Q_COMPILER_UNIFORM_INIT
+public: // can't give friendship to a namespace, so it needs to be public
+ Q_DECL_CONSTEXPR explicit QColor(Spec spec, ushort a1, ushort a2, ushort a3, ushort a4, ushort a5=0) noexcept
+ : cspec(spec), ct(a1, a2, a3, a4, a5) {}
+#endif // Q_COMPILER_UNIFORM_INIT
};
Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE);
@@ -326,6 +332,187 @@ inline QColor::QColor(const QString& aname)
inline bool QColor::isValid() const noexcept
{ return cspec != Invalid; }
+// define these namespaces even if the contents are ifdef'd out
+namespace QColorConstants
+{
+namespace Svg {}
+
+#if defined(Q_COMPILER_CONSTEXPR) & defined(Q_COMPILER_UNIFORM_INIT)
+ // Qt::GlobalColor names
+ constexpr Q_DECL_UNUSED QColor Color0 {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Color1 {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor Black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor White {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkGray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Gray {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0xa0 * 0x101, 0xa4 * 0x101};
+ constexpr Q_DECL_UNUSED QColor LightGray {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor Cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor Magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor Yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkRed {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkGreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkBlue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkCyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkMagenta {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor DarkYellow {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor Transparent {QColor::Rgb, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+
+ // SVG names supported by QColor (see qcolor.cpp).
+namespace Svg {
+ constexpr Q_DECL_UNUSED QColor aliceblue {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xf8 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor antiquewhite {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xeb * 0x101, 0xd7 * 0x101};
+ constexpr Q_DECL_UNUSED QColor aqua {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor aquamarine {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0xd4 * 0x101};
+ constexpr Q_DECL_UNUSED QColor azure {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor beige {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xdc * 0x101};
+ constexpr Q_DECL_UNUSED QColor bisque {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xc4 * 0x101};
+ constexpr Q_DECL_UNUSED QColor black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor blanchedalmond {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xeb * 0x101, 0xcd * 0x101};
+ constexpr Q_DECL_UNUSED QColor blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor blueviolet {QColor::Rgb, 0xff * 0x101, 0x8a * 0x101, 0x2b * 0x101, 0xe2 * 0x101};
+ constexpr Q_DECL_UNUSED QColor brown {QColor::Rgb, 0xff * 0x101, 0xa5 * 0x101, 0x2a * 0x101, 0x2a * 0x101};
+ constexpr Q_DECL_UNUSED QColor burlywood {QColor::Rgb, 0xff * 0x101, 0xde * 0x101, 0xb8 * 0x101, 0x87 * 0x101};
+ constexpr Q_DECL_UNUSED QColor cadetblue {QColor::Rgb, 0xff * 0x101, 0x5f * 0x101, 0x9e * 0x101, 0xa0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor chartreuse {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor chocolate {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0x69 * 0x101, 0x1e * 0x101};
+ constexpr Q_DECL_UNUSED QColor coral {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x7f * 0x101, 0x50 * 0x101};
+ constexpr Q_DECL_UNUSED QColor cornflowerblue {QColor::Rgb, 0xff * 0x101, 0x64 * 0x101, 0x95 * 0x101, 0xed * 0x101};
+ constexpr Q_DECL_UNUSED QColor cornsilk {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf8 * 0x101, 0xdc * 0x101};
+ constexpr Q_DECL_UNUSED QColor crimson {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0x14 * 0x101, 0x3c * 0x101};
+ constexpr Q_DECL_UNUSED QColor cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x8b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkcyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x8b * 0x101, 0x8b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkgoldenrod {QColor::Rgb, 0xff * 0x101, 0xb8 * 0x101, 0x86 * 0x101, 0x0b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkgray {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x64 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkgrey {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkkhaki {QColor::Rgb, 0xff * 0x101, 0xbd * 0x101, 0xb7 * 0x101, 0x6b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkmagenta {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x8b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkolivegreen {QColor::Rgb, 0xff * 0x101, 0x55 * 0x101, 0x6b * 0x101, 0x2f * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkorange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x8c * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkorchid {QColor::Rgb, 0xff * 0x101, 0x99 * 0x101, 0x32 * 0x101, 0xcc * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkred {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darksalmon {QColor::Rgb, 0xff * 0x101, 0xe9 * 0x101, 0x96 * 0x101, 0x7a * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkseagreen {QColor::Rgb, 0xff * 0x101, 0x8f * 0x101, 0xbc * 0x101, 0x8f * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkslateblue {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0x3d * 0x101, 0x8b * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkslategray {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkslategrey {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkturquoise {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xce * 0x101, 0xd1 * 0x101};
+ constexpr Q_DECL_UNUSED QColor darkviolet {QColor::Rgb, 0xff * 0x101, 0x94 * 0x101, 0x00 * 0x101, 0xd3 * 0x101};
+ constexpr Q_DECL_UNUSED QColor deeppink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x14 * 0x101, 0x93 * 0x101};
+ constexpr Q_DECL_UNUSED QColor deepskyblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xbf * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor dimgray {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101};
+ constexpr Q_DECL_UNUSED QColor dimgrey {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101};
+ constexpr Q_DECL_UNUSED QColor dodgerblue {QColor::Rgb, 0xff * 0x101, 0x1e * 0x101, 0x90 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor firebrick {QColor::Rgb, 0xff * 0x101, 0xb2 * 0x101, 0x22 * 0x101, 0x22 * 0x101};
+ constexpr Q_DECL_UNUSED QColor floralwhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor forestgreen {QColor::Rgb, 0xff * 0x101, 0x22 * 0x101, 0x8b * 0x101, 0x22 * 0x101};
+ constexpr Q_DECL_UNUSED QColor fuchsia {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor gainsboro {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0xdc * 0x101, 0xdc * 0x101};
+ constexpr Q_DECL_UNUSED QColor ghostwhite {QColor::Rgb, 0xff * 0x101, 0xf8 * 0x101, 0xf8 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor gold {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xd7 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor goldenrod {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0xa5 * 0x101, 0x20 * 0x101};
+ constexpr Q_DECL_UNUSED QColor gray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor greenyellow {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xff * 0x101, 0x2f * 0x101};
+ constexpr Q_DECL_UNUSED QColor grey {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor honeydew {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xf0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor hotpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x69 * 0x101, 0xb4 * 0x101};
+ constexpr Q_DECL_UNUSED QColor indianred {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x5c * 0x101, 0x5c * 0x101};
+ constexpr Q_DECL_UNUSED QColor indigo {QColor::Rgb, 0xff * 0x101, 0x4b * 0x101, 0x00 * 0x101, 0x82 * 0x101};
+ constexpr Q_DECL_UNUSED QColor ivory {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor khaki {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xe6 * 0x101, 0x8c * 0x101};
+ constexpr Q_DECL_UNUSED QColor lavender {QColor::Rgb, 0xff * 0x101, 0xe6 * 0x101, 0xe6 * 0x101, 0xfa * 0x101};
+ constexpr Q_DECL_UNUSED QColor lavenderblush {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101, 0xf5 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lawngreen {QColor::Rgb, 0xff * 0x101, 0x7c * 0x101, 0xfc * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lemonchiffon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xcd * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightblue {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xd8 * 0x101, 0xe6 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightcoral {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightcyan {QColor::Rgb, 0xff * 0x101, 0xe0 * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightgoldenrodyellow {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101, 0xd2 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightgray {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightgreen {QColor::Rgb, 0xff * 0x101, 0x90 * 0x101, 0xee * 0x101, 0x90 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightgrey {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xb6 * 0x101, 0xc1 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightsalmon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa0 * 0x101, 0x7a * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightseagreen {QColor::Rgb, 0xff * 0x101, 0x20 * 0x101, 0xb2 * 0x101, 0xaa * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightskyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xfa * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightslategray {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightslategrey {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightsteelblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xc4 * 0x101, 0xde * 0x101};
+ constexpr Q_DECL_UNUSED QColor lightyellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xe0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor lime {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor limegreen {QColor::Rgb, 0xff * 0x101, 0x32 * 0x101, 0xcd * 0x101, 0x32 * 0x101};
+ constexpr Q_DECL_UNUSED QColor linen {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101, 0xe6 * 0x101};
+ constexpr Q_DECL_UNUSED QColor magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor maroon {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumaquamarine {QColor::Rgb, 0xff * 0x101, 0x66 * 0x101, 0xcd * 0x101, 0xaa * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xcd * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumorchid {QColor::Rgb, 0xff * 0x101, 0xba * 0x101, 0x55 * 0x101, 0xd3 * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumpurple {QColor::Rgb, 0xff * 0x101, 0x93 * 0x101, 0x70 * 0x101, 0xdb * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumseagreen {QColor::Rgb, 0xff * 0x101, 0x3c * 0x101, 0xb3 * 0x101, 0x71 * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumslateblue {QColor::Rgb, 0xff * 0x101, 0x7b * 0x101, 0x68 * 0x101, 0xee * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumspringgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xfa * 0x101, 0x9a * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumturquoise {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0xd1 * 0x101, 0xcc * 0x101};
+ constexpr Q_DECL_UNUSED QColor mediumvioletred {QColor::Rgb, 0xff * 0x101, 0xc7 * 0x101, 0x15 * 0x101, 0x85 * 0x101};
+ constexpr Q_DECL_UNUSED QColor midnightblue {QColor::Rgb, 0xff * 0x101, 0x19 * 0x101, 0x19 * 0x101, 0x70 * 0x101};
+ constexpr Q_DECL_UNUSED QColor mintcream {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xff * 0x101, 0xfa * 0x101};
+ constexpr Q_DECL_UNUSED QColor mistyrose {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xe1 * 0x101};
+ constexpr Q_DECL_UNUSED QColor moccasin {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xb5 * 0x101};
+ constexpr Q_DECL_UNUSED QColor navajowhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xde * 0x101, 0xad * 0x101};
+ constexpr Q_DECL_UNUSED QColor navy {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor oldlace {QColor::Rgb, 0xff * 0x101, 0xfd * 0x101, 0xf5 * 0x101, 0xe6 * 0x101};
+ constexpr Q_DECL_UNUSED QColor olive {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor olivedrab {QColor::Rgb, 0xff * 0x101, 0x6b * 0x101, 0x8e * 0x101, 0x23 * 0x101};
+ constexpr Q_DECL_UNUSED QColor orange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa5 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor orangered {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x45 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor orchid {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0x70 * 0x101, 0xd6 * 0x101};
+ constexpr Q_DECL_UNUSED QColor palegoldenrod {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0xe8 * 0x101, 0xaa * 0x101};
+ constexpr Q_DECL_UNUSED QColor palegreen {QColor::Rgb, 0xff * 0x101, 0x98 * 0x101, 0xfb * 0x101, 0x98 * 0x101};
+ constexpr Q_DECL_UNUSED QColor paleturquoise {QColor::Rgb, 0xff * 0x101, 0xaf * 0x101, 0xee * 0x101, 0xee * 0x101};
+ constexpr Q_DECL_UNUSED QColor palevioletred {QColor::Rgb, 0xff * 0x101, 0xdb * 0x101, 0x70 * 0x101, 0x93 * 0x101};
+ constexpr Q_DECL_UNUSED QColor papayawhip {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xef * 0x101, 0xd5 * 0x101};
+ constexpr Q_DECL_UNUSED QColor peachpuff {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xda * 0x101, 0xb9 * 0x101};
+ constexpr Q_DECL_UNUSED QColor peru {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x85 * 0x101, 0x3f * 0x101};
+ constexpr Q_DECL_UNUSED QColor pink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xc0 * 0x101, 0xcb * 0x101};
+ constexpr Q_DECL_UNUSED QColor plum {QColor::Rgb, 0xff * 0x101, 0xdd * 0x101, 0xa0 * 0x101, 0xdd * 0x101};
+ constexpr Q_DECL_UNUSED QColor powderblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xe0 * 0x101, 0xe6 * 0x101};
+ constexpr Q_DECL_UNUSED QColor purple {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor rosybrown {QColor::Rgb, 0xff * 0x101, 0xbc * 0x101, 0x8f * 0x101, 0x8f * 0x101};
+ constexpr Q_DECL_UNUSED QColor royalblue {QColor::Rgb, 0xff * 0x101, 0x41 * 0x101, 0x69 * 0x101, 0xe1 * 0x101};
+ constexpr Q_DECL_UNUSED QColor saddlebrown {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x45 * 0x101, 0x13 * 0x101};
+ constexpr Q_DECL_UNUSED QColor salmon {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0x80 * 0x101, 0x72 * 0x101};
+ constexpr Q_DECL_UNUSED QColor sandybrown {QColor::Rgb, 0xff * 0x101, 0xf4 * 0x101, 0xa4 * 0x101, 0x60 * 0x101};
+ constexpr Q_DECL_UNUSED QColor seagreen {QColor::Rgb, 0xff * 0x101, 0x2e * 0x101, 0x8b * 0x101, 0x57 * 0x101};
+ constexpr Q_DECL_UNUSED QColor seashell {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf5 * 0x101, 0xee * 0x101};
+ constexpr Q_DECL_UNUSED QColor sienna {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0x52 * 0x101, 0x2d * 0x101};
+ constexpr Q_DECL_UNUSED QColor silver {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor skyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xeb * 0x101};
+ constexpr Q_DECL_UNUSED QColor slateblue {QColor::Rgb, 0xff * 0x101, 0x6a * 0x101, 0x5a * 0x101, 0xcd * 0x101};
+ constexpr Q_DECL_UNUSED QColor slategray {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101};
+ constexpr Q_DECL_UNUSED QColor slategrey {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101};
+ constexpr Q_DECL_UNUSED QColor snow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101};
+ constexpr Q_DECL_UNUSED QColor springgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x7f * 0x101};
+ constexpr Q_DECL_UNUSED QColor steelblue {QColor::Rgb, 0xff * 0x101, 0x46 * 0x101, 0x82 * 0x101, 0xb4 * 0x101};
+ constexpr Q_DECL_UNUSED QColor tan {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0xb4 * 0x101, 0x8c * 0x101};
+ constexpr Q_DECL_UNUSED QColor teal {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101};
+ constexpr Q_DECL_UNUSED QColor thistle {QColor::Rgb, 0xff * 0x101, 0xd8 * 0x101, 0xbf * 0x101, 0xd8 * 0x101};
+ constexpr Q_DECL_UNUSED QColor tomato {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x63 * 0x101, 0x47 * 0x101};
+ constexpr Q_DECL_UNUSED QColor turquoise {QColor::Rgb, 0xff * 0x101, 0x40 * 0x101, 0xe0 * 0x101, 0xd0 * 0x101};
+ constexpr Q_DECL_UNUSED QColor violet {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0x82 * 0x101, 0xee * 0x101};
+ constexpr Q_DECL_UNUSED QColor wheat {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xde * 0x101, 0xb3 * 0x101};
+ constexpr Q_DECL_UNUSED QColor white {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101};
+ constexpr Q_DECL_UNUSED QColor whitesmoke {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xf5 * 0x101};
+ constexpr Q_DECL_UNUSED QColor yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101};
+ constexpr Q_DECL_UNUSED QColor yellowgreen {QColor::Rgb, 0xff * 0x101, 0x9a * 0x101, 0xcd * 0x101, 0x32 * 0x101};
+} // namespace Svg
+#endif // Q_COMPILER_CONSTEXPR && Q_COMPILER_UNIFORM_INIT
+} // namespace QColorLiterals
+
QT_END_NAMESPACE
#endif // QCOLOR_H
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 043a951521..86d0c57cfe 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -461,19 +461,19 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
Creates a new colorspace object that represents \a colorSpaceId.
*/
QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId)
-{
- static QExplicitlySharedDataPointer<QColorSpacePrivate> predefinedColorspacePrivates[QColorSpace::Bt2020];
- if (colorSpaceId <= QColorSpace::Unknown) {
- if (!predefinedColorspacePrivates[0])
- predefinedColorspacePrivates[0] = new QColorSpacePrivate(QColorSpace::Undefined);
- d_ptr = predefinedColorspacePrivates[0]; // unknown and undefined both returns the static undefined colorspace.
- } else {
- if (!predefinedColorspacePrivates[colorSpaceId - 1])
- predefinedColorspacePrivates[colorSpaceId - 1] = new QColorSpacePrivate(colorSpaceId);
- d_ptr = predefinedColorspacePrivates[colorSpaceId - 1];
+ : d_ptr(nullptr)
+{
+ static QColorSpacePrivate *predefinedColorspacePrivates[QColorSpace::Bt2020];
+ // Unknown and undefined both returns the static undefined colorspace
+ if (colorSpaceId > QColorSpace::Unknown) {
+ if (!predefinedColorspacePrivates[colorSpaceId - 2]) {
+ predefinedColorspacePrivates[colorSpaceId - 2] = new QColorSpacePrivate(colorSpaceId);
+ predefinedColorspacePrivates[colorSpaceId - 2]->ref.ref();
+ }
+ d_ptr = predefinedColorspacePrivates[colorSpaceId - 2];
+ d_ptr->ref.ref();
+ Q_ASSERT(isValid());
}
-
- Q_ASSERT(colorSpaceId == QColorSpace::Undefined || isValid());
}
/*!
@@ -483,6 +483,7 @@ QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId)
QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
: d_ptr(new QColorSpacePrivate(primaries, fun, gamma))
{
+ d_ptr->ref.ref();
}
/*!
@@ -492,6 +493,7 @@ QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::Transfer
QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
: d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma))
{
+ d_ptr->ref.ref();
}
/*!
@@ -505,35 +507,34 @@ QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
if (!primaries.areValid()) {
qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
- d_ptr = QColorSpace(QColorSpace::Undefined).d_ptr;
+ d_ptr = nullptr;
return;
}
d_ptr = new QColorSpacePrivate(primaries, fun, gamma);
+ d_ptr->ref.ref();
}
QColorSpace::~QColorSpace()
{
-}
-
-QColorSpace::QColorSpace(QColorSpace &&colorSpace) noexcept
- : d_ptr(std::move(colorSpace.d_ptr))
-{
+ if (d_ptr && !d_ptr->ref.deref())
+ delete d_ptr;
}
QColorSpace::QColorSpace(const QColorSpace &colorSpace)
: d_ptr(colorSpace.d_ptr)
{
-}
-
-QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) noexcept
-{
- d_ptr = std::move(colorSpace.d_ptr);
- return *this;
+ if (d_ptr)
+ d_ptr->ref.ref();
}
QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
{
+ QColorSpacePrivate *oldD = d_ptr;
d_ptr = colorSpace.d_ptr;
+ if (d_ptr)
+ d_ptr->ref.ref();
+ if (oldD && !oldD->ref.deref())
+ delete oldD;
return *this;
}
@@ -549,6 +550,8 @@ QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
*/
QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept
{
+ if (Q_UNLIKELY(!d_ptr))
+ return QColorSpace::Undefined;
return d_ptr->id;
}
@@ -571,6 +574,8 @@ QColorSpace::Primaries QColorSpace::primaries() const noexcept
*/
QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
{
+ if (Q_UNLIKELY(!d_ptr))
+ return QColorSpace::TransferFunction::Custom;
return d_ptr->transferFunction;
}
@@ -583,6 +588,8 @@ QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
*/
float QColorSpace::gamma() const noexcept
{
+ if (Q_UNLIKELY(!d_ptr))
+ return 0.0f;
return d_ptr->gamma;
}
@@ -599,7 +606,7 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc
return;
if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
return;
- d_ptr.detach();
+ QColorSpacePrivate::getWritable(*this); // detach
d_ptr->description.clear();
d_ptr->transferFunction = transferFunction;
d_ptr->gamma = gamma;
@@ -637,7 +644,7 @@ void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
return;
if (d_ptr->primaries == primariesId)
return;
- d_ptr.detach();
+ QColorSpacePrivate::getWritable(*this); // detach
d_ptr->description.clear();
d_ptr->primaries = primariesId;
d_ptr->identifyColorSpace();
@@ -663,7 +670,7 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin
QColorMatrix toXyz = primaries.toXyzMatrix();
if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
return;
- d_ptr.detach();
+ QColorSpacePrivate::getWritable(*this); // detach
d_ptr->description.clear();
d_ptr->primaries = QColorSpace::Primaries::Custom;
d_ptr->toXyz = toXyz;
@@ -685,6 +692,8 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin
*/
QByteArray QColorSpace::iccProfile() const
{
+ if (Q_UNLIKELY(!d_ptr))
+ return QByteArray();
if (!d_ptr->iccProfile.isEmpty())
return d_ptr->iccProfile;
if (!isValid())
@@ -708,8 +717,9 @@ QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
QColorSpace colorSpace;
if (QIcc::fromIccProfile(iccProfile, &colorSpace))
return colorSpace;
- colorSpace.d_ptr->id = QColorSpace::Undefined;
- colorSpace.d_ptr->iccProfile = iccProfile;
+ QColorSpacePrivate *d = QColorSpacePrivate::getWritable(colorSpace);
+ d->id = QColorSpace::Undefined;
+ d->iccProfile = iccProfile;
return colorSpace;
}
@@ -718,7 +728,7 @@ QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
*/
bool QColorSpace::isValid() const noexcept
{
- return d_ptr->id != QColorSpace::Undefined && d_ptr->toXyz.isValid()
+ return d_ptr && d_ptr->id != QColorSpace::Undefined && d_ptr->toXyz.isValid()
&& d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
}
@@ -731,6 +741,8 @@ bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
{
if (colorSpace1.d_ptr == colorSpace2.d_ptr)
return true;
+ if (!colorSpace1.d_ptr || !colorSpace2.d_ptr)
+ return false;
if (colorSpace1.colorSpaceId() == QColorSpace::Undefined && colorSpace2.colorSpaceId() == QColorSpace::Undefined)
return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile;
@@ -780,7 +792,7 @@ QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &color
if (!isValid() || !colorspace.isValid())
return QColorTransform();
- return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData());
+ return d_ptr->transformationToColorSpace(colorspace.d_ptr);
}
/*****************************************************************************
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index a7c1091911..880f0ad4cf 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -42,11 +42,13 @@
#include <QtGui/qtguiglobal.h>
#include <QtGui/qcolortransform.h>
+#include <QtCore/qobjectdefs.h>
#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
class QColorSpacePrivate;
+class QPointF;
class Q_GUI_EXPORT QColorSpace
{
@@ -90,11 +92,19 @@ public:
TransferFunction fun, float gamma = 0.0f);
~QColorSpace();
- QColorSpace(QColorSpace &&colorSpace) noexcept;
QColorSpace(const QColorSpace &colorSpace);
- QColorSpace &operator=(QColorSpace &&colorSpace) noexcept;
QColorSpace &operator=(const QColorSpace &colorSpace);
+ QColorSpace(QColorSpace &&colorSpace) noexcept
+ : d_ptr(qExchange(colorSpace.d_ptr, nullptr))
+ { }
+ QColorSpace &operator=(QColorSpace &&colorSpace) noexcept
+ {
+ // Make the deallocation of this->d_ptr happen in ~QColorSpace()
+ QColorSpace(std::move(colorSpace)).swap(*this);
+ return *this;
+ }
+
void swap(QColorSpace &colorSpace) noexcept
{ qSwap(d_ptr, colorSpace.d_ptr); }
@@ -123,7 +133,7 @@ public:
private:
Q_DECLARE_PRIVATE(QColorSpace)
- QExplicitlySharedDataPointer<QColorSpacePrivate> d_ptr;
+ QColorSpacePrivate *d_ptr;
};
bool Q_GUI_EXPORT operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2);
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index 2a40a0cfd8..037111a0ae 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -95,15 +95,24 @@ public:
QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma);
QColorSpacePrivate(const QColorSpacePrivate &other) = default;
+ // named different from get to avoid accidental detachs
static QColorSpacePrivate *getWritable(QColorSpace &colorSpace)
{
- colorSpace.d_ptr.detach();
- return colorSpace.d_ptr.data();
+ if (!colorSpace.d_ptr) {
+ colorSpace.d_ptr = new QColorSpacePrivate;
+ colorSpace.d_ptr->ref.ref();
+ } else if (colorSpace.d_ptr->ref.loadRelaxed() != 1) {
+ colorSpace.d_ptr->ref.deref();
+ colorSpace.d_ptr = new QColorSpacePrivate(*colorSpace.d_ptr);
+ colorSpace.d_ptr->ref.ref();
+ }
+ Q_ASSERT(colorSpace.d_ptr->ref.loadRelaxed() == 1);
+ return colorSpace.d_ptr;
}
static const QColorSpacePrivate *get(const QColorSpace &colorSpace)
{
- return colorSpace.d_ptr.data();
+ return colorSpace.d_ptr;
}
void initialize();
diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h
index 5fb51739a7..94b6b3a385 100644
--- a/src/gui/painting/qcolortransform.h
+++ b/src/gui/painting/qcolortransform.h
@@ -41,7 +41,6 @@
#define QCOLORTRANSFORM_H
#include <QtGui/qtguiglobal.h>
-#include <QtCore/qsharedpointer.h>
#include <QtGui/qrgb.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index 06a849e790..ced213e36d 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -1813,8 +1813,10 @@ void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR
#endif
/*
- if Sca.Da + Dca.Sa >= Sa.Da
+ if Sca.Da + Dca.Sa > Sa.Da
Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+ else if Sca == Sa
+ Dca' = Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
otherwise
Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
*/
@@ -1825,8 +1827,10 @@ static inline int color_dodge_op(int dst, int src, int da, int sa)
const int src_da = src * da;
const int temp = src * (255 - da) + dst * (255 - sa);
- if (src_da + dst_sa >= sa_da)
+ if (src_da + dst_sa > sa_da)
return qt_div_255(sa_da + temp);
+ else if (src == sa || sa == 0)
+ return qt_div_255(temp);
else
return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp);
}
@@ -1870,8 +1874,10 @@ static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint6
const qint64 src_da = src * da;
const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
- if (src_da + dst_sa >= sa_da)
+ if (src_da + dst_sa > sa_da)
return qt_div_65535(sa_da + temp);
+ else if (src == sa || sa == 0)
+ return qt_div_65535(temp);
else
return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp);
}
@@ -1969,8 +1975,10 @@ void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
#endif
/*
- if Sca.Da + Dca.Sa <= Sa.Da
+ if Sca.Da + Dca.Sa < Sa.Da
Dca' = Sca.(1 - Da) + Dca.(1 - Sa)
+ else if Sca == 0
+ Dca' = Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
otherwise
Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
*/
@@ -1982,8 +1990,10 @@ static inline int color_burn_op(int dst, int src, int da, int sa)
const int temp = src * (255 - da) + dst * (255 - sa);
- if (src == 0 || src_da + dst_sa <= sa_da)
+ if (src_da + dst_sa < sa_da)
return qt_div_255(temp);
+ else if (src == 0)
+ return qt_div_255(dst_sa + temp);
return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp);
}
@@ -2027,8 +2037,10 @@ static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64
const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
- if (src == 0 || src_da + dst_sa <= sa_da)
+ if (src_da + dst_sa < sa_da)
return qt_div_65535(temp);
+ else if (src == 0)
+ return qt_div_65535(dst_sa + temp);
return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp);
}
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 674f52678d..c17bf2ddfd 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -88,6 +88,7 @@ template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB444>() { return 4; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB555>() { return 5; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB666>() { return 6; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_BGR888>() { return 8; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
@@ -101,6 +102,7 @@ template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8;
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB666>() { return 12; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_BGR888>() { return 0; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; }
template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; }
@@ -119,6 +121,7 @@ template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4;
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB666>() { return 6; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_BGR888>() { return 8; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; }
@@ -132,6 +135,7 @@ template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4;
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB666>() { return 6; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_BGR888>() { return 8; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; }
template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; }
@@ -150,6 +154,7 @@ template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4;
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB666>() { return 6; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_BGR888>() { return 8; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
@@ -163,6 +168,7 @@ template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0;
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB666>() { return 0; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_BGR888>() { return 16; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied>() { return 0; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
@@ -181,6 +187,7 @@ template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB444>() { return 0;
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB555>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB666>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_BGR888>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
@@ -194,6 +201,7 @@ template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB444>() { return 0;
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB555>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB666>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_BGR888>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplied>() { return 12; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; }
template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; }
@@ -214,6 +222,7 @@ template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { r
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_BGR888>() { 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; }
@@ -1528,7 +1537,8 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
{ false, false, QPixelLayout::BPP16, nullptr,
convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64,
fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64,
- storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 } // Format_Grayscale16
+ storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 }, // Format_Grayscale16
+ pixelLayoutRGB<QImage::Format_BGR888>(),
};
Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats);
@@ -1643,7 +1653,8 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = {
storeRGBX64FromRGBA64PM,
storeRGBA64FromRGBA64PM,
storeRGBA64PMFromRGBA64PM,
- storeGray16FromRGBA64PM
+ storeGray16FromRGBA64PM,
+ storeGenericFromRGBA64PM<QImage::Format_BGR888>,
};
/*
@@ -1732,6 +1743,7 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_RGBA64
destFetch, // Format_RGBA64_Premultiplied
destFetch, // Format_Grayscale16
+ destFetch, // Format_BGR888
};
#if QT_CONFIG(raster_64bit)
@@ -1782,6 +1794,7 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetch64, // Format_RGBA64
destFetchRGB64, // Format_RGBA64_Premultiplied
destFetch64, // Format_Grayscale16
+ destFetch64, // Format_BGR888
};
#endif
@@ -1922,6 +1935,7 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_RGBA64
destStore, // Format_RGBA64_Premultiplied
destStore, // Format_Grayscale16
+ destStore, // Format_BGR888
};
#if QT_CONFIG(raster_64bit)
@@ -1971,6 +1985,7 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
destStore64RGBA64, // Format_RGBA64
0, // Format_RGBA64_Premultiplied
destStore64, // Format_Grayscale16
+ destStore64, // Format_BGR888
};
#endif
@@ -3922,6 +3937,7 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
fetchUntransformed, // RGBA64
fetchUntransformed, // RGBA64_Premultiplied
fetchUntransformed, // Grayscale16
+ fetchUntransformed, // BGR888
};
static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
@@ -4544,7 +4560,7 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData)
while (count--) {
int x = spans->x;
int length = spans->len;
- if (solidFill && bpp >= QPixelLayout::BPP8 && spans->coverage == 255 && length) {
+ if (solidFill && bpp >= QPixelLayout::BPP8 && spans->coverage == 255 && length && op.destStore64) {
// If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
op.destStore64(data->rasterBuffer, x, spans->y, &color, 1);
spanfill_from_first(data->rasterBuffer, bpp, x, spans->y, length);
@@ -6589,6 +6605,14 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
qt_alphargbblit_generic,
qt_rectfill_quint16
},
+ // Format_BGR888
+ {
+ blend_color_generic,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ qt_rectfill_quint24
+ },
};
#if !defined(__SSE2__)
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 0a440e5b75..5ab5514dd4 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -2883,19 +2883,34 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
for (int i = 0; i < numGlyphs; i++) {
QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
- QPoint offset;
- const QImage *alphaMap = fontEngine->lockedAlphaMapForGlyph(glyphs[i], spp, neededFormat, s->matrix,
- &offset);
- if (alphaMap == 0 || alphaMap->isNull())
+ const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyphs[i], spp, neededFormat, s->matrix);
+ if (!alphaMap)
continue;
- alphaPenBlt(alphaMap->constBits(), alphaMap->bytesPerLine(), alphaMap->depth(),
- qFloor(positions[i].x) + offset.x(),
- qRound(positions[i].y) + offset.y(),
- alphaMap->width(), alphaMap->height(),
+ int depth;
+ int bytesPerLine;
+ switch (alphaMap->format) {
+ case QFontEngine::Format_Mono:
+ depth = 1;
+ bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
+ break;
+ case QFontEngine::Format_A8:
+ depth = 8;
+ bytesPerLine = (alphaMap->width + 3) & ~3;
+ break;
+ case QFontEngine::Format_A32:
+ depth = 32;
+ bytesPerLine = alphaMap->width * 4;
+ break;
+ default:
+ Q_UNREACHABLE();
+ };
+
+ alphaPenBlt(alphaMap->data, bytesPerLine, depth,
+ qFloor(positions[i].x) + alphaMap->x,
+ qRound(positions[i].y) - alphaMap->y,
+ alphaMap->width, alphaMap->height,
fontEngine->expectsGammaCorrectedBlending());
-
- fontEngine->unlockAlphaMapForGlyph();
}
} else {
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index b1d1f30800..1fb37ece56 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -1855,10 +1855,9 @@ static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt,
}
// split curve and try again...
- QBezier first_half, second_half;
- bezier.split(&first_half, &second_half);
- qt_painterpath_isect_curve(first_half, pt, winding, depth + 1);
- qt_painterpath_isect_curve(second_half, pt, winding, depth + 1);
+ const auto halves = bezier.split();
+ qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
+ qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
}
}
@@ -2013,10 +2012,9 @@ static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1,
if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
return true;
- QBezier first_half, second_half;
- bezier.split(&first_half, &second_half);
- if (qt_isect_curve_horizontal(first_half, y, x1, x2, depth + 1)
- || qt_isect_curve_horizontal(second_half, y, x1, x2, depth + 1))
+ const auto halves = bezier.split();
+ if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
+ || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
return true;
}
return false;
@@ -2032,10 +2030,9 @@ static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qr
if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
return true;
- QBezier first_half, second_half;
- bezier.split(&first_half, &second_half);
- if (qt_isect_curve_vertical(first_half, x, y1, y2, depth + 1)
- || qt_isect_curve_vertical(second_half, x, y1, y2, depth + 1))
+ const auto halves = bezier.split();
+ if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
+ || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
return true;
}
return false;
diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h
index f107b6eb20..3207f1944b 100644
--- a/src/gui/painting/qstroker_p.h
+++ b/src/gui/painting/qstroker_p.h
@@ -209,7 +209,7 @@ public:
QStroker();
~QStroker();
- void setStrokeWidth(qfixed width) { m_strokeWidth = width; m_curveThreshold = qt_real_to_fixed(width > 4 ? 1.0/width : 0.25); }
+ void setStrokeWidth(qfixed width) { m_strokeWidth = width; m_curveThreshold = qt_real_to_fixed(qBound(0.025, 1.0/width, 0.25)); }
qfixed strokeWidth() const { return m_strokeWidth; }
void setCapStyle(Qt::PenCapStyle capStyle) { m_capStyle = joinModeForCap(capStyle); }
diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
index 83f9415c69..85c894a74a 100644
--- a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
+++ b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
@@ -164,6 +164,43 @@ void openFile(const std::string &accept,
openFiles(accept, FileSelectMode::SingleFile, fileDialogClosedWithInt, acceptFile, fileDataReady);
}
+void saveFile(const char *content, size_t size, const std::string &fileNameHint)
+{
+ // Save a file by creating programatically clicking a download
+ // link to an object url to a Blob containing the file content.
+ // File content is copied once, so that the passed in content
+ // buffer can be released as soon as this function returns - we
+ // don't know for how long the browser will retain the TypedArray
+ // view used to create the Blob.
+
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val window = emscripten::val::global("window");
+
+ emscripten::val fileContentView = emscripten::val(emscripten::typed_memory_view(size, content));
+ emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(size);
+ emscripten::val fileContentCopyView = emscripten::val::global("Uint8Array").new_(fileContentCopy);
+ fileContentCopyView.call<void>("set", fileContentView);
+
+ emscripten::val contentArray = emscripten::val::array();
+ contentArray.call<void>("push", fileContentCopyView);
+ emscripten::val type = emscripten::val::object();
+ type.set("type","application/octet-stream");
+ emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type);
+
+ emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob);
+ emscripten::val contentLink = document.call<emscripten::val>("createElement", std::string("a"));
+ contentLink.set("href", contentUrl);
+ contentLink.set("download", fileNameHint);
+ contentLink.set("style", "display:none");
+
+ emscripten::val body = document["body"];
+ body.call<void>("appendChild", contentLink);
+ contentLink.call<void>("click");
+ body.call<void>("removeChild", contentLink);
+
+ window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl);
+}
+
} // namespace QWasmLocalFileAccess
QT_END_NAMESPACE
diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
index 794db8d9b2..ccd88570c8 100644
--- a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
+++ b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
@@ -71,6 +71,8 @@ void openFile(const std::string &accept,
const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
const std::function<void()> &fileDataReady);
+void saveFile(const char *content, size_t size, const std::string &fileNameHint);
+
} // namespace QWasmLocalFileAccess
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 7443c0a04f..4414b61d55 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -59,7 +59,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhi
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Accelerated 2D/3D graphics API abstraction.
@@ -274,6 +275,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
QRhi does not expose APIs for resource barriers or image layout
transitions. Such synchronization is done implicitly by the backends, where
applicable (for example, Vulkan), by tracking resource usage as necessary.
+ Buffer and image barriers are inserted before render or compute passes
+ transparently to the application.
\note Resources within a render or compute pass are expected to be bound to
a single usage during that pass. For example, a buffer can be used as
@@ -505,8 +508,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
to issue a \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} with a
non-aligned effective offset may lead to unspecified behavior.
- \value NPOTTextureRepeat Indicates that the \l{QRhiSampler::Repeat}{Repeat}
- mode is supported for textures with a non-power-of-two size.
+ \value NPOTTextureRepeat Indicates that the
+ \l{QRhiSampler::Repeat}{Repeat} wrap mode and mipmap filtering modes are
+ supported for textures with a non-power-of-two size. In practice this can
+ only be false with OpenGL ES 2.0 implementations without
+ \c{GL_OES_texture_npot}.
\value RedOrAlpha8IsRed Indicates that the
\l{QRhiTexture::RED_OR_ALPHA8}{RED_OR_ALPHA8} format maps to a one
@@ -550,6 +556,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\enum QRhi::BeginFrameFlag
Flag values for QRhi::beginFrame()
+
+ \value ExternalContentsInPass Specifies that one or more render or compute
+ passes in this frame will call QRhiCommandBuffer::beginExternal(). Some
+ backends, Vulkan in particular, will fail if this flag is not set and
+ beginExternal() is still called.
*/
/*!
@@ -601,7 +612,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhiInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for backend-specific initialization parameters.
Contains fields that are relevant to all backends.
@@ -609,7 +621,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhiDepthStencilClearValue
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies clear values for a depth or stencil buffer.
*/
@@ -676,7 +689,8 @@ QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
/*!
\class QRhiViewport
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies a viewport rectangle.
Used with QRhiCommandBuffer::setViewport().
@@ -775,7 +789,8 @@ QDebug operator<<(QDebug dbg, const QRhiViewport &v)
/*!
\class QRhiScissor
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies a scissor rectangle.
Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is
@@ -854,7 +869,8 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
/*!
\class QRhiVertexInputBinding
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a vertex input binding.
Specifies the stride (in bytes, must be a multiple of 4), the
@@ -984,7 +1000,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
/*!
\class QRhiVertexInputAttribute
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a single vertex input element.
The members specify the binding number, location, format, and offset for a
@@ -1137,7 +1154,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
/*!
\class QRhiVertexInputLayout
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the layout of vertex inputs consumed by a vertex shader.
The vertex input layout is defined by the collections of
@@ -1196,7 +1214,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
/*!
\class QRhiShaderStage
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the type and the shader code for a shader stage in the pipeline.
*/
@@ -1280,7 +1299,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
/*!
\class QRhiColorAttachment
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the a single color attachment of a render target.
A color attachment is either a QRhiTexture or a QRhiRenderBuffer. The
@@ -1338,7 +1358,8 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
/*!
\class QRhiTextureRenderTargetDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the color and depth or depth/stencil attachments of a render target.
A texture render target has zero or more textures as color attachments,
@@ -1393,7 +1414,8 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
/*!
\class QRhiTextureSubresourceUploadDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the source for one mip level in a layer in a texture upload operation.
The source content is specified either as a QImage or as a raw blob. The
@@ -1473,7 +1495,8 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
/*!
\class QRhiTextureUploadEntry
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes one layer (face for cubemaps) in a texture upload operation.
*/
@@ -1501,7 +1524,8 @@ QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
/*!
\class QRhiTextureUploadDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a texture upload operation.
Used with QRhiResourceUpdateBatch::uploadTexture(). That function has two
@@ -1606,7 +1630,8 @@ void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
/*!
\class QRhiTextureCopyDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a texture-to-texture copy operation.
An empty pixelSize() indicates that the entire subresource is to be copied.
@@ -1629,7 +1654,8 @@ void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
/*!
\class QRhiReadbackDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a readback (reading back texture contents from possibly GPU-only memory) operation.
The source of the readback operation is either a QRhiTexture or the
@@ -1675,7 +1701,8 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
/*!
\class QRhiReadbackResult
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the results of a potentially asynchronous readback operation.
When \l completed is set, the function is invoked when the \l data is
@@ -1685,13 +1712,15 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
/*!
\class QRhiNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for classes exposing backend-specific collections of native resource objects.
*/
/*!
\class QRhiResource
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for classes encapsulating native resource objects.
*/
@@ -1810,7 +1839,8 @@ quint64 QRhiResource::globalResourceId() const
/*!
\class QRhiBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Vertex, index, or uniform (constant) buffer resource.
*/
@@ -1911,7 +1941,8 @@ QRhiResource::Type QRhiBuffer::resourceType() const
/*!
\class QRhiRenderBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Renderbuffer resource.
Renderbuffers cannot be sampled or read but have some benefits over
@@ -1987,7 +2018,8 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const
/*!
\class QRhiTexture
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Texture resource.
*/
@@ -2155,7 +2187,8 @@ bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
/*!
\class QRhiSampler
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Sampler resource.
*/
@@ -2217,8 +2250,13 @@ QRhiResource::Type QRhiSampler::resourceType() const
/*!
\class QRhiRenderPassDescriptor
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Render pass resource.
+
+ A render pass, if such a concept exists in the underlying graphics API, is
+ a collection of attachments (color, depth, stencil) and describes how those
+ attachments are used.
*/
/*!
@@ -2238,8 +2276,21 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
}
/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanRenderPassNativeHandles. The returned value is null when exposing
+ the underlying native resources is not supported by the backend.
+
+ \sa QRhiVulkanRenderPassNativeHandles
+ */
+const QRhiNativeHandles *QRhiRenderPassDescriptor::nativeHandles()
+{
+ return nullptr;
+}
+
+/*!
\class QRhiRenderTarget
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Represents an onscreen (swapchain) or offscreen (texture) render target.
*/
@@ -2276,7 +2327,8 @@ QRhiResource::Type QRhiRenderTarget::resourceType() const
/*!
\class QRhiTextureRenderTarget
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Texture render target resource.
A texture render target allows rendering into one or more textures,
@@ -2393,7 +2445,8 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
/*!
\class QRhiShaderResourceBindings
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders.
A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding
@@ -2510,7 +2563,8 @@ bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBind
/*!
\class QRhiShaderResourceBinding
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the shader resource for a single binding point.
A QRhiShaderResourceBinding cannot be constructed directly. Instead, use
@@ -3035,7 +3089,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Graphics pipeline state resource.
\note Setting the shader resource bindings is mandatory. The referenced
@@ -3190,7 +3245,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline::TargetBlend
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the blend state for one color attachment.
Defaults to color write enabled, blending disabled. The blend values are
@@ -3200,7 +3256,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline::StencilOpState
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the stencil operation state.
*/
@@ -3255,7 +3312,8 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
/*!
\class QRhiSwapChain
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Swapchain resource.
A swapchain enables presenting rendering results to a surface. A swapchain
@@ -3517,7 +3575,8 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
/*!
\class QRhiComputePipeline
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Compute pipeline state resource.
\note Setting the shader resource bindings is mandatory. The referenced
@@ -3545,7 +3604,8 @@ QRhiComputePipeline::QRhiComputePipeline(QRhiImplementation *rhi)
/*!
\class QRhiCommandBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Command buffer resource.
Not creatable by applications at the moment. The only ways to obtain a
@@ -3592,6 +3652,42 @@ QRhiResource::Type QRhiCommandBuffer::resourceType() const
return CommandBuffer;
}
+#ifndef QT_NO_DEBUG
+static const char *resourceTypeStr(QRhiResource *res)
+{
+ switch (res->resourceType()) {
+ case QRhiResource::Buffer:
+ return "Buffer";
+ case QRhiResource::Texture:
+ return "Texture";
+ case QRhiResource::Sampler:
+ return "Sampler";
+ case QRhiResource::RenderBuffer:
+ return "RenderBuffer";
+ case QRhiResource::RenderPassDescriptor:
+ return "RenderPassDescriptor";
+ case QRhiResource::RenderTarget:
+ return "RenderTarget";
+ case QRhiResource::TextureRenderTarget:
+ return "TextureRenderTarget";
+ case QRhiResource::ShaderResourceBindings:
+ return "ShaderResourceBindings";
+ case QRhiResource::GraphicsPipeline:
+ return "GraphicsPipeline";
+ case QRhiResource::SwapChain:
+ return "SwapChain";
+ case QRhiResource::ComputePipeline:
+ return "ComputePipeline";
+ case QRhiResource::CommandBuffer:
+ return "CommandBuffer";
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return "";
+}
+#endif
+
QRhiImplementation::~QRhiImplementation()
{
qDeleteAll(resUpdPool);
@@ -3601,10 +3697,10 @@ QRhiImplementation::~QRhiImplementation()
// and freak out for unfreed graphics objects in the derived dtor already.
#ifndef QT_NO_DEBUG
if (!resources.isEmpty()) {
- qWarning("QRhi %p going down with %d unreleased resources. This is not nice.",
+ qWarning("QRhi %p going down with %d unreleased resources that own native graphics objects. This is not nice.",
q, resources.count());
for (QRhiResource *res : qAsConst(resources)) {
- qWarning(" Resource %p (%s)", res, res->m_objectName.constData());
+ qWarning(" %s resource %p (%s)", resourceTypeStr(res), res, res->m_objectName.constData());
res->m_rhi = nullptr;
}
}
@@ -3969,7 +4065,8 @@ void QRhi::runCleanup()
/*!
\class QRhiResourceUpdateBatch
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Records upload and copy type of operations.
With QRhi it is no longer possible to perform copy type of operations at
@@ -4711,6 +4808,11 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
enqueue commands to the current pass' command buffer by calling graphics
API functions directly.
+ \note This is only available when the intent was declared up front in
+ beginFrame(). Therefore this function must only be called when the frame
+ was started with specifying QRhi::ExternalContentsInPass in the flags
+ passed to QRhi::beginFrame().
+
With Vulkan or Metal one can query the native command buffer or encoder
objects via nativeHandles() and enqueue commands to them. With OpenGL or
Direct3D 11 the (device) context can be retrieved from
@@ -4728,6 +4830,16 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
functions (\c set* or \c draw*) must be called on the
QRhiCommandBuffer until endExternal().
+ \warning Some backends may return a native command buffer object from
+ QRhiCommandBuffer::nativeHandles() that is different from the primary one
+ when inside a beginExternal() - endExternal() block. Therefore it is
+ important to (re)query the native command buffer object after calling
+ beginExternal(). In practical terms this means that with Vulkan for example
+ the externally recorded Vulkan commands are placed onto a secondary command
+ buffer (with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT).
+ nativeHandles() returns this secondary command buffer when called between
+ begin/endExternal.
+
\sa endExternal(), nativeHandles()
*/
void QRhiCommandBuffer::beginExternal()
@@ -5034,6 +5146,13 @@ QRhiSwapChain *QRhi::newSwapChain()
/*!
Starts a new frame targeting the next available buffer of \a swapChain.
+ A frame consists of resource updates and one or more render and compute
+ passes.
+
+ \a flags can indicate certain special cases. For example, the fact that
+ QRhiCommandBuffer::beginExternal() will be called within this new frame
+ must be declared up front by setting the ExternalContentsInPass flag.
+
The high level pattern of rendering into a QWindow using a swapchain:
\list
@@ -5061,9 +5180,7 @@ QRhiSwapChain *QRhi::newSwapChain()
\endlist
- \a flags is currently unused.
-
- \sa endFrame()
+ \sa endFrame(), beginOffscreenFrame()
*/
QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags)
{
@@ -5163,7 +5280,8 @@ int QRhi::currentFrameSlot() const
/*!
Starts a new offscreen frame. Provides a command buffer suitable for
- recording rendering commands in \a cb.
+ recording rendering commands in \a cb. \a flags is used to indicate
+ certain special cases, just like with beginFrame().
\note The QRhiCommandBuffer stored to *cb is not owned by the caller.
@@ -5197,14 +5315,14 @@ int QRhi::currentFrameSlot() const
// image data available in rbResult
\endcode
- \sa endOffscreenFrame()
+ \sa endOffscreenFrame(), beginFrame()
*/
-QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags)
{
if (d->inFrame)
qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored");
- QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess;
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb, flags) : FrameOpSuccess;
if (r == FrameOpSuccess)
d->inFrame = true;
@@ -5216,12 +5334,12 @@ QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
\sa beginOffscreenFrame()
*/
-QRhi::FrameOpResult QRhi::endOffscreenFrame()
+QRhi::FrameOpResult QRhi::endOffscreenFrame(EndFrameFlags flags)
{
if (!d->inFrame)
qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored");
- QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess;
+ QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame(flags) : FrameOpSuccess;
d->inFrame = false;
qDeleteAll(d->pendingReleaseAndDestroyResources);
d->pendingReleaseAndDestroyResources.clear();
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index df30817ef4..2d36c19e99 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -843,6 +843,8 @@ class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
public:
QRhiResource::Type resourceType() const override;
+ virtual const QRhiNativeHandles *nativeHandles();
+
protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi);
};
@@ -1324,6 +1326,7 @@ public:
};
enum BeginFrameFlag {
+ ExternalContentsInPass = 0x01
};
Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
@@ -1384,8 +1387,8 @@ public:
bool isRecordingFrame() const;
int currentFrameSlot() const;
- FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb);
- FrameOpResult endOffscreenFrame();
+ FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags = BeginFrameFlags());
+ FrameOpResult endOffscreenFrame(EndFrameFlags flags = EndFrameFlags());
QRhi::FrameOpResult finish();
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index b592fe82f2..0914cf268b 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -95,8 +95,8 @@ public:
virtual QRhiSwapChain *createSwapChain() = 0;
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
- virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
- virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
+ virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
virtual QRhi::FrameOpResult finish() = 0;
virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 0c5df600a0..3e136cdb80 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11InitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Direct3D 11 specific initialization parameters.
A D3D11-based QRhi needs no special parameters for initialization. If
@@ -97,7 +98,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11NativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the D3D device and device context used by the QRhi.
\note The class uses \c{void *} as the type since including the COM-based
@@ -107,7 +109,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11TextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the D3D texture object that is backing a QRhiTexture instance.
\note The class uses \c{void *} as the type since including the COM-based
@@ -157,6 +160,36 @@ static inline Int aligned(Int v, Int byteAlign)
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
+static IDXGIFactory1 *createDXGIFactory2()
+{
+ IDXGIFactory1 *result = nullptr;
+ if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
+ using PtrCreateDXGIFactory2 = HRESULT (WINAPI *)(UINT, REFIID, void **);
+ QSystemLibrary dxgilib(QStringLiteral("dxgi"));
+ if (auto createDXGIFactory2 = (PtrCreateDXGIFactory2)dxgilib.resolve("CreateDXGIFactory2")) {
+ const HRESULT hr = createDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&result));
+ if (FAILED(hr)) {
+ qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ result = nullptr;
+ }
+ } else {
+ qWarning("Unable to resolve CreateDXGIFactory2()");
+ }
+ }
+ return result;
+}
+
+static IDXGIFactory1 *createDXGIFactory1()
+{
+ IDXGIFactory1 *result = nullptr;
+ const HRESULT hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&result));
+ if (FAILED(hr)) {
+ qWarning("CreateDXGIFactory1() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ result = nullptr;
+ }
+ return result;
+}
+
bool QRhiD3D11::create(QRhi::Flags flags)
{
Q_UNUSED(flags);
@@ -165,19 +198,22 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (debugLayer)
devFlags |= D3D11_CREATE_DEVICE_DEBUG;
- HRESULT hr;
-#if !defined(Q_CC_MINGW)
- hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7;
- if (hasDxgi2)
- hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&dxgiFactory));
- else
-#endif
- hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&dxgiFactory));
+ dxgiFactory = createDXGIFactory2();
+ if (dxgiFactory != nullptr) {
+ hasDxgi2 = true;
+ supportsFlipDiscardSwapchain = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
+ && !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
+ } else {
+ dxgiFactory = createDXGIFactory1();
+ hasDxgi2 = false;
+ supportsFlipDiscardSwapchain = false;
+ }
- if (FAILED(hr)) {
- qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ if (dxgiFactory == nullptr)
return false;
- }
+
+ qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s",
+ hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false");
if (!importedDevice) {
IDXGIAdapter1 *adapterToUse = nullptr;
@@ -837,8 +873,10 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
- flushCommandBuffer();
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ // no timestampSwapChain, in order to avoid timestamp mess
+ executeCommandBuffer(cbD);
+ cbD->resetCommands();
}
void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
@@ -891,7 +929,7 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->cb.resetState();
swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
- swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot];
+ swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
QRHI_PROF_F(beginSwapChainFrame(swapChain));
@@ -920,7 +958,7 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
executeCommandBuffer(&swapChainD->cb);
if (swapChainD->sampleDesc.Count > 1) {
- context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0,
+ context->ResolveSubresource(swapChainD->backBufferTex, 0,
swapChainD->msaaTex[currentFrameSlot], 0,
swapChainD->colorFormat);
}
@@ -953,8 +991,9 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
ofr.active = true;
ofr.cbWrapper.resetState();
@@ -963,8 +1002,9 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame()
+QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
ofr.active = false;
executeCommandBuffer(&ofr.cbWrapper);
@@ -1099,27 +1139,25 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format)
QRhi::FrameOpResult QRhiD3D11::finish()
{
- if (inFrame)
- flushCommandBuffer();
+ if (inFrame) {
+ if (ofr.active) {
+ Q_ASSERT(!contextState.currentSwapChain);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass);
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
+ executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
+ contextState.currentSwapChain->cb.resetCommands();
+ }
+ }
finishActiveReadbacks();
return QRhi::FrameOpSuccess;
}
-void QRhiD3D11::flushCommandBuffer()
-{
- if (ofr.active) {
- Q_ASSERT(!contextState.currentSwapChain);
- executeCommandBuffer(&ofr.cbWrapper);
- ofr.cbWrapper.resetCommands();
- } else {
- Q_ASSERT(contextState.currentSwapChain);
- executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
- contextState.currentSwapChain->cb.resetCommands();
- }
-}
-
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
{
@@ -1304,14 +1342,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
// has to be supported. Insert a resolve.
QD3D11CommandBuffer::Command rcmd;
rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
- rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
rcmd.args.resolveSubRes.dstSubRes = 0;
rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
rcmd.args.resolveSubRes.srcSubRes = 0;
rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
cbD->commands.append(rcmd);
}
- src = swapChainD->tex[swapChainD->currentFrameSlot];
+ src = swapChainD->backBufferTex;
dxgiFormat = swapChainD->colorFormat;
pixelSize = swapChainD->pixelSize;
format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
@@ -3503,9 +3541,9 @@ QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
rt(rhi),
cb(rhi)
{
+ backBufferTex = nullptr;
+ backBufferRtv = nullptr;
for (int i = 0; i < BUFFER_COUNT; ++i) {
- tex[i] = nullptr;
- rtv[i] = nullptr;
msaaTex[i] = nullptr;
msaaRtv[i] = nullptr;
timestampActive[i] = false;
@@ -3522,15 +3560,15 @@ QD3D11SwapChain::~QD3D11SwapChain()
void QD3D11SwapChain::releaseBuffers()
{
+ if (backBufferRtv) {
+ backBufferRtv->Release();
+ backBufferRtv = nullptr;
+ }
+ if (backBufferTex) {
+ backBufferTex->Release();
+ backBufferTex = nullptr;
+ }
for (int i = 0; i < BUFFER_COUNT; ++i) {
- if (rtv[i]) {
- rtv[i]->Release();
- rtv[i] = nullptr;
- }
- if (tex[i]) {
- tex[i]->Release();
- tex[i] = nullptr;
- }
if (msaaRtv[i]) {
msaaRtv[i]->Release();
msaaRtv[i] = nullptr;
@@ -3656,18 +3694,19 @@ bool QD3D11SwapChain::buildOrResize()
const UINT swapChainFlags = 0;
QRHI_RES_RHI(QRhiD3D11);
+ const bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain;
if (!swapChain) {
HWND hwnd = reinterpret_cast<HWND>(window->winId());
sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
- // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
- // old DISCARD with back buffer count == 1). This makes no difference for
- // the rest of the stuff except that automatic MSAA is unsupported and
- // needs to be implemented via a custom multisample render target and an
- // explicit resolve.
-
HRESULT hr;
- if (rhiD->hasDxgi2) {
+ if (useFlipDiscard) {
+ // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
+ // old DISCARD with back buffer count == 1). This makes no difference for
+ // the rest of the stuff except that automatic MSAA is unsupported and
+ // needs to be implemented via a custom multisample render target and an
+ // explicit resolve.
+
DXGI_SWAP_CHAIN_DESC1 desc;
memset(&desc, 0, sizeof(desc));
desc.Width = pixelSize.width();
@@ -3690,7 +3729,10 @@ bool QD3D11SwapChain::buildOrResize()
if (SUCCEEDED(hr))
swapChain = sc1;
} else {
- // Windows 7
+ // Windows 7 for instance. Use DISCARD mode. Regardless, keep on
+ // using our manual resolve for symmetry with the FLIP_DISCARD code
+ // path when MSAA is requested.
+
DXGI_SWAP_CHAIN_DESC desc;
memset(&desc, 0, sizeof(desc));
desc.BufferDesc.Width = pixelSize.width();
@@ -3700,10 +3742,10 @@ bool QD3D11SwapChain::buildOrResize()
desc.BufferDesc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- desc.BufferCount = BUFFER_COUNT;
+ desc.BufferCount = 1;
desc.OutputWindow = hwnd;
desc.Windowed = true;
- desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = swapChainFlags;
hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
@@ -3712,30 +3754,49 @@ bool QD3D11SwapChain::buildOrResize()
qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
+ rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
} else {
releaseBuffers();
- HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags);
+ const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
+ HRESULT hr = swapChain->ResizeBuffers(count, pixelSize.width(), pixelSize.height(),
+ colorFormat, swapChainFlags);
if (FAILED(hr)) {
qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
}
+ // This looks odd (for FLIP_DISCARD, esp. compared with backends for Vulkan
+ // & co.) but the backbuffer is always at index 0, with magic underneath.
+ // Some explanation from
+ // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
+ //
+ // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
+ // Every call to Present implicitly changed the resource identity of the
+ // returned interface. Direct3D 12 no longer supports that implicit
+ // resource identity change, due to the CPU overhead required and the
+ // flexible resource descriptor design. As a result, the application must
+ // manually call GetBuffer for every each buffer created with the
+ // swapchain."
+
+ // So just query index 0 once (per resize) and be done with it.
+ HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&backBufferTex));
+ if (FAILED(hr)) {
+ qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = srgbAdjustedFormat;
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
for (int i = 0; i < BUFFER_COUNT; ++i) {
- HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&tex[i]));
- if (FAILED(hr)) {
- qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
- return false;
- }
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
- rtvDesc.Format = srgbAdjustedFormat;
- rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
- hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]);
- if (FAILED(hr)) {
- qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
- return false;
- }
if (sampleDesc.Count > 1) {
if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
return false;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 34c9ff70f8..582146315d 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -473,9 +473,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
imageRetainPool.clear();
}
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+ resetCommands();
resetCachedState();
}
void resetCachedState() {
@@ -523,8 +523,8 @@ struct QD3D11SwapChain : public QRhiSwapChain
DXGI_FORMAT colorFormat;
IDXGISwapChain *swapChain = nullptr;
static const int BUFFER_COUNT = 2;
- ID3D11Texture2D *tex[BUFFER_COUNT];
- ID3D11RenderTargetView *rtv[BUFFER_COUNT];
+ ID3D11Texture2D *backBufferTex;
+ ID3D11RenderTargetView *backBufferRtv;
ID3D11Texture2D *msaaTex[BUFFER_COUNT];
ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
DXGI_SAMPLE_DESC sampleDesc;
@@ -569,8 +569,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -633,7 +633,6 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
- void flushCommandBuffer();
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
@@ -656,6 +655,7 @@ public:
ID3DUserDefinedAnnotation *annotations = nullptr;
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
+ bool supportsFlipDiscardSwapchain = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
struct {
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 379801efbd..f4e711e33e 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiGles2InitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief OpenGL specific initialization parameters.
An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
@@ -130,13 +131,15 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiGles2NativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the OpenGL context used by the QRhi.
*/
/*!
\class QRhiGles2TextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
*/
@@ -268,6 +271,10 @@ QT_BEGIN_NAMESPACE
#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
#endif
+#ifndef GL_POINT_SPRITE
+#define GL_POINT_SPRITE 0x8861
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -424,8 +431,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
&& f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
- caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
- caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
+ && f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
if (caps.gles)
@@ -472,9 +479,15 @@ bool QRhiGles2::create(QRhi::Flags flags)
else
caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
- if (!caps.gles)
+ if (caps.gles)
+ caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
+ else
+ caps.textureCompareMode = true;
+
+ if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
- // else (with gles) this is always on
+ f->glEnable(GL_POINT_SPRITE);
+ } // else (with gles) these are always on
nativeHandlesStruct.context = ctx;
@@ -547,43 +560,6 @@ int QRhiGles2::effectiveSampleCount(int sampleCount) const
return s;
}
-static inline bool isPowerOfTwo(int x)
-{
- // Assumption: x >= 1
- return x == (x & -x);
-}
-
-QSize QRhiGles2::safeTextureSize(const QSize &pixelSize) const
-{
- QSize size = pixelSize.isEmpty() ? QSize(1, 1) : pixelSize;
-
- if (!caps.npotTexture) {
- if (!isPowerOfTwo(size.width())) {
- qWarning("Texture width %d is not a power of two, adjusting",
- size.width());
- size.setWidth(qNextPowerOfTwo(size.width()));
- }
- if (!isPowerOfTwo(size.height())) {
- qWarning("Texture height %d is not a power of two, adjusting",
- size.height());
- size.setHeight(qNextPowerOfTwo(size.height()));
- }
- }
-
- if (size.width() > caps.maxTextureSize) {
- qWarning("Texture width %d exceeds maximum width %d, adjusting",
- size.width(), caps.maxTextureSize);
- size.setWidth(caps.maxTextureSize);
- }
- if (size.height() > caps.maxTextureSize) {
- qWarning("Texture height %d exceeds maximum height %d, adjusting",
- size.height(), caps.maxTextureSize);
- size.setHeight(caps.maxTextureSize);
- }
-
- return size;
-}
-
QRhiSwapChain *QRhiGles2::createSwapChain()
{
return new QGles2SwapChain(this);
@@ -725,7 +701,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
return true;
case QRhi::NPOTTextureRepeat:
- return caps.npotTextureRepeat;
+ return caps.npotTextureFull;
case QRhi::RedOrAlpha8IsRed:
return caps.coreProfile;
case QRhi::ElementIndexUint:
@@ -1118,15 +1094,36 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
- flushCommandBuffer(); // also ensures the context is current
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ if (!ensureContext())
+ return;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ if (!ensureContext(currentSwapChain->surface))
+ return;
+ }
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ executeCommandBuffer(cbD);
+ cbD->resetCommands();
}
void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
- Q_ASSERT(cbD->commands.isEmpty());
+ Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
+
cbD->resetCachedState();
+
+ if (cbD->recordingPass != QGles2CommandBuffer::NoPass) {
+ // Commands that come after this point need a resource tracker and also
+ // a BarriersForPass command enqueued. (the ones we had from
+ // beginPass() are now gone since beginExternal() processed all that
+ // due to calling executeCommandBuffer()).
+ enqueueBarriersForPass(cbD);
+ }
+
if (cbD->currentTarget)
enqueueBindFramebuffer(cbD->currentTarget, cbD);
}
@@ -1187,8 +1184,9 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
if (!ensureContext())
return QRhi::FrameOpError;
@@ -1203,8 +1201,9 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
+QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(ofr.active);
ofr.active = false;
@@ -1220,23 +1219,22 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
QRhi::FrameOpResult QRhiGles2::finish()
{
- return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess;
-}
-
-QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
-{
- if (ofr.active) {
- Q_ASSERT(!currentSwapChain);
- if (!ensureContext())
- return QRhi::FrameOpError;
- executeCommandBuffer(&ofr.cbWrapper);
- ofr.cbWrapper.resetCommands();
- } else {
- Q_ASSERT(currentSwapChain);
- if (!ensureContext(currentSwapChain->surface))
- return QRhi::FrameOpError;
- executeCommandBuffer(&currentSwapChain->cb);
- currentSwapChain->cb.resetCommands();
+ if (inFrame) {
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(currentSwapChain);
+ Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
+ if (!ensureContext(currentSwapChain->surface))
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&currentSwapChain->cb);
+ currentSwapChain->cb.resetCommands();
+ }
}
return QRhi::FrameOpSuccess;
}
@@ -2376,11 +2374,13 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, samplerD->d.glwrapt);
// 3D textures not supported by GLES 2.0 or by us atm...
//f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
- if (samplerD->d.gltexcomparefunc != GL_NEVER) {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
- } else {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ if (caps.textureCompareMode) {
+ if (samplerD->d.gltexcomparefunc != GL_NEVER) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
+ } else {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ }
}
texD->samplerState = samplerD->d;
}
@@ -2505,6 +2505,16 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
return rtD;
}
+void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
+{
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
+ cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
+ cbD->commands.append(cmd);
+}
+
void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
@@ -2519,12 +2529,7 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
// Get a new resource tracker. Then add a command that will generate
// glMemoryBarrier() calls based on that tracker when submitted.
- cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
- QGles2CommandBuffer::Command cmd;
- cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
- cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
- cbD->commands.append(cmd);
+ enqueueBarriersForPass(cbD);
bool wantsColorClear, wantsDsClear;
QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
@@ -2600,12 +2605,7 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
if (resourceUpdates)
enqueueResourceUpdates(cb, resourceUpdates);
- cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
- QGles2CommandBuffer::Command cmd;
- cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
- cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
- cbD->commands.append(cmd);
+ enqueueBarriersForPass(cbD);
cbD->recordingPass = QGles2CommandBuffer::ComputePass;
@@ -2935,7 +2935,7 @@ bool QGles2RenderBuffer::build()
rhiD->f->glGenRenderbuffers(1, &renderbuffer);
rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
- const QSize size = rhiD->safeTextureSize(m_pixelSize);
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
switch (m_type) {
case QRhiRenderBuffer::DepthStencil:
@@ -3032,7 +3032,7 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
if (!rhiD->ensureContext())
return false;
- const QSize size = rhiD->safeTextureSize(m_pixelSize);
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
@@ -3145,18 +3145,19 @@ bool QGles2Texture::build()
for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
for (int level = 0; level != mipLevelCount; ++level) {
const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
- rhiD->f->glTexImage2D(faceTargetBase + layer, level, glsizedintformat,
+ rhiD->f->glTexImage2D(faceTargetBase + layer, level, glintformat,
mipSize.width(), mipSize.height(), 0,
glformat, gltype, nullptr);
}
}
} else {
- rhiD->f->glTexImage2D(target, 0, glsizedintformat, size.width(), size.height(),
+ rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(),
0, glformat, gltype, nullptr);
}
} else {
// Must be specified with immutable storage functions otherwise
- // bindImageTexture may fail.
+ // bindImageTexture may fail. Also, the internal format must be a
+ // sized format here.
rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
}
specified = true;
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 88a6144b42..6da529be92 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -525,19 +525,16 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
}
void resetCommands() {
commands.clear();
- // beginExternal() can lead to calling resetCommands() inside a pass,
- // hence the condition
- if (recordingPass == NoPass) {
- passResTrackers.clear();
- currentPassResTrackerIndex = -1;
- }
dataRetainPool.clear();
imageRetainPool.clear();
+
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
}
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+ resetCommands();
resetCachedState();
}
void resetCachedState() {
@@ -605,8 +602,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -671,7 +668,6 @@ public:
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
- QRhi::FrameOpResult flushCommandBuffer();
void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
@@ -692,8 +688,8 @@ public:
const uint *dynOfsPairs, int dynOfsCount);
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
+ void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
int effectiveSampleCount(int sampleCount) const;
- QSize safeTextureSize(const QSize &size) const;
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage,
QShaderDescription *desc, int *glslVersionUsed);
bool linkProgram(GLuint program);
@@ -717,8 +713,7 @@ public:
maxTextureSize(2048),
maxDrawBuffers(4),
msaaRenderBuffer(false),
- npotTexture(true),
- npotTextureRepeat(true),
+ npotTextureFull(true),
gles(false),
fixedIndexPrimitiveRestart(false),
bgraExternalFormat(false),
@@ -747,8 +742,7 @@ public:
// Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
// the same as multisample textures!
uint msaaRenderBuffer : 1;
- uint npotTexture : 1;
- uint npotTextureRepeat : 1;
+ uint npotTextureFull : 1;
uint gles : 1;
uint fixedIndexPrimitiveRestart : 1;
uint bgraExternalFormat : 1;
@@ -768,6 +762,7 @@ public:
uint instancing : 1;
uint baseVertex : 1;
uint compute : 1;
+ uint textureCompareMode : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index ffb2283ae7..07753c985c 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -1243,8 +1243,10 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
+
currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
if (swapchains.count() > 1) {
for (QMetalSwapChain *sc : qAsConst(swapchains)) {
@@ -1268,8 +1270,9 @@ QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiMetal::endOffscreenFrame()
+QRhi::FrameOpResult QRhiMetal::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(d->ofr.active);
d->ofr.active = false;
@@ -1986,9 +1989,11 @@ bool QMetalBuffer::build()
}
#endif
- // Immutable and Static only has buf[0] and pendingUpdates[0] in use.
- // Dynamic uses all.
- d->slotted = m_type == Dynamic;
+ // Have QMTL_FRAMES_IN_FLIGHT versions regardless of the type, for now.
+ // This is because writing to a Managed buffer (which is what Immutable and
+ // Static maps to on macOS) is not safe when another frame reading from the
+ // same buffer is still in flight.
+ d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer); // except for SSBOs written in the shader
QRHI_RES_RHI(QRhiMetal);
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 8b0256991d..c448865f4d 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -354,8 +354,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 1314e53893..dff6e05268 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -41,7 +41,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Null backend specific initialization parameters.
A Null QRhi needs no special parameters for initialization.
@@ -60,13 +61,15 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Empty.
*/
/*!
\class QRhiNullTextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Empty.
*/
@@ -353,14 +356,16 @@ QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameF
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
*cb = &offscreenCommandBuffer;
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiNull::endOffscreenFrame()
+QRhi::FrameOpResult QRhiNull::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
return QRhi::FrameOpSuccess;
}
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index b0227bc110..bdf0d59724 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -220,8 +220,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp
index 15e3007d49..e74e446a1c 100644
--- a/src/gui/rhi/qrhiprofiler.cpp
+++ b/src/gui/rhi/qrhiprofiler.cpp
@@ -41,7 +41,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Collects resource and timing information from an active QRhi.
@@ -142,7 +143,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler::CpuTime
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains CPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
@@ -155,7 +157,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler::GpuTime
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains GPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 61a1595a50..dfc85fb853 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -58,11 +58,34 @@ QT_BEGIN_NAMESPACE
and a separate, host visible staging buffer is used to upload data to them.
"Dynamic" buffers are in host visible memory and are duplicated (since there
can be 2 frames in flight). This is handled transparently to the application.
+
+ Barriers are generated automatically for each render or compute pass, based
+ on the resources that are used in that pass (in QRhiShaderResourceBindings,
+ vertex inputs, etc.). This implies deferring the recording of the command
+ buffer since the barriers have to be placed at the right place (before the
+ pass), and that can only be done once we know all the things the pass does.
+
+ This in turn has implications for integrating external commands
+ (beginExternal() - direct Vulkan calls - endExternal()) because that is
+ incompatible with this approach by nature. Therefore we support another mode
+ of operation, where each render or compute pass uses one or more secondary
+ command buffers (recorded right away), with each beginExternal() leading to
+ closing the current secondary cb, creating a new secondary cb for the
+ external content, and then starting yet another one in endExternal() for
+ whatever comes afterwards in the pass. This way the primary command buffer
+ only has vkCmdExecuteCommand(s) within a renderpass instance
+ (Begin-EndRenderPass). (i.e. our only subpass is then
+ VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
+ VK_SUBPASS_CONTENTS_INLINE)
+
+ The command buffer management mode is decided on a per frame basis,
+ controlled by the ExternalContentsInPass flag of beginFrame().
*/
/*!
\class QRhiVulkanInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Vulkan specific initialization parameters.
A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
@@ -146,7 +169,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
\note Ownership of the Vulkan objects is never transferred.
@@ -154,7 +178,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanTextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the Vulkan image object that is backing a QRhiTexture.
Importing and exporting Vulkan image objects that back a QRhiTexture when
@@ -168,7 +193,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanCommandBufferNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
\note The Vulkan command buffer object is only guaranteed to be valid, and
@@ -178,6 +204,13 @@ QT_BEGIN_NAMESPACE
\l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
*/
+/*!
+ \class QRhiVulkanRenderPassNativeHandles
+ \internal
+ \inmodule QtGui
+ \brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
+ */
+
static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
{
return (v + byteAlign - 1) & ~(byteAlign - 1);
@@ -1465,7 +1498,6 @@ static inline bool checkDeviceLost(VkResult err)
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
- Q_UNUSED(flags);
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
@@ -1514,7 +1546,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
if (frame.timestampQueryIndex >= 0) {
quint64 timestamp[2] = { 0, 0 };
VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
- 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT);
+ 2 * sizeof(quint64), timestamp, sizeof(quint64),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
frame.timestampQueryIndex = -1;
if (err == VK_SUCCESS) {
@@ -1536,7 +1569,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
}
// build new draw command buffer
- QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf);
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
if (cbres != QRhi::FrameOpSuccess)
return cbres;
@@ -1560,6 +1593,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
}
swapChainD->cbWrapper.cb = frame.cmdBuf;
+ swapChainD->cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
+
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
swapChainD->rtWrapper.d.fb = image.fb;
@@ -1580,7 +1615,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
- recordCommandBuffer(&swapChainD->cbWrapper);
+ recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
@@ -1624,10 +1659,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
// stop recording and submit to the queue
Q_ASSERT(!frame.cmdFenceWaitable);
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf,
- frame.cmdFence,
- frame.imageSemWaitable ? &frame.imageSem : nullptr,
- needsPresent ? &frame.drawSem : nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(frame.cmdBuf,
+ frame.cmdFence,
+ frame.imageSemWaitable ? &frame.imageSem : nullptr,
+ needsPresent ? &frame.drawSem : nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
@@ -1698,7 +1733,7 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
}
-QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
+QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
{
if (*cb) {
df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
@@ -1737,8 +1772,8 @@ QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
- VkSemaphore *waitSem, VkSemaphore *signalSem)
+QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem)
{
VkResult err = df->vkEndCommandBuffer(cb);
if (err != VK_SUCCESS) {
@@ -1789,9 +1824,9 @@ void QRhiVulkan::waitCommandCompletion(int frameSlot)
}
}
-QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
- QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb);
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
if (cbres != QRhi::FrameOpSuccess)
return cbres;
@@ -1808,6 +1843,8 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
if (swapchains.count() > 1)
waitCommandCompletion(currentFrameSlot);
+ ofr.cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
+
prepareNewFrame(&ofr.cbWrapper);
ofr.active = true;
@@ -1815,12 +1852,13 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
+QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(ofr.active);
ofr.active = false;
- recordCommandBuffer(&ofr.cbWrapper);
+ recordPrimaryCommandBuffer(&ofr.cbWrapper);
if (!ofr.cmdFence) {
VkFenceCreateInfo fenceInfo;
@@ -1833,7 +1871,7 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
}
}
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
@@ -1857,15 +1895,19 @@ QRhi::FrameOpResult QRhiVulkan::finish()
VkCommandBuffer cb;
if (ofr.active) {
Q_ASSERT(!currentSwapChain);
- recordCommandBuffer(&ofr.cbWrapper);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
+ recordPrimaryCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
cb = ofr.cbWrapper.cb;
} else {
Q_ASSERT(currentSwapChain);
+ Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
swapChainD = currentSwapChain;
- recordCommandBuffer(&swapChainD->cbWrapper);
+ recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
+ swapChainD->cbWrapper.resetCommands();
cb = swapChainD->cbWrapper.cb;
}
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
}
@@ -1875,9 +1917,9 @@ QRhi::FrameOpResult QRhiVulkan::finish()
if (inFrame) {
// Allocate and begin recording on a new command buffer.
if (ofr.active)
- startCommandBuffer(&ofr.cbWrapper.cb);
+ startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
else
- startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
+ startPrimaryCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
}
executeDeferredReleases(true);
@@ -1951,6 +1993,69 @@ void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
enqueueResourceUpdates(cbD, resourceUpdates);
}
+VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD)
+{
+ VkCommandBuffer secondaryCb;
+
+ VkCommandBufferAllocateInfo cmdBufInfo;
+ memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool;
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+ cmdBufInfo.commandBufferCount = 1;
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create secondary command buffer: %d", err);
+ return VK_NULL_HANDLE;
+ }
+
+ VkCommandBufferBeginInfo cmdBufBeginInfo;
+ memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
+ VkCommandBufferInheritanceInfo cmdBufInheritInfo;
+ memset(&cmdBufInheritInfo, 0, sizeof(cmdBufInheritInfo));
+ cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+ cmdBufInheritInfo.subpass = 0;
+ if (rtD) {
+ cmdBufInheritInfo.renderPass = rtD->rp->rp;
+ cmdBufInheritInfo.framebuffer = rtD->fb;
+ }
+ cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
+
+ err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to begin secondary command buffer: %d", err);
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &secondaryCb);
+ return VK_NULL_HANDLE;
+ }
+
+ return secondaryCb;
+}
+
+void QRhiVulkan::endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD)
+{
+ VkResult err = df->vkEndCommandBuffer(cb);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to end secondary command buffer: %d", err);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ExecuteSecondary;
+ cmd.args.executeSecondary.cb = cb;
+ cbD->commands.append(cmd);
+
+ deferredReleaseSecondaryCommandBuffer(cb);
+}
+
+void QRhiVulkan::deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb)
+{
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::CommandBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.commandBuffer.cb = cb;
+ releaseQueue.append(e);
+}
+
void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
@@ -2030,6 +2135,9 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
cbD->pools.clearValue.append(cvs.constData(), cvs.count());
cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb)
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer(rtD));
}
void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2037,6 +2145,13 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+ if (cbD->useSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+ cbD->resetCachedState();
+ }
+
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
cbD->commands.append(cmd);
@@ -2059,6 +2174,9 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
enqueueTransitionPassResources(cbD);
cbD->recordingPass = QVkCommandBuffer::ComputePass;
+
+ if (cbD->useSecondaryCb)
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer());
}
void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2066,6 +2184,13 @@ void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+ if (cbD->useSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+ cbD->resetCachedState();
+ }
+
cbD->recordingPass = QVkCommandBuffer::NoPass;
if (resourceUpdates)
@@ -2080,11 +2205,15 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
- cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
- cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+ }
cbD->currentGraphicsPipeline = nullptr;
cbD->currentComputePipeline = ps;
@@ -2099,12 +2228,16 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::Dispatch;
- cmd.args.dispatch.x = x;
- cmd.args.dispatch.y = y;
- cmd.args.dispatch.z = z;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDispatch(cbD->secondaryCbs.last(), x, y, z);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Dispatch;
+ cmd.args.dispatch.x = x;
+ cmd.args.dispatch.y = y;
+ cmd.args.dispatch.z = z;
+ cbD->commands.append(cmd);
+ }
}
VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
@@ -3009,6 +3142,9 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
break;
+ case QRhiVulkan::DeferredReleaseEntry::CommandBuffer:
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &e.commandBuffer.cb);
+ break;
default:
Q_UNREACHABLE();
break;
@@ -3109,14 +3245,15 @@ VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
cbD->commands.append(cmd);
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
}
-void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
+void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
{
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
@@ -3161,7 +3298,8 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break;
case QVkCommandBuffer::Command::BeginRenderPass:
cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
- df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE);
+ df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
+ cbD->useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
break;
case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb);
@@ -3214,13 +3352,15 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break;
case QVkCommandBuffer::Command::DebugMarkerBegin:
cmd.args.debugMarkerBegin.marker.pMarkerName =
- cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData();
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData();
vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
break;
case QVkCommandBuffer::Command::DebugMarkerEnd:
vkCmdDebugMarkerEnd(cbD->cb);
break;
case QVkCommandBuffer::Command::DebugMarkerInsert:
+ cmd.args.debugMarkerInsert.marker.pMarkerName =
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData();
vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
break;
case QVkCommandBuffer::Command::TransitionPassResources:
@@ -3229,12 +3369,13 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::Dispatch:
df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
break;
+ case QVkCommandBuffer::Command::ExecuteSecondary:
+ df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
+ break;
default:
break;
}
}
-
- cbD->resetCommands();
}
static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
@@ -3651,11 +3792,15 @@ void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
- cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+ }
cbD->currentGraphicsPipeline = ps;
cbD->currentComputePipeline = nullptr;
@@ -3853,16 +3998,25 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
}
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
- cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
- : VK_PIPELINE_BIND_POINT_COMPUTE;
- cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
- cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
- cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
- cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
- cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindDescriptorSets(cbD->secondaryCbs.last(),
+ gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
+ gfxPsD ? gfxPsD->layout : compPsD->layout,
+ 0, 1, &srbD->descSets[descSetIdx],
+ dynOfs.count(),
+ dynOfs.count() ? dynOfs.constData() : nullptr);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
+ cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
+ : VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
+ cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
+ cbD->commands.append(cmd);
+ }
if (gfxPsD) {
cbD->currentGraphicsSrb = srb;
@@ -3918,15 +4072,20 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
QRhiPassResourceTracker::BufVertexInputStage);
}
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
- cmd.args.bindVertexBuffer.startBinding = startBinding;
- cmd.args.bindVertexBuffer.count = bufs.count();
- cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
- cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
- cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
- cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindVertexBuffers(cbD->secondaryCbs.last(), startBinding,
+ bufs.count(), bufs.constData(), ofs.constData());
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.startBinding = startBinding;
+ cmd.args.bindVertexBuffer.count = bufs.count();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
+ cbD->commands.append(cmd);
+ }
}
if (indexBuf) {
@@ -3949,12 +4108,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
cbD->currentIndexOffset = indexOffset;
cbD->currentIndexFormat = type;
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
- cmd.args.bindIndexBuffer.buf = vkindexbuf;
- cmd.args.bindIndexBuffer.ofs = indexOffset;
- cmd.args.bindIndexBuffer.type = type;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindIndexBuffer(cbD->secondaryCbs.last(), vkindexbuf, indexOffset, type);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buf = vkindexbuf;
+ cmd.args.bindIndexBuffer.ofs = indexOffset;
+ cmd.args.bindIndexBuffer.type = type;
+ cbD->commands.append(cmd);
+ }
trackedRegisterBuffer(&passResTracker, ibufD, slot,
QRhiPassResourceTracker::BufIndexRead,
@@ -3975,7 +4138,6 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
return;
QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetViewport;
VkViewport *vp = &cmd.args.setViewport.viewport;
vp->x = x;
vp->y = y;
@@ -3983,16 +4145,26 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
vp->height = h;
vp->minDepth = viewport.minDepth();
vp->maxDepth = viewport.maxDepth();
- cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetViewport(cbD->secondaryCbs.last(), 0, 1, vp);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetViewport;
+ cbD->commands.append(cmd);
+ }
if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
- cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x;
s->offset.y = y;
s->extent.width = w;
s->extent.height = h;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ cbD->commands.append(cmd);
+ }
}
}
@@ -4009,13 +4181,18 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
return;
QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x;
s->offset.y = y;
s->extent.width = w;
s->extent.height = h;
- cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
@@ -4023,13 +4200,18 @@ void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
- cmd.args.setBlendConstants.c[0] = c.redF();
- cmd.args.setBlendConstants.c[1] = c.greenF();
- cmd.args.setBlendConstants.c[2] = c.blueF();
- cmd.args.setBlendConstants.c[3] = c.alphaF();
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
+ df->vkCmdSetBlendConstants(cbD->secondaryCbs.last(), constants);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
+ cmd.args.setBlendConstants.c[0] = c.redF();
+ cmd.args.setBlendConstants.c[1] = c.greenF();
+ cmd.args.setBlendConstants.c[2] = c.blueF();
+ cmd.args.setBlendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
@@ -4037,10 +4219,14 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
- cmd.args.setStencilRef.ref = refValue;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetStencilReference(cbD->secondaryCbs.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
+ cmd.args.setStencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
@@ -4049,13 +4235,17 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::Draw;
- cmd.args.draw.vertexCount = vertexCount;
- cmd.args.draw.instanceCount = instanceCount;
- cmd.args.draw.firstVertex = firstVertex;
- cmd.args.draw.firstInstance = firstInstance;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDraw(cbD->secondaryCbs.last(), vertexCount, instanceCount, firstVertex, firstInstance);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Draw;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
@@ -4064,14 +4254,19 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
- cmd.args.drawIndexed.indexCount = indexCount;
- cmd.args.drawIndexed.instanceCount = instanceCount;
- cmd.args.drawIndexed.firstIndex = firstIndex;
- cmd.args.drawIndexed.vertexOffset = vertexOffset;
- cmd.args.drawIndexed.firstInstance = firstInstance;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDrawIndexed(cbD->secondaryCbs.last(), indexCount, instanceCount,
+ firstIndex, vertexOffset, firstInstance);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
@@ -4084,12 +4279,17 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
- cmd.args.debugMarkerBegin.marker = marker;
- cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count();
- cbD->pools.debugMarkerName.append(name);
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ marker.pMarkerName = name.constData();
+ vkCmdDebugMarkerBegin(cbD->secondaryCbs.last(), &marker);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
+ cmd.args.debugMarkerBegin.marker = marker;
+ cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cbD->pools.debugMarkerData.append(name);
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
@@ -4098,9 +4298,13 @@ void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
return;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ vkCmdDebugMarkerEnd(cbD->secondaryCbs.last());
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
@@ -4111,13 +4315,19 @@ void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
VkDebugMarkerMarkerInfoEXT marker;
memset(&marker, 0, sizeof(marker));
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
- marker.pMarkerName = msg.constData();
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
- cmd.args.debugMarkerInsert.marker = marker;
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ marker.pMarkerName = msg.constData();
+ vkCmdDebugMarkerInsert(cbD->secondaryCbs.last(), &marker);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
+ cmd.args.debugMarkerInsert.marker = marker;
+ cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cbD->pools.debugMarkerData.append(msg);
+ cbD->commands.append(cmd);
+ }
}
const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
@@ -4125,14 +4335,76 @@ const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
}
+static inline QVkRenderTargetData *maybeRenderTargetData(QVkCommandBuffer *cbD)
+{
+ Q_ASSERT(cbD->currentTarget);
+ QVkRenderTargetData *rtD = nullptr;
+ if (cbD->recordingPass == QVkCommandBuffer::RenderPass) {
+ switch (cbD->currentTarget->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QVkReferenceRenderTarget, cbD->currentTarget)->d;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ return rtD;
+}
+
void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ // When not in a pass, it is simple: record what we have (but do not
+ // submit), the cb can then be used to record more external commands.
+ if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
+ recordPrimaryCommandBuffer(cbD);
+ cbD->resetCommands();
+ return;
+ }
+
+ // Otherwise, inside a pass, have a secondary command buffer (with
+ // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
+ // cannot just record at this stage, that would mess up the resource
+ // tracking and commands like TransitionPassResources.
+
+ if (cbD->inExternal)
+ return;
+
+ if (!cbD->useSecondaryCb) {
+ qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
+ "This can be enabled by passing QRhi::ExternalContentsInPass to beginFrame().");
+ return;
+ }
+
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+
+ VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
+ if (extCb) {
+ cbD->secondaryCbs.append(extCb);
+ cbD->inExternal = true;
+ }
}
void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
+ Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
+ } else if (cbD->inExternal) {
+ VkCommandBuffer extCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
+ }
+
cbD->resetCachedState();
}
@@ -5094,6 +5366,12 @@ void QVkRenderPassDescriptor::release()
rhiD->unregisterResource(this);
}
+const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
+{
+ nativeHandlesStruct.renderPass = rp;
+ return &nativeHandlesStruct;
+}
+
QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi)
{
@@ -5782,6 +6060,22 @@ void QVkCommandBuffer::release()
// nothing to do here, cb is not owned by us
}
+const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
+{
+ // Ok this is messy but no other way has been devised yet. Outside
+ // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
+ // primary VkCommandBuffer. Inside, however, we need to provide the current
+ // secondary command buffer (typically the one started by beginExternal(),
+ // in case we are between beginExternal - endExternal inside a pass).
+
+ if (useSecondaryCb && !secondaryCbs.isEmpty())
+ nativeHandlesStruct.commandBuffer = secondaryCbs.last();
+ else
+ nativeHandlesStruct.commandBuffer = cb;
+
+ return &nativeHandlesStruct;
+}
+
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rtWrapper(rhi),
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index 545ef5ad72..ff19c7a54e 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -80,6 +80,11 @@ struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHand
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
};
+struct Q_GUI_EXPORT QRhiVulkanRenderPassNativeHandles : public QRhiNativeHandles
+{
+ VkRenderPass renderPass = VK_NULL_HANDLE;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index 31e0eaa585..962a1b8eb7 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -171,9 +171,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVkRenderPassDescriptor(QRhiImplementation *rhi);
~QVkRenderPassDescriptor();
void release() override;
+ const QRhiNativeHandles *nativeHandles() override;
VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false;
+ QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1;
};
@@ -307,13 +309,11 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
~QVkCommandBuffer();
void release() override;
- VkCommandBuffer cb = VK_NULL_HANDLE;
- QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
+ const QRhiNativeHandles *nativeHandles();
- const QRhiNativeHandles *nativeHandles() {
- nativeHandlesStruct.commandBuffer = cb;
- return &nativeHandlesStruct;
- }
+ VkCommandBuffer cb = VK_NULL_HANDLE; // primary
+ bool useSecondaryCb = false;
+ QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
enum PassType {
NoPass,
@@ -322,9 +322,12 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
};
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+
+ secondaryCbs.clear();
+
+ resetCommands();
resetCachedState();
}
@@ -341,6 +344,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
currentIndexFormat = VK_INDEX_TYPE_UINT16;
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ inExternal = false;
}
PassType recordingPass;
@@ -358,6 +362,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ QVarLengthArray<VkCommandBuffer, 4> secondaryCbs;
+ bool inExternal;
struct Command {
enum Cmd {
@@ -384,7 +390,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
DebugMarkerEnd,
DebugMarkerInsert,
TransitionPassResources,
- Dispatch
+ Dispatch,
+ ExecuteSecondary
};
Cmd cmd;
@@ -493,6 +500,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
} debugMarkerEnd;
struct {
VkDebugMarkerMarkerInfoEXT marker;
+ int markerNameIndex;
} debugMarkerInsert;
struct {
int trackerIndex;
@@ -500,6 +508,9 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct {
int x, y, z;
} dispatch;
+ struct {
+ VkCommandBuffer cb;
+ } executeSecondary;
} args;
};
QVector<Command> commands;
@@ -508,9 +519,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
void resetCommands() {
commands.clear();
+ resetPools();
+
passResTrackers.clear();
currentPassResTrackerIndex = -1;
- resetPools();
}
void resetPools() {
@@ -519,7 +531,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
pools.dynamicOffset.clear();
pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear();
- pools.debugMarkerName.clear();
+ pools.debugMarkerData.clear();
}
struct {
@@ -528,7 +540,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<uint32_t, 4> dynamicOffset;
QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
- QVarLengthArray<QByteArray, 4> debugMarkerName;
+ QVarLengthArray<QByteArray, 4> debugMarkerData;
} pools;
friend class QRhiVulkan;
@@ -592,7 +604,7 @@ struct QVkSwapChain : public QRhiSwapChain
bool imageAcquired = false;
bool imageSemWaitable = false;
quint32 imageIndex = 0;
- VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
+ VkCommandBuffer cmdBuf = VK_NULL_HANDLE; // primary
VkFence cmdFence = VK_NULL_HANDLE;
bool cmdFenceWaitable = false;
int timestampQueryIndex = -1;
@@ -637,8 +649,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -727,9 +739,12 @@ public:
VkShaderModule createShader(const QByteArray &spirv);
void prepareNewFrame(QRhiCommandBuffer *cb);
- QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb);
- QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
- VkSemaphore *waitSem, VkSemaphore *signalSem);
+ VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD = nullptr);
+ void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD);
+ void deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb);
+ QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb);
+ QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem);
void waitCommandCompletion(int frameSlot);
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
@@ -740,7 +755,7 @@ public:
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
- void recordCommandBuffer(QVkCommandBuffer *cbD);
+ void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD);
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
QVkBuffer *bufD,
int slot,
@@ -856,7 +871,8 @@ public:
Sampler,
TextureRenderTarget,
RenderPass,
- StagingBuffer
+ StagingBuffer,
+ CommandBuffer
};
Type type;
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
@@ -903,6 +919,9 @@ public:
VkBuffer stagingBuffer;
QVkAlloc stagingAllocation;
} stagingBuffer;
+ struct {
+ VkCommandBuffer cb;
+ } commandBuffer;
};
};
QVector<DeferredReleaseEntry> releaseQueue;
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index 9098180f69..72ce53c87a 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -42,7 +42,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShader
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains multiple versions of a shader translated to multiple shading languages,
together with reflection metadata.
@@ -134,7 +135,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderVersion
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the shading language version.
@@ -171,7 +173,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderKey
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the shading language, the version with flags, and the variant.
@@ -202,7 +205,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderCode
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains source or binary code for a shader and additional metadata.
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index 77aceaddba..c38a83c497 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -43,7 +43,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the interface of a shader.
@@ -231,21 +232,24 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription::InOutVariable
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes an input or output variable in the shader.
*/
/*!
\class QShaderDescription::BlockVariable
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a member of a uniform or push constant block.
*/
/*!
\class QShaderDescription::UniformBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a uniform block.
@@ -257,14 +261,16 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription::PushConstantBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a push constant block.
*/
/*!
\class QShaderDescription::StorageBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a shader storage block.
*/
diff --git a/src/gui/text/AGLFN_LICENSE.txt b/src/gui/text/AGLFN_LICENSE.txt
new file mode 100644
index 0000000000..50abffca15
--- /dev/null
+++ b/src/gui/text/AGLFN_LICENSE.txt
@@ -0,0 +1,26 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of Adobe Systems Incorporated nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index b5489c7ed9..ce7c7610c1 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -92,6 +92,7 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "border-bottom-right-radius", BorderBottomRightRadius },
{ "border-bottom-style", BorderBottomStyle },
{ "border-bottom-width", BorderBottomWidth },
+ { "border-collapse", BorderCollapse },
{ "border-color", BorderColor },
{ "border-image", BorderImage },
{ "border-left", BorderLeft },
@@ -222,6 +223,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "outset", Value_Outset },
{ "overline", Value_Overline },
{ "pre", Value_Pre },
+ { "pre-line", Value_PreLine },
{ "pre-wrap", Value_PreWrap },
{ "ridge", Value_Ridge },
{ "right", Value_Right },
@@ -248,10 +250,10 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
};
//Map id to strings as they appears in the 'values' array above
-static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 54, 35, 26, 70, 71, 25, 43, 5, 63, 47,
- 29, 58, 59, 27, 51, 61, 6, 10, 39, 56, 19, 13, 17, 18, 20, 21, 50, 24, 46, 67, 37, 3, 2, 40, 62, 16,
- 11, 57, 14, 32, 64, 33, 65, 55, 66, 34, 69, 8, 28, 38, 12, 36, 60, 7, 9, 4, 68, 53, 22, 23, 30, 31,
- 1, 15, 0, 52, 45, 44 };
+static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 50, 55, 35, 26, 71, 72, 25, 43, 5, 64, 48,
+ 29, 59, 60, 27, 52, 62, 6, 10, 39, 56, 19, 13, 17, 18, 20, 21, 51, 24, 46, 68, 37, 3, 2, 40, 63, 16,
+ 11, 58, 14, 32, 65, 33, 66, 56, 67, 34, 70, 8, 28, 38, 12, 36, 61, 7, 9, 4, 69, 54, 22, 23, 30, 31,
+ 1, 15, 0, 53, 45, 44 };
QString Value::toString() const
{
@@ -610,11 +612,7 @@ bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *st
case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
case BorderStyles: decl.styleValues(styles); break;
-#ifndef QT_OS_ANDROID_GCC_48_WORKAROUND
case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
-#else
- case BorderTopLeftRadius: new(radii)QSize(sizeValue(decl)); break;
-#endif
case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
@@ -1731,6 +1729,14 @@ void Declaration::borderImageValue(QString *image, int *cuts,
*h = *v;
}
+bool Declaration::borderCollapseValue() const
+{
+ if (d->values.count() != 1)
+ return false;
+ else
+ return d->values.at(0).toString() == QLatin1String("collapse");
+}
+
QIcon Declaration::iconValue() const
{
if (d->parsed.isValid())
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index b0fa4be682..ab85e76cf3 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -122,6 +122,7 @@ enum Property {
BorderRight,
BorderTop,
BorderBottom,
+ BorderCollapse,
Padding,
PaddingLeft,
PaddingRight,
@@ -205,6 +206,7 @@ enum KnownValue {
Value_Normal,
Value_Pre,
Value_NoWrap,
+ Value_PreLine,
Value_PreWrap,
Value_Small,
Value_Medium,
@@ -477,6 +479,7 @@ struct Q_GUI_EXPORT Declaration
QIcon iconValue() const;
void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const;
+ bool borderCollapseValue() const;
};
QT_CSS_DECLARE_TYPEINFO(Declaration, Q_MOVABLE_TYPE)
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 97e73f0723..3c1a052f37 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -180,14 +180,14 @@ Q_GUI_EXPORT int qt_defaultDpi()
}
QFontPrivate::QFontPrivate()
- : engineData(0), dpi(qt_defaultDpi()), screen(0),
+ : engineData(0), dpi(qt_defaultDpi()),
underline(false), overline(false), strikeOut(false), kerning(true),
capital(0), letterSpacingIsAbsolute(false), scFont(0)
{
}
QFontPrivate::QFontPrivate(const QFontPrivate &other)
- : request(other.request), engineData(0), dpi(other.dpi), screen(other.screen),
+ : request(other.request), engineData(0), dpi(other.dpi),
underline(other.underline), overline(other.overline),
strikeOut(other.strikeOut), kerning(other.kerning),
capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
@@ -581,11 +581,9 @@ QFont::QFont(const QFont &font, const QPaintDevice *pd)
{
Q_ASSERT(pd);
const int dpi = pd->logicalDpiY();
- const int screen = 0;
- if (font.d->dpi != dpi || font.d->screen != screen ) {
+ if (font.d->dpi != dpi) {
d = new QFontPrivate(*font.d);
d->dpi = dpi;
- d->screen = screen;
} else {
d = font.d;
}
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index 466e19e9cc..adbb7a0121 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_p.h
@@ -185,7 +185,6 @@ public:
QFontDef request;
mutable QFontEngineData *engineData;
int dpi;
- int screen;
uint underline : 1;
uint overline : 1;
@@ -230,19 +229,17 @@ public:
void clear();
struct Key {
- Key() : script(0), multi(0), screen(0) { }
- Key(const QFontDef &d, uchar c, bool m = 0, uchar s = 0)
- : def(d), script(c), multi(m), screen(s) { }
+ Key() : script(0), multi(0) { }
+ Key(const QFontDef &d, uchar c, bool m = 0)
+ : def(d), script(c), multi(m) { }
QFontDef def;
uchar script;
uchar multi: 1;
- uchar screen: 7;
inline bool operator<(const Key &other) const
{
if (script != other.script) return script < other.script;
- if (screen != other.screen) return screen < other.screen;
if (multi != other.multi) return multi < other.multi;
if (multi && def.fallBackFamilies.size() != other.def.fallBackFamilies.size())
return def.fallBackFamilies.size() < other.def.fallBackFamilies.size();
@@ -251,7 +248,6 @@ public:
inline bool operator==(const Key &other) const
{
return script == other.script
- && screen == other.screen
&& multi == other.multi
&& (!multi || def.fallBackFamilies == other.def.fallBackFamilies)
&& def == other.def;
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 4198df6e43..403a0510fa 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -923,29 +923,10 @@ QFixed QFontEngine::subPixelPositionForX(QFixed x) const
return subPixelPosition;
}
-QImage *QFontEngine::lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition,
- QFontEngine::GlyphFormat neededFormat,
- const QTransform &t, QPoint *offset)
+QFontEngine::Glyph *QFontEngine::glyphData(glyph_t, QFixed,
+ QFontEngine::GlyphFormat, const QTransform &)
{
- Q_ASSERT(currentlyLockedAlphaMap.isNull());
- if (neededFormat == Format_None)
- neededFormat = Format_A32;
-
- if (neededFormat != Format_A32)
- currentlyLockedAlphaMap = alphaMapForGlyph(glyph, subPixelPosition, t);
- else
- currentlyLockedAlphaMap = alphaRGBMapForGlyph(glyph, subPixelPosition, t);
-
- if (offset != 0)
- *offset = QPoint(0, 0);
-
- return &currentlyLockedAlphaMap;
-}
-
-void QFontEngine::unlockAlphaMapForGlyph()
-{
- Q_ASSERT(!currentlyLockedAlphaMap.isNull());
- currentlyLockedAlphaMap = QImage();
+ return nullptr;
}
QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index e20b52cb65..a5c78d5372 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -123,6 +123,22 @@ public:
};
Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag)
+ /* Used with the Freetype font engine. We don't cache glyphs that are too large anyway, so we can make this struct rather small */
+ struct Glyph {
+ Glyph() = default;
+ ~Glyph() { delete [] data; }
+ short linearAdvance = 0;
+ unsigned char width = 0;
+ unsigned char height = 0;
+ short x = 0;
+ short y = 0;
+ short advance = 0;
+ signed char format = 0;
+ uchar *data = nullptr;
+ private:
+ Q_DISABLE_COPY(Glyph);
+ };
+
virtual ~QFontEngine();
inline Type type() const { return m_type; }
@@ -190,11 +206,7 @@ public:
virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t);
virtual QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t, const QColor &color = QColor());
- virtual QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition,
- GlyphFormat neededFormat,
- const QTransform &t = QTransform(),
- QPoint *offset = nullptr);
- virtual void unlockAlphaMapForGlyph();
+ virtual Glyph *glyphData(glyph_t glyph, QFixed subPixelPosition, GlyphFormat neededFormat, const QTransform &t);
virtual bool hasInternalCaching() const { return false; }
virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &matrix, GlyphFormat /*format*/)
@@ -345,7 +357,6 @@ public:
void loadKerningPairs(QFixed scalingFactor);
GlyphFormat glyphFormat;
- QImage currentlyLockedAlphaMap;
int m_subPixelPositionCount; // Number of positions within a single pixel for this cache
inline QVariant userData() const { return m_userData; }
diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp
index c8dc8d676e..d3e4f11e8c 100644
--- a/src/gui/text/qfontmetrics.cpp
+++ b/src/gui/text/qfontmetrics.cpp
@@ -185,11 +185,9 @@ QFontMetrics::QFontMetrics(const QFont &font, const QPaintDevice *paintdevice)
#endif
{
const int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi();
- const int screen = 0;
- if (font.d->dpi != dpi || font.d->screen != screen ) {
+ if (font.d->dpi != dpi) {
d = new QFontPrivate(*font.d);
d->dpi = dpi;
- d->screen = screen;
} else {
d = font.d;
}
@@ -1036,8 +1034,15 @@ int QFontMetrics::lineWidth() const
return qRound(engine->lineThickness());
}
+/*!
+ \since 5.14
-
+ Returns the font DPI.
+*/
+qreal QFontMetrics::fontDpi() const
+{
+ return d->dpi;
+}
/*****************************************************************************
QFontMetricsF member functions
@@ -1171,11 +1176,9 @@ QFontMetricsF::QFontMetricsF(const QFont &font, const QPaintDevice *paintdevice)
#endif
{
int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi();
- const int screen = 0;
- if (font.d->dpi != dpi || font.d->screen != screen ) {
+ if (font.d->dpi != dpi) {
d = new QFontPrivate(*font.d);
d->dpi = dpi;
- d->screen = screen;
} else {
d = font.d;
}
@@ -1913,4 +1916,14 @@ qreal QFontMetricsF::lineWidth() const
return engine->lineThickness().toReal();
}
+/*!
+ \since 5.14
+
+ Returns the font DPI.
+*/
+qreal QFontMetricsF::fontDpi() const
+{
+ return d->dpi;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h
index 02ff335e68..e92a1514a1 100644
--- a/src/gui/text/qfontmetrics.h
+++ b/src/gui/text/qfontmetrics.h
@@ -135,6 +135,8 @@ public:
int strikeOutPos() const;
int lineWidth() const;
+ qreal fontDpi() const;
+
bool operator==(const QFontMetrics &other) const;
inline bool operator !=(const QFontMetrics &other) const { return !operator==(other); }
@@ -216,6 +218,8 @@ public:
qreal strikeOutPos() const;
qreal lineWidth() const;
+ qreal fontDpi() const;
+
bool operator==(const QFontMetricsF &other) const;
inline bool operator !=(const QFontMetricsF &other) const { return !operator==(other); }
diff --git a/src/gui/text/qt_attribution.json b/src/gui/text/qt_attribution.json
new file mode 100644
index 0000000000..c3a57267e2
--- /dev/null
+++ b/src/gui/text/qt_attribution.json
@@ -0,0 +1,17 @@
+[
+ {
+ "Id": "aglfn",
+ "Name": "Adobe Glyph List For New Fonts",
+ "QDocModule": "qtgui",
+ "Description": "Provides standardized names for glyphs.",
+ "QtUsage": "Used by PDF generator to make it easier for reader applications to resolve the original contents of rendered text.",
+ "Path": "qfontsubset_agl.cpp",
+
+ "Homepage": "https://github.com/adobe-type-tools/agl-aglfn",
+ "Version": "1.7",
+ "License": "BSD 3-Clause \"New\" or \"Revised\" License",
+ "LicenseId": "BSD-3-Clause",
+ "LicenseFile": "AGLFN_LICENSE.txt",
+ "Copyright": "Copyright 2002, 2003, 2005, 2006, 2008, 2010, 2015 Adobe Systems"
+ }
+]
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index dc34a96918..c80617f929 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -2593,51 +2593,43 @@ void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode
html += QLatin1Char('\"');
}
-void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
+static QLatin1String richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style)
{
- Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
-
- html += QLatin1String(" border-style:");
-
switch (style) {
case QTextFrameFormat::BorderStyle_None:
- html += QLatin1String("none");
- break;
+ return QLatin1String("none");
case QTextFrameFormat::BorderStyle_Dotted:
- html += QLatin1String("dotted");
- break;
+ return QLatin1String("dotted");
case QTextFrameFormat::BorderStyle_Dashed:
- html += QLatin1String("dashed");
- break;
+ return QLatin1String("dashed");
case QTextFrameFormat::BorderStyle_Solid:
- html += QLatin1String("solid");
- break;
+ return QLatin1String("solid");
case QTextFrameFormat::BorderStyle_Double:
- html += QLatin1String("double");
- break;
+ return QLatin1String("double");
case QTextFrameFormat::BorderStyle_DotDash:
- html += QLatin1String("dot-dash");
- break;
+ return QLatin1String("dot-dash");
case QTextFrameFormat::BorderStyle_DotDotDash:
- html += QLatin1String("dot-dot-dash");
- break;
+ return QLatin1String("dot-dot-dash");
case QTextFrameFormat::BorderStyle_Groove:
- html += QLatin1String("groove");
- break;
+ return QLatin1String("groove");
case QTextFrameFormat::BorderStyle_Ridge:
- html += QLatin1String("ridge");
- break;
+ return QLatin1String("ridge");
case QTextFrameFormat::BorderStyle_Inset:
- html += QLatin1String("inset");
- break;
+ return QLatin1String("inset");
case QTextFrameFormat::BorderStyle_Outset:
- html += QLatin1String("outset");
- break;
+ return QLatin1String("outset");
default:
- Q_ASSERT(false);
- break;
+ Q_UNREACHABLE();
};
+ return QLatin1String("");
+}
+
+void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
+{
+ Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
+ html += QLatin1String(" border-style:");
+ html += richtextBorderStyleToHtmlBorderStyle(style);
html += QLatin1Char(';');
}
@@ -3204,6 +3196,33 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
+ styleString += QLatin1String(" border-top:") + QString::number(cellFormat.topBorder()) + QLatin1String("px;");
+ if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
+ styleString += QLatin1String(" border-right:") + QString::number(cellFormat.rightBorder()) + QLatin1String("px;");
+ if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
+ styleString += QLatin1String(" border-bottom:") + QString::number(cellFormat.bottomBorder()) + QLatin1String("px;");
+ if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
+ styleString += QLatin1String(" border-left:") + QString::number(cellFormat.leftBorder()) + QLatin1String("px;");
+
+ if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
+ styleString += QLatin1String(" border-top-color:") + cellFormat.topBorderBrush().color().name() + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
+ styleString += QLatin1String(" border-right-color:") + cellFormat.rightBorderBrush().color().name() + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
+ styleString += QLatin1String(" border-bottom-color:") + cellFormat.bottomBorderBrush().color().name() + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
+ styleString += QLatin1String(" border-left-color:") + cellFormat.leftBorderBrush().color().name() + QLatin1Char(';');
+
+ if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
+ styleString += QLatin1String(" border-top-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
+ styleString += QLatin1String(" border-right-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
+ styleString += QLatin1String(" border-bottom-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + QLatin1Char(';');
+ if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
+ styleString += QLatin1String(" border-left-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + QLatin1Char(';');
+
if (!styleString.isEmpty())
html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
@@ -3310,6 +3329,9 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType
QString::number(format.leftMargin()),
QString::number(format.rightMargin()));
+ if (format.property(QTextFormat::TableBorderCollapse).toBool())
+ html += QLatin1String(" border-collapse:collapse;");
+
if (html.length() == originalHtmlLength) // nothing emitted?
html.chop(styleAttribute.size());
else
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index 0e3c8d0e83..a1b1c2e92b 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -1106,12 +1106,11 @@ void QTextDocumentPrivate::clearUndoRedoStacks(QTextDocument::Stacks stacksToCle
bool redoCommandsAvailable = undoState != undoStack.size();
if (stacksToClear == QTextDocument::UndoStack && undoCommandsAvailable) {
for (int i = 0; i < undoState; ++i) {
- QTextUndoCommand c = undoStack.at(undoState);
+ QTextUndoCommand c = undoStack.at(i);
if (c.command & QTextUndoCommand::Custom)
delete c.custom;
}
undoStack.remove(0, undoState);
- undoStack.resize(undoStack.size() - undoState);
undoState = 0;
if (emitSignals)
emitUndoAvailable(false);
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
index 1905d9a1b1..723e5c907c 100644
--- a/src/gui/text/qtextdocumentfragment.cpp
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -576,6 +576,9 @@ bool QTextHtmlImporter::appendNodeText()
&& ch != QChar::Nbsp
&& ch != QChar::ParagraphSeparator) {
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == QLatin1Char('\n') || ch == QLatin1Char('\r')))
+ compressNextWhitespace = PreserveWhiteSpace;
+
if (compressNextWhitespace == CollapseWhiteSpace)
compressNextWhitespace = RemoveWhiteSpace; // allow this one, and remove the ones coming next.
else if(compressNextWhitespace == RemoveWhiteSpace)
@@ -592,7 +595,9 @@ bool QTextHtmlImporter::appendNodeText()
}
} else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) {
compressNextWhitespace = RemoveWhiteSpace;
- if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && (ch == QLatin1Char('\n') || ch == QLatin1Char('\r')))
+ { }
+ else if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
ch = QChar::Nbsp;
else
ch = QLatin1Char(' ');
@@ -605,6 +610,8 @@ bool QTextHtmlImporter::appendNodeText()
|| ch == QChar::ParagraphSeparator) {
if (!textToInsert.isEmpty()) {
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && textToInsert.at(textToInsert.length() - 1) == QLatin1Char(' '))
+ textToInsert = textToInsert.chopped(1);
cursor.insertText(textToInsert, format);
textToInsert.clear();
}
@@ -979,6 +986,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
tableFmt.setColumns(table.columns);
tableFmt.setColumnWidthConstraints(columnWidths);
tableFmt.setHeaderRowCount(tableHeaderRowCount);
+ tableFmt.setBorderCollapse(node.borderCollapse);
fmt = tableFmt;
}
@@ -1054,6 +1062,31 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode()
fmt.setLeftPadding(leftPadding(currentNodeIdx));
if (rightPadding(currentNodeIdx) >= 0)
fmt.setRightPadding(rightPadding(currentNodeIdx));
+ if (tableCellBorder(currentNodeIdx, QCss::TopEdge) > 0)
+ fmt.setTopBorder(tableCellBorder(currentNodeIdx, QCss::TopEdge));
+ if (tableCellBorder(currentNodeIdx, QCss::RightEdge) > 0)
+ fmt.setRightBorder(tableCellBorder(currentNodeIdx, QCss::RightEdge));
+ if (tableCellBorder(currentNodeIdx, QCss::BottomEdge) > 0)
+ fmt.setBottomBorder(tableCellBorder(currentNodeIdx, QCss::BottomEdge));
+ if (tableCellBorder(currentNodeIdx, QCss::LeftEdge) > 0)
+ fmt.setLeftBorder(tableCellBorder(currentNodeIdx, QCss::LeftEdge));
+ if (tableCellBorderStyle(currentNodeIdx, QCss::TopEdge) != QTextFrameFormat::BorderStyle_None)
+ fmt.setTopBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::TopEdge));
+ if (tableCellBorderStyle(currentNodeIdx, QCss::RightEdge) != QTextFrameFormat::BorderStyle_None)
+ fmt.setRightBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::RightEdge));
+ if (tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge) != QTextFrameFormat::BorderStyle_None)
+ fmt.setBottomBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge));
+ if (tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge) != QTextFrameFormat::BorderStyle_None)
+ fmt.setLeftBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge));
+ if (tableCellBorderBrush(currentNodeIdx, QCss::TopEdge) != Qt::NoBrush)
+ fmt.setTopBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::TopEdge));
+ if (tableCellBorderBrush(currentNodeIdx, QCss::RightEdge) != Qt::NoBrush)
+ fmt.setRightBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::RightEdge));
+ if (tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge) != Qt::NoBrush)
+ fmt.setBottomBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge));
+ if (tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge) != Qt::NoBrush)
+ fmt.setLeftBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge));
+
cell.setFormat(fmt);
cursor.setPosition(cell.firstPosition());
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index a1b21b111b..b02723c047 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -155,6 +155,50 @@ struct QTextLayoutStruct {
{ if (pageHeight == QFIXED_MAX) return; pageBottom += pageHeight; y = qMax(y, pageBottom - pageHeight + pageBottomMargin + pageTopMargin - frameY); }
};
+#ifndef QT_NO_CSSPARSER
+// helper struct to collect edge data and priorize edges for border-collapse mode
+struct EdgeData {
+
+ enum EdgeClass {
+ // don't change order, used for comparison
+ ClassInvalid, // queried (adjacent) cell does not exist
+ ClassNone, // no explicit border, no grid, no table border
+ ClassGrid, // 1px grid if drawGrid is true
+ ClassTableBorder, // an outermost edge
+ ClassExplicit // set in cell's format
+ };
+
+ EdgeData(qreal width, const QTextTableCell &cell, QCss::Edge edge, EdgeClass edgeClass) :
+ width(width), cell(cell), edge(edge), edgeClass(edgeClass) {}
+ EdgeData() :
+ width(0), edge(QCss::NumEdges), edgeClass(ClassInvalid) {}
+
+ // used for priorization with qMax
+ bool operator< (const EdgeData &other) const {
+ if (width < other.width) return true;
+ if (width > other.width) return false;
+ if (edgeClass < other.edgeClass) return true;
+ if (edgeClass > other.edgeClass) return false;
+ if (edge == QCss::TopEdge && other.edge == QCss::BottomEdge) return true;
+ if (edge == QCss::BottomEdge && other.edge == QCss::TopEdge) return false;
+ if (edge == QCss::LeftEdge && other.edge == QCss::RightEdge) return true;
+ return false;
+ }
+ bool operator> (const EdgeData &other) const {
+ return other < *this;
+ }
+
+ qreal width;
+ QTextTableCell cell;
+ QCss::Edge edge;
+ EdgeClass edgeClass;
+};
+
+// axisEdgeData is referenced by QTextTableData's inline methods, so predeclare
+class QTextTableData;
+static inline EdgeData axisEdgeData(QTextTable *table, const QTextTableData *td, const QTextTableCell &cell, QCss::Edge edge);
+#endif
+
class QTextTableData : public QTextFrameData
{
public:
@@ -169,8 +213,19 @@ public:
QVector<QFixed> cellVerticalOffsets;
+ // without borderCollapse, those equal QTextFrameData::border;
+ // otherwise the widest outermost cell edge will be used
+ QFixed effectiveLeftBorder;
+ QFixed effectiveTopBorder;
+ QFixed effectiveRightBorder;
+ QFixed effectiveBottomBorder;
+
QFixed headerHeight;
+ QFixed borderCell; // 0 if borderCollapse is enabled, QTextFrameData::border otherwise
+ bool borderCollapse;
+ bool drawGrid;
+
// maps from cell index (row + col * rowCount) to child frames belonging to
// the specific cell
QMultiHash<int, QTextFrame *> childFrameMap;
@@ -182,7 +237,7 @@ public:
inline void calcRowPosition(int row)
{
if (row > 0)
- rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + border + cellSpacing + border;
+ rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + borderCell + cellSpacing + borderCell;
}
QRectF cellRect(const QTextTableCell &cell) const;
@@ -198,30 +253,55 @@ public:
}
}
- inline QFixed topPadding(const QTextFormat &format) const
+#ifndef QT_NO_CSSPARSER
+ inline QFixed cellBorderWidth(QTextTable *table, const QTextTableCell &cell, QCss::Edge edge) const
{
- return paddingProperty(format, QTextFormat::TableCellTopPadding);
+ qreal rv = axisEdgeData(table, this, cell, edge).width;
+ if (borderCollapse)
+ rv /= 2; // each cell has to add half of the border's width to its own padding
+ return QFixed::fromReal(rv * deviceScale);
}
+#endif
- inline QFixed bottomPadding(const QTextFormat &format) const
+ inline QFixed topPadding(QTextTable *table, const QTextTableCell &cell) const
{
- return paddingProperty(format, QTextFormat::TableCellBottomPadding);
+ return paddingProperty(cell.format(), QTextFormat::TableCellTopPadding)
+#ifndef QT_NO_CSSPARSER
+ + cellBorderWidth(table, cell, QCss::TopEdge)
+#endif
+ ;
+ }
+
+ inline QFixed bottomPadding(QTextTable *table, const QTextTableCell &cell) const
+ {
+ return paddingProperty(cell.format(), QTextFormat::TableCellBottomPadding)
+#ifndef QT_NO_CSSPARSER
+ + cellBorderWidth(table, cell, QCss::BottomEdge)
+#endif
+ ;
}
- inline QFixed leftPadding(const QTextFormat &format) const
+ inline QFixed leftPadding(QTextTable *table, const QTextTableCell &cell) const
{
- return paddingProperty(format, QTextFormat::TableCellLeftPadding);
+ return paddingProperty(cell.format(), QTextFormat::TableCellLeftPadding)
+#ifndef QT_NO_CSSPARSER
+ + cellBorderWidth(table, cell, QCss::LeftEdge)
+#endif
+ ;
}
- inline QFixed rightPadding(const QTextFormat &format) const
+ inline QFixed rightPadding(QTextTable *table, const QTextTableCell &cell) const
{
- return paddingProperty(format, QTextFormat::TableCellRightPadding);
+ return paddingProperty(cell.format(), QTextFormat::TableCellRightPadding)
+#ifndef QT_NO_CSSPARSER
+ + cellBorderWidth(table, cell, QCss::RightEdge)
+#endif
+ ;
}
- inline QFixedPoint cellPosition(const QTextTableCell &cell) const
+ inline QFixedPoint cellPosition(QTextTable *table, const QTextTableCell &cell) const
{
- const QTextFormat fmt = cell.format();
- return cellPosition(cell.row(), cell.column()) + QFixedPoint(leftPadding(fmt), topPadding(fmt));
+ return cellPosition(cell.row(), cell.column()) + QFixedPoint(leftPadding(table, cell), topPadding(table, cell));
}
void updateTableSize();
@@ -257,10 +337,10 @@ static bool isFrameFromInlineObject(QTextFrame *f)
void QTextTableData::updateTableSize()
{
- const QFixed effectiveTopMargin = this->topMargin + border + padding;
- const QFixed effectiveBottomMargin = this->bottomMargin + border + padding;
- const QFixed effectiveLeftMargin = this->leftMargin + border + padding;
- const QFixed effectiveRightMargin = this->rightMargin + border + padding;
+ const QFixed effectiveTopMargin = this->topMargin + effectiveTopBorder + padding;
+ const QFixed effectiveBottomMargin = this->bottomMargin + effectiveBottomBorder + padding;
+ const QFixed effectiveLeftMargin = this->leftMargin + effectiveLeftBorder + padding;
+ const QFixed effectiveRightMargin = this->rightMargin + effectiveRightBorder + padding;
size.height = contentsHeight == -1
? rowPositions.constLast() + heights.constLast() + padding + border + cellSpacing + effectiveBottomMargin
: effectiveTopMargin + contentsHeight + effectiveBottomMargin;
@@ -453,6 +533,7 @@ public:
const QTextBlock &bl, bool inRootFrame) const;
void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
const QTextBlock &bl, const QTextCharFormat *selectionFormat) const;
+ void drawTableCellBorder(const QRectF &cellRect, QPainter *painter, QTextTable *table, QTextTableData *td, const QTextTableCell &cell) const;
void drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
QTextTable *table, QTextTableData *td, int r, int c,
QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const;
@@ -719,7 +800,7 @@ QTextDocumentLayoutPrivate::hitTest(QTextTable *table, const QFixedPoint &point,
*position = cell.firstPosition();
- HitPoint hp = hitTest(cell.begin(), PointInside, point - td->cellPosition(cell), position, l, accuracy);
+ HitPoint hp = hitTest(cell.begin(), PointInside, point - td->cellPosition(table, cell), position, l, accuracy);
if (hp == PointExact)
return hp;
@@ -798,12 +879,45 @@ QFixed QTextDocumentLayoutPrivate::blockIndent(const QTextBlockFormat &blockForm
return QFixed::fromReal(indent * scale * document->indentWidth());
}
+struct BorderPaginator
+{
+ BorderPaginator(QTextDocument *document, const QRectF &rect, qreal topMarginAfterPageBreak, qreal bottomMargin, qreal border) :
+ pageHeight(document->pageSize().height()),
+ topPage(pageHeight > 0 ? static_cast<int>(rect.top() / pageHeight) : 0),
+ bottomPage(pageHeight > 0 ? static_cast<int>((rect.bottom() + border) / pageHeight) : 0),
+ rect(rect),
+ topMarginAfterPageBreak(topMarginAfterPageBreak),
+ bottomMargin(bottomMargin), border(border)
+ {}
+
+ QRectF clipRect(int page) const
+ {
+ QRectF clipped = rect.toRect();
+
+ if (topPage != bottomPage) {
+ clipped.setTop(qMax(clipped.top(), page * pageHeight + topMarginAfterPageBreak - border));
+ clipped.setBottom(qMin(clipped.bottom(), (page + 1) * pageHeight - bottomMargin));
+
+ if (clipped.bottom() <= clipped.top())
+ return QRectF();
+ }
+
+ return clipped;
+ }
+
+ qreal pageHeight;
+ int topPage;
+ int bottomPage;
+ QRectF rect;
+ qreal topMarginAfterPageBreak;
+ qreal bottomMargin;
+ qreal border;
+};
+
void QTextDocumentLayoutPrivate::drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin,
qreal border, const QBrush &brush, QTextFrameFormat::BorderStyle style) const
{
- const qreal pageHeight = document->pageSize().height();
- const int topPage = pageHeight > 0 ? static_cast<int>(rect.top() / pageHeight) : 0;
- const int bottomPage = pageHeight > 0 ? static_cast<int>((rect.bottom() + border) / pageHeight) : 0;
+ BorderPaginator paginator(document, rect, topMargin, bottomMargin, border);
#ifndef QT_NO_CSSPARSER
QCss::BorderStyle cssStyle = static_cast<QCss::BorderStyle>(style + 1);
@@ -814,16 +928,11 @@ void QTextDocumentLayoutPrivate::drawBorder(QPainter *painter, const QRectF &rec
bool turn_off_antialiasing = !(painter->renderHints() & QPainter::Antialiasing);
painter->setRenderHint(QPainter::Antialiasing);
- for (int i = topPage; i <= bottomPage; ++i) {
- QRectF clipped = rect.toRect();
-
- if (topPage != bottomPage) {
- clipped.setTop(qMax(clipped.top(), i * pageHeight + topMargin - border));
- clipped.setBottom(qMin(clipped.bottom(), (i + 1) * pageHeight - bottomMargin));
+ for (int i = paginator.topPage; i <= paginator.bottomPage; ++i) {
+ QRectF clipped = paginator.clipRect(i);
+ if (!clipped.isValid())
+ continue;
- if (clipped.bottom() <= clipped.top())
- continue;
- }
#ifndef QT_NO_CSSPARSER
qDrawEdge(painter, clipped.left(), clipped.top(), clipped.left() + border, clipped.bottom() + border, 0, 0, QCss::LeftEdge, cssStyle, brush);
qDrawEdge(painter, clipped.left() + border, clipped.top(), clipped.right() + border, clipped.top() + border, 0, 0, QCss::TopEdge, cssStyle, brush);
@@ -920,6 +1029,31 @@ static void adjustContextSelectionsForCell(QAbstractTextDocumentLayout::PaintCon
}
}
+static bool cellClipTest(QTextTable *table, QTextTableData *td,
+ const QAbstractTextDocumentLayout::PaintContext &cell_context,
+ const QTextTableCell &cell,
+ QRectF cellRect)
+{
+ if (!cell_context.clip.isValid())
+ return false;
+
+ if (td->borderCollapse) {
+ // we need to account for the cell borders in the clipping test
+ cellRect.adjust(-axisEdgeData(table, td, cell, QCss::LeftEdge).width / 2,
+ -axisEdgeData(table, td, cell, QCss::TopEdge).width / 2,
+ axisEdgeData(table, td, cell, QCss::RightEdge).width / 2,
+ axisEdgeData(table, td, cell, QCss::BottomEdge).width / 2);
+ } else {
+ qreal border = td->border.toReal();
+ cellRect.adjust(-border, -border, border, border);
+ }
+
+ if (!cellRect.intersects(cell_context.clip))
+ return true;
+
+ return false;
+}
+
void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *painter,
const QAbstractTextDocumentLayout::PaintContext &context,
QTextFrame *frame) const
@@ -982,10 +1116,12 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
const int tableStartPage = (absYPos / pageHeight).truncate();
const int tableEndPage = ((absYPos + td->size.height) / pageHeight).truncate();
- qreal border = td->border.toReal();
- drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
+ // for borderCollapse draw frame decoration by drawing the outermost
+ // cell edges with width = td->border
+ if (!td->borderCollapse)
+ drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
- // draw the table headers
+ // draw the repeated table headers for table continuation after page breaks
const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
int page = tableStartPage + 1;
while (page <= tableEndPage) {
@@ -999,9 +1135,7 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
QRectF cellRect = td->cellRect(cell);
cellRect.translate(off.x(), headerOffset);
- // we need to account for the cell border in the clipping test
- int leftAdjust = qMin(qreal(0), 1 - border);
- if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
+ if (cellClipTest(table, td, cell_context, cell, cellRect))
continue;
drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
@@ -1041,9 +1175,7 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
QRectF cellRect = td->cellRect(cell);
cellRect.translate(off);
- // we need to account for the cell border in the clipping test
- int leftAdjust = qMin(qreal(0), 1 - border);
- if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
+ if (cellClipTest(table, td, cell_context, cell, cellRect))
continue;
drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
@@ -1080,6 +1212,595 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
return;
}
+#ifndef QT_NO_CSSPARSER
+
+static inline QTextFormat::Property borderPropertyForEdge(QCss::Edge edge)
+{
+ switch (edge) {
+ case QCss::TopEdge:
+ return QTextFormat::TableCellTopBorder;
+ case QCss::BottomEdge:
+ return QTextFormat::TableCellBottomBorder;
+ case QCss::LeftEdge:
+ return QTextFormat::TableCellLeftBorder;
+ case QCss::RightEdge:
+ return QTextFormat::TableCellRightBorder;
+ default:
+ Q_UNREACHABLE();
+ return QTextFormat::UserProperty;
+ }
+}
+
+static inline QTextFormat::Property borderStylePropertyForEdge(QCss::Edge edge)
+{
+ switch (edge) {
+ case QCss::TopEdge:
+ return QTextFormat::TableCellTopBorderStyle;
+ case QCss::BottomEdge:
+ return QTextFormat::TableCellBottomBorderStyle;
+ case QCss::LeftEdge:
+ return QTextFormat::TableCellLeftBorderStyle;
+ case QCss::RightEdge:
+ return QTextFormat::TableCellRightBorderStyle;
+ default:
+ Q_UNREACHABLE();
+ return QTextFormat::UserProperty;
+ }
+}
+
+static inline QCss::Edge adjacentEdge(QCss::Edge edge)
+{
+ switch (edge) {
+ case QCss::TopEdge:
+ return QCss::BottomEdge;
+ case QCss::RightEdge:
+ return QCss::LeftEdge;
+ case QCss::BottomEdge:
+ return QCss::TopEdge;
+ case QCss::LeftEdge:
+ return QCss::RightEdge;
+ default:
+ Q_UNREACHABLE();
+ return QCss::NumEdges;
+ }
+}
+
+static inline bool isSameAxis(QCss::Edge e1, QCss::Edge e2)
+{
+ return e1 == e2 || e1 == adjacentEdge(e2);
+}
+
+static inline bool isVerticalAxis(QCss::Edge e)
+{
+ return e % 2 > 0;
+}
+
+static inline QTextTableCell adjacentCell(QTextTable *table, const QTextTableCell &cell,
+ QCss::Edge edge)
+{
+ int dc = 0;
+ int dr = 0;
+
+ switch (edge) {
+ case QCss::LeftEdge:
+ dc = -1;
+ break;
+ case QCss::RightEdge:
+ dc = cell.columnSpan();
+ break;
+ case QCss::TopEdge:
+ dr = -1;
+ break;
+ case QCss::BottomEdge:
+ dr = cell.rowSpan();
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ // get sibling cell
+ int col = cell.column() + dc;
+ int row = cell.row() + dr;
+
+ if (col < 0 || row < 0 || col >= table->columns() || row >= table->rows())
+ return QTextTableCell();
+ else
+ return table->cellAt(cell.row() + dr, cell.column() + dc);
+}
+
+// returns true if the specified edges of both cells
+// are "one the same line" aka axis.
+//
+// | C0
+// |-----|-----|----|----- < "axis"
+// | C1 | C2 | C3 | C4
+//
+// cell edge competingCell competingEdge result
+// C0 Left C1 Left true
+// C0 Left C2 Left false
+// C0 Bottom C2 Top true
+// C0 Bottom C4 Left INVALID
+static inline bool sharesAxis(const QTextTableCell &cell, QCss::Edge edge,
+ const QTextTableCell &competingCell, QCss::Edge competingCellEdge)
+{
+ Q_ASSERT(isVerticalAxis(edge) == isVerticalAxis(competingCellEdge));
+
+ switch (edge) {
+ case QCss::TopEdge:
+ return cell.row() ==
+ competingCell.row() + (competingCellEdge == QCss::BottomEdge ? competingCell.rowSpan() : 0);
+ case QCss::BottomEdge:
+ return cell.row() + cell.rowSpan() ==
+ competingCell.row() + (competingCellEdge == QCss::TopEdge ? 0 : competingCell.rowSpan());
+ case QCss::LeftEdge:
+ return cell.column() ==
+ competingCell.column() + (competingCellEdge == QCss::RightEdge ? competingCell.columnSpan() : 0);
+ case QCss::RightEdge:
+ return cell.column() + cell.columnSpan() ==
+ competingCell.column() + (competingCellEdge == QCss::LeftEdge ? 0 : competingCell.columnSpan());
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+// returns the applicable EdgeData for the given cell and edge.
+// this is either set explicitly by the cell's format, an activated grid
+// or the general table border width for outermost edges.
+static inline EdgeData cellEdgeData(QTextTable *table, const QTextTableData *td,
+ const QTextTableCell &cell, QCss::Edge edge)
+{
+ if (!cell.isValid()) {
+ // e.g. non-existing adjacent cell
+ return EdgeData();
+ }
+
+ QTextTableCellFormat f = cell.format().toTableCellFormat();
+ if (f.hasProperty(borderStylePropertyForEdge(edge))) {
+ // border style is set
+ double width = 3; // default to 3 like browsers do
+ if (f.hasProperty(borderPropertyForEdge(edge)))
+ width = f.property(borderPropertyForEdge(edge)).toDouble();
+ return EdgeData(width, cell, edge, EdgeData::ClassExplicit);
+ } else if (td->drawGrid) {
+ const bool outermost =
+ (edge == QCss::LeftEdge && cell.column() == 0) ||
+ (edge == QCss::TopEdge && cell.row() == 0) ||
+ (edge == QCss::RightEdge && cell.column() + cell.columnSpan() >= table->columns()) ||
+ (edge == QCss::BottomEdge && cell.row() + cell.rowSpan() >= table->rows());
+
+ if (outermost) {
+ qreal border = table->format().border();
+ if (border > 1.0) {
+ // table border
+ return EdgeData(border, cell, edge, EdgeData::ClassTableBorder);
+ }
+ }
+ // 1px clean grid
+ return EdgeData(1.0, cell, edge, EdgeData::ClassGrid);
+ }
+ else {
+ return EdgeData(0, cell, edge, EdgeData::ClassNone);
+ }
+}
+
+// returns the EdgeData with the larger width of either the cell's edge its adjacent cell's edge
+static inline EdgeData axisEdgeData(QTextTable *table, const QTextTableData *td,
+ const QTextTableCell &cell, QCss::Edge edge)
+{
+ Q_ASSERT(cell.isValid());
+
+ EdgeData result = cellEdgeData(table, td, cell, edge);
+ if (!td->borderCollapse)
+ return result;
+
+ QTextTableCell ac = adjacentCell(table, cell, edge);
+ result = qMax(result, cellEdgeData(table, td, ac, adjacentEdge(edge)));
+
+ bool mustCheckThirdCell = false;
+ if (ac.isValid()) {
+ /* if C0 and C3 don't share the left/top axis, we must
+ * also check C1.
+ *
+ * C0 and C4 don't share the left axis so we have
+ * to take the top edge of C1 (T1) into account
+ * because this might be wider than C0's bottom
+ * edge (B0). For the sake of simplicity we skip
+ * checking T2 and T3.
+ *
+ * | C0
+ * |-----|-----|----|-----
+ * | C1 | C2 | C3 | C4
+ *
+ * width(T4) = max(T4, B0, T1) (T2 and T3 won't be checked)
+ */
+ switch (edge) {
+ case QCss::TopEdge:
+ case QCss::BottomEdge:
+ mustCheckThirdCell = !sharesAxis(cell, QCss::LeftEdge, ac, QCss::LeftEdge);
+ break;
+ case QCss::LeftEdge:
+ case QCss::RightEdge:
+ mustCheckThirdCell = !sharesAxis(cell, QCss::TopEdge, ac, QCss::TopEdge);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (mustCheckThirdCell)
+ result = qMax(result, cellEdgeData(table, td, adjacentCell(table, ac, adjacentEdge(edge)), edge));
+
+ return result;
+}
+
+// checks an edge's joined competing edge according to priority rules and
+// adjusts maxCompetingEdgeData and maxOrthogonalEdgeData
+static inline void checkJoinedEdge(QTextTable *table, const QTextTableData *td, const QTextTableCell &cell,
+ QCss::Edge competingEdge,
+ const EdgeData &edgeData,
+ bool couldHaveContinuation,
+ EdgeData *maxCompetingEdgeData,
+ EdgeData *maxOrthogonalEdgeData)
+{
+ EdgeData competingEdgeData = axisEdgeData(table, td, cell, competingEdge);
+
+ if (competingEdgeData > edgeData) {
+ *maxCompetingEdgeData = competingEdgeData;
+ } else if (competingEdgeData.width == edgeData.width) {
+ if ((isSameAxis(edgeData.edge, competingEdge) && couldHaveContinuation)
+ || (!isVerticalAxis(edgeData.edge) && isVerticalAxis(competingEdge)) /* both widths are equal, vertical edge has priority */ ) {
+ *maxCompetingEdgeData = competingEdgeData;
+ }
+ }
+
+ if (maxOrthogonalEdgeData && competingEdgeData.width > maxOrthogonalEdgeData->width)
+ *maxOrthogonalEdgeData = competingEdgeData;
+}
+
+// the offset to make adjacent edges overlap in border collapse mode
+static inline qreal collapseOffset(const QTextDocumentLayoutPrivate *p, const EdgeData &w)
+{
+ return p->scaleToDevice(w.width) / 2.0;
+}
+
+// returns the offset that must be applied to the edge's
+// anchor (start point or end point) to avoid overlapping edges.
+//
+// Example 1:
+// 2
+// 2
+// 11111144444444 4 = top edge of cell, 4 pixels width
+// 3 3 = right edge of cell, 3 pixels width
+// 3 cell 4
+//
+// cell 4's top border is the widest border and will be
+// drawn with horiz. offset = -3/2 whereas its left border
+// of width 3 will be drawn with vert. offset = +4/2.
+//
+// Example 2:
+// 2
+// 2
+// 11111143333333
+// 4
+// 4 cell 4
+//
+// cell 4's left border is the widest and will be drawn
+// with vert. offset = -3/2 whereas its top border
+// of of width 3 will be drawn with hor. offset = +4/2.
+//
+// couldHaveContinuation: true for "end" anchor of an edge:
+// C
+// AAAAABBBBBB
+// D
+// width(A) == width(B) we consider B to be a continuation of A, so that B wins
+// and will be painted. A would only be painted including the right anchor if
+// there was no edge B (due to a rowspan or the axis C-D being the table's right
+// border).
+//
+// ignoreEdgesAbove: true if an egde (left, right or top) for the first row
+// after a table page break should be painted. In this case the edges of the
+// row above must be ignored.
+static inline double prioritizedEdgeAnchorOffset(const QTextDocumentLayoutPrivate *p,
+ QTextTable *table, const QTextTableData *td,
+ const QTextTableCell &cell,
+ const EdgeData &edgeData,
+ QCss::Edge orthogonalEdge,
+ bool couldHaveContinuation,
+ bool ignoreEdgesAbove)
+{
+ EdgeData maxCompetingEdgeData;
+ EdgeData maxOrthogonalEdgeData;
+ QTextTableCell competingCell;
+
+ // reference scenario for the inline comments:
+ // - edgeData being the top "T0" edge of C0
+ // - right anchor is '+', orthogonal edge is "R0"
+ // B C3 R|L C2 B
+ // ------+------
+ // T C0 R|L C1 T
+
+ // C0: T0/B3
+ // this is "edgeData"
+
+ // C0: R0/L1
+ checkJoinedEdge(table, td, cell, orthogonalEdge, edgeData, false,
+ &maxCompetingEdgeData, &maxOrthogonalEdgeData);
+
+ if (td->borderCollapse) {
+ // C1: T1/B2
+ if (!isVerticalAxis(edgeData.edge) || !ignoreEdgesAbove) {
+ competingCell = adjacentCell(table, cell, orthogonalEdge);
+ if (competingCell.isValid()) {
+ checkJoinedEdge(table, td, competingCell, edgeData.edge, edgeData, couldHaveContinuation,
+ &maxCompetingEdgeData, 0);
+ }
+ }
+
+ // C3: R3/L2
+ if (edgeData.edge != QCss::TopEdge || !ignoreEdgesAbove) {
+ competingCell = adjacentCell(table, cell, edgeData.edge);
+ if (competingCell.isValid() && sharesAxis(cell, orthogonalEdge, competingCell, orthogonalEdge)) {
+ checkJoinedEdge(table, td, competingCell, orthogonalEdge, edgeData, false,
+ &maxCompetingEdgeData, &maxOrthogonalEdgeData);
+ }
+ }
+ }
+
+ // wider edge has priority
+ bool hasPriority = edgeData > maxCompetingEdgeData;
+
+ if (td->borderCollapse) {
+ qreal offset = collapseOffset(p, maxOrthogonalEdgeData);
+ return hasPriority ? -offset : offset;
+ }
+ else
+ return hasPriority ? 0 : p->scaleToDevice(maxOrthogonalEdgeData.width);
+}
+
+// draw one edge of the given cell
+//
+// these options are for pagination / pagebreak handling:
+//
+// forceHeaderRow: true for all rows directly below a (repeated) header row.
+// if the table has headers the first row after a page break must check against
+// the last table header's row, not its actual predecessor.
+//
+// adjustTopAnchor: false for rows that are a continuation of a row after a page break
+// only evaluated for left/right edges
+//
+// adjustBottomAnchor: false for rows that will continue after a page break
+// only evaluated for left/right edges
+//
+// ignoreEdgesAbove: true if a row starts on top of the page and the
+// bottom edges of the prior row can therefore be ignored.
+static inline
+void drawCellBorder(const QTextDocumentLayoutPrivate *p, QPainter *painter,
+ QTextTable *table, const QTextTableData *td, const QTextTableCell &cell,
+ const QRectF &borderRect, QCss::Edge edge,
+ int forceHeaderRow, bool adjustTopAnchor, bool adjustBottomAnchor,
+ bool ignoreEdgesAbove)
+{
+ QPointF p1, p2;
+ qreal wh = 0;
+ qreal wv = 0;
+ EdgeData edgeData = axisEdgeData(table, td, cell, edge);
+
+ if (edgeData.width == 0)
+ return;
+
+ QTextTableCellFormat fmt = edgeData.cell.format().toTableCellFormat();
+ QTextFrameFormat::BorderStyle borderStyle = QTextFrameFormat::BorderStyle_None;
+ QBrush brush;
+
+ if (edgeData.edgeClass != EdgeData::ClassExplicit && td->drawGrid) {
+ borderStyle = QTextFrameFormat::BorderStyle_Solid;
+ brush = table->format().borderBrush();
+ }
+ else {
+ switch (edgeData.edge) {
+ case QCss::TopEdge:
+ brush = fmt.topBorderBrush();
+ borderStyle = fmt.topBorderStyle();
+ break;
+ case QCss::BottomEdge:
+ brush = fmt.bottomBorderBrush();
+ borderStyle = fmt.bottomBorderStyle();
+ break;
+ case QCss::LeftEdge:
+ brush = fmt.leftBorderBrush();
+ borderStyle = fmt.leftBorderStyle();
+ break;
+ case QCss::RightEdge:
+ brush = fmt.rightBorderBrush();
+ borderStyle = fmt.rightBorderStyle();
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (borderStyle == QTextFrameFormat::BorderStyle_None)
+ return;
+
+ // assume black if not explicit brush is set
+ if (brush.style() == Qt::NoBrush)
+ brush = Qt::black;
+
+ QTextTableCell cellOrHeader = cell;
+ if (forceHeaderRow != -1)
+ cellOrHeader = table->cellAt(forceHeaderRow, cell.column());
+
+ // adjust start and end anchors (e.g. left/right for top) according to priority rules
+ switch (edge) {
+ case QCss::TopEdge:
+ wv = p->scaleToDevice(edgeData.width);
+ p1 = borderRect.topLeft()
+ + QPointF(qFloor(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::LeftEdge, false, ignoreEdgesAbove)), 0);
+ p2 = borderRect.topRight()
+ + QPointF(-qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::RightEdge, true, ignoreEdgesAbove)), 0);
+ break;
+ case QCss::BottomEdge:
+ wv = p->scaleToDevice(edgeData.width);
+ p1 = borderRect.bottomLeft()
+ + QPointF(qFloor(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::LeftEdge, false, false)), -wv);
+ p2 = borderRect.bottomRight()
+ + QPointF(-qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::RightEdge, true, false)), -wv);
+ break;
+ case QCss::LeftEdge:
+ wh = p->scaleToDevice(edgeData.width);
+ p1 = borderRect.topLeft()
+ + QPointF(0, adjustTopAnchor ? qFloor(prioritizedEdgeAnchorOffset(p, table, td, cellOrHeader, edgeData,
+ forceHeaderRow != -1 ? QCss::BottomEdge : QCss::TopEdge,
+ false, ignoreEdgesAbove))
+ : 0);
+ p2 = borderRect.bottomLeft()
+ + QPointF(0, adjustBottomAnchor ? -qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::BottomEdge, true, false))
+ : 0);
+ break;
+ case QCss::RightEdge:
+ wh = p->scaleToDevice(edgeData.width);
+ p1 = borderRect.topRight()
+ + QPointF(-wh, adjustTopAnchor ? qFloor(prioritizedEdgeAnchorOffset(p, table, td, cellOrHeader, edgeData,
+ forceHeaderRow != -1 ? QCss::BottomEdge : QCss::TopEdge,
+ false, ignoreEdgesAbove))
+ : 0);
+ p2 = borderRect.bottomRight()
+ + QPointF(-wh, adjustBottomAnchor ? -qCeil(prioritizedEdgeAnchorOffset(p, table, td, cell, edgeData, QCss::BottomEdge, true, false))
+ : 0);
+ break;
+ default: break;
+ }
+
+ // for borderCollapse move edge width/2 pixel out of the borderRect
+ // so that it shares space with the adjacent cell's edge.
+ // to avoid fractional offsets, qCeil/qFloor is used
+ if (td->borderCollapse) {
+ QPointF offset;
+ switch (edge) {
+ case QCss::TopEdge:
+ offset = QPointF(0, -qCeil(collapseOffset(p, edgeData)));
+ break;
+ case QCss::BottomEdge:
+ offset = QPointF(0, qFloor(collapseOffset(p, edgeData)));
+ break;
+ case QCss::LeftEdge:
+ offset = QPointF(-qCeil(collapseOffset(p, edgeData)), 0);
+ break;
+ case QCss::RightEdge:
+ offset = QPointF(qFloor(collapseOffset(p, edgeData)), 0);
+ break;
+ default: break;
+ }
+ p1 += offset;
+ p2 += offset;
+ }
+
+ QCss::BorderStyle cssStyle = static_cast<QCss::BorderStyle>(borderStyle + 1);
+
+// this reveals errors in the drawing logic
+#ifdef COLLAPSE_DEBUG
+ QColor c = brush.color();
+ c.setAlpha(150);
+ brush.setColor(c);
+#endif
+
+ qDrawEdge(painter, p1.x(), p1.y(), p2.x() + wh, p2.y() + wv, 0, 0, edge, cssStyle, brush);
+}
+#endif
+
+void QTextDocumentLayoutPrivate::drawTableCellBorder(const QRectF &cellRect, QPainter *painter,
+ QTextTable *table, QTextTableData *td,
+ const QTextTableCell &cell) const
+{
+#ifndef QT_NO_CSSPARSER
+ qreal topMarginAfterPageBreak = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
+ qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
+
+ const int headerRowCount = qMin(table->format().headerRowCount(), table->rows() - 1);
+ if (headerRowCount > 0 && cell.row() >= headerRowCount)
+ topMarginAfterPageBreak += td->headerHeight.toReal();
+
+ BorderPaginator paginator(document, cellRect, topMarginAfterPageBreak, bottomMargin, 0);
+
+ bool turn_off_antialiasing = !(painter->renderHints() & QPainter::Antialiasing);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ // paint cell borders for every page the cell appears on
+ for (int page = paginator.topPage; page <= paginator.bottomPage; ++page) {
+ const QRectF clipped = paginator.clipRect(page);
+ if (!clipped.isValid())
+ continue;
+
+ const qreal offset = cellRect.top() - td->rowPositions.at(cell.row()).toReal();
+ const int lastHeaderRow = table->format().headerRowCount() - 1;
+ const bool tableHasHeader = table->format().headerRowCount() > 0;
+ const bool isHeaderRow = cell.row() < table->format().headerRowCount();
+ const bool isFirstRow = cell.row() == lastHeaderRow + 1;
+ const bool isLastRow = cell.row() + cell.rowSpan() >= table->rows();
+ const bool previousRowOnPreviousPage = !isFirstRow
+ && !isHeaderRow
+ && BorderPaginator(document,
+ td->cellRect(adjacentCell(table, cell, QCss::TopEdge)).translated(0, offset),
+ topMarginAfterPageBreak,
+ bottomMargin,
+ 0).bottomPage < page;
+ const bool nextRowOnNextPage = !isLastRow
+ && BorderPaginator(document,
+ td->cellRect(adjacentCell(table, cell, QCss::BottomEdge)).translated(0, offset),
+ topMarginAfterPageBreak,
+ bottomMargin,
+ 0).topPage > page;
+ const bool rowStartsOnPage = page == paginator.topPage;
+ const bool rowEndsOnPage = page == paginator.bottomPage;
+ const bool rowStartsOnPageTop = !tableHasHeader
+ && rowStartsOnPage
+ && previousRowOnPreviousPage;
+ const bool rowStartsOnPageBelowHeader = tableHasHeader
+ && rowStartsOnPage
+ && previousRowOnPreviousPage;
+
+ const bool suppressTopBorder = td->borderCollapse
+ ? !isHeaderRow && (!rowStartsOnPage || rowStartsOnPageBelowHeader)
+ : !rowStartsOnPage;
+ const bool suppressBottomBorder = td->borderCollapse
+ ? !isHeaderRow && (!rowEndsOnPage || nextRowOnNextPage)
+ : !rowEndsOnPage;
+ const bool doNotAdjustTopAnchor = td->borderCollapse
+ ? !tableHasHeader && !rowStartsOnPage
+ : !rowStartsOnPage;
+ const bool doNotAdjustBottomAnchor = suppressBottomBorder;
+
+ if (!suppressTopBorder) {
+ drawCellBorder(this, painter, table, td, cell, clipped, QCss::TopEdge,
+ -1, true, true, rowStartsOnPageTop);
+ }
+
+ drawCellBorder(this, painter, table, td, cell, clipped, QCss::LeftEdge,
+ suppressTopBorder ? lastHeaderRow : -1,
+ !doNotAdjustTopAnchor,
+ !doNotAdjustBottomAnchor,
+ rowStartsOnPageTop);
+ drawCellBorder(this, painter, table, td, cell, clipped, QCss::RightEdge,
+ suppressTopBorder ? lastHeaderRow : -1,
+ !doNotAdjustTopAnchor,
+ !doNotAdjustBottomAnchor,
+ rowStartsOnPageTop);
+
+ if (!suppressBottomBorder) {
+ drawCellBorder(this, painter, table, td, cell, clipped, QCss::BottomEdge,
+ -1, true, true, false);
+ }
+ }
+
+ if (turn_off_antialiasing)
+ painter->setRenderHint(QPainter::Antialiasing, false);
+#endif
+}
+
void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
QTextTable *table, QTextTableData *td, int r, int c,
QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const
@@ -1098,9 +1819,8 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
return;
}
- QTextFormat fmt = cell.format();
- const QFixed leftPadding = td->leftPadding(fmt);
- const QFixed topPadding = td->topPadding(fmt);
+ const QFixed leftPadding = td->leftPadding(table, cell);
+ const QFixed topPadding = td->topPadding(table, cell);
qreal topMargin = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
@@ -1109,7 +1829,7 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
if (r >= headerRowCount)
topMargin += td->headerHeight.toReal();
- if (td->border != 0) {
+ if (!td->borderCollapse && td->border != 0) {
const QBrush oldBrush = painter->brush();
const QPen oldPen = painter->pen();
@@ -1175,6 +1895,9 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
painter->setBrushOrigin(cellRect.topLeft());
}
+ // paint over the background - otherwise we would have to adjust the background paint cellRect for the border values
+ drawTableCellBorder(cellRect, painter, table, td, cell);
+
const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
const QPointF cellPos = QPointF(cellRect.left() + leftPadding.toReal(),
@@ -1538,8 +2261,7 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT
layoutStruct.maximumWidth = QFIXED_MAX;
layoutStruct.y = 0;
- const QTextFormat fmt = cell.format();
- const QFixed topPadding = td->topPadding(fmt);
+ const QFixed topPadding = td->topPadding(t, cell);
if (withPageBreaks) {
layoutStruct.frameY = absoluteTableY + td->rowPositions.at(cell.row()) + topPadding;
}
@@ -1557,8 +2279,20 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT
if (layoutStruct.pageHeight < 0 || !withPageBreaks)
layoutStruct.pageHeight = QFIXED_MAX;
const int currentPage = layoutStruct.currentPage();
- layoutStruct.pageTopMargin = td->effectiveTopMargin + td->cellSpacing + td->border + topPadding;
- layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->border + td->bottomPadding(fmt);
+
+ layoutStruct.pageTopMargin = td->effectiveTopMargin
+ + td->cellSpacing
+ + td->border
+ + td->paddingProperty(cell.format(), QTextFormat::TableCellTopPadding); // top cell-border is not repeated
+
+ const int headerRowCount = t->format().headerRowCount();
+ if (td->borderCollapse && headerRowCount > 0) {
+ // consider the header row's bottom edge width
+ qreal headerRowBottomBorderWidth = axisEdgeData(t, td, t->cellAt(headerRowCount - 1, cell.column()), QCss::BottomEdge).width;
+ layoutStruct.pageTopMargin += QFixed::fromReal(scaleToDevice(headerRowBottomBorderWidth) / 2);
+ }
+
+ layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->effectiveBottomBorder + td->bottomPadding(t, cell);
layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
layoutStruct.fullLayout = true;
@@ -1604,6 +2338,17 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT
return layoutStruct;
}
+#ifndef QT_NO_CSSPARSER
+static inline void findWidestOutermostBorder(QTextTable *table, QTextTableData *td,
+ const QTextTableCell &cell, QCss::Edge edge,
+ qreal *outerBorders)
+{
+ EdgeData w = cellEdgeData(table, td, cell, edge);
+ if (w.width > outerBorders[edge])
+ outerBorders[edge] = w.width;
+}
+#endif
+
QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY)
{
qCDebug(lcTable) << "layoutTable from" << layoutFrom << "to" << layoutTo << "parentY" << parentY;
@@ -1629,12 +2374,49 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
columnWidthConstraints.resize(columns);
Q_ASSERT(columnWidthConstraints.count() == columns);
- const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(fmt.cellSpacing()));
+ // borderCollapse will disable drawing the html4 style table cell borders
+ // and draw a 1px grid instead. This also sets a fixed cellspacing
+ // of 1px if border > 0 (for the grid) and ignore any explicitly set
+ // cellspacing.
+ td->borderCollapse = fmt.borderCollapse();
+ td->borderCell = td->borderCollapse ? 0 : td->border;
+ const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(td->borderCollapse ? 0 : fmt.cellSpacing())).round();
+
+ td->drawGrid = (td->borderCollapse && fmt.border() >= 1);
+
+ td->effectiveTopBorder = td->effectiveBottomBorder = td->effectiveLeftBorder = td->effectiveRightBorder = td->border;
+
+#ifndef QT_NO_CSSPARSER
+ if (td->borderCollapse) {
+ // find the widest borders of the outermost cells
+ qreal outerBorders[QCss::NumEdges];
+ for (int i = 0; i < QCss::NumEdges; ++i)
+ outerBorders[i] = 0;
+
+ for (int r = 0; r < rows; ++r) {
+ if (r == 0) {
+ for (int c = 0; c < columns; ++c)
+ findWidestOutermostBorder(table, td, table->cellAt(r, c), QCss::TopEdge, outerBorders);
+ }
+ if (r == rows - 1) {
+ for (int c = 0; c < columns; ++c)
+ findWidestOutermostBorder(table, td, table->cellAt(r, c), QCss::BottomEdge, outerBorders);
+ }
+ findWidestOutermostBorder(table, td, table->cellAt(r, 0), QCss::LeftEdge, outerBorders);
+ findWidestOutermostBorder(table, td, table->cellAt(r, columns - 1), QCss::RightEdge, outerBorders);
+ }
+ td->effectiveTopBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::TopEdge] / 2)).round();
+ td->effectiveBottomBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::BottomEdge] / 2)).round();
+ td->effectiveLeftBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::LeftEdge] / 2)).round();
+ td->effectiveRightBorder = QFixed::fromReal(scaleToDevice(outerBorders[QCss::RightEdge] / 2)).round();
+ }
+#endif
+
td->deviceScale = scaleToDevice(qreal(1));
td->cellPadding = QFixed::fromReal(scaleToDevice(fmt.cellPadding()));
- const QFixed leftMargin = td->leftMargin + td->border + td->padding;
- const QFixed rightMargin = td->rightMargin + td->border + td->padding;
- const QFixed topMargin = td->topMargin + td->border + td->padding;
+ const QFixed leftMargin = td->leftMargin + td->padding + td->effectiveLeftBorder;
+ const QFixed rightMargin = td->rightMargin + td->padding + td->effectiveRightBorder;
+ const QFixed topMargin = td->topMargin + td->padding + td->effectiveTopBorder;
const QFixed absoluteTableY = parentY + td->position.y;
@@ -1644,11 +2426,17 @@ recalc_minmax_widths:
QFixed remainingWidth = td->contentsWidth;
// two (vertical) borders per cell per column
- remainingWidth -= columns * 2 * td->border;
+ remainingWidth -= columns * 2 * td->borderCell;
// inter-cell spacing
remainingWidth -= (columns - 1) * cellSpacing;
// cell spacing at the left and right hand side
remainingWidth -= 2 * cellSpacing;
+
+ if (td->borderCollapse) {
+ remainingWidth -= td->effectiveLeftBorder;
+ remainingWidth -= td->effectiveRightBorder;
+ }
+
// remember the width used to distribute to percentaged columns
const QFixed initialTotalWidth = remainingWidth;
@@ -1673,9 +2461,8 @@ recalc_minmax_widths:
if (cspan > 1 && i != cell.column())
continue;
- const QTextFormat fmt = cell.format();
- const QFixed leftPadding = td->leftPadding(fmt);
- const QFixed rightPadding = td->rightPadding(fmt);
+ const QFixed leftPadding = td->leftPadding(table, cell);
+ const QFixed rightPadding = td->rightPadding(table, cell);
const QFixed widthPadding = leftPadding + rightPadding;
// to figure out the min and the max width lay out the cell at
@@ -1707,6 +2494,12 @@ recalc_minmax_widths:
if (maxW == QFIXED_MAX)
continue;
+ // for variable columns the maxWidth will later be considered as the
+ // column width (column width = content width). We must avoid that the
+ // pixel-alignment rounding step floors this value and thus the text
+ // rendering later erroneously wraps the content.
+ maxW = maxW.ceil();
+
widthToDistribute = maxW;
for (int n = 0; n < cspan; ++n) {
const int col = i + n;
@@ -1798,7 +2591,7 @@ recalc_minmax_widths:
}
// in order to get a correct border rendering we must ensure that the distance between
- // two cells is exactly 2 * td->border pixel. we do this by rounding the calculated width
+ // two cells is exactly 2 * td->cellBorder pixel. we do this by rounding the calculated width
// values here.
// to minimize the total rounding error we propagate the rounding error for each width
// to its successor.
@@ -1813,7 +2606,7 @@ recalc_minmax_widths:
td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border;
for (int i = 1; i < columns; ++i)
- td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->border + cellSpacing;
+ td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->borderCell + cellSpacing;
// - margin to compensate the + margin in columnPositions[0]
const QFixed contentsWidth = td->columnPositions.constLast() + td->widths.constLast() + td->padding + td->border + cellSpacing - leftMargin;
@@ -1914,12 +2707,10 @@ relayout:
}
}
- const QTextFormat fmt = cell.format();
-
- const QFixed topPadding = td->topPadding(fmt);
- const QFixed bottomPadding = td->bottomPadding(fmt);
- const QFixed leftPadding = td->leftPadding(fmt);
- const QFixed rightPadding = td->rightPadding(fmt);
+ const QFixed topPadding = td->topPadding(table, cell);
+ const QFixed bottomPadding = td->bottomPadding(table, cell);
+ const QFixed leftPadding = td->leftPadding(table, cell);
+ const QFixed rightPadding = td->rightPadding(table, cell);
const QFixed widthPadding = leftPadding + rightPadding;
++rowCellCount;
@@ -1956,13 +2747,13 @@ relayout:
}
if (haveRowSpannedCells) {
- const QFixed effectiveHeight = td->heights.at(r) + td->border + cellSpacing + td->border;
+ const QFixed effectiveHeight = td->heights.at(r) + td->borderCell + cellSpacing + td->borderCell;
for (int c = 0; c < columns; ++c)
heightToDistribute[c] = qMax(heightToDistribute.at(c) - effectiveHeight - dropDistance, QFixed(0));
}
if (r == headerRowCount - 1) {
- td->headerHeight = td->rowPositions.at(r) + td->heights.at(r) - td->rowPositions.at(0) + td->cellSpacing + 2 * td->border;
+ td->headerHeight = td->rowPositions.at(r) + td->heights.at(r) - td->rowPositions.at(0) + td->cellSpacing + 2 * td->borderCell;
td->headerHeight -= td->headerHeight * (td->headerHeight / pageHeight).truncate();
td->effectiveTopMargin += td->headerHeight;
}
@@ -1984,7 +2775,7 @@ relayout:
const QFixed availableHeight = td->rowPositions.at(r + rowSpan - 1) + td->heights.at(r + rowSpan - 1) - td->rowPositions.at(r);
const QTextCharFormat cellFormat = cell.format();
- const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(cellFormat) + td->bottomPadding(cellFormat);
+ const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(table, cell) + td->bottomPadding(table, cell);
QFixed offset = 0;
switch (cellFormat.verticalAlignment()) {
@@ -2009,14 +2800,14 @@ relayout:
td->minimumWidth = td->columnPositions.at(0);
for (int i = 0; i < columns; ++i) {
- td->minimumWidth += td->minWidths.at(i) + 2 * td->border + cellSpacing;
+ td->minimumWidth += td->minWidths.at(i) + 2 * td->borderCell + cellSpacing;
}
td->minimumWidth += rightMargin - td->border;
td->maximumWidth = td->columnPositions.at(0);
for (int i = 0; i < columns; ++i) {
if (td->maxWidths.at(i) != QFIXED_MAX)
- td->maximumWidth += td->maxWidths.at(i) + 2 * td->border + cellSpacing;
+ td->maximumWidth += td->maxWidths.at(i) + 2 * td->borderCell + cellSpacing;
qCDebug(lcTable) << "column" << i << "has final width" << td->widths.at(i).toReal()
<< "min" << td->minWidths.at(i).toReal() << "max" << td->maxWidths.at(i).toReal();
}
@@ -3250,7 +4041,7 @@ QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
QTextTableCell cell = table->cellAt(framePos);
if (cell.isValid())
- pos += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
+ pos += static_cast<QTextTableData *>(fd)->cellPosition(table, cell).toPointF();
}
}
@@ -3280,7 +4071,7 @@ QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame)
if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
QTextTableCell cell = table->cellAt(framePos);
if (cell.isValid())
- pos += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
+ pos += static_cast<QTextTableData *>(fd)->cellPosition(table, cell).toPointF();
}
f = f->parentFrame();
@@ -3305,7 +4096,7 @@ QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
QTextTableCell cell = table->cellAt(blockPos);
if (cell.isValid())
- offset += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
+ offset += static_cast<QTextTableData *>(fd)->cellPosition(table, cell).toPointF();
}
frame = frame->parentFrame();
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index 090c6cc4ce..47a38db3ad 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -660,6 +660,23 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value TableCellTopPadding
\value TableCellBottomPadding
+ Table cell properties intended for use with \l QTextTableFormat::borderCollapse enabled
+
+ \value TableCellTopBorder
+ \value TableCellBottomBorder
+ \value TableCellLeftBorder
+ \value TableCellRightBorder
+
+ \value TableCellTopBorderStyle
+ \value TableCellBottomBorderStyle
+ \value TableCellLeftBorderStyle
+ \value TableCellRightBorderStyle
+
+ \value TableCellTopBorderBrush
+ \value TableCellBottomBorderBrush
+ \value TableCellLeftBorderBrush
+ \value TableCellRightBorderBrush
+
Image properties
\value ImageName The filename or source of the image.
@@ -3100,6 +3117,8 @@ QTextTableFormat::QTextTableFormat(const QTextFormat &fmt)
Sets the cell \a spacing for the table. This determines the distance
between adjacent cells.
+
+ This property will be ignored if \l borderCollapse is enabled.
*/
/*!
@@ -3151,6 +3170,44 @@ QTextTableFormat::QTextTableFormat(const QTextFormat &fmt)
*/
/*!
+ \fn void QTextTableFormat::setBorderCollapse(bool borderCollapse)
+ \since 5.14
+
+ Enabling \a borderCollapse will have the following implications:
+ \list
+ \li The borders and grid of the table will be rendered following the
+ CSS table \c border-collapse: \c collapse rules
+ \li Setting the \c border property to a minimum value of \c 1 will render a
+ one pixel solid inner table grid using the \l borderBrush property and an
+ outer border as specified
+ \li The various border style properties of \l QTextTableCellFormat can be used to
+ customize the grid and have precedence over the border and grid of the table
+ \li The \l cellSpacing property will be ignored
+ \li For print pagination:
+ \list
+ \li Columns continued on a page will not have their top cell border rendered
+ \li Repeated header rows will always have their bottom cell border rendered
+ \endlist
+ \endlist
+
+ With borderCollapse disabled, cell borders can still be styled
+ using QTextTableCellFormat but styling will be applied only within
+ the cell's frame, which is probably not very useful in practice.
+
+ \sa setBorder(), setBorderBrush(), setBorderStyle()
+ \sa QTextTableCellFormat
+*/
+
+/*!
+ \fn bool QTextTableFormat::borderCollapse() const
+ \since 5.14
+
+ Returns true if borderCollapse is enabled.
+
+ \sa setBorderCollapse()
+*/
+
+/*!
\fn void QTextFormat::setBackground(const QBrush &brush)
Sets the brush use to paint the document's background to the
@@ -3489,6 +3546,228 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
*/
/*!
+ \fn void QTextTableCellFormat::setTopBorder(qreal width)
+ \since 5.14
+
+ Sets the top border \a width of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::topBorder() const
+ \since 5.14
+
+ Returns the top border width of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBottomBorder(qreal width)
+ \since 5.14
+
+ Sets the bottom border \a width of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::bottomBorder() const
+ \since 5.14
+
+ Returns the bottom border width of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setLeftBorder(qreal width)
+ \since 5.14
+
+ Sets the left border \a width of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::leftBorder() const
+ \since 5.14
+
+ Returns the left border width of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setRightBorder(qreal width)
+ \since 5.14
+
+ Sets the right border \a width of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn qreal QTextTableCellFormat::rightBorder() const
+ \since 5.14
+
+ Returns the right border width of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBorder(qreal width)
+ \since 5.14
+
+ Sets the left, right, top, and bottom border \a width of the table cell.
+
+ \sa setLeftBorder(), setRightBorder(), setTopBorder(), setBottomBorder()
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setTopBorderStyle(QTextFrameFormat::BorderStyle style)
+ \since 5.14
+
+ Sets the top border \a style of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QTextFrameFormat::BorderStyle QTextTableCellFormat::topBorderStyle() const
+ \since 5.14
+
+ Returns the top border style of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBottomBorderStyle(QTextFrameFormat::BorderStyle style)
+ \since 5.14
+
+ Sets the bottom border \a style of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QTextFrameFormat::BorderStyle QTextTableCellFormat::bottomBorderStyle() const
+ \since 5.14
+
+ Returns the bottom border style of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setLeftBorderStyle(QTextFrameFormat::BorderStyle style)
+ \since 5.14
+
+ Sets the left border \a style of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QTextFrameFormat::BorderStyle QTextTableCellFormat::leftBorderStyle() const
+ \since 5.14
+
+ Returns the left border style of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setRightBorderStyle(QTextFrameFormat::BorderStyle style)
+ \since 5.14
+
+ Sets the right border \a style of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QTextFrameFormat::BorderStyle QTextTableCellFormat::rightBorderStyle() const
+ \since 5.14
+
+ Returns the right border style of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBorderStyle(QTextFrameFormat::BorderStyle style)
+ \since 5.14
+
+ Sets the left, right, top, and bottom border \a style of the table cell.
+
+ \sa setLeftBorderStyle(), setRightBorderStyle(), setTopBorderStyle(), setBottomBorderStyle()
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setTopBorderBrush(const QBrush &brush)
+ \since 5.14
+
+ Sets the top border \a brush of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QBrush QTextTableCellFormat::topBorderBrush() const
+ \since 5.14
+
+ Returns the top border brush of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBottomBorderBrush(const QBrush &brush)
+ \since 5.14
+
+ Sets the bottom border \a brush of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QBrush QTextTableCellFormat::bottomBorderBrush() const
+ \since 5.14
+
+ Returns the bottom border brush of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setLeftBorderBrush(const QBrush &brush)
+ \since 5.14
+
+ Sets the left border \a brush of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QBrush QTextTableCellFormat::leftBorderBrush() const
+ \since 5.14
+
+ Returns the left border brush of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setRightBorderBrush(const QBrush &brush)
+ \since 5.14
+
+ Sets the right border \a brush of the table cell.
+
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
+ \fn QBrush QTextTableCellFormat::rightBorderBrush() const
+ \since 5.14
+
+ Returns the right border brush of the table cell.
+*/
+
+/*!
+ \fn void QTextTableCellFormat::setBorderBrush(const QBrush &brush)
+ \since 5.14
+
+ Sets the left, right, top, and bottom border \a brush of the table cell.
+
+ \sa setLeftBorderBrush(), setRightBorderBrush(), setTopBorderBrush(), setBottomBorderBrush()
+ \sa QTextTableFormat::setBorderCollapse
+*/
+
+/*!
\fn bool QTextTableCellFormat::isValid() const
\since 4.4
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index a91461dcae..12f14a1555 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.h
@@ -242,6 +242,7 @@ public:
TableCellSpacing = 0x4102,
TableCellPadding = 0x4103,
TableHeaderRowCount = 0x4104,
+ TableBorderCollapse = 0x4105,
// table cell properties
TableCellRowSpan = 0x4810,
@@ -252,6 +253,21 @@ public:
TableCellLeftPadding = 0x4814,
TableCellRightPadding = 0x4815,
+ TableCellTopBorder = 0x4816,
+ TableCellBottomBorder = 0x4817,
+ TableCellLeftBorder = 0x4818,
+ TableCellRightBorder = 0x4819,
+
+ TableCellTopBorderStyle = 0x481a,
+ TableCellBottomBorderStyle = 0x481b,
+ TableCellLeftBorderStyle = 0x481c,
+ TableCellRightBorderStyle = 0x481d,
+
+ TableCellTopBorderBrush = 0x481e,
+ TableCellBottomBorderBrush = 0x481f,
+ TableCellLeftBorderBrush = 0x4820,
+ TableCellRightBorderBrush = 0x4821,
+
// image properties
ImageName = 0x5000,
ImageTitle = 0x5001,
@@ -966,6 +982,11 @@ public:
inline int headerRowCount() const
{ return intProperty(TableHeaderRowCount); }
+ inline void setBorderCollapse(bool borderCollapse)
+ { setProperty(TableBorderCollapse, borderCollapse); }
+ inline bool borderCollapse() const
+ { return boolProperty(TableBorderCollapse); }
+
protected:
explicit QTextTableFormat(const QTextFormat &fmt);
friend class QTextFormat;
@@ -1007,6 +1028,72 @@ public:
inline void setPadding(qreal padding);
+ inline void setTopBorder(qreal width)
+ { setProperty(TableCellTopBorder, width); }
+ inline qreal topBorder() const
+ { return doubleProperty(TableCellTopBorder); }
+
+ inline void setBottomBorder(qreal width)
+ { setProperty(TableCellBottomBorder, width); }
+ inline qreal bottomBorder() const
+ { return doubleProperty(TableCellBottomBorder); }
+
+ inline void setLeftBorder(qreal width)
+ { setProperty(TableCellLeftBorder, width); }
+ inline qreal leftBorder() const
+ { return doubleProperty(TableCellLeftBorder); }
+
+ inline void setRightBorder(qreal width)
+ { setProperty(TableCellRightBorder, width); }
+ inline qreal rightBorder() const
+ { return doubleProperty(TableCellRightBorder); }
+
+ inline void setBorder(qreal width);
+
+ inline void setTopBorderStyle(QTextFrameFormat::BorderStyle style)
+ { setProperty(TableCellTopBorderStyle, style); }
+ inline QTextFrameFormat::BorderStyle topBorderStyle() const
+ { return static_cast<QTextFrameFormat::BorderStyle>(intProperty(TableCellTopBorderStyle)); }
+
+ inline void setBottomBorderStyle(QTextFrameFormat::BorderStyle style)
+ { setProperty(TableCellBottomBorderStyle, style); }
+ inline QTextFrameFormat::BorderStyle bottomBorderStyle() const
+ { return static_cast<QTextFrameFormat::BorderStyle>(intProperty(TableCellBottomBorderStyle)); }
+
+ inline void setLeftBorderStyle(QTextFrameFormat::BorderStyle style)
+ { setProperty(TableCellLeftBorderStyle, style); }
+ inline QTextFrameFormat::BorderStyle leftBorderStyle() const
+ { return static_cast<QTextFrameFormat::BorderStyle>(intProperty(TableCellLeftBorderStyle)); }
+
+ inline void setRightBorderStyle(QTextFrameFormat::BorderStyle style)
+ { setProperty(TableCellRightBorderStyle, style); }
+ inline QTextFrameFormat::BorderStyle rightBorderStyle() const
+ { return static_cast<QTextFrameFormat::BorderStyle>(intProperty(TableCellRightBorderStyle)); }
+
+ inline void setBorderStyle(QTextFrameFormat::BorderStyle style);
+
+ inline void setTopBorderBrush(const QBrush &brush)
+ { setProperty(TableCellTopBorderBrush, brush); }
+ inline QBrush topBorderBrush() const
+ { return brushProperty(TableCellTopBorderBrush); }
+
+ inline void setBottomBorderBrush(const QBrush &brush)
+ { setProperty(TableCellBottomBorderBrush, brush); }
+ inline QBrush bottomBorderBrush() const
+ { return brushProperty(TableCellBottomBorderBrush); }
+
+ inline void setLeftBorderBrush(const QBrush &brush)
+ { setProperty(TableCellLeftBorderBrush, brush); }
+ inline QBrush leftBorderBrush() const
+ { return brushProperty(TableCellLeftBorderBrush); }
+
+ inline void setRightBorderBrush(const QBrush &brush)
+ { setProperty(TableCellRightBorderBrush, brush); }
+ inline QBrush rightBorderBrush() const
+ { return brushProperty(TableCellRightBorderBrush); }
+
+ inline void setBorderBrush(const QBrush &brush);
+
protected:
explicit QTextTableCellFormat(const QTextFormat &fmt);
friend class QTextFormat;
@@ -1062,6 +1149,29 @@ inline void QTextTableCellFormat::setPadding(qreal padding)
setRightPadding(padding);
}
+inline void QTextTableCellFormat::setBorder(qreal width)
+{
+ setTopBorder(width);
+ setBottomBorder(width);
+ setLeftBorder(width);
+ setRightBorder(width);
+}
+
+inline void QTextTableCellFormat::setBorderStyle(QTextFrameFormat::BorderStyle style)
+{
+ setTopBorderStyle(style);
+ setBottomBorderStyle(style);
+ setLeftBorderStyle(style);
+ setRightBorderStyle(style);
+}
+
+inline void QTextTableCellFormat::setBorderBrush(const QBrush &brush)
+{
+ setTopBorderBrush(brush);
+ setBottomBorderBrush(brush);
+ setLeftBorderBrush(brush);
+ setRightBorderBrush(brush);
+}
QT_END_NAMESPACE
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index 1ee317d27c..5d37982a8b 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -491,12 +491,19 @@ QTextHtmlParserNode::QTextHtmlParserNode()
listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0),
borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset),
+ borderCollapse(false),
userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined)
{
margin[QTextHtmlParser::MarginLeft] = 0;
margin[QTextHtmlParser::MarginRight] = 0;
margin[QTextHtmlParser::MarginTop] = 0;
margin[QTextHtmlParser::MarginBottom] = 0;
+
+ for (int i = 0; i < 4; ++i) {
+ tableCellBorderStyle[i] = QTextFrameFormat::BorderStyle_None;
+ tableCellBorder[i] = 0;
+ tableCellBorderBrush[i] = Qt::NoBrush;
+ }
}
void QTextHtmlParser::dumpHtml()
@@ -649,7 +656,7 @@ void QTextHtmlParser::parseTag()
parseExclamationTag();
if (nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePre
&& nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePreWrap
- && !textEditMode)
+ && !textEditMode)
eatSpace();
return;
}
@@ -717,7 +724,8 @@ void QTextHtmlParser::parseTag()
// in a white-space preserving environment strip off a initial newline
// since the element itself already generates a newline
if ((node->wsm == QTextHtmlParserNode::WhiteSpacePre
- || node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ || node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap
+ || node->wsm == QTextHtmlParserNode::WhiteSpacePreLine)
&& node->isBlock()) {
if (pos < len - 1 && txt.at(pos) == QLatin1Char('\n'))
++pos;
@@ -761,7 +769,8 @@ void QTextHtmlParser::parseCloseTag()
// in a new block for elements following the <pre>
// ...foo\n</pre><p>blah -> foo</pre><p>blah
if ((at(p).wsm == QTextHtmlParserNode::WhiteSpacePre
- || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
+ || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap
+ || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreLine)
&& at(p).isBlock()) {
if (at(last()).text.endsWith(QLatin1Char('\n')))
nodes[last()].text.chop(1);
@@ -1167,6 +1176,25 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration>
QCss::ValueExtractor extractor(declarations);
extractor.extractBox(margin, padding);
+ if (id == Html_td || id == Html_th) {
+ QCss::BorderStyle cssStyles[4];
+ int cssBorder[4];
+ QSize cssRadii[4]; // unused
+ for (int i = 0; i < 4; ++i) {
+ cssStyles[i] = QCss::BorderStyle_None;
+ cssBorder[i] = 0;
+ }
+ // this will parse (and cache) "border-width" as a list so the
+ // QCss::BorderWidth parsing below which expects a single value
+ // will not work as expected - which in this case does not matter
+ // because tableBorder is not relevant for cells.
+ extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii);
+ for (int i = 0; i < 4; ++i) {
+ tableCellBorderStyle[i] = static_cast<QTextFrameFormat::BorderStyle>(cssStyles[i] - 1);
+ tableCellBorder[i] = static_cast<qreal>(cssBorder[i]);
+ }
+ }
+
for (int i = 0; i < declarations.count(); ++i) {
const QCss::Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty()) continue;
@@ -1184,6 +1212,9 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration>
case QCss::BorderWidth:
tableBorder = extractor.lengthValue(decl);
break;
+ case QCss::BorderCollapse:
+ borderCollapse = decl.borderCollapseValue();
+ break;
case QCss::Color: charFormat.setForeground(decl.colorValue()); break;
case QCss::Float:
cssFloat = QTextFrameFormat::InFlow;
@@ -1278,6 +1309,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration>
case QCss::Value_Pre: wsm = QTextHtmlParserNode::WhiteSpacePre; break;
case QCss::Value_NoWrap: wsm = QTextHtmlParserNode::WhiteSpaceNoWrap; break;
case QCss::Value_PreWrap: wsm = QTextHtmlParserNode::WhiteSpacePreWrap; break;
+ case QCss::Value_PreLine: wsm = QTextHtmlParserNode::WhiteSpacePreLine; break;
default: break;
}
break;
@@ -1651,6 +1683,11 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
if (!c.isValid())
qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
node->charFormat.setBackground(c);
+ } else if (key == QLatin1String("bordercolor")) {
+ QColor c; c.setNamedColor(value);
+ if (!c.isValid())
+ qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
+ node->borderBrush = c;
} else if (key == QLatin1String("background")) {
node->applyBackgroundImage(value, resourceProvider);
} else if (key == QLatin1String("cellspacing")) {
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
index c174b54a61..31f558709f 100644
--- a/src/gui/text/qtexthtmlparser_p.h
+++ b/src/gui/text/qtexthtmlparser_p.h
@@ -158,6 +158,7 @@ struct QTextHtmlParserNode {
WhiteSpacePre,
WhiteSpaceNoWrap,
WhiteSpacePreWrap,
+ WhiteSpacePreLine,
WhiteSpaceModeUndefined = -1
};
@@ -194,8 +195,12 @@ struct QTextHtmlParserNode {
int tableCellColSpan;
qreal tableCellSpacing;
qreal tableCellPadding;
+ qreal tableCellBorder[4];
+ QBrush tableCellBorderBrush[4];
+ QTextFrameFormat::BorderStyle tableCellBorderStyle[4];
QBrush borderBrush;
QTextFrameFormat::BorderStyle borderStyle;
+ bool borderCollapse;
int userState;
int cssListIndent;
@@ -289,6 +294,10 @@ public:
inline int leftPadding(int i) const { return at(i).padding[MarginLeft]; }
inline int rightPadding(int i) const { return at(i).padding[MarginRight]; }
+ inline qreal tableCellBorder(int i, int edge) const { return at(i).tableCellBorder[edge]; }
+ inline QTextFrameFormat::BorderStyle tableCellBorderStyle(int i, int edge) const { return at(i).tableCellBorderStyle[edge]; }
+ inline QBrush tableCellBorderBrush(int i, int edge) const { return at(i).tableCellBorderBrush[edge]; }
+
void dumpHtml();
void parse(const QString &text, const QTextDocument *resourceProvider);
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index a29791534e..2fc0fcd55a 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -114,8 +114,8 @@ public:
// not ambiguous. Implementation detail that should not be documented.
template<char = 0>
#endif
- QTextLayout(const QString &text, const QFont &font, const QPaintDevice *paintdevice)
- : QTextLayout(text, font, const_cast<QPaintDevice*>(paintdevice))
+ QTextLayout(const QString &textData, const QFont &textFont, const QPaintDevice *paintdevice)
+ : QTextLayout(textData, textFont, const_cast<QPaintDevice*>(paintdevice))
{}
#else
QTextLayout(const QString &text, const QFont &font, const QPaintDevice *paintdevice = nullptr);