diff --git a/3rdparty/ext_qt/disable-wintab.diff b/3rdparty/ext_qt/0001-disable-wintab.patch rename from 3rdparty/ext_qt/disable-wintab.diff rename to 3rdparty/ext_qt/0001-disable-wintab.patch --- a/3rdparty/ext_qt/disable-wintab.diff +++ b/3rdparty/ext_qt/0001-disable-wintab.patch @@ -1,8 +1,18 @@ -diff --git a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -index e6e6ee8b1a..89143b6d3f 100644 ---- a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -+++ b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -@@ -240,9 +240,6 @@ struct QWindowsContextPrivate { +From 16a528dfc3ef30fee213026391c923d4d19fd4a0 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 11 Feb 2019 17:51:39 +0300 +Subject: [PATCH 1/4] disable wintab + +Change-Id: I10095ac8c486fcccb97f4a8d7036441eea6a1bc0 +--- + .../platforms/windows/qwindowscontext.cpp | 24 ------------------- + 1 file changed, 24 deletions(-) + +diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp +index 1f80ac5872..ded8130398 100644 +--- a/src/plugins/platforms/windows/qwindowscontext.cpp ++++ b/src/plugins/platforms/windows/qwindowscontext.cpp +@@ -265,9 +265,6 @@ struct QWindowsContextPrivate { QWindowsMimeConverter m_mimeConverter; QWindowsScreenManager m_screenManager; QSharedPointer m_creationContext; @@ -10,17 +20,9 @@ - QScopedPointer m_tabletSupport; -#endif const HRESULT m_oleInitializeResult; - const QByteArray m_eventType; QWindow *m_lastActiveWindow = nullptr; -@@ -281,17 +278,10 @@ QWindowsContext::QWindowsContext() : - const QByteArray bv = qgetenv("QT_QPA_VERBOSE"); - if (!bv.isEmpty()) - QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv)); --#if QT_CONFIG(tabletevent) -- d->m_tabletSupport.reset(QWindowsTabletSupport::create()); -- qCDebug(lcQpaTablet) << "Tablet support: " << (d->m_tabletSupport.isNull() ? QStringLiteral("None") : d->m_tabletSupport->description()); --#endif - } + bool m_asyncExpose = false; +@@ -308,9 +305,6 @@ QWindowsContext::QWindowsContext() : QWindowsContext::~QWindowsContext() { @@ -30,7 +32,20 @@ unregisterWindowClasses(); if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) OleUninitialize(); -@@ -337,12 +327,7 @@ bool QWindowsContext::initTouch(unsigned integrationOptions) +@@ -357,12 +351,7 @@ bool QWindowsContext::initTouch(unsigned integrationOptions) + bool QWindowsContext::initTablet(unsigned integrationOptions) + { + Q_UNUSED(integrationOptions); +-#if QT_CONFIG(tabletevent) +- d->m_tabletSupport.reset(QWindowsTabletSupport::create()); +- return true; +-#else + return false; +-#endif + } + + bool QWindowsContext::initPointer(unsigned integrationOptions) +@@ -383,12 +372,7 @@ bool QWindowsContext::initPointer(unsigned integrationOptions) void QWindowsContext::setTabletAbsoluteRange(int a) { @@ -42,8 +57,8 @@ -#endif } - int QWindowsContext::processDpiAwareness() -@@ -702,11 +687,7 @@ QWindowsScreenManager &QWindowsContext::screenManager() + void QWindowsContext::setDetectAltGrModifier(bool a) +@@ -799,11 +783,7 @@ QWindowsScreenManager &QWindowsContext::screenManager() QWindowsTabletSupport *QWindowsContext::tabletSupport() const { @@ -55,7 +70,7 @@ } /*! -@@ -1112,10 +1093,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, +@@ -1288,10 +1268,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, *result = LRESULT(MA_NOACTIVATE); return true; } @@ -66,3 +81,6 @@ if (platformWindow->testFlag(QWindowsWindow::BlockedByModal)) if (const QWindow *modalWindow = QGuiApplication::modalWindow()) { QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow); +-- +2.20.1.windows.1 + diff --git a/3rdparty/ext_qt/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch b/3rdparty/ext_qt/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch rename from 3rdparty/ext_qt/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch rename to 3rdparty/ext_qt/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch --- a/3rdparty/ext_qt/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch +++ b/3rdparty/ext_qt/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch @@ -1,19 +1,19 @@ -From 674cfce97c4972176d2bcb55d7013c0ac2190029 Mon Sep 17 00:00:00 2001 +From 9081571e58ceeaa4c623c83ecb41b04dfc7d90f9 Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Tue, 21 Jun 2016 14:50:07 +0300 -Subject: [PATCH 1/2] Don't request the MIME image every time Windows asks for +Subject: [PATCH 2/4] Don't request the MIME image every time Windows asks for the list of supported types Change-Id: I05516d83dc4e0f192bc94f92cefc722f25dae4d4 --- - qtbase/src/plugins/platforms/windows/qwindowsmime.cpp | 9 ++++++--- + src/plugins/platforms/windows/qwindowsmime.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) -diff --git a/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp b/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -index a8264b5..90d646b 100644 ---- a/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -+++ b/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -@@ -1081,12 +1081,15 @@ bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *p +diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp +index ff0dccb0d9..a702a2a90f 100644 +--- a/src/plugins/platforms/windows/qwindowsmime.cpp ++++ b/src/plugins/platforms/windows/qwindowsmime.cpp +@@ -1082,12 +1082,15 @@ bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *p bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { int cf = getCf(formatetc); @@ -33,5 +33,5 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const -- -2.6.4 +2.20.1.windows.1 diff --git a/3rdparty/ext_qt/0002-Hack-always-return-we-support-DIBV5.patch b/3rdparty/ext_qt/0003-Hack-always-return-we-support-DIBV5.patch rename from 3rdparty/ext_qt/0002-Hack-always-return-we-support-DIBV5.patch rename to 3rdparty/ext_qt/0003-Hack-always-return-we-support-DIBV5.patch --- a/3rdparty/ext_qt/0002-Hack-always-return-we-support-DIBV5.patch +++ b/3rdparty/ext_qt/0003-Hack-always-return-we-support-DIBV5.patch @@ -1,20 +1,20 @@ -From 5e5026beb420018266d3d00fdb530bb714a841a6 Mon Sep 17 00:00:00 2001 +From 6ce23924948d9697e07681bef44f0a47f9fd0d09 Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Tue, 21 Jun 2016 14:50:47 +0300 -Subject: [PATCH 2/2] Hack: always return we support DIBV5 +Subject: [PATCH 3/4] Hack: always return we support DIBV5 Asking for the entire image may be too expensive Change-Id: I44c38fad73f1bb5859eb58b941054eeb6c3c6b66 --- - qtbase/src/plugins/platforms/windows/qwindowsmime.cpp | 4 +--- + src/plugins/platforms/windows/qwindowsmime.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) -diff --git a/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp b/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -index 90d646b..ef97db7 100644 ---- a/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -+++ b/qtbase/src/plugins/platforms/windows/qwindowsmime.cpp -@@ -1052,9 +1052,7 @@ QVector QWindowsMimeImage::formatsForMime(const QString &mimeType, co +diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp +index a702a2a90f..03f8948a86 100644 +--- a/src/plugins/platforms/windows/qwindowsmime.cpp ++++ b/src/plugins/platforms/windows/qwindowsmime.cpp +@@ -1055,9 +1055,7 @@ QVector QWindowsMimeImage::formatsForMime(const QString &mimeType, co QVector formatetcs; if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) { //add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656). @@ -26,5 +26,5 @@ } if (!formatetcs.isEmpty()) -- -2.6.4 +2.20.1.windows.1 diff --git a/3rdparty/ext_qt/0003-Hack-for-fullscreen-workaround.patch b/3rdparty/ext_qt/0003-Hack-for-fullscreen-workaround.patch deleted file mode 100644 --- a/3rdparty/ext_qt/0003-Hack-for-fullscreen-workaround.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 84a774e00e9d2535fdb8c798d7789130a9a008f6 Mon Sep 17 00:00:00 2001 -From: Michael Abrahams -Date: Wed, 22 Jun 2016 13:37:06 -0400 -Subject: [PATCH 3/4] Hack for fullscreen workaround - -https://bugreports.qt.io/browse/QTBUG-41309 ---- - qtbase/src/plugins/platforms/windows/qwindowswindow.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp -index 9c6cb53..d0829e3 100644 ---- a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp -+++ b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp -@@ -1769,7 +1769,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) - const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; - const bool wasSync = testFlag(SynchronousGeometryChangeEvent); - setFlag(SynchronousGeometryChangeEvent); -- SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf); -+ SetWindowPos(m_data.hwnd, HWND_TOP, r.left()-1, r.top()-1, r.width()+2, r.height()+2, swpf); - if (!wasSync) - clearFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleGeometryChange(window(), r); --- -2.7.4.windows.1 - diff --git a/3rdparty/ext_qt/0004-Fix-debug-on-openGL-ES.patch b/3rdparty/ext_qt/0004-Fix-debug-on-openGL-ES.patch new file mode 100644 --- /dev/null +++ b/3rdparty/ext_qt/0004-Fix-debug-on-openGL-ES.patch @@ -0,0 +1,26 @@ +From 4c1e4e693307c3169c6db488ad6bf6cff6aae864 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 11 Feb 2019 18:07:20 +0300 +Subject: [PATCH 4/4] Fix debug on openGL ES + +Change-Id: I08d1adf87b305c380a0f2177edf4ff9de109e4d6 +--- + src/gui/opengl/qopengldebug.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/gui/opengl/qopengldebug.cpp b/src/gui/opengl/qopengldebug.cpp +index 2e628a2bd5..9f1bb76869 100644 +--- a/src/gui/opengl/qopengldebug.cpp ++++ b/src/gui/opengl/qopengldebug.cpp +@@ -1366,7 +1366,7 @@ bool QOpenGLDebugLogger::initialize() + + #define GET_DEBUG_PROC_ADDRESS(procName) \ + d->procName = reinterpret_cast< qt_ ## procName ## _t >( \ +- d->context->getProcAddress(#procName) \ ++ d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \ + ); + + GET_DEBUG_PROC_ADDRESS(glDebugMessageControl); +-- +2.20.1.windows.1 + diff --git a/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch b/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch new file mode 100644 --- /dev/null +++ b/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch @@ -0,0 +1,4574 @@ +From 94a1fa7324a4c4b65f28426755428cd4ded01cfb Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Sat, 8 Dec 2018 15:35:43 +0300 +Subject: [PATCH 05/20] Unconditionally enable D3D11_1 + +I don't know why we apply the patch that disables it on MinGW. Perhaps +we should abandone it. + +# Conflicts: +# src/3rdparty/angle/src/common/platform.h +--- + src/3rdparty/angle/src/common/platform.h | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h +index fb251da579..89359f954e 100644 +--- a/src/3rdparty/angle/src/common/platform.h ++++ b/src/3rdparty/angle/src/common/platform.h +@@ -59,12 +59,22 @@ + # endif + + # if defined(ANGLE_ENABLE_D3D11) +-#include +-#include +-#include +-#include +-#include +-#include ++# include ++# include ++# include ++# if defined(__MINGW32__) && !defined(__d3d11sdklayers_h__) ++# define ANGLE_MINGW32_COMPAT ++# endif ++//# if defined(_MSC_VER) && _MSC_VER >= 1800 ++# define ANGLE_ENABLE_D3D11_1 ++//# endif ++# if defined(ANGLE_ENABLE_D3D11_1) ++# include ++# include ++# include ++# include // TODO: This is actually D3D12!!! ++# endif ++# include + # endif + + #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) +-- +2.20.1.windows.1 + + +From 3a3352f55fbfa2e45da41a5fb89ea9d66064409d Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Sat, 8 Dec 2018 18:03:58 +0300 +Subject: [PATCH 06/20] Implement proper color management selection and + activation + +1) D3D11 implementation of angle now supports GL_RGB10_A2 format + +2) Technically, Angle's EGL implementation now supports the following + display extensions: + * EGL_KHR_gl_colorspace + * EGL_EXT_gl_colorspace_scrgb_linear + * EGL_EXT_gl_colorspace_bt2020_pq + +3) D3D11 implementation of angle now supports GL_COLOR_SPACE attribute, + which allows selection one of four color modes: + * Linear --- just pass-through data to GPU + * sRGB --- p709-g22 color space. WARNING: in 8-bit mode the system + becomes clever and automatically converts linear framebuffer + attachments to sRGB space, as per EGL_KHR_gl_colorspace definition. + It is not possible to select sRGB without this extra "feature". + * scRGB --- p709-g10 color space. This mode is the only mode + supported in f16-bit mode (and it is also not supported in other + bit depths). + * bt2020-pq --- p2020-pq color space. Supported only in 10-bit mode. + +4) QSurfaceFormat now supports setting color spaces from the list above + +5) SwapChain is now created in DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL mode: + * because non-flip mode is considered deprecated and HDR is not + supported in it; + * because in flip-discard mode partial updates from + SwapChain11::present() are not supported and return an error, + which is never checked :) + +# Conflicts: +# src/3rdparty/angle/src/libANGLE/Caps.cpp +# src/3rdparty/angle/src/libANGLE/Caps.h +# src/3rdparty/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h +# src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +# src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +# src/gui/opengl/qopenglframebufferobject.cpp +--- + src/3rdparty/angle/src/libANGLE/Caps.cpp | 8 +- + src/3rdparty/angle/src/libANGLE/Caps.h | 9 ++ + .../src/libANGLE/renderer/d3d/RendererD3D.h | 3 +- + .../src/libANGLE/renderer/d3d/SurfaceD3D.cpp | 26 +++++- + .../src/libANGLE/renderer/d3d/SurfaceD3D.h | 1 + + .../renderer/d3d/d3d11/Renderer11.cpp | 18 +++- + .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 4 +- + .../renderer/d3d/d3d11/SwapChain11.cpp | 90 ++++++++++++++++++- + .../libANGLE/renderer/d3d/d3d11/SwapChain11.h | 4 +- + .../d3d/d3d11/win32/NativeWindow11Win32.cpp | 18 +++- + .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 4 +- + .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 3 +- + .../angle/src/libANGLE/validationEGL.cpp | 26 ++++++ + src/gui/kernel/qsurfaceformat.cpp | 11 +++ + src/gui/kernel/qsurfaceformat.h | 4 +- + src/gui/opengl/qopenglframebufferobject.cpp | 7 +- + .../platforms/windows/qwindowseglcontext.cpp | 33 ++++++- + .../platforms/windows/qwindowseglcontext.h | 2 +- + .../platforms/windows/qwindowsopenglcontext.h | 2 +- + .../platforms/windows/qwindowswindow.cpp | 8 +- + 20 files changed, 256 insertions(+), 25 deletions(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/Caps.cpp b/src/3rdparty/angle/src/libANGLE/Caps.cpp +index 44da2bbe27..2088457458 100644 +--- a/src/3rdparty/angle/src/libANGLE/Caps.cpp ++++ b/src/3rdparty/angle/src/libANGLE/Caps.cpp +@@ -1101,7 +1101,10 @@ DisplayExtensions::DisplayExtensions() + displayTextureShareGroup(false), + createContextClientArrays(false), + programCacheControl(false), +- robustResourceInitialization(false) ++ robustResourceInitialization(false), ++ colorspaceSRGB(false), ++ colorspaceSCRGBLinear(false), ++ colorspaceBt2020PQ(false) + { + } + +@@ -1146,6 +1149,9 @@ std::vector DisplayExtensions::getStrings() const + InsertExtensionString("EGL_ANGLE_create_context_client_arrays", createContextClientArrays, &extensionStrings); + InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings); + InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings); ++ InsertExtensionString("EGL_KHR_gl_colorspace", colorspaceSRGB, &extensionStrings); ++ InsertExtensionString("EGL_EXT_gl_colorspace_scrgb_linear", colorspaceSCRGBLinear, &extensionStrings); ++ InsertExtensionString("EGL_EXT_gl_colorspace_bt2020_pq", colorspaceBt2020PQ, &extensionStrings); + // TODO(jmadill): Enable this when complete. + //InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings); + // clang-format on +diff --git a/src/3rdparty/angle/src/libANGLE/Caps.h b/src/3rdparty/angle/src/libANGLE/Caps.h +index 64bdf97112..8157af5800 100644 +--- a/src/3rdparty/angle/src/libANGLE/Caps.h ++++ b/src/3rdparty/angle/src/libANGLE/Caps.h +@@ -692,6 +692,15 @@ struct DisplayExtensions + + // EGL_ANGLE_robust_resource_initialization + bool robustResourceInitialization; ++ ++ // EGL_KHR_gl_colorspace ++ bool colorspaceSRGB; ++ ++ // EGL_EXT_gl_colorspace_scrgb_linear ++ bool colorspaceSCRGBLinear; ++ ++ // EGL_EXT_gl_colorspace_bt2020_pq ++ bool colorspaceBt2020PQ; + }; + + struct DeviceExtensions +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h +index dcc98f2ec6..b8ee635625 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h +@@ -130,7 +130,8 @@ class RendererD3D : public BufferFactoryD3D, public MultisampleTextureInitialize + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) = 0; ++ EGLint samples, ++ EGLint colorSpace) = 0; + virtual egl::Error getD3DTextureInfo(const egl::Config *configuration, + IUnknown *d3dTexture, + EGLint *width, +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +index 7657aef79e..efd4dd1a24 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +@@ -22,6 +22,27 @@ + namespace rx + { + ++GLenum renderTargetFormatFromColorSpace(egl::Display *display, GLenum baseFormat, EGLint colorSpace) ++{ ++ GLenum result = baseFormat; ++ ++ /** ++ * If sRGB extension is supported, we should change the surface format ++ * to a specific one that does support automated gamma conversion. ++ * ++ * TODO: openGL doesn't support BGRA-sRGB texture format, so creation of ++ * textures in this format technically is not supported! ++ */ ++ if (display->getExtensions().colorspaceSRGB && ++ baseFormat == GL_RGBA8_OES && ++ colorSpace == EGL_GL_COLORSPACE_SRGB_KHR) ++ { ++ result = GL_SRGB8_ALPHA8; ++ } ++ ++ return result; ++} ++ + SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, + RendererD3D *renderer, + egl::Display *display, +@@ -34,7 +55,8 @@ SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, + mDisplay(display), + mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE), + mOrientation(static_cast(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))), +- mRenderTargetFormat(state.config->renderTargetFormat), ++ mColorSpace(static_cast(attribs.get(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_LINEAR_KHR))), ++ mRenderTargetFormat(renderTargetFormatFromColorSpace(display, state.config->renderTargetFormat, mColorSpace)), + mDepthStencilFormat(state.config->depthStencilFormat), + mSwapChain(nullptr), + mSwapIntervalDirty(true), +@@ -148,7 +170,7 @@ egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display) + + mSwapChain = + mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat, +- mDepthStencilFormat, mOrientation, mState.config->samples); ++ mDepthStencilFormat, mOrientation, mState.config->samples, mColorSpace); + if (!mSwapChain) + { + return egl::EglBadAlloc(); +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +index 01d2573244..ccb793d423 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +@@ -90,6 +90,7 @@ class SurfaceD3D : public SurfaceImpl + + bool mFixedSize; + GLint mOrientation; ++ EGLint mColorSpace; + + GLenum mRenderTargetFormat; + GLenum mDepthStencilFormat; +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +index b0ef9abddc..ac46690090 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +@@ -465,6 +465,7 @@ Renderer11::Renderer11(egl::Display *display) + mRenderer11DeviceCaps.supportsConstantBufferOffsets = false; + mRenderer11DeviceCaps.supportsVpRtIndexWriteFromVertexShader = false; + mRenderer11DeviceCaps.supportsDXGI1_2 = false; ++ mRenderer11DeviceCaps.supportsDXGI1_4 = false; + mRenderer11DeviceCaps.B5G6R5support = 0; + mRenderer11DeviceCaps.B4G4R4A4support = 0; + mRenderer11DeviceCaps.B5G5R5A1support = 0; +@@ -918,6 +919,7 @@ egl::Error Renderer11::initializeDevice() + + // Gather stats on DXGI and D3D feature level + ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_2", mRenderer11DeviceCaps.supportsDXGI1_2); ++ ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_4", mRenderer11DeviceCaps.supportsDXGI1_4); + + ANGLEFeatureLevel angleFeatureLevel = GetANGLEFeatureLevel(mRenderer11DeviceCaps.featureLevel); + +@@ -999,9 +1001,15 @@ void Renderer11::populateRenderer11DeviceCaps() + &mRenderer11DeviceCaps.B5G5R5A1support, + &mRenderer11DeviceCaps.B5G5R5A1maxSamples); + ++//#if defined(ANGLE_ENABLE_D3D11_1) + IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject(mDxgiAdapter); + mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); + SafeRelease(dxgiAdapter2); ++ ++ IDXGIAdapter3 *dxgiAdapter3 = d3d11::DynamicCastComObject(mDxgiAdapter); ++ mRenderer11DeviceCaps.supportsDXGI1_4 = (dxgiAdapter3 != nullptr); ++ SafeRelease(dxgiAdapter3); ++//#endif + } + + gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( +@@ -1241,6 +1249,11 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions + + // All D3D feature levels support robust resource init + outExtensions->robustResourceInitialization = true; ++ ++ // color space selection is always supported in DirectX11 ++ outExtensions->colorspaceSRGB = true; ++ outExtensions->colorspaceSCRGBLinear = true; ++ outExtensions->colorspaceBt2020PQ = true; + } + + gl::Error Renderer11::flush() +@@ -1436,10 +1449,11 @@ SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) ++ EGLint samples, ++ EGLint colorSpace) + { + return new SwapChain11(this, GetAs(nativeWindow), shareHandle, d3dTexture, +- backBufferFormat, depthBufferFormat, orientation, samples); ++ backBufferFormat, depthBufferFormat, orientation, samples, colorSpace); + } + + void *Renderer11::getD3DDevice() +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +index a8c24e681b..3516bf779d 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +@@ -49,6 +49,7 @@ struct Renderer11DeviceCaps + + D3D_FEATURE_LEVEL featureLevel; + bool supportsDXGI1_2; // Support for DXGI 1.2 ++ bool supportsDXGI1_4; // Support for DXGI 1.4 + bool supportsClearView; // Support for ID3D11DeviceContext1::ClearView + bool supportsConstantBufferOffsets; // Support for Constant buffer offset + bool supportsVpRtIndexWriteFromVertexShader; // VP/RT can be selected in the Vertex Shader +@@ -138,7 +139,8 @@ class Renderer11 : public RendererD3D + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) override; ++ EGLint samples, ++ EGLint colorSpace) override; + egl::Error getD3DTextureInfo(const egl::Config *configuration, + IUnknown *d3dTexture, + EGLint *width, +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +index dcfd06484d..8f72c5c9aa 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +@@ -18,6 +18,8 @@ + #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" + #include "third_party/trace_event/trace_event.h" + ++#include ++ + // Precompiled shaders + #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h" + #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h" +@@ -56,12 +58,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) ++ EGLint samples, ++ EGLint colorSpace) + : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat), + mRenderer(renderer), + mWidth(-1), + mHeight(-1), + mOrientation(orientation), ++ mColorSpace(colorSpace), + mAppCreatedShareHandle(mShareHandle != nullptr), + mSwapInterval(0), + mPassThroughResourcesInit(false), +@@ -620,10 +624,94 @@ EGLint SwapChain11::reset(const gl::Context *context, + mSwapChain1 = d3d11::DynamicCastComObject(mSwapChain); + } + ++ if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_4) ++ { ++#if defined(ANGLE_ENABLE_D3D11_1) ++ IDXGISwapChain3 *swapChain3 = d3d11::DynamicCastComObject(mSwapChain); ++ ++ printf("*** EGL colorSpace: 0x%X\n", mColorSpace); ++ printf("*** EGL format: 0x%X\n", mOffscreenRenderTargetFormat); ++ printf("*** Native format: 0x%X\n", getSwapChainNativeFormat()); ++ ++ if (mColorSpace != EGL_GL_COLORSPACE_LINEAR_KHR) { ++ DXGI_COLOR_SPACE_TYPE nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; ++ switch (mColorSpace) ++ { ++ case EGL_GL_COLORSPACE_SRGB_KHR: ++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; ++ break; ++ case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: ++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; ++ break; ++ case EGL_GL_COLORSPACE_BT2020_PQ_EXT: ++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; ++ break; ++ default: ++ ASSERT(0 && "Unsupported colorspace requested"); ++ } ++ ++ printf("*** Native colorSpace: 0x%X\n", nativeColorSpace); ++ ++ UINT supported = 0; ++ result = swapChain3->CheckColorSpaceSupport(nativeColorSpace, &supported); ++ ASSERT(SUCCEEDED(result)); ++ if (!(supported & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { ++ SafeRelease(swapChain3); ++ return EGL_BAD_MATCH; ++ } ++ ++ result = swapChain3->SetColorSpace1(nativeColorSpace); ++ ASSERT(SUCCEEDED(result)); ++ } ++ ++ SafeRelease(swapChain3); ++ ++#if 0 ++ ++ IDXGISwapChain4 *swapChain4 = d3d11::DynamicCastComObject(mSwapChain); ++ ++ DXGI_HDR_METADATA_HDR10 md; ++ md.RedPrimary[0] = 0.680 * 50000; ++ md.RedPrimary[1] = 0.320 * 50000; ++ md.GreenPrimary[0] = 0.265 * 50000; ++ md.GreenPrimary[1] = 0.690 * 50000; ++ md.BluePrimary[0] = 0.150 * 50000; ++ md.BluePrimary[1] = 0.060 * 50000; ++ md.WhitePoint[0] = 0.3127 * 50000; ++ md.WhitePoint[1] = 0.3290 * 50000; ++ md.MaxMasteringLuminance = 1000 * 10000; ++ md.MinMasteringLuminance = 0.001 * 10000; ++ md.MaxContentLightLevel = 1000; ++ md.MaxFrameAverageLightLevel = 200; ++ result = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(md), &md); ++ printf("*** Result hdr 0x%X\n", result); ++ SafeRelease(swapChain4); ++#endif ++#endif ++ } ++ + ID3D11Texture2D *backbufferTex = nullptr; + result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + reinterpret_cast(&backbufferTex)); + ASSERT(SUCCEEDED(result)); ++ ++ // TODO: recover rendering to sRGB ++ // ++ // D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc; ++ // offscreenRTVDesc.Format = getSwapChainNativeFormat(); ++ // ++ // if (mColorSpace == EGL_GL_COLORSPACE_SRGB_KHR) { ++ // if (offscreenRTVDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM) { ++ // offscreenRTVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; ++ // } ++ // ++ // if (offscreenRTVDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) { ++ // offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; ++ // } ++ // } ++ // ++ // printf("*** Render target format: 0x%X\n", offscreenRTVDesc.Format); ++ + const auto &format = + d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); + mBackBufferTexture.set(backbufferTex, format); +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +index eca068210b..2a4b9ba274 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +@@ -28,7 +28,8 @@ class SwapChain11 final : public SwapChainD3D + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples); ++ EGLint samples, ++ EGLint colorSpace); + ~SwapChain11() override; + + EGLint resize(const gl::Context *context, +@@ -93,6 +94,7 @@ class SwapChain11 final : public SwapChainD3D + EGLint mWidth; + EGLint mHeight; + const EGLint mOrientation; ++ EGLint mColorSpace; + bool mAppCreatedShareHandle; + unsigned int mSwapInterval; + bool mPassThroughResourcesInit; +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +index 5394e3d3e7..c81b33fee9 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +@@ -158,9 +158,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = + DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; +- swapChainDesc.BufferCount = 1; ++ swapChainDesc.BufferCount = 2; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; +- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; ++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapChainDesc.Flags = 0; + IDXGISwapChain1 *swapChain1 = nullptr; +@@ -176,7 +176,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + } + + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; +- swapChainDesc.BufferCount = 1; ++ swapChainDesc.BufferCount = 2; + swapChainDesc.BufferDesc.Format = format; + swapChainDesc.BufferDesc.Width = width; + swapChainDesc.BufferDesc.Height = height; +@@ -191,7 +191,17 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + swapChainDesc.SampleDesc.Count = samples; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Windowed = TRUE; +- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; ++ ++ /** ++ * NOTE1: in flip-discard mode the swap chain doesn't support partial ++ * presentatiopn with Present1() call. Though it is not a big ++ * problem, because in case DXGI 1.2 is supported this code is ++ * unreachable. ++ * ++ * NOTE2: in non-flip mode HDR rendering is not supported, so use it ++ * bt default ++ */ ++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + + HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); + if (SUCCEEDED(result)) +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +index 75c6298868..58596169a8 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +@@ -718,8 +718,10 @@ SwapChainD3D *Renderer9::createSwapChain(NativeWindowD3D *nativeWindow, + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) ++ EGLint samples, ++ EGLint colorSpace) + { ++ UNUSED_VARIABLE(colorSpace); + return new SwapChain9(this, GetAs(nativeWindow), shareHandle, d3dTexture, + backBufferFormat, depthBufferFormat, orientation); + } +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +index 9ddee45f0f..ce4bb201e5 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +@@ -92,7 +92,8 @@ class Renderer9 : public RendererD3D + GLenum backBufferFormat, + GLenum depthBufferFormat, + EGLint orientation, +- EGLint samples) override; ++ EGLint samples, ++ EGLint colorSpace) override; + egl::Error getD3DTextureInfo(const egl::Config *configuration, + IUnknown *d3dTexture, + EGLint *width, +diff --git a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +index 13a3a9e280..3f6a426320 100644 +--- a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++++ b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +@@ -885,6 +885,32 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin + "either EGL_TRUE or EGL_FALSE."; + } + break; ++ case EGL_GL_COLORSPACE: ++ ++ if (!displayExtensions.colorspaceSRGB) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_KHR_gl_colorspace is not supported on this platform."); ++ } ++ ++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) ++ { ++ if (!displayExtensions.colorspaceSCRGBLinear) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."); ++ } ++ } ++ else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) ++ { ++ if (!displayExtensions.colorspaceBt2020PQ) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."); ++ } ++ } ++ else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) ++ { ++ return Error(EGL_BAD_ATTRIBUTE); ++ } ++ break; + + default: + return EglBadAttribute(); +diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp +index 1a814ec21f..fc8b9c4f43 100644 +--- a/src/gui/kernel/qsurfaceformat.cpp ++++ b/src/gui/kernel/qsurfaceformat.cpp +@@ -221,6 +221,17 @@ public: + set, the window will be created with an sRGB-capable default + framebuffer. Note that some platforms may return windows with a sRGB-capable + default framebuffer even when not requested explicitly. ++ ++ \value scRGBColorSpace When \c{EGL_EXT_gl_colorspace_scrgb_linear} ++ is supported by the platform and this value is set, the window will ++ be created with an scRGB-capable default framebuffer. Note that some ++ platforms may return windows with a scRGB-capable default framebuffer ++ even when not requested explicitly. It usually happens when the application ++ requests 16-bit surface format. ++ ++ \value bt2020PQColorSpace When \c{EGL_EXT_gl_colorspace_bt2020_pq} ++ is supported by the platform and this value is set, the window will ++ be created with an bt2020 PQ default framebuffer. + */ + + /*! +diff --git a/src/gui/kernel/qsurfaceformat.h b/src/gui/kernel/qsurfaceformat.h +index ed63eb8bbf..9ba6a29b7a 100644 +--- a/src/gui/kernel/qsurfaceformat.h ++++ b/src/gui/kernel/qsurfaceformat.h +@@ -87,7 +87,9 @@ public: + + enum ColorSpace { + DefaultColorSpace, +- sRGBColorSpace ++ sRGBColorSpace, ++ scRGBColorSpace, ++ bt2020PQColorSpace + }; + Q_ENUM(ColorSpace) + +diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp +index cae3d516c4..ccdccb637a 100644 +--- a/src/gui/opengl/qopenglframebufferobject.cpp ++++ b/src/gui/opengl/qopenglframebufferobject.cpp +@@ -545,10 +545,13 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx) + ColorAttachment &color(colorAttachments[idx]); + + GLuint pixelType = GL_UNSIGNED_BYTE; +- if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) ++ if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; +- else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) ++ } else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) { + pixelType = GL_UNSIGNED_SHORT; ++ } else if (color.internalFormat == GL_RGBA16F) { ++ pixelType = GL_HALF_FLOAT; ++ } + + funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, + GL_RGBA, pixelType, NULL); +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp +index 52f3c56beb..18a55c9e1f 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp ++++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp +@@ -297,11 +297,36 @@ QWindowsOpenGLContext *QWindowsEGLStaticContext::createContext(QOpenGLContext *c + return new QWindowsEGLContext(this, context->format(), context->shareHandle()); + } + +-void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) ++void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, ++ QSurfaceFormat::ColorSpace colorSpace, int *err) + { + *err = 0; ++ ++ EGLint eglColorSpace = EGL_GL_COLORSPACE_LINEAR_KHR; ++ ++ switch (colorSpace) { ++ case QSurfaceFormat::DefaultColorSpace: ++ break; ++ case QSurfaceFormat::sRGBColorSpace: ++ eglColorSpace = EGL_GL_COLORSPACE_SRGB_KHR; ++ break; ++ case QSurfaceFormat::scRGBColorSpace: ++ eglColorSpace = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; ++ break; ++ case QSurfaceFormat::bt2020PQColorSpace: ++ eglColorSpace = EGL_GL_COLORSPACE_BT2020_PQ_EXT; ++ break; ++ } ++ ++ // TODO: check if the attribute is actually suportef by the implementation ++ const EGLint attributes[] = { ++ EGL_GL_COLORSPACE, eglColorSpace, ++ EGL_NONE ++ }; ++ + EGLSurface surface = libEGL.eglCreateWindowSurface(m_display, nativeConfig, +- static_cast(nativeWindow), 0); ++ static_cast(nativeWindow), ++ attributes); + if (surface == EGL_NO_SURFACE) { + *err = libEGL.eglGetError(); + qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); +@@ -349,6 +374,7 @@ QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EG + format.setSamples(sampleCount); + format.setStereo(false); + format.setSwapInterval(referenceFormat.swapInterval()); ++ format.setColorSpace(referenceFormat.colorSpace()); + + // Clear the EGL error state because some of the above may + // have errored out because the attribute is not applicable +@@ -378,7 +404,6 @@ QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EG + \internal + \ingroup qt-lighthouse-win + */ +- + QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share) +@@ -477,6 +502,8 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) + // Simulate context loss as the context is useless. + QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; ++ } else if (err == EGL_BAD_MATCH) { ++ qCDebug(lcQpaGl) << "Got bad match in createWindowSurface() for context" << this << "Check color space configuration."; + } + return false; + } +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h +index 8a1e1ddae8..4f0c2c88ef 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.h ++++ b/src/plugins/platforms/windows/qwindowseglcontext.h +@@ -121,7 +121,7 @@ public: + void *moduleHandle() const override { return libGLESv2.moduleHandle(); } + QOpenGLContext::OpenGLModuleType moduleType() const override { return QOpenGLContext::LibGLES; } + +- void *createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) override; ++ void *createWindowSurface(void *nativeWindow, void *nativeConfig, QSurfaceFormat::ColorSpace colorSpace, int *err) override; + void destroyWindowSurface(void *nativeSurface) override; + + QSurfaceFormat formatFromConfig(EGLDisplay display, EGLConfig config, const QSurfaceFormat &referenceFormat); +diff --git a/src/plugins/platforms/windows/qwindowsopenglcontext.h b/src/plugins/platforms/windows/qwindowsopenglcontext.h +index cc6d93d35e..61c0e28767 100644 +--- a/src/plugins/platforms/windows/qwindowsopenglcontext.h ++++ b/src/plugins/platforms/windows/qwindowsopenglcontext.h +@@ -63,7 +63,7 @@ public: + + // If the windowing system interface needs explicitly created window surfaces (like EGL), + // reimplement these. +- virtual void *createWindowSurface(void * /*nativeWindow*/, void * /*nativeConfig*/, int * /*err*/) { return 0; } ++ virtual void *createWindowSurface(void * /*nativeWindow*/, void * /*nativeConfig*/, QSurfaceFormat::ColorSpace /*colorSpace*/, int * /*err*/) { return 0; } + virtual void destroyWindowSurface(void * /*nativeSurface*/) { } + + protected: +diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp +index f340f16679..702e788ccc 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.cpp ++++ b/src/plugins/platforms/windows/qwindowswindow.cpp +@@ -2726,9 +2726,13 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err) + return 0; + #endif + #ifndef QT_NO_OPENGL ++ ++ ++ + if (!m_surface) { +- if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) +- m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err); ++ if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) { ++ m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, m_format.colorSpace(), err); ++ } + } + + return m_surface; +-- +2.20.1.windows.1 + + +From ed5a3b9bbc973399094f100fa7a3b35067e47aeb Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Thu, 22 Nov 2018 15:47:48 +0300 +Subject: [PATCH 07/20] Implement color conversion for the backing store + texture + +If the window surface is not in sRGB mode, then the backing store +surface should be converted into the destinations color space. + +This patch adds the most popular color space transitions into +QOpenGLTextureBlitter. +--- + src/gui/opengl/qopengltextureblitter.cpp | 180 ++++++++++++++++++--- + src/gui/opengl/qopengltextureblitter.h | 12 +- + src/gui/painting/qplatformbackingstore.cpp | 7 + + 3 files changed, 179 insertions(+), 20 deletions(-) + +diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp +index b65df9dc82..819c1855b2 100644 +--- a/src/gui/opengl/qopengltextureblitter.cpp ++++ b/src/gui/opengl/qopengltextureblitter.cpp +@@ -131,13 +131,59 @@ static const char vertex_shader[] = + "}"; + + static const char fragment_shader[] = +- "varying highp vec2 uv;" +- "uniform sampler2D textureSampler;" +- "uniform bool swizzle;" +- "uniform highp float opacity;" ++ "varying highp vec2 uv;\n" ++ "uniform sampler2D textureSampler;\n" ++ "uniform bool swizzle;\n" ++ "uniform highp float opacity;\n" ++ "#if defined SRGB_TO_SCRGB || defined SRGB_TO_BT2020PQ\n" ++ "highp vec4 sRgbToLinear(highp vec4 sRGB)\n" ++ "{\n" ++ " bvec4 cutoff = lessThan(sRGB, vec4(0.04045));\n" ++ " const highp vec2 a1 = vec2(0.055, 0.0);\n" ++ " const highp vec2 c2 = vec2(1.055, 1.0);\n" ++ " const highp vec2 m3 = vec2(2.4, 1.0);\n" ++ " const highp vec2 c4 = vec2(12.92, 1.0);\n" ++ " highp vec4 higher = pow((sRGB + a1.xxxy) / c2.xxxy, m3.xxxy);\n" ++ " highp vec4 lower = sRGB / c4.xxxy;\n" ++ " return mix(higher, lower, vec4(cutoff));\n" ++ "}\n" ++ "#endif\n" ++ "#if defined SRGB_TO_BT2020PQ\n" ++ "highp vec4 applySmpte2084Curve(highp vec4 L)\n" ++ "{" ++ " const highp vec2 m1 = vec2(2610.0 / 4096.0 / 4.0, 1.0);\n" ++ " const highp vec2 m2 = vec2(2523.0 / 4096.0 * 128.0, 1.0);\n" ++ " const highp vec2 a1 = vec2(3424.0 / 4096.0, 0.0);\n" ++ " const highp vec2 c2 = vec2(2413.0 / 4096.0 * 32.0, 1.0);\n" ++ " const highp vec2 c3 = vec2(2392.0 / 4096.0 * 32.0, 1.0);\n" ++ " const highp vec2 a4 = vec2(1.0, 0.0);\n" ++ " highp vec4 Lp = pow(L, m1.xxxy);\n" ++ " highp vec4 res = pow((a1.xxxy + c2.xxxy * Lp) / (a4.xxxy + c3.xxxy * Lp), m2.xxxy);\n" ++ " return res;" ++ "}\n" ++ "" ++ "highp vec4 sRgbToBt2020pq(highp vec4 value)\n" ++ "{\n" ++ " value = sRgbToLinear(value);" ++ " const highp mat4 convMat = " ++ " mat4(0.627402, 0.069095, 0.016394, 0.0," ++ " 0.329292, 0.919544, 0.088028, 0.0," ++ " 0.043306, 0.011360, 0.895578, 0.0," ++ " 0.0, 0.0, 0.0, 1.0);" ++ "" ++ " value = convMat * value;\n" ++ " return applySmpte2084Curve(0.008 * value);" ++ "}\n" ++ "#endif\n" ++ "\n" + "void main() {" + " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" +- " tmpFragColor.a *= opacity;" ++ " tmpFragColor.a *= opacity;\n" ++ "#if defined SRGB_TO_SCRGB\n" ++ " tmpFragColor = sRgbToLinear(tmpFragColor);\n" ++ "#elif defined SRGB_TO_BT2020PQ\n" ++ " tmpFragColor = sRgbToBt2020pq(tmpFragColor);\n" ++ "#endif\n" + " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + "}"; + +@@ -187,6 +233,23 @@ private: + GLenum m_target; + }; + ++class ColorSpaceConversion : public QPair ++{ ++public: ++ ColorSpaceConversion() { }; ++ ColorSpaceConversion(QSurfaceFormat::ColorSpace srcColorSpace, ++ QSurfaceFormat::ColorSpace dstColorSpace) ++ : QPair(srcColorSpace, dstColorSpace) ++ { } ++ ++ QSurfaceFormat::ColorSpace source() const { ++ return first; ++ } ++ QSurfaceFormat::ColorSpace destination() const { ++ return second; ++ } ++}; ++ + class QOpenGLTextureBlitterPrivate + { + public: +@@ -197,16 +260,25 @@ public: + }; + + enum ProgramIndex { +- TEXTURE_2D, +- TEXTURE_EXTERNAL_OES ++ TEXTURE_2D = 0, ++ TEXTURE_2D_SRGB_TO_SCRGB, ++ TEXTURE_2D_SRGB_TO_BT2020PQ, ++ TEXTURE_EXTERNAL_OES, ++ ++ PROGRAM_COUNT + }; + + QOpenGLTextureBlitterPrivate() : + swizzle(false), + opacity(1.0f), + vao(new QOpenGLVertexArrayObject), +- currentTarget(TEXTURE_2D) +- { } ++ currentTarget(GL_NONE), ++ colorSpaceConversion(0) ++ { ++ supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::DefaultColorSpace, QSurfaceFormat::DefaultColorSpace); ++ supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::scRGBColorSpace); ++ supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::bt2020PQColorSpace); ++ } + + bool buildProgram(ProgramIndex idx, const char *vs, const char *fs); + +@@ -214,6 +286,7 @@ public: + void blit(GLuint texture, const QMatrix4x4 &vertexTransform, QOpenGLTextureBlitter::Origin origin); + + void prepareProgram(const QMatrix4x4 &vertexTransform); ++ int calcColorSpaceConversionIndex(QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace); + + QOpenGLBuffer vertexBuffer; + QOpenGLBuffer textureBuffer; +@@ -239,18 +312,47 @@ public: + bool swizzle; + float opacity; + TextureMatrixUniform textureMatrixUniformState; +- } programs[2]; ++ } programs[PROGRAM_COUNT]; + bool swizzle; + float opacity; + QScopedPointer vao; + GLenum currentTarget; ++ ++ int colorSpaceConversion; ++ QVector supportedColorSpaceConversions; + }; + +-static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target) ++int QOpenGLTextureBlitterPrivate::calcColorSpaceConversionIndex(QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace) ++{ ++ // TODO: auto-detect destination color space of the surface ++ // in case of default color space ++ ++ // disable color management if at least one of the color ++ // spaces is declared as default ++ if (srcColorSpace == QSurfaceFormat::DefaultColorSpace || ++ dstColorSpace == QSurfaceFormat::DefaultColorSpace) { ++ ++ return 0; ++ } ++ ++ // disable color management if source and destination color ++ // spaces are the same ++ if (srcColorSpace == dstColorSpace) { ++ return 0; ++ } ++ ++ ColorSpaceConversion conversion(srcColorSpace, dstColorSpace); ++ return supportedColorSpaceConversions.indexOf(conversion); ++} ++ ++static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target, int colorSpaceConversion) + { + switch (target) { +- case GL_TEXTURE_2D: +- return QOpenGLTextureBlitterPrivate::TEXTURE_2D; ++ case GL_TEXTURE_2D: { ++ QOpenGLTextureBlitterPrivate::ProgramIndex index( ++ int(QOpenGLTextureBlitterPrivate::TEXTURE_2D) + colorSpaceConversion); ++ return index; ++ } + case GL_TEXTURE_EXTERNAL_OES: + return QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES; + default: +@@ -261,7 +363,7 @@ static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GL + + void QOpenGLTextureBlitterPrivate::prepareProgram(const QMatrix4x4 &vertexTransform) + { +- Program *program = &programs[targetToProgramIndex(currentTarget)]; ++ Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; + + vertexBuffer.bind(); + program->glProgram->setAttributeBuffer(program->vertexCoordAttribPos, GL_FLOAT, 0, 3, 0); +@@ -293,7 +395,7 @@ void QOpenGLTextureBlitterPrivate::blit(GLuint texture, + TextureBinder binder(currentTarget, texture); + prepareProgram(vertexTransform); + +- Program *program = &programs[targetToProgramIndex(currentTarget)]; ++ Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; + program->glProgram->setUniformValue(program->textureTransformUniformPos, textureTransform); + program->textureMatrixUniformState = User; + +@@ -307,7 +409,7 @@ void QOpenGLTextureBlitterPrivate::blit(GLuint texture, + TextureBinder binder(currentTarget, texture); + prepareProgram(vertexTransform); + +- Program *program = &programs[targetToProgramIndex(currentTarget)]; ++ Program *program = &programs[targetToProgramIndex(currentTarget, colorSpaceConversion)]; + if (origin == QOpenGLTextureBlitter::OriginTopLeft) { + if (program->textureMatrixUniformState != IdentityFlipped) { + QMatrix3x3 flipped; +@@ -408,6 +510,18 @@ bool QOpenGLTextureBlitter::create() + } else { + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D, vertex_shader, fragment_shader)) + return false; ++ ++ // TODO: create non-default transformations on-demand ++ { ++ const QString shader = QString("#define SRGB_TO_SCRGB\n %1").arg(fragment_shader); ++ if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_SCRGB, vertex_shader, shader.toLatin1().constData())) ++ return false; ++ } ++ { ++ const QString shader = QString("#define SRGB_TO_BT2020PQ\n %1").arg(fragment_shader); ++ if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_BT2020PQ, vertex_shader, shader.toLatin1().constData())) ++ return false; ++ } + if (supportsExternalOESTarget()) + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES, vertex_shader, fragment_shader_external_oes)) + return false; +@@ -455,6 +569,8 @@ void QOpenGLTextureBlitter::destroy() + return; + Q_D(QOpenGLTextureBlitter); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram.reset(); ++ d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_SCRGB].glProgram.reset(); ++ d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_BT2020PQ].glProgram.reset(); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES].glProgram.reset(); + d->vertexBuffer.destroy(); + d->textureBuffer.destroy(); +@@ -484,15 +600,26 @@ bool QOpenGLTextureBlitter::supportsExternalOESTarget() const + + \sa release(), blit() + */ +-void QOpenGLTextureBlitter::bind(GLenum target) ++void QOpenGLTextureBlitter::bind(GLenum target, ++ QSurfaceFormat::ColorSpace srcColorSpace, ++ QSurfaceFormat::ColorSpace dstColorSpace) + { + Q_D(QOpenGLTextureBlitter); + + if (d->vao->isCreated()) + d->vao->bind(); + ++ const int index = d->calcColorSpaceConversionIndex(srcColorSpace, dstColorSpace); ++ ++ if (index >= 0) { ++ d->colorSpaceConversion = index; ++ } else { ++ qWarning() << "QOpenGLTextureBlitter::bind(): color space conversion is not supported" << srcColorSpace << dstColorSpace; ++ d->colorSpaceConversion = 0; // noop conversion ++ } ++ + d->currentTarget = target; +- QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target)]; ++ QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target, d->colorSpaceConversion)]; + p->glProgram->bind(); + + d->vertexBuffer.bind(); +@@ -506,6 +633,21 @@ void QOpenGLTextureBlitter::bind(GLenum target) + d->textureBuffer.release(); + } + ++void QOpenGLTextureBlitter::rebind(GLenum target, QSurfaceFormat::ColorSpace srcColorSpace, QSurfaceFormat::ColorSpace dstColorSpace) ++{ ++ Q_D(QOpenGLTextureBlitter); ++ ++ if (d->vao->isCreated() && ++ d->currentTarget == target && ++ d->colorSpaceConversion == d->calcColorSpaceConversionIndex(srcColorSpace, dstColorSpace)) { ++ ++ // the blitter is already configured in the correct state, so just skip it ++ return; ++ } ++ ++ bind(target, srcColorSpace, dstColorSpace); ++} ++ + /*! + Unbinds the graphics resources used by the blitter. + +@@ -514,7 +656,7 @@ void QOpenGLTextureBlitter::bind(GLenum target) + void QOpenGLTextureBlitter::release() + { + Q_D(QOpenGLTextureBlitter); +- d->programs[targetToProgramIndex(d->currentTarget)].glProgram->release(); ++ d->programs[targetToProgramIndex(d->currentTarget, d->colorSpaceConversion)].glProgram->release(); + if (d->vao->isCreated()) + d->vao->release(); + } +diff --git a/src/gui/opengl/qopengltextureblitter.h b/src/gui/opengl/qopengltextureblitter.h +index 2f7c6b1a0a..3c87e4e2b5 100644 +--- a/src/gui/opengl/qopengltextureblitter.h ++++ b/src/gui/opengl/qopengltextureblitter.h +@@ -48,6 +48,9 @@ + #include + #include + ++// TODO: less includes!!! ++#include ++ + QT_BEGIN_NAMESPACE + + class QOpenGLTextureBlitterPrivate; +@@ -69,7 +72,14 @@ public: + + bool supportsExternalOESTarget() const; + +- void bind(GLenum target = GL_TEXTURE_2D); ++ void bind(GLenum target = GL_TEXTURE_2D, ++ QSurfaceFormat::ColorSpace srcColorSpace = QSurfaceFormat::DefaultColorSpace, ++ QSurfaceFormat::ColorSpace dstColorSpace = QSurfaceFormat::DefaultColorSpace); ++ ++ void rebind(GLenum target = GL_TEXTURE_2D, ++ QSurfaceFormat::ColorSpace srcColorSpace = QSurfaceFormat::DefaultColorSpace, ++ QSurfaceFormat::ColorSpace dstColorSpace = QSurfaceFormat::DefaultColorSpace); ++ + void release(); + + void setRedBlueSwizzle(bool swizzle); +diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp +index 3081a4b1b6..a9b7bd39a2 100644 +--- a/src/gui/painting/qplatformbackingstore.cpp ++++ b/src/gui/painting/qplatformbackingstore.cpp +@@ -300,6 +300,8 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, + if (srgb && canUseSrgb) + funcs->glEnable(GL_FRAMEBUFFER_SRGB); + ++ // TODO: fetch real color space from the widget!!! ++ blitter->rebind(GL_TEXTURE_2D, QSurfaceFormat::DefaultColorSpace, window->format().colorSpace()); + blitter->blit(textures->textureId(idx), target, source); + + if (srgb && canUseSrgb) +@@ -432,6 +434,11 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i + funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + + if (textureId) { ++ // GUI texture is always in sRGB color space ++ d_ptr->blitter->rebind(GL_TEXTURE_2D, ++ QSurfaceFormat::sRGBColorSpace, ++ window->format().colorSpace()); ++ + if (d_ptr->needsSwizzle) + d_ptr->blitter->setRedBlueSwizzle(true); + // The backingstore is for the entire tlw. +-- +2.20.1.windows.1 + + +From d6c4a34a4a12704e9130ffdc1356dd3f4ef5bc37 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Thu, 22 Nov 2018 16:03:28 +0300 +Subject: [PATCH 08/20] Fix compilation + +--- + src/gui/opengl/qopengltextureblitter.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp +index 819c1855b2..69f9708469 100644 +--- a/src/gui/opengl/qopengltextureblitter.cpp ++++ b/src/gui/opengl/qopengltextureblitter.cpp +@@ -349,8 +349,9 @@ static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GL + { + switch (target) { + case GL_TEXTURE_2D: { +- QOpenGLTextureBlitterPrivate::ProgramIndex index( +- int(QOpenGLTextureBlitterPrivate::TEXTURE_2D) + colorSpaceConversion); ++ QOpenGLTextureBlitterPrivate::ProgramIndex index = ++ QOpenGLTextureBlitterPrivate::ProgramIndex( ++ int(QOpenGLTextureBlitterPrivate::TEXTURE_2D) + colorSpaceConversion); + return index; + } + case GL_TEXTURE_EXTERNAL_OES: +-- +2.20.1.windows.1 + + +From b484b5e627042396a1ba652b5df09e22e362f421 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Wed, 28 Nov 2018 23:17:05 +0300 +Subject: [PATCH 09/20] Implement QOpenGLWidget::setTextureColorSpace() + +This special method notifies the composing system if it should +apply any color conversion when merging this widget's texture into +the root surface of the window. +--- + src/gui/opengl/qopengltextureblitter.cpp | 48 +++++++++++++++++-- + src/gui/painting/qplatformbackingstore.cpp | 13 +++-- + src/gui/painting/qplatformbackingstore.h | 4 +- + .../qopenglcompositorbackingstore.cpp | 2 +- + src/widgets/kernel/qopenglwidget.cpp | 45 ++++++++++++++++- + src/widgets/kernel/qopenglwidget.h | 3 ++ + src/widgets/kernel/qwidget_p.h | 1 + + src/widgets/kernel/qwidgetbackingstore.cpp | 2 +- + 8 files changed, 106 insertions(+), 12 deletions(-) + +diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp +index 69f9708469..5d0abb0dd8 100644 +--- a/src/gui/opengl/qopengltextureblitter.cpp ++++ b/src/gui/opengl/qopengltextureblitter.cpp +@@ -135,7 +135,20 @@ static const char fragment_shader[] = + "uniform sampler2D textureSampler;\n" + "uniform bool swizzle;\n" + "uniform highp float opacity;\n" +- "#if defined SRGB_TO_SCRGB || defined SRGB_TO_BT2020PQ\n" ++ "#if defined SCRGB_TO_SRGB\n" ++ "highp vec4 linearToSRGB(highp vec4 value)\n" ++ "{\n" ++ " bvec4 cutoff = lessThan(value, vec4(0.0031308));\n" ++ " const highp vec2 a1 = vec2(0.055, 0.0);\n" ++ " const highp vec2 c2 = vec2(1.055, 1.0);\n" ++ " const highp vec2 m3 = vec2(2.4, 1.0);\n" ++ " const highp vec2 c4 = vec2(12.92, 1.0);\n" ++ " highp vec4 higher = c2.xxxy * pow(value, 1.0 / m3.xxxy) - a1.xxxy;\n" ++ " highp vec4 lower = value * c4.xxxy;\n" ++ " return mix(higher, lower, vec4(cutoff));\n" ++ "}\n" ++ "#endif\n" ++ "#if defined SRGB_TO_SCRGB || defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" + "highp vec4 sRgbToLinear(highp vec4 sRGB)\n" + "{\n" + " bvec4 cutoff = lessThan(sRGB, vec4(0.04045));\n" +@@ -148,7 +161,7 @@ static const char fragment_shader[] = + " return mix(higher, lower, vec4(cutoff));\n" + "}\n" + "#endif\n" +- "#if defined SRGB_TO_BT2020PQ\n" ++ "#if defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" + "highp vec4 applySmpte2084Curve(highp vec4 L)\n" + "{" + " const highp vec2 m1 = vec2(2610.0 / 4096.0 / 4.0, 1.0);\n" +@@ -161,10 +174,10 @@ static const char fragment_shader[] = + " highp vec4 res = pow((a1.xxxy + c2.xxxy * Lp) / (a4.xxxy + c3.xxxy * Lp), m2.xxxy);\n" + " return res;" + "}\n" +- "" +- "highp vec4 sRgbToBt2020pq(highp vec4 value)\n" ++ "#endif\n" ++ "#if defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" ++ "highp vec4 scRgbToBt2020pq(highp vec4 value)\n" + "{\n" +- " value = sRgbToLinear(value);" + " const highp mat4 convMat = " + " mat4(0.627402, 0.069095, 0.016394, 0.0," + " 0.329292, 0.919544, 0.088028, 0.0," +@@ -175,6 +188,13 @@ static const char fragment_shader[] = + " return applySmpte2084Curve(0.008 * value);" + "}\n" + "#endif\n" ++ "#if defined SRGB_TO_BT2020PQ\n" ++ "highp vec4 sRgbToBt2020pq(highp vec4 value)\n" ++ "{\n" ++ " value = sRgbToLinear(value);" ++ " return scRgbToBt2020pq(value);" ++ "}\n" ++ "#endif\n" + "\n" + "void main() {" + " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" +@@ -183,6 +203,10 @@ static const char fragment_shader[] = + " tmpFragColor = sRgbToLinear(tmpFragColor);\n" + "#elif defined SRGB_TO_BT2020PQ\n" + " tmpFragColor = sRgbToBt2020pq(tmpFragColor);\n" ++ "#elif defined SCRGB_TO_BT2020PQ\n" ++ " tmpFragColor = scRgbToBt2020pq(tmpFragColor);\n" ++ "#elif defined SCRGB_TO_SRGB\n" ++ " tmpFragColor = linearToSRGB(tmpFragColor);\n" + "#endif\n" + " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + "}"; +@@ -262,7 +286,9 @@ public: + enum ProgramIndex { + TEXTURE_2D = 0, + TEXTURE_2D_SRGB_TO_SCRGB, ++ TEXTURE_2D_SCRGB_TO_SRGB, + TEXTURE_2D_SRGB_TO_BT2020PQ, ++ TEXTURE_2D_SCRGB_TO_BT2020PQ, + TEXTURE_EXTERNAL_OES, + + PROGRAM_COUNT +@@ -277,7 +303,9 @@ public: + { + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::DefaultColorSpace, QSurfaceFormat::DefaultColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::scRGBColorSpace); ++ supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::scRGBColorSpace, QSurfaceFormat::sRGBColorSpace); + supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::sRGBColorSpace, QSurfaceFormat::bt2020PQColorSpace); ++ supportedColorSpaceConversions << ColorSpaceConversion(QSurfaceFormat::scRGBColorSpace, QSurfaceFormat::bt2020PQColorSpace); + } + + bool buildProgram(ProgramIndex idx, const char *vs, const char *fs); +@@ -518,11 +546,21 @@ bool QOpenGLTextureBlitter::create() + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_SCRGB, vertex_shader, shader.toLatin1().constData())) + return false; + } ++ { ++ const QString shader = QString("#define SCRGB_TO_SRGB\n %1").arg(fragment_shader); ++ if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SCRGB_TO_SRGB, vertex_shader, shader.toLatin1().constData())) ++ return false; ++ } + { + const QString shader = QString("#define SRGB_TO_BT2020PQ\n %1").arg(fragment_shader); + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SRGB_TO_BT2020PQ, vertex_shader, shader.toLatin1().constData())) + return false; + } ++ { ++ const QString shader = QString("#define SCRGB_TO_BT2020PQ\n %1").arg(fragment_shader); ++ if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D_SCRGB_TO_BT2020PQ, vertex_shader, shader.toLatin1().constData())) ++ return false; ++ } + if (supportsExternalOESTarget()) + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES, vertex_shader, fragment_shader_external_oes)) + return false; +diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp +index a9b7bd39a2..4be3950f9a 100644 +--- a/src/gui/painting/qplatformbackingstore.cpp ++++ b/src/gui/painting/qplatformbackingstore.cpp +@@ -132,6 +132,7 @@ struct QBackingstoreTextureInfo + QRect rect; + QRect clipRect; + QPlatformTextureList::Flags flags; ++ QSurfaceFormat::ColorSpace colorSpace; + }; + + Q_DECLARE_TYPEINFO(QBackingstoreTextureInfo, Q_MOVABLE_TYPE); +@@ -181,6 +182,12 @@ QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const + return d->textures.at(index).flags; + } + ++QSurfaceFormat::ColorSpace QPlatformTextureList::colorSpace(int index) const ++{ ++ Q_D(const QPlatformTextureList); ++ return d->textures.at(index).colorSpace; ++} ++ + QRect QPlatformTextureList::geometry(int index) const + { + Q_D(const QPlatformTextureList); +@@ -209,7 +216,7 @@ bool QPlatformTextureList::isLocked() const + } + + void QPlatformTextureList::appendTexture(void *source, GLuint textureId, const QRect &geometry, +- const QRect &clipRect, Flags flags) ++ const QRect &clipRect, Flags flags, QSurfaceFormat::ColorSpace colorSpace) + { + Q_D(QPlatformTextureList); + QBackingstoreTextureInfo bi; +@@ -218,6 +225,7 @@ void QPlatformTextureList::appendTexture(void *source, GLuint textureId, const Q + bi.rect = geometry; + bi.clipRect = clipRect; + bi.flags = flags; ++ bi.colorSpace = colorSpace; + d->textures.append(bi); + } + +@@ -300,8 +308,7 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, + if (srgb && canUseSrgb) + funcs->glEnable(GL_FRAMEBUFFER_SRGB); + +- // TODO: fetch real color space from the widget!!! +- blitter->rebind(GL_TEXTURE_2D, QSurfaceFormat::DefaultColorSpace, window->format().colorSpace()); ++ blitter->rebind(GL_TEXTURE_2D, textures->colorSpace(idx), window->format().colorSpace()); + blitter->blit(textures->textureId(idx), target, source); + + if (srgb && canUseSrgb) +diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h +index de5ba964dc..f8887bd4cd 100644 +--- a/src/gui/painting/qplatformbackingstore.h ++++ b/src/gui/painting/qplatformbackingstore.h +@@ -95,11 +95,13 @@ public: + QRect clipRect(int index) const; + void *source(int index); + Flags flags(int index) const; ++ QSurfaceFormat::ColorSpace colorSpace(int index) const; + void lock(bool on); + bool isLocked() const; + + void appendTexture(void *source, GLuint textureId, const QRect &geometry, +- const QRect &clipRect = QRect(), Flags flags = 0); ++ const QRect &clipRect = QRect(), Flags flags = 0, ++ QSurfaceFormat::ColorSpace colorSpace = QSurfaceFormat::DefaultColorSpace); + void clear(); + + Q_SIGNALS: +diff --git a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp +index 40400e2a19..5d44e62455 100644 +--- a/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp ++++ b/src/platformsupport/platformcompositor/qopenglcompositorbackingstore.cpp +@@ -230,7 +230,7 @@ void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegi + m_textures->clear(); + for (int i = 0; i < textures->count(); ++i) + m_textures->appendTexture(textures->source(i), textures->textureId(i), textures->geometry(i), +- textures->clipRect(i), textures->flags(i)); ++ textures->clipRect(i), textures->flags(i), textures->colorSpace(i)); + + updateTexture(); + m_textures->appendTexture(nullptr, m_bsTexture, window->geometry()); +diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp +index 89f860150f..0abf707e01 100644 +--- a/src/widgets/kernel/qopenglwidget.cpp ++++ b/src/widgets/kernel/qopenglwidget.cpp +@@ -568,7 +568,8 @@ public: + updateBehavior(QOpenGLWidget::NoPartialUpdate), + requestedSamples(0), + inPaintGL(false), +- textureFormat(0) ++ textureFormat(0), ++ textureColorSpace(QSurfaceFormat::DefaultColorSpace) + { + requestedFormat = QSurfaceFormat::defaultFormat(); + } +@@ -578,6 +579,7 @@ public: + + GLuint textureId() const override; + QPlatformTextureList::Flags textureListFlags() override; ++ QSurfaceFormat::ColorSpace colorSpace() const override; + + void initialize(); + void invokeUserPaint(); +@@ -609,6 +611,7 @@ public: + int requestedSamples; + bool inPaintGL; + GLenum textureFormat; ++ QSurfaceFormat::ColorSpace textureColorSpace; + }; + + void QOpenGLWidgetPaintDevicePrivate::beginPaint() +@@ -695,6 +698,11 @@ QPlatformTextureList::Flags QOpenGLWidgetPrivate::textureListFlags() + return flags; + } + ++QSurfaceFormat::ColorSpace QOpenGLWidgetPrivate::colorSpace() const ++{ ++ return textureColorSpace; ++} ++ + void QOpenGLWidgetPrivate::reset() + { + Q_Q(QOpenGLWidget); +@@ -1115,6 +1123,41 @@ void QOpenGLWidget::setTextureFormat(GLenum texFormat) + d->textureFormat = texFormat; + } + ++/*! ++ \return the declared color space of the internal texture of the widget. ++ ++ The texture's color space will be used when composing the widget ++ into the root window surface. ++ ++ \note when the color space is set to QSurfaceFormat::DefaultColorSpace, ++ color conversion is effectively disabled. ++ ++ \since 5.99 ++ */ ++QSurfaceFormat::ColorSpace QOpenGLWidget::textureColorSpace() const ++{ ++ Q_D(const QOpenGLWidget); ++ return d->textureColorSpace; ++} ++ ++/*! ++ Sets a custom color space for the internal texture of the widget ++ ++ The color space of the texture will be compared against the color ++ space of the root surface and conversion will be performed if needed. ++ ++ \note setting the color space to QSurfaceFormat::DefaultColorSpace will ++ effectively disable color conversion when composing this texture on ++ screen. ++ ++ \since 5.99 ++ */ ++void QOpenGLWidget::setTextureColorSpace(QSurfaceFormat::ColorSpace colorSpace) ++{ ++ Q_D(QOpenGLWidget); ++ d->textureColorSpace = colorSpace; ++} ++ + /*! + \return the active internal texture format if the widget has already + initialized, the requested format if one was set but the widget has not yet +diff --git a/src/widgets/kernel/qopenglwidget.h b/src/widgets/kernel/qopenglwidget.h +index 9eb4a9ba5a..eff2d9796d 100644 +--- a/src/widgets/kernel/qopenglwidget.h ++++ b/src/widgets/kernel/qopenglwidget.h +@@ -75,6 +75,9 @@ public: + GLenum textureFormat() const; + void setTextureFormat(GLenum texFormat); + ++ QSurfaceFormat::ColorSpace textureColorSpace() const; ++ void setTextureColorSpace(QSurfaceFormat::ColorSpace colorSpace); ++ + bool isValid() const; + + void makeCurrent(); +diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h +index 6f1ce67c4c..c6f84972c3 100644 +--- a/src/widgets/kernel/qwidget_p.h ++++ b/src/widgets/kernel/qwidget_p.h +@@ -653,6 +653,7 @@ public: + ? QPlatformTextureList::StacksOnTop + : QPlatformTextureList::Flags(0); + } ++ virtual QSurfaceFormat::ColorSpace colorSpace() const { return QSurfaceFormat::DefaultColorSpace; } + virtual QImage grabFramebuffer() { return QImage(); } + virtual void beginBackingStorePainting() { } + virtual void endBackingStorePainting() { } +diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp +index a32eb2a03b..db60338034 100644 +--- a/src/widgets/kernel/qwidgetbackingstore.cpp ++++ b/src/widgets/kernel/qwidgetbackingstore.cpp +@@ -1007,7 +1007,7 @@ static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatfo + if (wd->renderToTexture) { + QPlatformTextureList::Flags flags = wd->textureListFlags(); + const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); +- widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); ++ widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags, wd->colorSpace()); + } + + for (int i = 0; i < wd->children.size(); ++i) { +-- +2.20.1.windows.1 + + +From add34922934b971b33f1da610ddaf5aba36c0348 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Tue, 4 Dec 2018 20:11:34 +0300 +Subject: [PATCH 10/20] Return QScreen's HMONITOR handle via + QPlatformNativeInterface + +It is needed to be able to fetch extra information about the display via +DXGI interface. +--- + .../windows/qwindowsnativeinterface.cpp | 16 ++++++++++++++++ + .../platforms/windows/qwindowsnativeinterface.h | 1 + + src/plugins/platforms/windows/qwindowsscreen.cpp | 5 +++++ + src/plugins/platforms/windows/qwindowsscreen.h | 2 ++ + 4 files changed, 24 insertions(+) + +diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +index de11356fd4..0b762206a2 100644 +--- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp ++++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +@@ -40,6 +40,7 @@ + #include "qwindowsnativeinterface.h" + #include "qwindowsclipboard.h" + #include "qwindowswindow.h" ++#include "qwindowsscreen.h" + #include "qwindowscontext.h" + #include "qwindowscursor.h" + #include "qwindowsopenglcontext.h" +@@ -124,6 +125,21 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc + return 0; + } + ++void *QWindowsNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) ++{ ++ if (!screen || !screen->handle()) { ++ qWarning("%s: '%s' requested for null screen or screen without handle.", __FUNCTION__, resource.constData()); ++ return 0; ++ } ++ QWindowsScreen *bs = static_cast(screen->handle()); ++ int type = resourceType(resource); ++ if (type == HandleType) ++ return bs->handle(); ++ ++ qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); ++ return 0; ++} ++ + #ifndef QT_NO_CURSOR + void *QWindowsNativeInterface::nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) + { +diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.h b/src/plugins/platforms/windows/qwindowsnativeinterface.h +index d085a4afb3..bd7cad01c1 100644 +--- a/src/plugins/platforms/windows/qwindowsnativeinterface.h ++++ b/src/plugins/platforms/windows/qwindowsnativeinterface.h +@@ -73,6 +73,7 @@ public: + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override; + #endif + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; ++ void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override; + #ifndef QT_NO_CURSOR + void *nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) override; + #endif +diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp +index 2eaf386d42..620828d597 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.cpp ++++ b/src/plugins/platforms/windows/qwindowsscreen.cpp +@@ -321,6 +321,11 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) + } + } + ++HMONITOR QWindowsScreen::handle() const ++{ ++ return m_data.hMonitor; ++} ++ + QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry() + { + QRect result; +diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h +index 824bcb1ad6..33c9effa2a 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.h ++++ b/src/plugins/platforms/windows/qwindowsscreen.h +@@ -104,6 +104,8 @@ public: + + inline void handleChanges(const QWindowsScreenData &newData); + ++ HMONITOR handle() const; ++ + #ifndef QT_NO_CURSOR + QPlatformCursor *cursor() const override { return m_cursor.data(); } + const CursorPtr &cursorPtr() const { return m_cursor; } +-- +2.20.1.windows.1 + + +From 9189775b9060c0399334d2744d31c9d119ab7dd2 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Thu, 6 Dec 2018 16:16:27 +0300 +Subject: [PATCH 11/20] Fix notification of QDockWidget when it gets undocked + +Before the patch the notification was emitted only when the docker +was attached to the panel or changed a position on it. +--- + src/widgets/widgets/qdockwidget.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp +index 6c871aae2c..19fc2d1677 100644 +--- a/src/widgets/widgets/qdockwidget.cpp ++++ b/src/widgets/widgets/qdockwidget.cpp +@@ -1171,6 +1171,8 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect + QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q); + if (mwlayout) + emit q->dockLocationChanged(mwlayout->dockWidgetArea(q)); ++ } else { ++ emit q->dockLocationChanged(Qt::NoDockWidgetArea); + } + } + +-- +2.20.1.windows.1 + + +From 5fdb36fef8f866b34f5a0884b0a0e33107ee4a76 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 10 Dec 2018 11:33:00 +0300 +Subject: [PATCH 12/20] Fix D3D shader compiler location when using Qt 5.12 on + MinGW 7.3 + +For some reason this patch was removed in Qt 5.12, which caused a +regression on MinGW 7.3, which still points to an older version +of HLSL dll. +--- + ...ically-load-D3D-compiler-from-a-list.patch | 62 +++++++++++++++++++ + 1 file changed, 62 insertions(+) + create mode 100644 src/angle/patches/0002-ANGLE-Dynamically-load-D3D-compiler-from-a-list.patch + +diff --git a/src/angle/patches/0002-ANGLE-Dynamically-load-D3D-compiler-from-a-list.patch b/src/angle/patches/0002-ANGLE-Dynamically-load-D3D-compiler-from-a-list.patch +new file mode 100644 +index 0000000000..9503ae1090 +--- /dev/null ++++ b/src/angle/patches/0002-ANGLE-Dynamically-load-D3D-compiler-from-a-list.patch +@@ -0,0 +1,62 @@ ++From 7dd1de8a519324e6ec7dbfede1b446980cb5954f Mon Sep 17 00:00:00 2001 ++From: Oliver Wolff ++Date: Tue, 1 Mar 2016 13:28:02 +0100 ++Subject: [PATCH 2/7] ANGLE: Dynamically load D3D compiler from a list ++ ++If the default compiler cannot be found, load it from a list of DLL names, ++including a non-versioned proxy DLL provided by Qt. On Desktop Windows, ++the default compiler can also be specified by an environment variable, ++QT_D3DCOMPILER_DLL. ++ ++Change-Id: Ic6d6e37095b838b8a636b029b72467f156b850cb ++--- ++ .../src/libANGLE/renderer/d3d/HLSLCompiler.cpp | 26 ++++++++++++++++++++++ ++ 1 file changed, 26 insertions(+) ++ ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp ++index e8b1af3..0d298bb 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp ++@@ -12,6 +12,10 @@ ++ #include "libANGLE/histogram_macros.h" ++ #include "third_party/trace_event/trace_event.h" ++ +++#ifndef QT_D3DCOMPILER_DLL +++#define QT_D3DCOMPILER_DLL D3DCOMPILER_DLL +++#endif +++ ++ #if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED ++ namespace ++ { ++@@ -128,6 +132,28 @@ gl::Error HLSLCompiler::initialize() ++ } ++ #endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES ++ +++ // Load the compiler DLL specified by the environment, or default to QT_D3DCOMPILER_DLL +++ const wchar_t *defaultCompiler = _wgetenv(L"QT_D3DCOMPILER_DLL"); +++ if (!defaultCompiler) +++ defaultCompiler = QT_D3DCOMPILER_DLL; +++ +++ const wchar_t *compilerDlls[] = { +++ defaultCompiler, +++ L"d3dcompiler_47.dll", +++ L"d3dcompiler_46.dll", +++ L"d3dcompiler_43.dll", +++ 0 +++ }; +++ +++ // Load the first available known compiler DLL +++ for (int i = 0; compilerDlls[i]; ++i) +++ { +++ mD3DCompilerModule = LoadLibrary(compilerDlls[i]); +++ if (mD3DCompilerModule) +++ break; +++ } +++ +++ ++ if (!mD3DCompilerModule) ++ { ++ // Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with. ++-- ++2.7.0.windows.1 ++ +-- +2.20.1.windows.1 + + +From 6224b96ddc2fd90dc41ee683d5f97475b0e25824 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 10 Dec 2018 11:33:09 +0300 +Subject: [PATCH 13/20] Fix misprint + +--- + src/angle/patches/0001-Fix-build-for-MinGW.patch | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/angle/patches/0001-Fix-build-for-MinGW.patch b/src/angle/patches/0001-Fix-build-for-MinGW.patch +index 186acd39dc..26098c528a 100644 +--- a/src/angle/patches/0001-Fix-build-for-MinGW.patch ++++ b/src/angle/patches/0001-Fix-build-for-MinGW.patch +@@ -8,7 +8,7 @@ SSE is not properly supported for Mingw yet. + src/common/platform.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +-diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/srccommon/platform.h ++diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h + index 0065fd2..47cd57b 100644 + --- a/src/3rdparty/angle/src/common/platform.h + +++ b/src/3rdparty/angle/src/common/platform.h +@@ -21,6 +21,5 @@ index 0065fd2..47cd57b 100644 + #include + #define ANGLE_USE_SSE + #endif +--- ++-- + 2.10.2.windows.1 +- +-- +2.20.1.windows.1 + + +From 88950ea2006ad815e6f50f24862438d283f689e8 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 14 Jan 2019 12:32:34 +0300 +Subject: [PATCH 14/20] Fix rendering of Qt widgets on Rec 2020 PQ surface + +The channels should be swizzled into RGBA *before* applying the +color space conversion matrix. Otherwise the colors will be skewed, +because the conversion function for R and B channels is not the same. +--- + src/gui/opengl/qopengltextureblitter.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp +index 5d0abb0dd8..5f6dbff292 100644 +--- a/src/gui/opengl/qopengltextureblitter.cpp ++++ b/src/gui/opengl/qopengltextureblitter.cpp +@@ -199,6 +199,7 @@ static const char fragment_shader[] = + "void main() {" + " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" + " tmpFragColor.a *= opacity;\n" ++ " tmpFragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;\n" + "#if defined SRGB_TO_SCRGB\n" + " tmpFragColor = sRgbToLinear(tmpFragColor);\n" + "#elif defined SRGB_TO_BT2020PQ\n" +@@ -208,7 +209,7 @@ static const char fragment_shader[] = + "#elif defined SCRGB_TO_SRGB\n" + " tmpFragColor = linearToSRGB(tmpFragColor);\n" + "#endif\n" +- " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" ++ " gl_FragColor = tmpFragColor;" + "}"; + + static const char fragment_shader_external_oes[] = +-- +2.20.1.windows.1 + + +From 68a113ed266f344a898933c2c4a3686afc6609de Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Wed, 23 Jan 2019 23:58:40 +0300 +Subject: [PATCH 15/20] Implement API for requesting if current EGL + implementation supports color spaces + +QOpenGLContext::isSurfaceColorSpaceSupported() returns if the color +space is supported. + +TODO: implement this feature for other implementation, which are not EGL. +--- + src/3rdparty/angle/src/common/platform.h | 7 +-- + .../renderer/d3d/d3d11/Renderer11.cpp | 10 ++-- + .../renderer/d3d/d3d11/SwapChain11.cpp | 7 ++- + src/gui/kernel/qopenglcontext.cpp | 16 +++++ + src/gui/kernel/qopenglcontext.h | 1 + + src/gui/kernel/qplatformopenglcontext.cpp | 12 ++++ + src/gui/kernel/qplatformopenglcontext.h | 2 + + .../platforms/windows/qwindowseglcontext.cpp | 58 ++++++++++++++++--- + .../platforms/windows/qwindowseglcontext.h | 10 ++++ + 9 files changed, 100 insertions(+), 23 deletions(-) + +diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h +index 89359f954e..265f49f875 100644 +--- a/src/3rdparty/angle/src/common/platform.h ++++ b/src/3rdparty/angle/src/common/platform.h +@@ -65,15 +65,10 @@ + # if defined(__MINGW32__) && !defined(__d3d11sdklayers_h__) + # define ANGLE_MINGW32_COMPAT + # endif +-//# if defined(_MSC_VER) && _MSC_VER >= 1800 +-# define ANGLE_ENABLE_D3D11_1 +-//# endif +-# if defined(ANGLE_ENABLE_D3D11_1) + # include + # include + # include +-# include // TODO: This is actually D3D12!!! +-# endif ++# include // WARNING: This is actually D3D12! + # include + # endif + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +index ac46690090..f0e497b52f 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +@@ -1001,7 +1001,6 @@ void Renderer11::populateRenderer11DeviceCaps() + &mRenderer11DeviceCaps.B5G5R5A1support, + &mRenderer11DeviceCaps.B5G5R5A1maxSamples); + +-//#if defined(ANGLE_ENABLE_D3D11_1) + IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject(mDxgiAdapter); + mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); + SafeRelease(dxgiAdapter2); +@@ -1009,7 +1008,6 @@ void Renderer11::populateRenderer11DeviceCaps() + IDXGIAdapter3 *dxgiAdapter3 = d3d11::DynamicCastComObject(mDxgiAdapter); + mRenderer11DeviceCaps.supportsDXGI1_4 = (dxgiAdapter3 != nullptr); + SafeRelease(dxgiAdapter3); +-//#endif + } + + gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( +@@ -1250,10 +1248,10 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions + // All D3D feature levels support robust resource init + outExtensions->robustResourceInitialization = true; + +- // color space selection is always supported in DirectX11 +- outExtensions->colorspaceSRGB = true; +- outExtensions->colorspaceSCRGBLinear = true; +- outExtensions->colorspaceBt2020PQ = true; ++ // color space selection supported in DXGI 1.4 only ++ outExtensions->colorspaceSRGB = mRenderer11DeviceCaps.supportsDXGI1_4; ++ outExtensions->colorspaceSCRGBLinear = mRenderer11DeviceCaps.supportsDXGI1_4; ++ outExtensions->colorspaceBt2020PQ = mRenderer11DeviceCaps.supportsDXGI1_4; + } + + gl::Error Renderer11::flush() +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +index 8f72c5c9aa..fc967b90d0 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +@@ -18,7 +18,10 @@ + #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" + #include "third_party/trace_event/trace_event.h" + +-#include ++#if 0 ++// used only for HDR metadata configuration options ++#include ++#endif + + // Precompiled shaders + #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h" +@@ -626,7 +629,6 @@ EGLint SwapChain11::reset(const gl::Context *context, + + if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_4) + { +-#if defined(ANGLE_ENABLE_D3D11_1) + IDXGISwapChain3 *swapChain3 = d3d11::DynamicCastComObject(mSwapChain); + + printf("*** EGL colorSpace: 0x%X\n", mColorSpace); +@@ -686,7 +688,6 @@ EGLint SwapChain11::reset(const gl::Context *context, + result = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(md), &md); + printf("*** Result hdr 0x%X\n", result); + SafeRelease(swapChain4); +-#endif + #endif + } + +diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp +index c5d5490ea0..9f1e7e3768 100644 +--- a/src/gui/kernel/qopenglcontext.cpp ++++ b/src/gui/kernel/qopenglcontext.cpp +@@ -1311,6 +1311,22 @@ bool QOpenGLContext::isOpenGLES() const + return format().renderableType() == QSurfaceFormat::OpenGLES; + } + ++/*! ++ Returns \c true if the platform supports creation of surfaces with a color space ++ tag. Such surfaces will be converted by the display color space automatically by ++ the platform. ++ ++ The value is controlled by the platform plugin in use and may also depend on the ++ graphics drivers. ++ ++ \since 5.XX ++ */ ++bool QOpenGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) const ++{ ++ Q_D(const QOpenGLContext); ++ return d->platformGLContext->isSurfaceColorSpaceSupported(colorSpace); ++} ++ + /*! + Returns \c true if the platform supports OpenGL rendering outside the main (gui) + thread. +diff --git a/src/gui/kernel/qopenglcontext.h b/src/gui/kernel/qopenglcontext.h +index 9cfaa52f17..1256ab8b5a 100644 +--- a/src/gui/kernel/qopenglcontext.h ++++ b/src/gui/kernel/qopenglcontext.h +@@ -210,6 +210,7 @@ public: + static OpenGLModuleType openGLModuleType(); + + bool isOpenGLES() const; ++ bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) const; + + static bool supportsThreadedOpenGL(); + static QOpenGLContext *globalShareContext(); +diff --git a/src/gui/kernel/qplatformopenglcontext.cpp b/src/gui/kernel/qplatformopenglcontext.cpp +index 07b5a0dda6..72fb818df1 100644 +--- a/src/gui/kernel/qplatformopenglcontext.cpp ++++ b/src/gui/kernel/qplatformopenglcontext.cpp +@@ -165,4 +165,16 @@ bool QPlatformOpenGLContext::parseOpenGLVersion(const QByteArray &versionString, + return (majorOk && minorOk); + } + ++/*! ++ Reimplement in subclass if your platform supports setting a color space for the ++ surface. ++ ++ The default implementation returns false. ++*/ ++bool QPlatformOpenGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) ++{ ++ Q_UNUSED(colorSpace); ++ return false; ++} ++ + QT_END_NAMESPACE +diff --git a/src/gui/kernel/qplatformopenglcontext.h b/src/gui/kernel/qplatformopenglcontext.h +index f307cc14f9..52e744ab29 100644 +--- a/src/gui/kernel/qplatformopenglcontext.h ++++ b/src/gui/kernel/qplatformopenglcontext.h +@@ -90,6 +90,8 @@ public: + + static bool parseOpenGLVersion(const QByteArray &versionString, int &major, int &minor); + ++ virtual bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace); ++ + private: + friend class QOpenGLContext; + +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp +index 18a55c9e1f..7ca8917e34 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp ++++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp +@@ -151,8 +151,9 @@ bool QWindowsLibEGL::init() + eglGetCurrentDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(void)), eglGetCurrentDisplay); + eglSwapBuffers = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface)), eglSwapBuffers); + eglGetProcAddress = RESOLVE((QFunctionPointer (EGLAPIENTRY * )(const char *)), eglGetProcAddress); ++ eglQueryString = RESOLVE((const char* (EGLAPIENTRY *)(EGLDisplay, EGLint)), eglQueryString); + +- if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress) ++ if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress || !eglQueryString) + return false; + + eglGetPlatformDisplayEXT = 0; +@@ -197,8 +198,15 @@ bool QWindowsLibGLESv2::init() + } + + QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) +- : m_display(display) ++ : m_display(display), ++ m_hasSRGBColorSpaceSupport(false), ++ m_hasSCRGBColorSpaceSupport(false), ++ m_hasBt2020PQColorSpaceSupport(false) + { ++ const char *eglExtensions = libEGL.eglQueryString(display, EGL_EXTENSIONS); ++ m_hasSRGBColorSpaceSupport = strstr(eglExtensions, "EGL_KHR_gl_colorspace") != nullptr; ++ m_hasSCRGBColorSpaceSupport = strstr(eglExtensions, "EGL_EXT_gl_colorspace_scrgb_linear") != nullptr; ++ m_hasBt2020PQColorSpaceSupport = strstr(eglExtensions, "EGL_EXT_gl_colorspace_bt2020_pq") != nullptr; + } + + bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, +@@ -303,30 +311,42 @@ void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *na + *err = 0; + + EGLint eglColorSpace = EGL_GL_COLORSPACE_LINEAR_KHR; ++ bool colorSpaceSupported = false; + + switch (colorSpace) { + case QSurfaceFormat::DefaultColorSpace: ++ colorSpaceSupported = m_hasSRGBColorSpaceSupport; + break; + case QSurfaceFormat::sRGBColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_SRGB_KHR; ++ colorSpaceSupported = m_hasSRGBColorSpaceSupport; + break; + case QSurfaceFormat::scRGBColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; ++ colorSpaceSupported = m_hasSCRGBColorSpaceSupport; + break; + case QSurfaceFormat::bt2020PQColorSpace: + eglColorSpace = EGL_GL_COLORSPACE_BT2020_PQ_EXT; ++ colorSpaceSupported = m_hasBt2020PQColorSpaceSupport; + break; + } + +- // TODO: check if the attribute is actually suportef by the implementation +- const EGLint attributes[] = { +- EGL_GL_COLORSPACE, eglColorSpace, +- EGL_NONE +- }; ++ QVector attributes; ++ ++ if (colorSpaceSupported) { ++ attributes << EGL_GL_COLORSPACE << eglColorSpace; ++ } ++ ++ attributes << EGL_NONE; ++ ++ if (!colorSpaceSupported && colorSpace != QSurfaceFormat::DefaultColorSpace) { ++ qWarning().nospace() << __FUNCTION__ << ": Requested color space is not supported by EGL implementation: " << colorSpace << " (egl: 0x" << hex << eglColorSpace << ")"; ++ } ++ + + EGLSurface surface = libEGL.eglCreateWindowSurface(m_display, nativeConfig, + static_cast(nativeWindow), +- attributes); ++ attributes.constData()); + if (surface == EGL_NO_SURFACE) { + *err = libEGL.eglGetError(); + qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); +@@ -764,6 +784,28 @@ QFunctionPointer QWindowsEGLContext::getProcAddress(const char *procName) + return procAddress; + } + ++bool QWindowsEGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) ++{ ++ bool supported = false; ++ ++ switch (colorSpace) { ++ case QSurfaceFormat::DefaultColorSpace: ++ supported = true; ++ break; ++ case QSurfaceFormat::sRGBColorSpace: ++ supported = m_staticContext->hasSRGBColorSpaceSupport(); ++ break; ++ case QSurfaceFormat::scRGBColorSpace: ++ supported = m_staticContext->hasSCRGBColorSpaceSupport(); ++ break; ++ case QSurfaceFormat::bt2020PQColorSpace: ++ supported = m_staticContext->hasBt2020PQColorSpaceSupport(); ++ break; ++ } ++ ++ return supported; ++} ++ + static QVector createConfigAttributesFromFormat(const QSurfaceFormat &format) + { + int redSize = format.redBufferSize(); +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h +index 4f0c2c88ef..cf088e7c15 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.h ++++ b/src/plugins/platforms/windows/qwindowseglcontext.h +@@ -80,6 +80,7 @@ struct QWindowsLibEGL + QFunctionPointer (EGLAPIENTRY *eglGetProcAddress)(const char *procname); + + EGLDisplay (EGLAPIENTRY * eglGetPlatformDisplayEXT)(EGLenum platform, void *native_display, const EGLint *attrib_list); ++ const char* (EGLAPIENTRY * eglQueryString)(EGLDisplay dpy, EGLint name); + + private: + #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) +@@ -129,12 +130,19 @@ public: + static QWindowsLibEGL libEGL; + static QWindowsLibGLESv2 libGLESv2; + ++ bool hasSRGBColorSpaceSupport() { return m_hasSRGBColorSpaceSupport; } ++ bool hasSCRGBColorSpaceSupport() { return m_hasSCRGBColorSpaceSupport; } ++ bool hasBt2020PQColorSpaceSupport() { return m_hasBt2020PQColorSpaceSupport; } ++ + private: + explicit QWindowsEGLStaticContext(EGLDisplay display); + static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor); + + const EGLDisplay m_display; ++ bool m_hasSRGBColorSpaceSupport; ++ bool m_hasSCRGBColorSpaceSupport; ++ bool m_hasBt2020PQColorSpaceSupport; + }; + + class QWindowsEGLContext : public QWindowsOpenGLContext +@@ -158,6 +166,8 @@ public: + void *nativeDisplay() const override { return m_eglDisplay; } + void *nativeConfig() const override { return m_eglConfig; } + ++ bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) override; ++ + private: + EGLConfig chooseConfig(const QSurfaceFormat &format); + +-- +2.20.1.windows.1 + + +From 1d69e29db52ed5039feedf2fea51af9f37d42eb3 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 28 Jan 2019 12:55:35 +0300 +Subject: [PATCH 16/20] Fix Angle to work correctly on Windows 7 + +"Flip" modes are not available on older versions of DirectX, +so it is not safe to request it as a fallback case. +--- + .../renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +index c81b33fee9..794ab971ab 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +@@ -146,6 +146,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + + // Use IDXGIFactory2::CreateSwapChainForHwnd if DXGI 1.2 is available to create a + // DXGI_SWAP_EFFECT_SEQUENTIAL swap chain. ++ // ++ // NOTE: in non-flip mode HDR rendering is not supported, so use it ++ // by default + IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); + if (factory2 != nullptr) + { +@@ -193,21 +196,20 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + swapChainDesc.Windowed = TRUE; + + /** +- * NOTE1: in flip-discard mode the swap chain doesn't support partial ++ * NOTE: in discard mode the swap chain doesn't support partial + * presentatiopn with Present1() call. Though it is not a big + * problem, because in case DXGI 1.2 is supported this code is + * unreachable. +- * +- * NOTE2: in non-flip mode HDR rendering is not supported, so use it +- * bt default + */ +- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; ++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); ++ + if (SUCCEEDED(result)) + { + factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); + } ++ + return result; + } + +-- +2.20.1.windows.1 + + +From 9b08f39610885381c61a3314ed40e658d2ddcaae Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 28 Jan 2019 14:39:47 +0300 +Subject: [PATCH 17/20] Allow color space selection attributes for + eglCreatePbufferSurface + +Notes: + +eglCreatePixmapSurface() is not implemented in Angle, so the support is +not added. + +eglCreatePlatformWindowSurface() and eglCreatePlatformPixmapSurface() +do not have support for color spaces according to the extension wording +(and they are also not supported by Angle :) ) +--- + .../angle/src/libANGLE/validationEGL.cpp | 27 +++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +index 3f6a426320..1e9535ee1c 100644 +--- a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++++ b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +@@ -1003,6 +1003,33 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri + } + break; + ++ case EGL_GL_COLORSPACE: ++ ++ if (!displayExtensions.colorspaceSRGB) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_KHR_gl_colorspace is not supported on this platform."); ++ } ++ ++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) ++ { ++ if (!displayExtensions.colorspaceSCRGBLinear) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."); ++ } ++ } ++ else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) ++ { ++ if (!displayExtensions.colorspaceBt2020PQ) ++ { ++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."); ++ } ++ } ++ else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) ++ { ++ return Error(EGL_BAD_ATTRIBUTE); ++ } ++ break; ++ + default: + return EglBadAttribute(); + } +-- +2.20.1.windows.1 + + +From 14d1b9c586003fbc446abb32f36de7fa2972b5a9 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Sun, 10 Feb 2019 22:55:59 +0300 +Subject: [PATCH 18/20] Implement a manual test for checking is HDR features + work + +Test plan: + +1) Run without arguments: `hdr-openglwidget.exe` + It should show you three rectangles: the left one should be HDR'ly + bright, the other ones should be SDR'ly dim and look exactly the same. + +3) Run in Bt. 2020 PQ mode: `hdr-openglwidget.exe --bt2020pq` + The result should look exactly the same. + +4) Run in SDR sRGB mode: `hdr-openglwidget.exe --srgb`. + All three images should look SDR'ly dim. + +NOTE: +Please note that the current implementation of SDR compositing +in QOpenGLTextureBlitter doesn't support user configuration for +SDR brightness from the system. This API is available for UWP +applications only. It means that when changing "SDR brightness" +slider in Windows' settings, the brightness of our SDR widget +will not change. More that that, it might even be different from +teh brightness of other SDr applications. + +Change-Id: Idccc790937c9061ec618ab21f6b71bd0620cd2cc +--- + .../hdr-qopenglwidget/KisGLImageF16.cpp | 131 +++++++++ + .../manual/hdr-qopenglwidget/KisGLImageF16.h | 68 +++++ + .../hdr-qopenglwidget/KisGLImageWidget.cpp | 252 ++++++++++++++++++ + .../hdr-qopenglwidget/KisGLImageWidget.h | 77 ++++++ + .../hdr-qopenglwidget/hdr-openglwidget.pro | 20 ++ + .../kis_gl_image_widget.frag | 23 ++ + .../hdr-qopenglwidget/kis_gl_image_widget.qrc | 6 + + .../kis_gl_image_widget.vert | 17 ++ + tests/manual/hdr-qopenglwidget/main.cpp | 153 +++++++++++ + .../hdr-qopenglwidget/openglprobeutils.cpp | 139 ++++++++++ + .../hdr-qopenglwidget/openglprobeutils.h | 42 +++ + tests/manual/hdr-qopenglwidget/window.cpp | 219 +++++++++++++++ + tests/manual/hdr-qopenglwidget/window.h | 69 +++++ + 13 files changed, 1216 insertions(+) + create mode 100644 tests/manual/hdr-qopenglwidget/KisGLImageF16.cpp + create mode 100644 tests/manual/hdr-qopenglwidget/KisGLImageF16.h + create mode 100644 tests/manual/hdr-qopenglwidget/KisGLImageWidget.cpp + create mode 100644 tests/manual/hdr-qopenglwidget/KisGLImageWidget.h + create mode 100644 tests/manual/hdr-qopenglwidget/hdr-openglwidget.pro + create mode 100644 tests/manual/hdr-qopenglwidget/kis_gl_image_widget.frag + create mode 100644 tests/manual/hdr-qopenglwidget/kis_gl_image_widget.qrc + create mode 100644 tests/manual/hdr-qopenglwidget/kis_gl_image_widget.vert + create mode 100644 tests/manual/hdr-qopenglwidget/main.cpp + create mode 100644 tests/manual/hdr-qopenglwidget/openglprobeutils.cpp + create mode 100644 tests/manual/hdr-qopenglwidget/openglprobeutils.h + create mode 100644 tests/manual/hdr-qopenglwidget/window.cpp + create mode 100644 tests/manual/hdr-qopenglwidget/window.h + +diff --git a/tests/manual/hdr-qopenglwidget/KisGLImageF16.cpp b/tests/manual/hdr-qopenglwidget/KisGLImageF16.cpp +new file mode 100644 +index 0000000000..a84b676f5b +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/KisGLImageF16.cpp +@@ -0,0 +1,131 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "KisGLImageF16.h" ++ ++#include ++#include ++ ++struct KisGLImageF16::Private : public QSharedData ++{ ++ QSize size; ++ QByteArray data; ++}; ++ ++KisGLImageF16::KisGLImageF16() ++ : m_d(new Private) ++{ ++} ++ ++KisGLImageF16::KisGLImageF16(const QSize &size, bool clearPixels) ++ : m_d(new Private) ++{ ++ resize(size, clearPixels); ++} ++ ++KisGLImageF16::KisGLImageF16(int width, int height, bool clearPixels) ++ : KisGLImageF16(QSize(width, height), clearPixels) ++{ ++} ++ ++KisGLImageF16::KisGLImageF16(const KisGLImageF16 &rhs) ++ : m_d(rhs.m_d) ++{ ++} ++ ++KisGLImageF16 &KisGLImageF16::operator=(const KisGLImageF16 &rhs) ++{ ++ m_d = rhs.m_d; ++} ++ ++bool operator==(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs) ++{ ++ return lhs.m_d == rhs.m_d; ++} ++ ++bool operator!=(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs) ++{ ++ return !(lhs == rhs); ++} ++ ++KisGLImageF16::~KisGLImageF16() ++{ ++} ++ ++void KisGLImageF16::clearPixels() ++{ ++ if (!m_d->data.isEmpty()) { ++ m_d->data.fill(0); ++ } ++} ++ ++void KisGLImageF16::resize(const QSize &size, bool clearPixels) ++{ ++ const int pixelSize = 2 * 4; ++ ++ m_d->size = size; ++ m_d->data.resize(size.width() * size.height() * pixelSize); ++ ++ if (clearPixels) { ++ m_d->data.fill(0); ++ } ++} ++ ++const qfloat16 *KisGLImageF16::constData() const ++{ ++ Q_ASSERT(!m_d->data.isNull()); ++ return reinterpret_cast(m_d->data.data()); ++} ++ ++qfloat16 *KisGLImageF16::data() ++{ ++ m_d->data.detach(); ++ Q_ASSERT(!m_d->data.isNull()); ++ ++ return reinterpret_cast(m_d->data.data()); ++} ++ ++QSize KisGLImageF16::size() const ++{ ++ return m_d->size; ++} ++ ++int KisGLImageF16::width() const ++{ ++ return m_d->size.width(); ++} ++ ++int KisGLImageF16::height() const ++{ ++ return m_d->size.height(); ++} ++ ++bool KisGLImageF16::isNull() const ++{ ++ return m_d->data.isNull(); ++} +diff --git a/tests/manual/hdr-qopenglwidget/KisGLImageF16.h b/tests/manual/hdr-qopenglwidget/KisGLImageF16.h +new file mode 100644 +index 0000000000..335e42ee68 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/KisGLImageF16.h +@@ -0,0 +1,68 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef KISGLIMAGEF16_H ++#define KISGLIMAGEF16_H ++ ++#include ++#include ++ ++class QSize; ++ ++class KisGLImageF16 ++{ ++public: ++ KisGLImageF16(); ++ KisGLImageF16(const QSize &size, bool clearPixels = false); ++ KisGLImageF16(int width, int height, bool clearPixels = false); ++ KisGLImageF16(const KisGLImageF16 &rhs); ++ KisGLImageF16& operator=(const KisGLImageF16 &rhs); ++ ++ friend bool operator==(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs); ++ friend bool operator!=(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs); ++ ++ ~KisGLImageF16(); ++ ++ void clearPixels(); ++ void resize(const QSize &size, bool clearPixels = false); ++ ++ const qfloat16* constData() const; ++ qfloat16* data(); ++ ++ QSize size() const; ++ int width() const; ++ int height() const; ++ ++ bool isNull() const; ++ ++private: ++ struct Private; ++ QSharedDataPointer m_d; ++}; ++ ++#endif // KISGLIMAGEF16_H +diff --git a/tests/manual/hdr-qopenglwidget/KisGLImageWidget.cpp b/tests/manual/hdr-qopenglwidget/KisGLImageWidget.cpp +new file mode 100644 +index 0000000000..da36ac1619 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/KisGLImageWidget.cpp +@@ -0,0 +1,252 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "KisGLImageWidget.h" ++ ++#include ++#include ++#include ++ ++#include "KisGLImageF16.h" ++ ++namespace { ++inline void rectToVertices(QVector3D* vertices, const QRectF &rc) ++{ ++ vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); ++ vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); ++ vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); ++ vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); ++ vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); ++ vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); ++} ++ ++inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) ++{ ++ texCoords[0] = QVector2D(rc.left(), rc.bottom()); ++ texCoords[1] = QVector2D(rc.left(), rc.top()); ++ texCoords[2] = QVector2D(rc.right(), rc.bottom()); ++ texCoords[3] = QVector2D(rc.left(), rc.top()); ++ texCoords[4] = QVector2D(rc.right(), rc.top()); ++ texCoords[5] = QVector2D(rc.right(), rc.bottom()); ++} ++} ++ ++KisGLImageWidget::KisGLImageWidget(QWidget *parent) ++ : KisGLImageWidget(QSurfaceFormat::sRGBColorSpace, parent) ++{ ++} ++ ++KisGLImageWidget::KisGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, ++ QWidget *parent) ++ : QOpenGLWidget(parent), ++ m_texture(QOpenGLTexture::Target2D) ++{ ++ ++ qDebug() << "Crating gl widget"; ++ ++ setTextureFormat(GL_RGBA16F); ++ setTextureColorSpace(colorSpace); ++ ++ setUpdateBehavior(QOpenGLWidget::NoPartialUpdate); ++} ++ ++void KisGLImageWidget::initializeGL() ++{ ++ initializeOpenGLFunctions(); ++ ++ qDebug() << "Initialized with format:" << context()->format(); ++ ++ QFile vertexShaderFile(QString(":/") + "kis_gl_image_widget.vert"); ++ vertexShaderFile.open(QIODevice::ReadOnly); ++ QString vertSource = vertexShaderFile.readAll(); ++ ++ QFile fragShaderFile(QString(":/") + "kis_gl_image_widget.frag"); ++ fragShaderFile.open(QIODevice::ReadOnly); ++ QString fragSource = fragShaderFile.readAll(); ++ ++ if (context()->isOpenGLES()) { ++ const char *versionHelper = "#define USE_OPENGLES\n"; ++ vertSource.prepend(versionHelper); ++ fragSource.prepend(versionHelper); ++ ++ const char *versionDefinition = "#version 100\n"; ++ vertSource.prepend(versionDefinition); ++ fragSource.prepend(versionDefinition); ++ } else { ++ const char *versionDefinition = "#version 330 core\n"; ++ vertSource.prepend(versionDefinition); ++ fragSource.prepend(versionDefinition); ++ } ++ ++ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource)) { ++ qDebug() << "Could not add vertex code"; ++ return; ++ } ++ ++ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) { ++ qDebug() << "Could not add fragment code"; ++ return; ++ } ++ ++ if (!m_shader.link()) { ++ qDebug() << "Could not link"; ++ return; ++ } ++ ++ if (!m_shader.bind()) { ++ qDebug() << "Could not bind"; ++ return; ++ } ++ ++ m_shader.release(); ++ ++ ++ m_vao.create(); ++ m_vao.bind(); ++ ++ m_verticesBuffer.create(); ++ updateVerticesBuffer(this->rect()); ++ ++ QVector textureVertices(6); ++ rectToTexCoords(textureVertices.data(), QRect(0.0, 0.0, 1.0, 1.0)); ++ ++ m_textureVerticesBuffer.create(); ++ m_textureVerticesBuffer.bind(); ++ m_textureVerticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); ++ m_textureVerticesBuffer.allocate(2 * 3 * sizeof(QVector2D)); ++ m_verticesBuffer.write(0, textureVertices.data(), m_textureVerticesBuffer.size()); ++ m_textureVerticesBuffer.release(); ++ ++ m_vao.release(); ++ ++ ++ if (!m_sourceImage.isNull()) { ++ loadImage(m_sourceImage); ++ } ++} ++ ++void KisGLImageWidget::updateVerticesBuffer(const QRect &rect) ++{ ++ if (!m_vao.isCreated() || !m_verticesBuffer.isCreated()) return; ++ ++ QVector vertices(6); ++ rectToVertices(vertices.data(), rect); ++ ++ m_verticesBuffer.bind(); ++ m_verticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); ++ m_verticesBuffer.allocate(2 * 3 * sizeof(QVector3D)); ++ m_verticesBuffer.write(0, vertices.data(), m_verticesBuffer.size()); ++ m_verticesBuffer.release(); ++} ++ ++ ++void KisGLImageWidget::paintGL() ++{ ++ const QColor bgColor = palette().background().color(); ++ glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), 1.0f); ++ glClear(GL_COLOR_BUFFER_BIT); ++ ++ if (!m_texture.isCreated()) return; ++ ++ glViewport(0, 0, width(), height()); ++ ++ m_vao.bind(); ++ m_shader.bind(); ++ ++ { ++ QMatrix4x4 projectionMatrix; ++ projectionMatrix.setToIdentity(); ++ projectionMatrix.ortho(0, width(), height(), 0, -1, 1); ++ QMatrix4x4 viewProjectionMatrix; ++ ++ // use a QTransform to scale, translate, rotate your view ++ QTransform transform; // TODO: noop! ++ viewProjectionMatrix = projectionMatrix * QMatrix4x4(transform); ++ ++ m_shader.setUniformValue("viewProjectionMatrix", viewProjectionMatrix); ++ } ++ ++ m_shader.enableAttributeArray("vertexPosition"); ++ m_verticesBuffer.bind(); ++ m_shader.setAttributeBuffer("vertexPosition", GL_FLOAT, 0, 3); ++ ++ m_shader.enableAttributeArray("texturePosition"); ++ m_textureVerticesBuffer.bind(); ++ m_shader.setAttributeBuffer("texturePosition", GL_FLOAT, 0, 2); ++ ++ glActiveTexture(GL_TEXTURE0); ++ m_texture.bind(); ++ ++ // draw 2 triangles = 6 vertices starting at offset 0 in the buffer ++ glDrawArrays(GL_TRIANGLES, 0, 6); ++ ++ m_verticesBuffer.release(); ++ m_textureVerticesBuffer.release(); ++ m_texture.release(); ++ m_shader.release(); ++ m_vao.release(); ++} ++ ++void KisGLImageWidget::loadImage(const KisGLImageF16 &image) ++{ ++ if (m_sourceImage != image) { ++ m_sourceImage = image; ++ } ++ ++ if (m_vao.isCreated()) { ++ m_texture.setFormat(QOpenGLTexture::RGBA16F); ++ m_texture.setSize(image.width(), image.height()); ++ m_texture.allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float16); ++ m_texture.setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); ++ m_texture.setMagnificationFilter(QOpenGLTexture::Linear); ++ m_texture.setData(QOpenGLTexture::RGBA, QOpenGLTexture::Float16, image.constData()); ++ updateGeometry(); ++ } ++} ++ ++void KisGLImageWidget::paintEvent(QPaintEvent *event) ++{ ++ QOpenGLWidget::paintEvent(event); ++} ++ ++void KisGLImageWidget::resizeEvent(QResizeEvent *event) ++{ ++ updateVerticesBuffer(QRect(QPoint(),event->size())); ++ QOpenGLWidget::resizeEvent(event); ++} ++ ++QSize KisGLImageWidget::sizeHint() const ++{ ++ return m_sourceImage.size(); ++} ++ ++KisGLImageF16 KisGLImageWidget::image() const ++{ ++ return m_sourceImage; ++} ++ +diff --git a/tests/manual/hdr-qopenglwidget/KisGLImageWidget.h b/tests/manual/hdr-qopenglwidget/KisGLImageWidget.h +new file mode 100644 +index 0000000000..e807064cb4 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/KisGLImageWidget.h +@@ -0,0 +1,77 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef KISGLIMAGEWIDGET_H ++#define KISGLIMAGEWIDGET_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++class KisGLImageWidget : public QOpenGLWidget, protected QOpenGLFunctions ++{ ++ Q_OBJECT ++public: ++ KisGLImageWidget(QWidget *parent = nullptr); ++ KisGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, ++ QWidget *parent = nullptr); ++ ++ void initializeGL() override; ++ void paintGL() override; ++ ++ void loadImage(const KisGLImageF16 &image); ++ ++ void paintEvent(QPaintEvent *event) override; ++ void resizeEvent(QResizeEvent *event) override; ++ ++ QSize sizeHint() const override; ++ ++ KisGLImageF16 image() const; ++ ++public Q_SLOTS: ++ ++private: ++ void updateVerticesBuffer(const QRect &rect); ++ ++private: ++ KisGLImageF16 m_sourceImage; ++ ++ QOpenGLShaderProgram m_shader; ++ QOpenGLVertexArrayObject m_vao; ++ QOpenGLBuffer m_verticesBuffer; ++ QOpenGLBuffer m_textureVerticesBuffer; ++ QOpenGLTexture m_texture; ++}; ++ ++#endif // KISGLIMAGEWIDGET_H +diff --git a/tests/manual/hdr-qopenglwidget/hdr-openglwidget.pro b/tests/manual/hdr-qopenglwidget/hdr-openglwidget.pro +new file mode 100644 +index 0000000000..b418e54b43 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/hdr-openglwidget.pro +@@ -0,0 +1,20 @@ ++QT += widgets widgets-private gui-private core-private ++ ++TARGET = hdr-openglwidget ++TEMPLATE = app ++ ++SOURCES += main.cpp \ ++ #hdr-openglwidget.cpp \ ++ openglprobeutils.cpp \ ++ KisGLImageWidget.cpp \ ++ KisGLImageF16.cpp \ ++ window.cpp ++ ++HEADERS += \ ++#hdr-openglwidget.h \ ++ openglprobeutils.h \ ++ KisGLImageWidget.h \ ++ KisGLImageF16.h \ ++ window.h ++ ++RESOURCES += kis_gl_image_widget.qrc +diff --git a/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.frag b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.frag +new file mode 100644 +index 0000000000..57475a31dc +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.frag +@@ -0,0 +1,23 @@ ++#ifndef USE_OPENGLES ++#define INATTR in ++#define OUTATTR out ++#define DECLARE_OUT_VAR out vec4 f_fragColor; ++#define OUT_VAR f_fragColor ++#define highp ++#define texture2D texture ++#else ++#define INATTR varying ++#define DECLARE_OUT_VAR ++#define OUT_VAR gl_FragColor ++#endif ++// vertices datas ++INATTR highp vec4 textureCoordinates; ++uniform sampler2D f_tileTexture; ++DECLARE_OUT_VAR ++ ++void main() ++{ ++ // get the fragment color from the tile texture ++ highp vec4 color = texture2D(f_tileTexture, textureCoordinates.st); ++ OUT_VAR = vec4(color); ++} +diff --git a/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.qrc b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.qrc +new file mode 100644 +index 0000000000..ab5b5719a9 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.qrc +@@ -0,0 +1,6 @@ ++ ++ ++ kis_gl_image_widget.frag ++ kis_gl_image_widget.vert ++ ++ +diff --git a/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.vert b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.vert +new file mode 100644 +index 0000000000..9578f47945 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/kis_gl_image_widget.vert +@@ -0,0 +1,17 @@ ++#ifndef USE_OPENGLES ++#define INATTR in ++#define OUTATTR out ++#define highp ++#else ++#define INATTR attribute ++#define OUTATTR varying ++#endif ++uniform mat4 viewProjectionMatrix; ++INATTR highp vec3 vertexPosition; ++INATTR highp vec2 texturePosition; ++OUTATTR highp vec4 textureCoordinates; ++void main() ++{ ++ textureCoordinates = vec4(texturePosition.x, texturePosition.y, 0.0, 1.0); ++ gl_Position = viewProjectionMatrix * vec4(vertexPosition.x, vertexPosition.y, 0.0, 1.0); ++} +diff --git a/tests/manual/hdr-qopenglwidget/main.cpp b/tests/manual/hdr-qopenglwidget/main.cpp +new file mode 100644 +index 0000000000..d5bae17734 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/main.cpp +@@ -0,0 +1,153 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2016 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include ++#include "window.h" ++ ++#include "openglprobeutils.h" ++#include ++ ++QSurfaceFormat generateSurfaceFormat(QSurfaceFormat::RenderableType renderer, ++ QSurfaceFormat::ColorSpace colorSpace, ++ int bitDepth) ++{ ++ QSurfaceFormat format; ++#ifdef Q_OS_OSX ++ format.setVersion(3, 2); ++ format.setProfile(QSurfaceFormat::CoreProfile); ++#else ++ format.setVersion(3, 0); ++ format.setProfile(QSurfaceFormat::CoreProfile); ++#endif ++ format.setDepthBufferSize(24); ++ format.setStencilBufferSize(8); ++ ++ switch (bitDepth) { ++ case 8: ++ format.setRedBufferSize(8); ++ format.setGreenBufferSize(8); ++ format.setBlueBufferSize(8); ++ format.setAlphaBufferSize(8); ++ break; ++ case 10: ++ format.setRedBufferSize(10); ++ format.setGreenBufferSize(10); ++ format.setBlueBufferSize(10); ++ format.setAlphaBufferSize(2); ++ break; ++ case 16: ++ format.setRedBufferSize(16); ++ format.setGreenBufferSize(16); ++ format.setBlueBufferSize(16); ++ format.setAlphaBufferSize(16); ++ break; ++ default: ++ qFatal("Unsupported surface bit depth %d", bitDepth); ++ } ++ ++ format.setRenderableType(renderer); ++ format.setColorSpace(colorSpace); ++ ++ format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); ++ format.setSwapInterval(0); // Disable vertical refresh syncing ++ ++ return format; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ QVector allFormats; ++ ++ QVector availableRenderers; ++ availableRenderers << QSurfaceFormat::OpenGL; ++ availableRenderers << QSurfaceFormat::OpenGLES; ++ ++ for (QSurfaceFormat::RenderableType renderer : availableRenderers) { ++ allFormats << generateSurfaceFormat(renderer, QSurfaceFormat::sRGBColorSpace, 8); ++ allFormats << generateSurfaceFormat(renderer, QSurfaceFormat::bt2020PQColorSpace, 8); ++ allFormats << generateSurfaceFormat(renderer, QSurfaceFormat::sRGBColorSpace, 10); ++ allFormats << generateSurfaceFormat(renderer, QSurfaceFormat::bt2020PQColorSpace, 10); ++ allFormats << generateSurfaceFormat(renderer, QSurfaceFormat::scRGBColorSpace, 16); ++ } ++ ++ for (QSurfaceFormat format : allFormats) { ++ qDebug() << "Probing: " << format; ++ bool result = OpenGLProbeUtils::probeFormat(format, true); ++ qDebug() << " result:" << result; ++ } ++ ++ ++ if (argc > 1 && !strcmp(argv[1], "--sharecontext")) { ++ qDebug("Requesting all contexts to share"); ++ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); ++ } ++ ++ QApplication a(argc, argv); ++ ++ QSurfaceFormat::RenderableType renderer = QSurfaceFormat::OpenGLES; ++ QSurfaceFormat::ColorSpace colorSpace = QSurfaceFormat::scRGBColorSpace; ++ int bitDepth = 16; ++ ++ ++ if (QCoreApplication::arguments().contains(QLatin1String("--scrgb"))) { ++ colorSpace = QSurfaceFormat::scRGBColorSpace; ++ bitDepth = 16; ++ } else if (QCoreApplication::arguments().contains(QLatin1String("--bt2020pq"))) { ++ colorSpace = QSurfaceFormat::bt2020PQColorSpace; ++ bitDepth = 10; ++ } else if (QCoreApplication::arguments().contains(QLatin1String("--srgb"))) { ++ colorSpace = QSurfaceFormat::sRGBColorSpace; ++ bitDepth = 8; ++ } ++ ++ if (QCoreApplication::arguments().contains(QLatin1String("--opengl"))) { ++ renderer = QSurfaceFormat::OpenGL; ++ } else if (QCoreApplication::arguments().contains(QLatin1String("--opengles"))) { ++ renderer = QSurfaceFormat::OpenGLES; ++ } ++ ++ QSurfaceFormat format = generateSurfaceFormat(renderer, colorSpace, bitDepth); ++ ++ if (QCoreApplication::arguments().contains(QLatin1String("--multisample"))) { ++ format.setSamples(4); ++ } ++ ++ if (format.renderableType() == QSurfaceFormat::OpenGL) { ++ QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); ++ } else if (format.renderableType() == QSurfaceFormat::OpenGLES) { ++ QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); ++ } ++ ++ qDebug() << "Requesting" << format.renderableType() << format; ++ QSurfaceFormat::setDefaultFormat(format); ++ ++ Window window; ++ window.show(); ++ ++ return a.exec(); ++} +diff --git a/tests/manual/hdr-qopenglwidget/openglprobeutils.cpp b/tests/manual/hdr-qopenglwidget/openglprobeutils.cpp +new file mode 100644 +index 0000000000..687cc08904 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/openglprobeutils.cpp +@@ -0,0 +1,139 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "openglprobeutils.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++namespace OpenGLProbeUtils { ++ ++namespace { ++ ++struct AppAttributeSetter ++{ ++ AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES) ++ : m_attribute(attribute), ++ m_oldValue(QCoreApplication::testAttribute(attribute)) ++ { ++ QCoreApplication::setAttribute(attribute, useOpenGLES); ++ } ++ ++ ~AppAttributeSetter() { ++ QCoreApplication::setAttribute(m_attribute, m_oldValue); ++ } ++ ++private: ++ Qt::ApplicationAttribute m_attribute; ++ bool m_oldValue = false; ++}; ++ ++struct SurfaceFormatSetter ++{ ++ SurfaceFormatSetter(const QSurfaceFormat &format) ++ : m_oldFormat(QSurfaceFormat::defaultFormat()) ++ { ++ QSurfaceFormat::setDefaultFormat(format); ++ } ++ ++ ~SurfaceFormatSetter() { ++ QSurfaceFormat::setDefaultFormat(m_oldFormat); ++ } ++ ++private: ++ QSurfaceFormat m_oldFormat; ++}; ++ ++} ++ ++bool fuzzyCompareColorSpaces(const QSurfaceFormat::ColorSpace &lhs, const QSurfaceFormat::ColorSpace &rhs) ++{ ++ return lhs == rhs || ++ ((lhs == QSurfaceFormat::DefaultColorSpace || ++ lhs == QSurfaceFormat::sRGBColorSpace) && ++ (rhs == QSurfaceFormat::DefaultColorSpace || ++ rhs == QSurfaceFormat::sRGBColorSpace)); ++} ++ ++bool probeFormat(const QSurfaceFormat &format, bool adjustGlobalState) ++{ ++ QScopedPointer sharedContextSetter; ++ QScopedPointer glSetter; ++ QScopedPointer glesSetter; ++ QScopedPointer formatSetter; ++ QScopedPointer application; ++ ++ if (adjustGlobalState) { ++ sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false)); ++ ++ if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) { ++ glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES)); ++ glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES)); ++ } ++ ++ formatSetter.reset(new SurfaceFormatSetter(format)); ++ ++ int argc = 1; ++ QByteArray data("krita"); ++ char *argv = data.data(); ++ application.reset(new QApplication(argc, &argv)); ++ } ++ ++ QWindow surface; ++ surface.setFormat(format); ++ surface.setSurfaceType(QSurface::OpenGLSurface); ++ surface.create(); ++ QOpenGLContext context; ++ context.setFormat(format); ++ ++ ++ if (!context.create()) { ++ qCritical() << "OpenGL context cannot be created"; ++ return false; ++ } ++ if (!context.isValid()) { ++ qCritical() << "OpenGL context is not valid while checking Qt's OpenGL status"; ++ return false; ++ } ++ if (!context.makeCurrent(&surface)) { ++ qCritical() << "OpenGL context cannot be made current"; ++ return false; ++ } ++ ++ if (!fuzzyCompareColorSpaces(context.format().colorSpace(), format.colorSpace())) { ++ qCritical() << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace(); ++ return false; ++ } ++ ++ return true; ++} ++ ++} +diff --git a/tests/manual/hdr-qopenglwidget/openglprobeutils.h b/tests/manual/hdr-qopenglwidget/openglprobeutils.h +new file mode 100644 +index 0000000000..3b2f1ec3d0 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/openglprobeutils.h +@@ -0,0 +1,42 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef OPENGLPROBEUTILS_H ++#define OPENGLPROBEUTILS_H ++ ++#include ++ ++namespace OpenGLProbeUtils ++{ ++ ++bool fuzzyCompareColorSpaces(const QSurfaceFormat::ColorSpace &lhs, const QSurfaceFormat::ColorSpace &rhs); ++bool probeFormat(const QSurfaceFormat &format, bool adjustGlobalState); ++ ++}; ++ ++#endif // OPENGLPROBEUTILS_H +diff --git a/tests/manual/hdr-qopenglwidget/window.cpp b/tests/manual/hdr-qopenglwidget/window.cpp +new file mode 100644 +index 0000000000..5729660a4f +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/window.cpp +@@ -0,0 +1,219 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "window.h" ++ ++#include "KisGLImageWidget.h" ++#include "KisGLImageF16.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++ ++Window::Window() ++{ ++ setWindowTitle("16 bit float QOpenGLWidget test"); ++ QMenu *menu = menuBar()->addMenu("File"); ++ QToolBar *tb = addToolBar("File"); ++ ++ m_quitAction = new QAction("Quit", this); ++ connect(m_quitAction, SIGNAL(triggered(bool)), this, SLOT(close())); ++ menu->addAction(m_quitAction); ++ tb->addAction(m_quitAction); ++ ++ QWidget *centralWidget = new QWidget(this); ++ QVBoxLayout *layout = new QVBoxLayout(centralWidget); ++ ++ QHBoxLayout *hLayout = new QHBoxLayout(centralWidget); ++ ++ m_imageWidget = new KisGLImageWidget(QSurfaceFormat::scRGBColorSpace, this); ++ m_imageWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); ++ hLayout->addWidget(m_imageWidget, 0, Qt::AlignLeft); ++ ++ m_imageWidgetSdr = new KisGLImageWidget(QSurfaceFormat::scRGBColorSpace, this); ++ m_imageWidgetSdr->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); ++ hLayout->addWidget(m_imageWidgetSdr, 0, Qt::AlignLeft); ++ ++ QImage image(QSize(255,255), QImage::Format_ARGB32); ++ image.fill(Qt::red); ++ ++ QLabel *label = new QLabel(this); ++ label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); ++ hLayout->addWidget(label, 0, Qt::AlignLeft); ++ ++ m_imageWidget->loadImage(initializeImage(false)); ++ m_imageWidgetSdr->loadImage(initializeImage(true)); ++ label->setPixmap(QPixmap::fromImage(convertToQImage(m_imageWidget->image()))); ++ ++ layout->addLayout(hLayout); ++ ++ m_lblContextInfo = new QLabel(this); ++ layout->addWidget(m_lblContextInfo); ++ ++ QLabel *lblNotes = new QLabel(this); ++ lblNotes->setWordWrap(true); ++ lblNotes->setText("* In SDR display mode all three images should look exactly the same\n" ++ "* In HDR display mode: image 1 should look brighter than the others " ++ "(it is HDR), images 2 and 3 should have exactly the same brightness and look"); ++ ++ layout->addWidget(lblNotes); ++ ++ centralWidget->setLayout(layout); ++ setCentralWidget(centralWidget); ++} ++ ++inline qfloat16 floatToFloat16(float x) { ++ qfloat16 result; ++ qFloatToFloat16(&result, &x, 1); ++ return result; ++} ++ ++inline float float16ToFloat(qfloat16 x) { ++ float result; ++ qFloatFromFloat16(&result, &x, 1); ++ return result; ++} ++ ++ ++KisGLImageF16 Window::initializeImage(bool cropRange) const ++{ ++ const int size = 256; ++ KisGLImageF16 image(size, size); ++ image.clearPixels(); ++ qfloat16 *pixelPtr = image.data(); ++ ++ for (int y = 0; y < size; y++) { ++ for (int x = 0; x < size; x++) { ++ qfloat16 *pxl = reinterpret_cast(pixelPtr); ++ ++ float hdrRedValue = 25.0f * std::pow(float(x) / size, 5.0f); ++ ++ if (cropRange) { ++ hdrRedValue = qMin(hdrRedValue, 1.0f); ++ } ++ ++ pxl[0] = floatToFloat16(hdrRedValue); ++ ++ if (y > size / 2) { ++ const float portion = (float(y) / size - 0.5f) * 2.0f; ++ const float value = qMin(1.0f, 0.2f + 1.8f * portion); ++ ++ pxl[1] = floatToFloat16(value); ++ pxl[2] = floatToFloat16(value); ++ } else { ++ pxl[1] = floatToFloat16(0.2); ++ pxl[2] = floatToFloat16(0.2); ++ } ++ ++ pxl[3] = floatToFloat16(1.0); ++ ++ pixelPtr += 4; ++ } ++ } ++ ++ return image; ++} ++ ++inline float linearToSRGB(float value) ++{ ++ if (value <= 0.0f) { ++ value = 0.0f; ++ } else if (value < 0.0031308f) { ++ value = value * 12.92f; ++ } else if (value < 1.0f) { ++ value = std::pow(value, 0.41666f) * 1.055f - 0.055f; ++ } else { ++ value = 1.0f; ++ } ++ return value; ++} ++ ++QImage Window::convertToQImage(const KisGLImageF16 &image) const ++{ ++ const QSize size = image.size(); ++ const qfloat16 *pixelPtr = image.constData(); ++ ++ QImage qimage(size, QImage::Format_ARGB32); ++ quint8 *qimagePixelPtr = qimage.bits(); ++ ++ ++ for (int y = 0; y < size.height(); y++) { ++ for (int x = 0; x < size.width(); x++) { ++ const qfloat16 *srcPxl = pixelPtr; ++ quint8 *dstPxl = qimagePixelPtr; ++ ++ auto convertChannel = [] (qfloat16 x) { ++ float value = float16ToFloat(x); ++ return quint8(linearToSRGB(value) * 255.0f); ++ }; ++ ++ dstPxl[0] = convertChannel(srcPxl[2]); ++ dstPxl[1] = convertChannel(srcPxl[1]); ++ dstPxl[2] = convertChannel(srcPxl[0]); ++ dstPxl[3] = convertChannel(srcPxl[3]); ++ ++ pixelPtr += 4; ++ qimagePixelPtr += 4; ++ } ++ } ++ ++ return qimage; ++} ++ ++void Window::updateSurfaceInfo() ++{ ++ const QSurfaceFormat format = m_imageWidget->context()->format(); ++ ++ m_lblContextInfo->setText( ++ QString("renderer: %1\ncolorSpace: %2\n\n") ++ .arg(format.renderableType() == QSurfaceFormat::OpenGL ? "openGL" : "openGL ES") ++ .arg(format.colorSpace() == QSurfaceFormat::sRGBColorSpace ? "sRGB" : ++ format.colorSpace() == QSurfaceFormat::scRGBColorSpace ? "scRGB" : ++ format.colorSpace() == QSurfaceFormat::bt2020PQColorSpace ? "Bt. 2020 PQ" : ++ "unknown")); ++} ++ ++void Window::showEvent(QShowEvent *ev) ++{ ++ QMainWindow::showEvent(ev); ++ ++ if (m_lblContextInfo->text().isEmpty()) { ++ updateSurfaceInfo(); ++ } ++} +diff --git a/tests/manual/hdr-qopenglwidget/window.h b/tests/manual/hdr-qopenglwidget/window.h +new file mode 100644 +index 0000000000..fd8e5c0393 +--- /dev/null ++++ b/tests/manual/hdr-qopenglwidget/window.h +@@ -0,0 +1,69 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2019 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3 as published by the Free Software ++** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef WINDOW_H ++#define WINDOW_H ++ ++#include ++ ++class QAction; ++ ++class GLWidget; ++class KisGLImageWidget; ++class KisGLImageF16; ++class QLabel; ++ ++class Window : public QMainWindow ++{ ++ Q_OBJECT ++ ++public: ++ Window(); ++ ++ void showEvent(QShowEvent *ev) override; ++ ++public Q_SLOTS: ++ ++ ++private: ++ KisGLImageF16 initializeImage(bool cropRange) const; ++ QImage convertToQImage(const KisGLImageF16 &image) const; ++ ++ void updateSurfaceInfo(); ++ ++private: ++ GLWidget *m_glWidget {0}; ++ QAction *m_openAction {0}; ++ QAction *m_quitAction {0}; ++ KisGLImageWidget *m_imageWidget; ++ KisGLImageWidget *m_imageWidgetSdr; ++ QLabel *m_lblContextInfo; ++ ++}; ++ ++#endif +-- +2.20.1.windows.1 + + +From c7a81bd92463649423ecbdad9fdd09b1c0ce0720 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 11 Feb 2019 13:30:28 +0300 +Subject: [PATCH 19/20] Remove isSurfaceColorSpaceSupported() interface + +Anyway, it is not a 100% guarantee of the color space support, +the user still has to probe the specific mode. + +The amount of work to support that on all the platforms overweights +the benefits. + +Change-Id: I0f6f49e3aceecda57e438d6d9422301e3b8daa51 +--- + src/gui/kernel/qopenglcontext.cpp | 16 -------------- + src/gui/kernel/qopenglcontext.h | 1 - + src/gui/kernel/qplatformopenglcontext.cpp | 12 ---------- + src/gui/kernel/qplatformopenglcontext.h | 2 -- + .../platforms/windows/qwindowseglcontext.cpp | 22 ------------------- + .../platforms/windows/qwindowseglcontext.h | 6 ----- + 6 files changed, 59 deletions(-) + +diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp +index 9f1e7e3768..c5d5490ea0 100644 +--- a/src/gui/kernel/qopenglcontext.cpp ++++ b/src/gui/kernel/qopenglcontext.cpp +@@ -1311,22 +1311,6 @@ bool QOpenGLContext::isOpenGLES() const + return format().renderableType() == QSurfaceFormat::OpenGLES; + } + +-/*! +- Returns \c true if the platform supports creation of surfaces with a color space +- tag. Such surfaces will be converted by the display color space automatically by +- the platform. +- +- The value is controlled by the platform plugin in use and may also depend on the +- graphics drivers. +- +- \since 5.XX +- */ +-bool QOpenGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) const +-{ +- Q_D(const QOpenGLContext); +- return d->platformGLContext->isSurfaceColorSpaceSupported(colorSpace); +-} +- + /*! + Returns \c true if the platform supports OpenGL rendering outside the main (gui) + thread. +diff --git a/src/gui/kernel/qopenglcontext.h b/src/gui/kernel/qopenglcontext.h +index 1256ab8b5a..9cfaa52f17 100644 +--- a/src/gui/kernel/qopenglcontext.h ++++ b/src/gui/kernel/qopenglcontext.h +@@ -210,7 +210,6 @@ public: + static OpenGLModuleType openGLModuleType(); + + bool isOpenGLES() const; +- bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) const; + + static bool supportsThreadedOpenGL(); + static QOpenGLContext *globalShareContext(); +diff --git a/src/gui/kernel/qplatformopenglcontext.cpp b/src/gui/kernel/qplatformopenglcontext.cpp +index 72fb818df1..07b5a0dda6 100644 +--- a/src/gui/kernel/qplatformopenglcontext.cpp ++++ b/src/gui/kernel/qplatformopenglcontext.cpp +@@ -165,16 +165,4 @@ bool QPlatformOpenGLContext::parseOpenGLVersion(const QByteArray &versionString, + return (majorOk && minorOk); + } + +-/*! +- Reimplement in subclass if your platform supports setting a color space for the +- surface. +- +- The default implementation returns false. +-*/ +-bool QPlatformOpenGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) +-{ +- Q_UNUSED(colorSpace); +- return false; +-} +- + QT_END_NAMESPACE +diff --git a/src/gui/kernel/qplatformopenglcontext.h b/src/gui/kernel/qplatformopenglcontext.h +index 52e744ab29..f307cc14f9 100644 +--- a/src/gui/kernel/qplatformopenglcontext.h ++++ b/src/gui/kernel/qplatformopenglcontext.h +@@ -90,8 +90,6 @@ public: + + static bool parseOpenGLVersion(const QByteArray &versionString, int &major, int &minor); + +- virtual bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace); +- + private: + friend class QOpenGLContext; + +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp +index 7ca8917e34..3db723acee 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp ++++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp +@@ -784,28 +784,6 @@ QFunctionPointer QWindowsEGLContext::getProcAddress(const char *procName) + return procAddress; + } + +-bool QWindowsEGLContext::isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) +-{ +- bool supported = false; +- +- switch (colorSpace) { +- case QSurfaceFormat::DefaultColorSpace: +- supported = true; +- break; +- case QSurfaceFormat::sRGBColorSpace: +- supported = m_staticContext->hasSRGBColorSpaceSupport(); +- break; +- case QSurfaceFormat::scRGBColorSpace: +- supported = m_staticContext->hasSCRGBColorSpaceSupport(); +- break; +- case QSurfaceFormat::bt2020PQColorSpace: +- supported = m_staticContext->hasBt2020PQColorSpaceSupport(); +- break; +- } +- +- return supported; +-} +- + static QVector createConfigAttributesFromFormat(const QSurfaceFormat &format) + { + int redSize = format.redBufferSize(); +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h +index cf088e7c15..9f7742e6fb 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.h ++++ b/src/plugins/platforms/windows/qwindowseglcontext.h +@@ -130,10 +130,6 @@ public: + static QWindowsLibEGL libEGL; + static QWindowsLibGLESv2 libGLESv2; + +- bool hasSRGBColorSpaceSupport() { return m_hasSRGBColorSpaceSupport; } +- bool hasSCRGBColorSpaceSupport() { return m_hasSCRGBColorSpaceSupport; } +- bool hasBt2020PQColorSpaceSupport() { return m_hasBt2020PQColorSpaceSupport; } +- + private: + explicit QWindowsEGLStaticContext(EGLDisplay display); + static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, +@@ -166,8 +162,6 @@ public: + void *nativeDisplay() const override { return m_eglDisplay; } + void *nativeConfig() const override { return m_eglConfig; } + +- bool isSurfaceColorSpaceSupported(QSurfaceFormat::ColorSpace colorSpace) override; +- + private: + EGLConfig chooseConfig(const QSurfaceFormat &format); + +-- +2.20.1.windows.1 + + +From d0d158d5b884399d3e64688069f7519dd61acfb4 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Mon, 11 Feb 2019 17:23:36 +0300 +Subject: [PATCH 20/20] Add a cumulative patch for Angle for HDR support + +The patch basically is an essence of all the previous commits, but +saved in a repository to make updating Angle easier in the future. + +Change-Id: I8d2424237d1b834d920eb6908d018677840afb5c +--- + ...013-Add-HDR-surface-support-to-Angle.patch | 797 ++++++++++++++++++ + 1 file changed, 797 insertions(+) + create mode 100644 src/angle/patches/0013-Add-HDR-surface-support-to-Angle.patch + +diff --git a/src/angle/patches/0013-Add-HDR-surface-support-to-Angle.patch b/src/angle/patches/0013-Add-HDR-surface-support-to-Angle.patch +new file mode 100644 +index 0000000000..8181e86080 +--- /dev/null ++++ b/src/angle/patches/0013-Add-HDR-surface-support-to-Angle.patch +@@ -0,0 +1,797 @@ ++From 5b9ff2aea11612b2ef2202261fe9f383fc9fceaf Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Sat, 8 Dec 2018 15:35:43 +0300 ++Subject: [PATCH 1/5] Unconditionally enable D3D11_1 ++ ++Just a temparary change. Proper configuration will be ++implemented later. ++ ++Change-Id: I68204a5db6bbd7066a83a8d1d021ce76cd1cf6f6 ++--- ++ src/3rdparty/angle/src/common/platform.h | 22 ++++++++++++++++------ ++ 1 file changed, 16 insertions(+), 6 deletions(-) ++ ++diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h ++index fb251da579..89359f954e 100644 ++--- a/src/3rdparty/angle/src/common/platform.h +++++ b/src/3rdparty/angle/src/common/platform.h ++@@ -59,12 +59,22 @@ ++ # endif ++ ++ # if defined(ANGLE_ENABLE_D3D11) ++-#include ++-#include ++-#include ++-#include ++-#include ++-#include +++# include +++# include +++# include +++# if defined(__MINGW32__) && !defined(__d3d11sdklayers_h__) +++# define ANGLE_MINGW32_COMPAT +++# endif +++//# if defined(_MSC_VER) && _MSC_VER >= 1800 +++# define ANGLE_ENABLE_D3D11_1 +++//# endif +++# if defined(ANGLE_ENABLE_D3D11_1) +++# include +++# include +++# include +++# include // TODO: This is actually D3D12!!! +++# endif +++# include ++ # endif ++ ++ #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) ++-- ++2.20.1.windows.1 ++ ++ ++From 5c9267ec3b777aa7a9fce5c85004ac091b96dd6b Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Sat, 8 Dec 2018 18:03:58 +0300 ++Subject: [PATCH 2/5] Implement proper color management selection and ++ activation ++ ++1) D3D11 implementation of angle now supports GL_RGB10_A2 format ++ ++2) Technically, Angle's EGL implementation now supports the following ++ display extensions: ++ * EGL_KHR_gl_colorspace ++ * EGL_EXT_gl_colorspace_scrgb_linear ++ * EGL_EXT_gl_colorspace_bt2020_pq ++ ++3) D3D11 implementation of angle now supports GL_COLOR_SPACE attribute, ++ which allows selection one of four color modes: ++ * Linear --- just pass-through data to GPU ++ * sRGB --- p709-g22 color space. WARNING: in 8-bit mode the system ++ becomes clever and automatically converts linear framebuffer ++ attachments to sRGB space, as per EGL_KHR_gl_colorspace definition. ++ It is not possible to select sRGB without this extra "feature". ++ * scRGB --- p709-g10 color space. This mode is the only mode ++ supported in f16-bit mode (and it is also not supported in other ++ bit depths). ++ * bt2020-pq --- p2020-pq color space. Supported only in 10-bit mode. ++ ++4) QSurfaceFormat now supports setting color spaces from the list above ++ ++5) SwapChain is now created in DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL mode: ++ * because non-flip mode is considered deprecated and HDR is not ++ supported in it; ++ * because in flip-discard mode partial updates from ++ SwapChain11::present() are not supported and return an error, ++ which is never checked :) ++ ++Change-Id: I8bdcbf092ae149b67247e8e41502916cedc200df ++--- ++ src/3rdparty/angle/src/libANGLE/Caps.cpp | 8 +- ++ src/3rdparty/angle/src/libANGLE/Caps.h | 9 ++ ++ .../src/libANGLE/renderer/d3d/RendererD3D.h | 3 +- ++ .../src/libANGLE/renderer/d3d/SurfaceD3D.cpp | 26 +++++- ++ .../src/libANGLE/renderer/d3d/SurfaceD3D.h | 1 + ++ .../renderer/d3d/d3d11/Renderer11.cpp | 18 +++- ++ .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 4 +- ++ .../renderer/d3d/d3d11/SwapChain11.cpp | 90 ++++++++++++++++++- ++ .../libANGLE/renderer/d3d/d3d11/SwapChain11.h | 4 +- ++ .../d3d/d3d11/win32/NativeWindow11Win32.cpp | 18 +++- ++ .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 4 +- ++ .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 3 +- ++ .../angle/src/libANGLE/validationEGL.cpp | 26 ++++++ ++ 13 files changed, 199 insertions(+), 15 deletions(-) ++ ++diff --git a/src/3rdparty/angle/src/libANGLE/Caps.cpp b/src/3rdparty/angle/src/libANGLE/Caps.cpp ++index 44da2bbe27..2088457458 100644 ++--- a/src/3rdparty/angle/src/libANGLE/Caps.cpp +++++ b/src/3rdparty/angle/src/libANGLE/Caps.cpp ++@@ -1101,7 +1101,10 @@ DisplayExtensions::DisplayExtensions() ++ displayTextureShareGroup(false), ++ createContextClientArrays(false), ++ programCacheControl(false), ++- robustResourceInitialization(false) +++ robustResourceInitialization(false), +++ colorspaceSRGB(false), +++ colorspaceSCRGBLinear(false), +++ colorspaceBt2020PQ(false) ++ { ++ } ++ ++@@ -1146,6 +1149,9 @@ std::vector DisplayExtensions::getStrings() const ++ InsertExtensionString("EGL_ANGLE_create_context_client_arrays", createContextClientArrays, &extensionStrings); ++ InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings); ++ InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings); +++ InsertExtensionString("EGL_KHR_gl_colorspace", colorspaceSRGB, &extensionStrings); +++ InsertExtensionString("EGL_EXT_gl_colorspace_scrgb_linear", colorspaceSCRGBLinear, &extensionStrings); +++ InsertExtensionString("EGL_EXT_gl_colorspace_bt2020_pq", colorspaceBt2020PQ, &extensionStrings); ++ // TODO(jmadill): Enable this when complete. ++ //InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings); ++ // clang-format on ++diff --git a/src/3rdparty/angle/src/libANGLE/Caps.h b/src/3rdparty/angle/src/libANGLE/Caps.h ++index 64bdf97112..8157af5800 100644 ++--- a/src/3rdparty/angle/src/libANGLE/Caps.h +++++ b/src/3rdparty/angle/src/libANGLE/Caps.h ++@@ -692,6 +692,15 @@ struct DisplayExtensions ++ ++ // EGL_ANGLE_robust_resource_initialization ++ bool robustResourceInitialization; +++ +++ // EGL_KHR_gl_colorspace +++ bool colorspaceSRGB; +++ +++ // EGL_EXT_gl_colorspace_scrgb_linear +++ bool colorspaceSCRGBLinear; +++ +++ // EGL_EXT_gl_colorspace_bt2020_pq +++ bool colorspaceBt2020PQ; ++ }; ++ ++ struct DeviceExtensions ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h ++index dcc98f2ec6..b8ee635625 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/RendererD3D.h ++@@ -130,7 +130,8 @@ class RendererD3D : public BufferFactoryD3D, public MultisampleTextureInitialize ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) = 0; +++ EGLint samples, +++ EGLint colorSpace) = 0; ++ virtual egl::Error getD3DTextureInfo(const egl::Config *configuration, ++ IUnknown *d3dTexture, ++ EGLint *width, ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp ++index 7657aef79e..efd4dd1a24 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp ++@@ -22,6 +22,27 @@ ++ namespace rx ++ { ++ +++GLenum renderTargetFormatFromColorSpace(egl::Display *display, GLenum baseFormat, EGLint colorSpace) +++{ +++ GLenum result = baseFormat; +++ +++ /** +++ * If sRGB extension is supported, we should change the surface format +++ * to a specific one that does support automated gamma conversion. +++ * +++ * TODO: openGL doesn't support BGRA-sRGB texture format, so creation of +++ * textures in this format technically is not supported! +++ */ +++ if (display->getExtensions().colorspaceSRGB && +++ baseFormat == GL_RGBA8_OES && +++ colorSpace == EGL_GL_COLORSPACE_SRGB_KHR) +++ { +++ result = GL_SRGB8_ALPHA8; +++ } +++ +++ return result; +++} +++ ++ SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, ++ RendererD3D *renderer, ++ egl::Display *display, ++@@ -34,7 +55,8 @@ SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, ++ mDisplay(display), ++ mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE), ++ mOrientation(static_cast(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))), ++- mRenderTargetFormat(state.config->renderTargetFormat), +++ mColorSpace(static_cast(attribs.get(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_LINEAR_KHR))), +++ mRenderTargetFormat(renderTargetFormatFromColorSpace(display, state.config->renderTargetFormat, mColorSpace)), ++ mDepthStencilFormat(state.config->depthStencilFormat), ++ mSwapChain(nullptr), ++ mSwapIntervalDirty(true), ++@@ -148,7 +170,7 @@ egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display) ++ ++ mSwapChain = ++ mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat, ++- mDepthStencilFormat, mOrientation, mState.config->samples); +++ mDepthStencilFormat, mOrientation, mState.config->samples, mColorSpace); ++ if (!mSwapChain) ++ { ++ return egl::EglBadAlloc(); ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h ++index 01d2573244..ccb793d423 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h ++@@ -90,6 +90,7 @@ class SurfaceD3D : public SurfaceImpl ++ ++ bool mFixedSize; ++ GLint mOrientation; +++ EGLint mColorSpace; ++ ++ GLenum mRenderTargetFormat; ++ GLenum mDepthStencilFormat; ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++index b0ef9abddc..ac46690090 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++@@ -465,6 +465,7 @@ Renderer11::Renderer11(egl::Display *display) ++ mRenderer11DeviceCaps.supportsConstantBufferOffsets = false; ++ mRenderer11DeviceCaps.supportsVpRtIndexWriteFromVertexShader = false; ++ mRenderer11DeviceCaps.supportsDXGI1_2 = false; +++ mRenderer11DeviceCaps.supportsDXGI1_4 = false; ++ mRenderer11DeviceCaps.B5G6R5support = 0; ++ mRenderer11DeviceCaps.B4G4R4A4support = 0; ++ mRenderer11DeviceCaps.B5G5R5A1support = 0; ++@@ -918,6 +919,7 @@ egl::Error Renderer11::initializeDevice() ++ ++ // Gather stats on DXGI and D3D feature level ++ ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_2", mRenderer11DeviceCaps.supportsDXGI1_2); +++ ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.SupportsDXGI1_4", mRenderer11DeviceCaps.supportsDXGI1_4); ++ ++ ANGLEFeatureLevel angleFeatureLevel = GetANGLEFeatureLevel(mRenderer11DeviceCaps.featureLevel); ++ ++@@ -999,9 +1001,15 @@ void Renderer11::populateRenderer11DeviceCaps() ++ &mRenderer11DeviceCaps.B5G5R5A1support, ++ &mRenderer11DeviceCaps.B5G5R5A1maxSamples); ++ +++//#if defined(ANGLE_ENABLE_D3D11_1) ++ IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject(mDxgiAdapter); ++ mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); ++ SafeRelease(dxgiAdapter2); +++ +++ IDXGIAdapter3 *dxgiAdapter3 = d3d11::DynamicCastComObject(mDxgiAdapter); +++ mRenderer11DeviceCaps.supportsDXGI1_4 = (dxgiAdapter3 != nullptr); +++ SafeRelease(dxgiAdapter3); +++//#endif ++ } ++ ++ gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( ++@@ -1241,6 +1249,11 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions ++ ++ // All D3D feature levels support robust resource init ++ outExtensions->robustResourceInitialization = true; +++ +++ // color space selection is always supported in DirectX11 +++ outExtensions->colorspaceSRGB = true; +++ outExtensions->colorspaceSCRGBLinear = true; +++ outExtensions->colorspaceBt2020PQ = true; ++ } ++ ++ gl::Error Renderer11::flush() ++@@ -1436,10 +1449,11 @@ SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) +++ EGLint samples, +++ EGLint colorSpace) ++ { ++ return new SwapChain11(this, GetAs(nativeWindow), shareHandle, d3dTexture, ++- backBufferFormat, depthBufferFormat, orientation, samples); +++ backBufferFormat, depthBufferFormat, orientation, samples, colorSpace); ++ } ++ ++ void *Renderer11::getD3DDevice() ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h ++index a8c24e681b..3516bf779d 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h ++@@ -49,6 +49,7 @@ struct Renderer11DeviceCaps ++ ++ D3D_FEATURE_LEVEL featureLevel; ++ bool supportsDXGI1_2; // Support for DXGI 1.2 +++ bool supportsDXGI1_4; // Support for DXGI 1.4 ++ bool supportsClearView; // Support for ID3D11DeviceContext1::ClearView ++ bool supportsConstantBufferOffsets; // Support for Constant buffer offset ++ bool supportsVpRtIndexWriteFromVertexShader; // VP/RT can be selected in the Vertex Shader ++@@ -138,7 +139,8 @@ class Renderer11 : public RendererD3D ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) override; +++ EGLint samples, +++ EGLint colorSpace) override; ++ egl::Error getD3DTextureInfo(const egl::Config *configuration, ++ IUnknown *d3dTexture, ++ EGLint *width, ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++index dcfd06484d..8f72c5c9aa 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++@@ -18,6 +18,8 @@ ++ #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" ++ #include "third_party/trace_event/trace_event.h" ++ +++#include +++ ++ // Precompiled shaders ++ #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h" ++ #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h" ++@@ -56,12 +58,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) +++ EGLint samples, +++ EGLint colorSpace) ++ : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat), ++ mRenderer(renderer), ++ mWidth(-1), ++ mHeight(-1), ++ mOrientation(orientation), +++ mColorSpace(colorSpace), ++ mAppCreatedShareHandle(mShareHandle != nullptr), ++ mSwapInterval(0), ++ mPassThroughResourcesInit(false), ++@@ -620,10 +624,94 @@ EGLint SwapChain11::reset(const gl::Context *context, ++ mSwapChain1 = d3d11::DynamicCastComObject(mSwapChain); ++ } ++ +++ if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_4) +++ { +++#if defined(ANGLE_ENABLE_D3D11_1) +++ IDXGISwapChain3 *swapChain3 = d3d11::DynamicCastComObject(mSwapChain); +++ +++ printf("*** EGL colorSpace: 0x%X\n", mColorSpace); +++ printf("*** EGL format: 0x%X\n", mOffscreenRenderTargetFormat); +++ printf("*** Native format: 0x%X\n", getSwapChainNativeFormat()); +++ +++ if (mColorSpace != EGL_GL_COLORSPACE_LINEAR_KHR) { +++ DXGI_COLOR_SPACE_TYPE nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; +++ switch (mColorSpace) +++ { +++ case EGL_GL_COLORSPACE_SRGB_KHR: +++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; +++ break; +++ case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: +++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; +++ break; +++ case EGL_GL_COLORSPACE_BT2020_PQ_EXT: +++ nativeColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; +++ break; +++ default: +++ ASSERT(0 && "Unsupported colorspace requested"); +++ } +++ +++ printf("*** Native colorSpace: 0x%X\n", nativeColorSpace); +++ +++ UINT supported = 0; +++ result = swapChain3->CheckColorSpaceSupport(nativeColorSpace, &supported); +++ ASSERT(SUCCEEDED(result)); +++ if (!(supported & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { +++ SafeRelease(swapChain3); +++ return EGL_BAD_MATCH; +++ } +++ +++ result = swapChain3->SetColorSpace1(nativeColorSpace); +++ ASSERT(SUCCEEDED(result)); +++ } +++ +++ SafeRelease(swapChain3); +++ +++#if 0 +++ +++ IDXGISwapChain4 *swapChain4 = d3d11::DynamicCastComObject(mSwapChain); +++ +++ DXGI_HDR_METADATA_HDR10 md; +++ md.RedPrimary[0] = 0.680 * 50000; +++ md.RedPrimary[1] = 0.320 * 50000; +++ md.GreenPrimary[0] = 0.265 * 50000; +++ md.GreenPrimary[1] = 0.690 * 50000; +++ md.BluePrimary[0] = 0.150 * 50000; +++ md.BluePrimary[1] = 0.060 * 50000; +++ md.WhitePoint[0] = 0.3127 * 50000; +++ md.WhitePoint[1] = 0.3290 * 50000; +++ md.MaxMasteringLuminance = 1000 * 10000; +++ md.MinMasteringLuminance = 0.001 * 10000; +++ md.MaxContentLightLevel = 1000; +++ md.MaxFrameAverageLightLevel = 200; +++ result = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(md), &md); +++ printf("*** Result hdr 0x%X\n", result); +++ SafeRelease(swapChain4); +++#endif +++#endif +++ } +++ ++ ID3D11Texture2D *backbufferTex = nullptr; ++ result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), ++ reinterpret_cast(&backbufferTex)); ++ ASSERT(SUCCEEDED(result)); +++ +++ // TODO: recover rendering to sRGB +++ // +++ // D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc; +++ // offscreenRTVDesc.Format = getSwapChainNativeFormat(); +++ // +++ // if (mColorSpace == EGL_GL_COLORSPACE_SRGB_KHR) { +++ // if (offscreenRTVDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM) { +++ // offscreenRTVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; +++ // } +++ // +++ // if (offscreenRTVDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) { +++ // offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; +++ // } +++ // } +++ // +++ // printf("*** Render target format: 0x%X\n", offscreenRTVDesc.Format); +++ ++ const auto &format = ++ d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps()); ++ mBackBufferTexture.set(backbufferTex, format); ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h ++index eca068210b..2a4b9ba274 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h ++@@ -28,7 +28,8 @@ class SwapChain11 final : public SwapChainD3D ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples); +++ EGLint samples, +++ EGLint colorSpace); ++ ~SwapChain11() override; ++ ++ EGLint resize(const gl::Context *context, ++@@ -93,6 +94,7 @@ class SwapChain11 final : public SwapChainD3D ++ EGLint mWidth; ++ EGLint mHeight; ++ const EGLint mOrientation; +++ EGLint mColorSpace; ++ bool mAppCreatedShareHandle; ++ unsigned int mSwapInterval; ++ bool mPassThroughResourcesInit; ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++index 5394e3d3e7..c81b33fee9 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++@@ -158,9 +158,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, ++ swapChainDesc.SampleDesc.Quality = 0; ++ swapChainDesc.BufferUsage = ++ DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; ++- swapChainDesc.BufferCount = 1; +++ swapChainDesc.BufferCount = 2; ++ swapChainDesc.Scaling = DXGI_SCALING_STRETCH; ++- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; +++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; ++ swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; ++ swapChainDesc.Flags = 0; ++ IDXGISwapChain1 *swapChain1 = nullptr; ++@@ -176,7 +176,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, ++ } ++ ++ DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; ++- swapChainDesc.BufferCount = 1; +++ swapChainDesc.BufferCount = 2; ++ swapChainDesc.BufferDesc.Format = format; ++ swapChainDesc.BufferDesc.Width = width; ++ swapChainDesc.BufferDesc.Height = height; ++@@ -191,7 +191,17 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, ++ swapChainDesc.SampleDesc.Count = samples; ++ swapChainDesc.SampleDesc.Quality = 0; ++ swapChainDesc.Windowed = TRUE; ++- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; +++ +++ /** +++ * NOTE1: in flip-discard mode the swap chain doesn't support partial +++ * presentatiopn with Present1() call. Though it is not a big +++ * problem, because in case DXGI 1.2 is supported this code is +++ * unreachable. +++ * +++ * NOTE2: in non-flip mode HDR rendering is not supported, so use it +++ * bt default +++ */ +++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; ++ ++ HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); ++ if (SUCCEEDED(result)) ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp ++index 75c6298868..58596169a8 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp ++@@ -718,8 +718,10 @@ SwapChainD3D *Renderer9::createSwapChain(NativeWindowD3D *nativeWindow, ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) +++ EGLint samples, +++ EGLint colorSpace) ++ { +++ UNUSED_VARIABLE(colorSpace); ++ return new SwapChain9(this, GetAs(nativeWindow), shareHandle, d3dTexture, ++ backBufferFormat, depthBufferFormat, orientation); ++ } ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h ++index 9ddee45f0f..ce4bb201e5 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h ++@@ -92,7 +92,8 @@ class Renderer9 : public RendererD3D ++ GLenum backBufferFormat, ++ GLenum depthBufferFormat, ++ EGLint orientation, ++- EGLint samples) override; +++ EGLint samples, +++ EGLint colorSpace) override; ++ egl::Error getD3DTextureInfo(const egl::Config *configuration, ++ IUnknown *d3dTexture, ++ EGLint *width, ++diff --git a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++index 13a3a9e280..3f6a426320 100644 ++--- a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +++++ b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++@@ -885,6 +885,32 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin ++ "either EGL_TRUE or EGL_FALSE."; ++ } ++ break; +++ case EGL_GL_COLORSPACE: +++ +++ if (!displayExtensions.colorspaceSRGB) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_KHR_gl_colorspace is not supported on this platform."); +++ } +++ +++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) +++ { +++ if (!displayExtensions.colorspaceSCRGBLinear) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."); +++ } +++ } +++ else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) +++ { +++ if (!displayExtensions.colorspaceBt2020PQ) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."); +++ } +++ } +++ else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) +++ { +++ return Error(EGL_BAD_ATTRIBUTE); +++ } +++ break; ++ ++ default: ++ return EglBadAttribute(); ++-- ++2.20.1.windows.1 ++ ++ ++From b99e1010b22bb678e441814e42913e0b2c40f083 Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Wed, 23 Jan 2019 23:58:40 +0300 ++Subject: [PATCH 3/5] Implement API for requesting if current EGL ++ implementation supports color spaces ++ ++QOpenGLContext::isSurfaceColorSpaceSupported() returns if the color ++space is supported. ++ ++Change-Id: Ib823270a388f6cfc85511f24ba777bca019edd3e ++TODO: implement this feature for other implementation, which are not EGL. ++--- ++ src/3rdparty/angle/src/common/platform.h | 7 +------ ++ .../src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp | 10 ++++------ ++ .../src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp | 7 ++++--- ++ 3 files changed, 9 insertions(+), 15 deletions(-) ++ ++diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h ++index 89359f954e..265f49f875 100644 ++--- a/src/3rdparty/angle/src/common/platform.h +++++ b/src/3rdparty/angle/src/common/platform.h ++@@ -65,15 +65,10 @@ ++ # if defined(__MINGW32__) && !defined(__d3d11sdklayers_h__) ++ # define ANGLE_MINGW32_COMPAT ++ # endif ++-//# if defined(_MSC_VER) && _MSC_VER >= 1800 ++-# define ANGLE_ENABLE_D3D11_1 ++-//# endif ++-# if defined(ANGLE_ENABLE_D3D11_1) ++ # include ++ # include ++ # include ++-# include // TODO: This is actually D3D12!!! ++-# endif +++# include // WARNING: This is actually D3D12! ++ # include ++ # endif ++ ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++index ac46690090..f0e497b52f 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp ++@@ -1001,7 +1001,6 @@ void Renderer11::populateRenderer11DeviceCaps() ++ &mRenderer11DeviceCaps.B5G5R5A1support, ++ &mRenderer11DeviceCaps.B5G5R5A1maxSamples); ++ ++-//#if defined(ANGLE_ENABLE_D3D11_1) ++ IDXGIAdapter2 *dxgiAdapter2 = d3d11::DynamicCastComObject(mDxgiAdapter); ++ mRenderer11DeviceCaps.supportsDXGI1_2 = (dxgiAdapter2 != nullptr); ++ SafeRelease(dxgiAdapter2); ++@@ -1009,7 +1008,6 @@ void Renderer11::populateRenderer11DeviceCaps() ++ IDXGIAdapter3 *dxgiAdapter3 = d3d11::DynamicCastComObject(mDxgiAdapter); ++ mRenderer11DeviceCaps.supportsDXGI1_4 = (dxgiAdapter3 != nullptr); ++ SafeRelease(dxgiAdapter3); ++-//#endif ++ } ++ ++ gl::SupportedSampleSet Renderer11::generateSampleSetForEGLConfig( ++@@ -1250,10 +1248,10 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions ++ // All D3D feature levels support robust resource init ++ outExtensions->robustResourceInitialization = true; ++ ++- // color space selection is always supported in DirectX11 ++- outExtensions->colorspaceSRGB = true; ++- outExtensions->colorspaceSCRGBLinear = true; ++- outExtensions->colorspaceBt2020PQ = true; +++ // color space selection supported in DXGI 1.4 only +++ outExtensions->colorspaceSRGB = mRenderer11DeviceCaps.supportsDXGI1_4; +++ outExtensions->colorspaceSCRGBLinear = mRenderer11DeviceCaps.supportsDXGI1_4; +++ outExtensions->colorspaceBt2020PQ = mRenderer11DeviceCaps.supportsDXGI1_4; ++ } ++ ++ gl::Error Renderer11::flush() ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++index 8f72c5c9aa..fc967b90d0 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++@@ -18,7 +18,10 @@ ++ #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h" ++ #include "third_party/trace_event/trace_event.h" ++ ++-#include +++#if 0 +++// used only for HDR metadata configuration options +++#include +++#endif ++ ++ // Precompiled shaders ++ #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h" ++@@ -626,7 +629,6 @@ EGLint SwapChain11::reset(const gl::Context *context, ++ ++ if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_4) ++ { ++-#if defined(ANGLE_ENABLE_D3D11_1) ++ IDXGISwapChain3 *swapChain3 = d3d11::DynamicCastComObject(mSwapChain); ++ ++ printf("*** EGL colorSpace: 0x%X\n", mColorSpace); ++@@ -686,7 +688,6 @@ EGLint SwapChain11::reset(const gl::Context *context, ++ result = swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(md), &md); ++ printf("*** Result hdr 0x%X\n", result); ++ SafeRelease(swapChain4); ++-#endif ++ #endif ++ } ++ ++-- ++2.20.1.windows.1 ++ ++ ++From 40b70067421d48c22e1c7059c416d51e19724558 Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Mon, 28 Jan 2019 12:55:35 +0300 ++Subject: [PATCH 4/5] Fix Angle to work correctly on Windows 7 ++ ++"Flip" modes are not available on older versions of DirectX, ++so it is not safe to request it as a fallback case. ++ ++Change-Id: I3f00e64e5a11a8c5ef2dab9b27a5d9e677f1ed58 ++--- ++ .../renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp | 12 +++++++----- ++ 1 file changed, 7 insertions(+), 5 deletions(-) ++ ++diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++index c81b33fee9..794ab971ab 100644 ++--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++@@ -146,6 +146,9 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, ++ ++ // Use IDXGIFactory2::CreateSwapChainForHwnd if DXGI 1.2 is available to create a ++ // DXGI_SWAP_EFFECT_SEQUENTIAL swap chain. +++ // +++ // NOTE: in non-flip mode HDR rendering is not supported, so use it +++ // by default ++ IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject(factory); ++ if (factory2 != nullptr) ++ { ++@@ -193,21 +196,20 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, ++ swapChainDesc.Windowed = TRUE; ++ ++ /** ++- * NOTE1: in flip-discard mode the swap chain doesn't support partial +++ * NOTE: in discard mode the swap chain doesn't support partial ++ * presentatiopn with Present1() call. Though it is not a big ++ * problem, because in case DXGI 1.2 is supported this code is ++ * unreachable. ++- * ++- * NOTE2: in non-flip mode HDR rendering is not supported, so use it ++- * bt default ++ */ ++- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; +++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; ++ ++ HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); +++ ++ if (SUCCEEDED(result)) ++ { ++ factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); ++ } +++ ++ return result; ++ } ++ ++-- ++2.20.1.windows.1 ++ ++ ++From b3aab4c1f7efc3b074bb50dc5a2eab0b5d735b63 Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Mon, 28 Jan 2019 14:39:47 +0300 ++Subject: [PATCH 5/5] Allow color space selection attributes for ++ eglCreatePbufferSurface ++ ++Notes: ++ ++eglCreatePixmapSurface() is not implemented in Angle, so the support is ++not added. ++ ++eglCreatePlatformWindowSurface() and eglCreatePlatformPixmapSurface() ++do not have support for color spaces according to the extension wording ++(and they are also not supported by Angle :) ) ++ ++Change-Id: Ic780a96c6a7e98fba7524fbabc6043ea2de435b0 ++--- ++ .../angle/src/libANGLE/validationEGL.cpp | 27 +++++++++++++++++++ ++ 1 file changed, 27 insertions(+) ++ ++diff --git a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++index 3f6a426320..1e9535ee1c 100644 ++--- a/src/3rdparty/angle/src/libANGLE/validationEGL.cpp +++++ b/src/3rdparty/angle/src/libANGLE/validationEGL.cpp ++@@ -1003,6 +1003,33 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri ++ } ++ break; ++ +++ case EGL_GL_COLORSPACE: +++ +++ if (!displayExtensions.colorspaceSRGB) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_KHR_gl_colorspace is not supported on this platform."); +++ } +++ +++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) +++ { +++ if (!displayExtensions.colorspaceSCRGBLinear) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_scrgb_linear is not supported on this platform."); +++ } +++ } +++ else if (value == EGL_GL_COLORSPACE_BT2020_PQ_EXT) +++ { +++ if (!displayExtensions.colorspaceBt2020PQ) +++ { +++ return Error(EGL_BAD_ATTRIBUTE, "EGL_EXT_gl_colorspace_bt2020_pq is not supported on this platform."); +++ } +++ } +++ else if (value != EGL_GL_COLORSPACE_SRGB_KHR && value != EGL_GL_COLORSPACE_LINEAR_KHR) +++ { +++ return Error(EGL_BAD_ATTRIBUTE); +++ } +++ break; +++ ++ default: ++ return EglBadAttribute(); ++ } ++-- ++2.20.1.windows.1 ++ +-- +2.20.1.windows.1 + diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,7 +1,7 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) list(APPEND _QT_conf -skip qt3d -skip qtactiveqt -skip qtcanvas3d - -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects + -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools @@ -33,17 +33,13 @@ ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://download.qt.io/archive/qt/5.9/5.9.3/single/qt-everywhere-opensource-src-5.9.3.zip - URL_HASH SHA1=2d3c53cd9dc76a479873548921a20d3d9b6fb9ac - PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qopengldebug-gles.patch - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-189539-ANGLE-mingw-fix.patch - COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-212811_qtbase-angle-d3d11-warp-crash-fix.patch - COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/QTBUG-57299.diff + URL https://download.qt.io/archive/qt/5.12/5.12.1/single/qt-everywhere-src-5.12.1.zip + URL_HASH SHA1=5ba3e56475912e8784f825fd8d0d139ec5eca8b3 + PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-disable-wintab.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-always-return-we-support-DIBV5.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0004-Fix-debug-on-openGL-ES.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0005-cumulative-patch-for-hdr.patch INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure.bat ${_QT_conf} @@ -64,7 +60,7 @@ URL_MD5 c5e275ab0ed7ee61d0f4b82cd471770d #PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff - CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -verbose -nomake examples -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport -skip qtdatavis3d -skip qtvirtualkeyboard -skip qtspeech -skip qtsensors -skip qtgamepad -skip qtscxml -skip qtremoteobjects -skip qtxmlpatterns -skip qtnetworkauth -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtpurchasing -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard + CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -verbose -nomake examples -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport -skip qtdatavis3d -skip qtvirtualkeyboard -skip qtspeech -skip qtsensors -skip qtgamepad -skip qtscxml -skip qtremoteobjects -skip qtxmlpatterns -skip qtnetworkauth -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtpurchasing -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard INSTALL_DIR ${EXTPREFIX_qt} BUILD_COMMAND $(MAKE) @@ -208,7 +204,7 @@ INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure - -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland + -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth -skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-qml-debug -no-libproxy -no-system-proxies -no-icu -no-mtdev -system-zlib -qt-pcre -opensource -confirm-license -prefix ${EXTPREFIX_qt} BUILD_COMMAND ${PARALLEL_MAKE} INSTALL_COMMAND make install diff --git a/3rdparty/ext_qt/QTBUG-57299.diff b/3rdparty/ext_qt/QTBUG-57299.diff deleted file mode 100644 --- a/3rdparty/ext_qt/QTBUG-57299.diff +++ /dev/null @@ -1,111 +0,0 @@ -From 7a5828621ce1f44c6af39257bc62cf6fac5f22c4 Mon Sep 17 00:00:00 2001 -From: Friedemann Kleint -Date: Fri, 26 Jan 2018 08:37:40 +0100 -Subject: [PATCH] Windows/QSaveFile: Fix locking issues on Dropbox drives - -Add a flag to QTemporaryFileEngine causing the file to be opened in -non-shared mode, preventing renaming failures caused by the Dropbox -driver accessing it. - -Task-number: QTBUG-57299 -Change-Id: Id7afc3559fd15784d4166efbbd057d592b5e0ab2 -Reviewed-by: Oswald Buddenhagen -Reviewed-by: Oliver Wolff -Reviewed-by: Thiago Macieira -(cherry picked from commit fe5edcee602f0ab2912bbdd1a21f4309ed7dbfd6) ---- - src/corelib/io/qsavefile.cpp | 2 +- - src/corelib/io/qtemporaryfile.cpp | 11 +++++++---- - src/corelib/io/qtemporaryfile_p.h | 5 +++++ - 3 files changed, 13 insertions(+), 5 deletions(-) - -diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp -index 3f45ca5f913..aaf3d1f5fb1 100644 ---- a/src/corelib/io/qsavefile.cpp -+++ b/src/corelib/io/qsavefile.cpp -@@ -231,7 +231,7 @@ bool QSaveFile::open(OpenMode mode) - d->finalFileName = existingFile.filePath(); - } - -- d->fileEngine = new QTemporaryFileEngine; -+ d->fileEngine = new QTemporaryFileEngine(QTemporaryFileEngine::Win32NonShared); - // if the target file exists, we'll copy its permissions below, - // but until then, let's ensure the temporary file is not accessible - // to a third party -diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp -index 8a99873fee1..4712e65a419 100644 ---- a/src/corelib/io/qtemporaryfile.cpp -+++ b/src/corelib/io/qtemporaryfile.cpp -@@ -117,7 +117,7 @@ typedef int NativeFileHandle; - */ - static bool createFileFromTemplate(NativeFileHandle &file, - QFileSystemEntry::NativePath &path, size_t pos, size_t length, quint32 mode, -- QSystemError &error) -+ int flags, QSystemError &error) - { - Q_ASSERT(length != 0); - Q_ASSERT(pos < size_t(path.length())); -@@ -151,16 +151,18 @@ static bool createFileFromTemplate(NativeFileHandle &file, - // Atomically create file and obtain handle - #if defined(Q_OS_WIN) - Q_UNUSED(mode); -+ const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared) -+ ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE); - - # ifndef Q_OS_WINRT - file = CreateFile((const wchar_t *)path.constData(), - GENERIC_READ | GENERIC_WRITE, -- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, -+ shareMode, NULL, CREATE_NEW, - FILE_ATTRIBUTE_NORMAL, NULL); - # else // !Q_OS_WINRT - file = CreateFile2((const wchar_t *)path.constData(), - GENERIC_READ | GENERIC_WRITE, -- FILE_SHARE_READ | FILE_SHARE_WRITE, CREATE_NEW, -+ shareMode, CREATE_NEW, - NULL); - # endif // Q_OS_WINRT - -@@ -182,6 +184,7 @@ static bool createFileFromTemplate(NativeFileHandle &file, - return false; - } - #else // POSIX -+ Q_UNUSED(flags) - file = QT_OPEN(path.constData(), - QT_OPEN_CREAT | O_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE, - static_cast(mode)); -@@ -342,7 +345,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) - NativeFileHandle &file = d->fd; - #endif - -- if (!createFileFromTemplate(file, filename, phPos, phLength, fileMode, error)) { -+ if (!createFileFromTemplate(file, filename, phPos, phLength, fileMode, flags, error)) { - setError(QFile::OpenError, error.toString()); - return false; - } -diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h -index 7f365f0e8a1..b0fab3a2558 100644 ---- a/src/corelib/io/qtemporaryfile_p.h -+++ b/src/corelib/io/qtemporaryfile_p.h -@@ -85,6 +85,10 @@ class QTemporaryFileEngine : public QFSFileEngine - { - Q_DECLARE_PRIVATE(QFSFileEngine) - public: -+ enum Flags { Win32NonShared = 0x1 }; -+ -+ explicit QTemporaryFileEngine(int _flags = 0) : flags(_flags) {} -+ - void initialize(const QString &file, quint32 mode, bool nameIsTemplate = true) - { - Q_D(QFSFileEngine); -@@ -109,6 +113,7 @@ public: - bool close() override; - - quint32 fileMode; -+ int flags; - bool filePathIsTemplate; - bool filePathWasTemplate; - }; --- -2.16.3 - diff --git a/3rdparty/ext_qt/gerrit-189539-ANGLE-mingw-fix.patch b/3rdparty/ext_qt/gerrit-189539-ANGLE-mingw-fix.patch deleted file mode 100644 --- a/3rdparty/ext_qt/gerrit-189539-ANGLE-mingw-fix.patch +++ /dev/null @@ -1,145 +0,0 @@ -diff --git a/qtbase/src/angle/src/common/common.pri b/qtbase/src/angle/src/common/common.pri -index c1fad14951..6a558a957b 100644 ---- a/qtbase/src/angle/src/common/common.pri -+++ b/qtbase/src/angle/src/common/common.pri -@@ -21,20 +21,6 @@ lib_replace.replace = \$\$\$\$[QT_INSTALL_LIBS] - lib_replace.CONFIG = path - QMAKE_PRL_INSTALL_REPLACE += lib_replace - --# DirectX is included in the Windows 8 Kit, but everything else requires the DX SDK. --winrt|msvc { -- FXC = fxc.exe --} else { -- DX_DIR = $$(DXSDK_DIR) -- isEmpty(DX_DIR) { -- error("Cannot determine DirectX SDK location. Please set DXSDK_DIR environment variable.") -- } -- -- equals(QMAKE_TARGET.arch, x86_64) { -- FXC = \"$${DX_DIR}Utilities\\bin\\x64\\fxc.exe\" -- } else { -- FXC = \"$${DX_DIR}Utilities\\bin\\x86\\fxc.exe\" -- } --} -+FXC = $$QMAKE_FXC_LOCATION - - static: DEFINES *= LIBGLESV2_EXPORT_H_ ANGLE_EXPORT= -diff --git a/qtbase/src/gui/configure.json b/qtbase/src/gui/configure.json -index 28c8034c75..77cfb6b592 100644 ---- a/qtbase/src/gui/configure.json -+++ b/qtbase/src/gui/configure.json -@@ -615,11 +615,14 @@ - "label": "DirectX SDK", - "type": "directX", - "files": [ -- "d3dcompiler.h", -- "d3d11.lib", -- "fxc.exe" -+ "d3dcompiler.h" - ] - }, -+ "fxc": { -+ "label": "DirectX Shader Compiler", -+ "type": "fxc", -+ "log": "value" -+ }, - "egl-x11": { - "label": "EGL on X11", - "type": "compile", -@@ -842,10 +845,11 @@ - "angle": { - "label": "ANGLE", - "autoDetect": "features.opengles2 || features.opengl-dynamic", -- "condition": "config.win32 && tests.directx", -+ "condition": "config.win32 && tests.directx && tests.fxc", - "output": [ - "publicFeature", -- { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" } -+ { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" }, -+ { "type": "varAssign", "name": "QMAKE_FXC_LOCATION", "value": "tests.fxc.value" } - ] - }, - "angle_d3d11_qdtd": { -diff --git a/qtbase/src/gui/configure.pri b/qtbase/src/gui/configure.pri -index aaffa835dc..566686b4f6 100644 ---- a/qtbase/src/gui/configure.pri -+++ b/qtbase/src/gui/configure.pri -@@ -15,22 +15,12 @@ defineTest(qtConfLibrary_freetype) { - return(true) - } - --# Check for Direct X SDK (include, lib, and direct shader compiler 'fxc'). --# Up to Direct X SDK June 2010 and for MinGW, this is pointed to by the --# DXSDK_DIR variable. Starting with Windows Kit 8, it is included in --# the Windows SDK. Checking for the header is not sufficient, since it --# is also present in MinGW. -+# For MSVC everything DirectX related is included in Windows Kit >= 8, -+# so we do not do any magic in this case. -+# For MinGW we need the shader compiler (fxc.exe), which -+# are not part of MinGW. They can either be obtained from a DirectX SDK -+# (keep the old approach working) or Windows Kit (>= 8). - defineTest(qtConfTest_directX) { -- dxdir = $$getenv("DXSDK_DIR") -- !isEmpty(dxdir) { -- EXTRA_INCLUDEPATH += $$dxdir/include -- equals(QT_ARCH, x86_64): \ -- EXTRA_LIBDIR += $$dxdir/lib/x64 -- else: \ -- EXTRA_LIBDIR += $$dxdir/lib/x86 -- EXTRA_PATH += $$dxdir/Utilities/bin/x86 -- } -- - $$qtConfEvaluate("features.sse2") { - ky = $$size($${1}.files._KEYS_) - $${1}.files._KEYS_ += $$ky -@@ -42,6 +32,50 @@ defineTest(qtConfTest_directX) { - return(false) - } - -+defineTest(qtConfTest_fxc) { -+ !mingw { -+ fxc = $$qtConfFindInPath("fxc.exe") -+ } else { -+ dxdir = $$getenv("DXSDK_DIR") -+ winkitdir = $$getenv("WindowsSdkDir") -+ !isEmpty(dxdir) { -+ equals(QT_ARCH, x86_64): \ -+ fxc = $$dxdir/Utilities/bin/x64/fxc.exe -+ else: \ -+ fxc = $$dxdir/Utilities/bin/x86/fxc.exe -+ } else: !isEmpty(winkitdir) { -+ equals(QT_ARCH, x86_64): \ -+ fxc = $$winkitdir/bin/x64/fxc.exe -+ else: \ -+ fxc = $$winkitdir/bin/x86/fxc.exe -+ -+ !exists($$fxc) { -+ binsubdirs = $$files($$winkitdir/bin/*) -+ for (dir, binsubdirs) { -+ equals(QT_ARCH, x86_64): \ -+ finalBinDir = $$dir/x64 -+ else: \ -+ finalBinDir = $$dir/x86 -+ -+ fxc = $${finalBinDir}/fxc.exe -+ exists($$fxc) { -+ break() -+ } -+ } -+ } -+ } -+ } -+ -+ !isEmpty(fxc):exists($$fxc) { -+ $${1}.value = $$fxc -+ export($${1}.value) -+ $${1}.cache += value -+ export($${1}.cache) -+ return(true) -+ } -+ return(false) -+} -+ - defineTest(qtConfTest_xkbConfigRoot) { - qtConfTest_getPkgConfigVariable($${1}): return(true) - diff --git a/3rdparty/ext_qt/gerrit-212811_qtbase-angle-d3d11-warp-crash-fix.patch b/3rdparty/ext_qt/gerrit-212811_qtbase-angle-d3d11-warp-crash-fix.patch deleted file mode 100644 --- a/3rdparty/ext_qt/gerrit-212811_qtbase-angle-d3d11-warp-crash-fix.patch +++ /dev/null @@ -1,50 +0,0 @@ -commit 644ff9428853f138649de0419de4b49bf41bc738 -Author: Oliver Wolff -Date: Tue Nov 28 13:30:52 2017 +0100 - - ANGLE: D3D11: Fix shared handle support detection for WARP when MinGW is used - - The MinGW version we support supports IsWindows8OrGreater so that we can - check the windows version properly. As the OpenGL detection falls back - to WARP in case of RDP it was possible, that shared handles were wrongly - stated as supported, which caused crashes in users' code. - - Task-number: QTBUG-64657 - Change-Id: Iaca2bd169f2764cf6ec68a1d36112a735246b29a - Reviewed-by: Andre de la Rocha - Reviewed-by: Andy Shaw - (cherry picked from commit 6508fdca1dcc7105947befadba272d0fd4bbc27f) - -diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -index 0173311bc6..5118bdbe9c 100644 ---- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -+++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -@@ -2645,7 +2645,7 @@ bool Renderer11::getShareHandleSupport() const - - if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP) - { --#if !defined(ANGLE_ENABLE_WINDOWS_STORE) && !defined(__GNUC__) -+#ifndef ANGLE_ENABLE_WINDOWS_STORE - if (!IsWindows8OrGreater()) - { - // WARP on Windows 7 doesn't support shared handles -diff --git a/src/angle/patches/0002-ANGLE-Fix-compilation-with-MinGW.patch b/src/angle/patches/0002-ANGLE-Fix-compilation-with-MinGW.patch -index dc091b0497..f42ff2141b 100644 ---- a/src/angle/patches/0002-ANGLE-Fix-compilation-with-MinGW.patch -+++ b/src/angle/patches/0002-ANGLE-Fix-compilation-with-MinGW.patch -@@ -405,15 +405,6 @@ index ea84783..62badcc 100644 - - if (mD3d11Module) - { --@@ -2618,7 +2642,7 @@ bool Renderer11::getShareHandleSupport() const -- -- if (deviceType == d3d11::ANGLE_D3D11_DEVICE_TYPE_WARP) -- { ---#ifndef ANGLE_ENABLE_WINDOWS_STORE --+#if !defined(ANGLE_ENABLE_WINDOWS_STORE) && !defined(__GNUC__) -- if (!IsWindows8OrGreater()) -- { -- // WARP on Windows 7 doesn't support shared handles - diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h - index 62e9816..b4e7761 100644 - --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h diff --git a/3rdparty/ext_qt/qopengldebug-gles.patch b/3rdparty/ext_qt/qopengldebug-gles.patch deleted file mode 100644 --- a/3rdparty/ext_qt/qopengldebug-gles.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/qtbase/src/gui/opengl/qopengldebug.cpp b/qtbase/src/gui/opengl/qopengldebug.cpp -index f6c3af37dd..70bf7530e1 100644 ---- a/qtbase/src/gui/opengl/qopengldebug.cpp -+++ b/qtbase/src/gui/opengl/qopengldebug.cpp -@@ -1413,7 +1413,7 @@ bool QOpenGLDebugLogger::initialize() - - #define GET_DEBUG_PROC_ADDRESS(procName) \ - d->procName = reinterpret_cast< qt_ ## procName ## _t >( \ -- d->context->getProcAddress(#procName) \ -+ d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \ - ); - - GET_DEBUG_PROC_ADDRESS(glDebugMessageControl); diff --git a/3rdparty/ext_qt/qtgui-private-headers.diff b/3rdparty/ext_qt/qtgui-private-headers.diff deleted file mode 100644 --- a/3rdparty/ext_qt/qtgui-private-headers.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/qtbase/mkspecs/features/create_cmake.prf b/qtbase/mkspecs/features/create_cmake.prf -index 11fb52a..aec9ea9 100644 ---- a/qtbase/mkspecs/features/create_cmake.prf -+++ b/qtbase/mkspecs/features/create_cmake.prf -@@ -30,7 +30,6 @@ CMAKE_MODULE_NAME = $$cmakeModuleName($${MODULE}) - - split_incpath { - CMAKE_ADD_SOURCE_INCLUDE_DIRS = true -- CMAKE_NO_PRIVATE_INCLUDES = true # Don't add private includes in the build dir which don't exist - CMAKE_SOURCE_INCLUDES = \ - $$cmakeTargetPaths($$QT_MODULE_INCLUDE_BASE $$QT_MODULE_INCLUDE_BASE/Qt$${CMAKE_MODULE_NAME}) - CMAKE_SOURCE_PRIVATE_INCLUDES = \ diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,10 @@ option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") +option(HAVE_HDR "Enable HDR surface format selection. Available only on certain patched versions of Qt" OFF) +configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) +add_feature_info("Enable HDR" HAVE_HDR "Enable selection of HDR surface fort Krita window. Needs a particular patched version of Qt") + option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd --- a/build-tools/windows/build.cmd +++ b/build-tools/windows/build.cmd @@ -423,10 +423,12 @@ pushd "%WindowsSdkDir%" if exist "bin\x64\fxc.exe" ( set HAVE_FXC_EXE=1 + if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%" ) else ( for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do ( if exist "bin\%%a\x64\fxc.exe" ( set HAVE_FXC_EXE=1 + if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%\bin\%%a\" ) ) ) @@ -744,6 +746,7 @@ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -DFOUNDATION_BUILD=ON ^ + -DHAVE_HDR=ON ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% @@ -758,6 +761,7 @@ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -DFOUNDATION_BUILD=ON ^ + -DHAVE_HDR=ON ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% diff --git a/config-hdr.h.cmake b/config-hdr.h.cmake new file mode 100644 --- /dev/null +++ b/config-hdr.h.cmake @@ -0,0 +1,4 @@ +/* config-hdr.h. Generated by cmake from config-hdr.h.cmake */ + +/* Define if you want to have HDR surface selection capabilities */ +#cmakedefine HAVE_HDR 1 diff --git a/krita/data/profiles/CMakeLists.txt b/krita/data/profiles/CMakeLists.txt --- a/krita/data/profiles/CMakeLists.txt +++ b/krita/data/profiles/CMakeLists.txt @@ -5,4 +5,5 @@ scRGB.icm cmyk.icm krita25_lcms-builtin-sRGB_g100-truegamma.icc + ITUR_2100_PQ_FULL.ICC DESTINATION ${SHARE_INSTALL_PREFIX}/color/icc/krita) diff --git a/krita/data/profiles/ITUR_2100_PQ_FULL.ICC b/krita/data/profiles/ITUR_2100_PQ_FULL.ICC new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ - template -class KisCallbackBasedPaintopProperty : public ParentClass +class KRITAIMAGE_EXPORT KisCallbackBasedPaintopProperty : public ParentClass { public: KisCallbackBasedPaintopProperty(typename ParentClass::Type type, const QString &id, const QString &name, KisPaintOpSettingsRestrictedSP settings, - QObject *parent) - : ParentClass(type, id, name, settings, parent) {} + QObject *parent); KisCallbackBasedPaintopProperty(const QString &id, const QString &name, KisPaintOpSettingsRestrictedSP settings, - QObject *parent) - : ParentClass(id, name, settings, parent) {} + QObject *parent); typedef std::function Callback; typedef std::function VisibleCallback; - void setReadCallback(Callback func) { m_readFunc = func; } - void setWriteCallback(Callback func) { m_writeFunc = func; } - void setIsVisibleCallback(VisibleCallback func) { m_visibleFunc = func; } + void setReadCallback(Callback func); + void setWriteCallback(Callback func); + void setIsVisibleCallback(VisibleCallback func); protected: - void readValueImpl() override { if (m_readFunc) m_readFunc(this); } - void writeValueImpl() override { if (m_writeFunc) m_writeFunc(this); } - bool isVisible() const override { return m_visibleFunc ? m_visibleFunc(this) : true; } + void readValueImpl() override; + void writeValueImpl() override; + bool isVisible() const override; private: Callback m_readFunc; diff --git a/libs/image/brushengine/kis_callback_based_paintop_property_impl.h b/libs/image/brushengine/kis_callback_based_paintop_property_impl.h new file mode 100644 --- /dev/null +++ b/libs/image/brushengine/kis_callback_based_paintop_property_impl.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KIS_CALLBACK_BASED_PAINTOP_PROPERTY_IMPL_H +#define __KIS_CALLBACK_BASED_PAINTOP_PROPERTY_IMPL_H + +#include + +template +KisCallbackBasedPaintopProperty::KisCallbackBasedPaintopProperty(typename ParentClass::Type type, + const QString &id, + const QString &name, + KisPaintOpSettingsRestrictedSP settings, + QObject *parent) + : ParentClass(type, id, name, settings, parent) +{ +} + +template +KisCallbackBasedPaintopProperty::KisCallbackBasedPaintopProperty(const QString &id, + const QString &name, + KisPaintOpSettingsRestrictedSP settings, + QObject *parent) + : ParentClass(id, name, settings, parent) +{ +} + +template +void KisCallbackBasedPaintopProperty::setReadCallback(Callback func) +{ + m_readFunc = func; +} + +template +void KisCallbackBasedPaintopProperty::setWriteCallback(Callback func) +{ + m_writeFunc = func; +} + +template +void KisCallbackBasedPaintopProperty::setIsVisibleCallback(VisibleCallback func) +{ + m_visibleFunc = func; +} + +template +void KisCallbackBasedPaintopProperty::readValueImpl() +{ + if (m_readFunc) m_readFunc(this); +} + +template +void KisCallbackBasedPaintopProperty::writeValueImpl() +{ + if (m_writeFunc) m_writeFunc(this); +} + +template +bool KisCallbackBasedPaintopProperty::isVisible() const +{ + return m_visibleFunc ? m_visibleFunc(this) : true; +} + +#endif /* __KIS_CALLBACK_BASED_PAINTOP_PROPERTY_IMPL_H */ diff --git a/libs/image/brushengine/kis_combo_based_paintop_property.h b/libs/image/brushengine/kis_combo_based_paintop_property.h --- a/libs/image/brushengine/kis_combo_based_paintop_property.h +++ b/libs/image/brushengine/kis_combo_based_paintop_property.h @@ -56,7 +56,7 @@ }; #include "kis_callback_based_paintop_property.h" -extern template class KRITAIMAGE_EXPORT KisCallbackBasedPaintopProperty; +extern template class KisCallbackBasedPaintopProperty; typedef KisCallbackBasedPaintopProperty KisComboBasedPaintOpPropertyCallback; #endif /* __KIS_COMBO_BASED_PAINTOP_PROPERTY_H */ diff --git a/libs/image/brushengine/kis_combo_based_paintop_property.cpp b/libs/image/brushengine/kis_combo_based_paintop_property.cpp --- a/libs/image/brushengine/kis_combo_based_paintop_property.cpp +++ b/libs/image/brushengine/kis_combo_based_paintop_property.cpp @@ -72,4 +72,5 @@ m_d->icons = list; } +#include "kis_callback_based_paintop_property_impl.h" template class KisCallbackBasedPaintopProperty; diff --git a/libs/image/brushengine/kis_slider_based_paintop_property.h b/libs/image/brushengine/kis_slider_based_paintop_property.h --- a/libs/image/brushengine/kis_slider_based_paintop_property.h +++ b/libs/image/brushengine/kis_slider_based_paintop_property.h @@ -33,86 +33,38 @@ */ template -class KisSliderBasedPaintOpProperty : public KisUniformPaintOpProperty +class KRITAIMAGE_EXPORT KisSliderBasedPaintOpProperty : public KisUniformPaintOpProperty { public: KisSliderBasedPaintOpProperty(Type type, const QString &id, const QString &name, KisPaintOpSettingsRestrictedSP settings, - QObject *parent) - : KisUniformPaintOpProperty(type, id, name, settings, parent), - m_min(T(0)), - m_max(T(100)), - m_singleStep(T(1)), - m_pageStep(T(10)), - m_exponentRatio(1.0), - m_decimals(2) - { - } + QObject *parent); KisSliderBasedPaintOpProperty(const QString &id, const QString &name, KisPaintOpSettingsRestrictedSP settings, - QObject *parent) - : KisUniformPaintOpProperty(Int, id, name, settings, parent), - m_min(T(0)), - m_max(T(100)), - m_singleStep(T(1)), - m_pageStep(T(10)), - m_exponentRatio(1.0), - m_decimals(2) - { - qFatal("Should have never been called!"); - } - - T min() const { - return m_min; - } - T max() const { - return m_max; - } - void setRange(T min, T max) { - m_min = min; - m_max = max; - } - - T singleStep() const { - return m_singleStep; - } - void setSingleStep(T value) { - m_singleStep = value; - } - - T pageStep() const { - return m_pageStep; - } - void setPageStep(T value) { - m_pageStep = value; - } - - qreal exponentRatio() const { - return m_exponentRatio; - } - void setExponentRatio(qreal value) { - m_exponentRatio = value; - } - - int decimals() const { - return m_decimals; - } - void setDecimals(int value) { - m_decimals = value; - } - - QString suffix() const { - return m_suffix; - } - void setSuffix(QString value) { - m_suffix = value; - } + QObject *parent); + T min() const; + T max() const; + void setRange(T min, T max); + + T singleStep() const; + void setSingleStep(T value); + T pageStep() const; + void setPageStep(T value); + + qreal exponentRatio() const; + void setExponentRatio(qreal value); + int decimals() const; + void setDecimals(int value); + + QString suffix() const; + void setSuffix(QString value); + private: T m_min; T m_max; @@ -127,10 +79,10 @@ #include "kis_callback_based_paintop_property.h" -extern template class KRITAIMAGE_EXPORT KisSliderBasedPaintOpProperty; -extern template class KRITAIMAGE_EXPORT KisSliderBasedPaintOpProperty; -extern template class KRITAIMAGE_EXPORT KisCallbackBasedPaintopProperty>; -extern template class KRITAIMAGE_EXPORT KisCallbackBasedPaintopProperty>; +extern template class KisSliderBasedPaintOpProperty; +extern template class KisSliderBasedPaintOpProperty; +extern template class KisCallbackBasedPaintopProperty>; +extern template class KisCallbackBasedPaintopProperty>; typedef KisSliderBasedPaintOpProperty KisIntSliderBasedPaintOpProperty; typedef KisSliderBasedPaintOpProperty KisDoubleSliderBasedPaintOpProperty; diff --git a/libs/image/brushengine/kis_slider_based_paintop_property.cpp b/libs/image/brushengine/kis_slider_based_paintop_property.cpp --- a/libs/image/brushengine/kis_slider_based_paintop_property.cpp +++ b/libs/image/brushengine/kis_slider_based_paintop_property.cpp @@ -20,6 +20,121 @@ #include "kis_paintop_settings.h" + +template +KisSliderBasedPaintOpProperty::KisSliderBasedPaintOpProperty(Type type, + const QString &id, + const QString &name, + KisPaintOpSettingsRestrictedSP settings, + QObject *parent) + : KisUniformPaintOpProperty(type, id, name, settings, parent), + m_min(T(0)), + m_max(T(100)), + m_singleStep(T(1)), + m_pageStep(T(10)), + m_exponentRatio(1.0), + m_decimals(2) +{ +} + +template +KisSliderBasedPaintOpProperty::KisSliderBasedPaintOpProperty(const QString &id, + const QString &name, + KisPaintOpSettingsRestrictedSP settings, + QObject *parent) + : KisUniformPaintOpProperty(Int, id, name, settings, parent), + m_min(T(0)), + m_max(T(100)), + m_singleStep(T(1)), + m_pageStep(T(10)), + m_exponentRatio(1.0), + m_decimals(2) +{ + qFatal("Should have never been called!"); +} + +template +T KisSliderBasedPaintOpProperty::min() const +{ + return m_min; +} + +template +T KisSliderBasedPaintOpProperty::max() const +{ + return m_max; +} + +template +void KisSliderBasedPaintOpProperty::setRange(T min, T max) +{ + m_min = min; + m_max = max; +} + +template +T KisSliderBasedPaintOpProperty::singleStep() const +{ + return m_singleStep; +} + +template +void KisSliderBasedPaintOpProperty::setSingleStep(T value) +{ + m_singleStep = value; +} + +template +T KisSliderBasedPaintOpProperty::pageStep() const +{ + return m_pageStep; +} + +template +void KisSliderBasedPaintOpProperty::setPageStep(T value) +{ + m_pageStep = value; +} + +template +qreal KisSliderBasedPaintOpProperty::exponentRatio() const +{ + return m_exponentRatio; +} + +template +void KisSliderBasedPaintOpProperty::setExponentRatio(qreal value) +{ + m_exponentRatio = value; +} + +template +int KisSliderBasedPaintOpProperty::decimals() const +{ + return m_decimals; +} + +template +void KisSliderBasedPaintOpProperty::setDecimals(int value) +{ + m_decimals = value; +} + +template +QString KisSliderBasedPaintOpProperty::suffix() const +{ + return m_suffix; +} + +template +void KisSliderBasedPaintOpProperty::setSuffix(QString value) +{ + m_suffix = value; +} + + +#include "kis_callback_based_paintop_property_impl.h" + template class KisSliderBasedPaintOpProperty; template class KisSliderBasedPaintOpProperty; diff --git a/libs/image/brushengine/kis_uniform_paintop_property.cpp b/libs/image/brushengine/kis_uniform_paintop_property.cpp --- a/libs/image/brushengine/kis_uniform_paintop_property.cpp +++ b/libs/image/brushengine/kis_uniform_paintop_property.cpp @@ -136,4 +136,5 @@ { } +#include "kis_callback_based_paintop_property_impl.h" template class KisCallbackBasedPaintopProperty; diff --git a/libs/image/kis_fixed_paint_device.h b/libs/image/kis_fixed_paint_device.h --- a/libs/image/kis_fixed_paint_device.h +++ b/libs/image/kis_fixed_paint_device.h @@ -133,6 +133,12 @@ void convertTo(const KoColorSpace * dstColorSpace = 0, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); + + /** + * Set color profile for the device without converting actual pixel data + */ + void setProfile(const KoColorProfile *profile); + /** * Fill this paint device with the data from image * diff --git a/libs/image/kis_fixed_paint_device.cpp b/libs/image/kis_fixed_paint_device.cpp --- a/libs/image/kis_fixed_paint_device.cpp +++ b/libs/image/kis_fixed_paint_device.cpp @@ -144,6 +144,21 @@ m_data = dstData; } +void KisFixedPaintDevice::setProfile(const KoColorProfile *profile) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(profile); + + const KoColorSpace *dstColorSpace = + KoColorSpaceRegistry::instance()->colorSpace( + colorSpace()->colorModelId().id(), + colorSpace()->colorDepthId().id(), + profile); + + KIS_SAFE_ASSERT_RECOVER_RETURN(dstColorSpace); + + m_colorSpace = dstColorSpace; +} + void KisFixedPaintDevice::convertFromQImage(const QImage& _image, const QString &srcProfileName) { QImage image = _image; diff --git a/libs/pigment/KoColorConversionSystem.cpp b/libs/pigment/KoColorConversionSystem.cpp --- a/libs/pigment/KoColorConversionSystem.cpp +++ b/libs/pigment/KoColorConversionSystem.cpp @@ -97,7 +97,10 @@ } else { engineNode = insertEngine(engine); } - connectToEngine(n, engineNode); + + if (engine->supportsColorSpace(modelId, depthId, profile)) { + connectToEngine(n, engineNode); + } } } } @@ -138,7 +141,10 @@ Q_ASSERT(engine); Node* engineNode = d->graph[ NodeKey(engine->id(), engine->id(), engine->id())]; Q_ASSERT(engineNode); - connectToEngine(n, engineNode); + + if (engine->supportsColorSpace(modelId, depthId, _profile)) { + connectToEngine(n, engineNode); + } } const QList cctfs = factory->colorConversionLinks(); Q_FOREACH (KoColorConversionTransformationFactory* cctf, cctfs) { diff --git a/libs/pigment/KoColorConversionTransformation.h b/libs/pigment/KoColorConversionTransformation.h --- a/libs/pigment/KoColorConversionTransformation.h +++ b/libs/pigment/KoColorConversionTransformation.h @@ -116,11 +116,19 @@ ConversionFlags conversionFlags() const; /** - * perform the color conversion between two buffers. + * perform the color conversion between two buffers. Make sure that + * \p src is not the same as \p dst! * @param nPixels the number of pixels in the buffers. */ void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override = 0; + /** + * perform the color conversion between two or one buffer. This is a convenience + * function that allows doing the conversion in-place + * @param nPixels the number of pixels in the buffers. + */ + void transformInPlace(const quint8 *src, quint8 *dst, qint32 nPixels) const; + /** * @return false if the transformation is not valid */ diff --git a/libs/pigment/KoColorConversionTransformation.cpp b/libs/pigment/KoColorConversionTransformation.cpp --- a/libs/pigment/KoColorConversionTransformation.cpp +++ b/libs/pigment/KoColorConversionTransformation.cpp @@ -68,6 +68,17 @@ return d->conversionFlags; } +void KoColorConversionTransformation::transformInPlace(const quint8 *src, quint8 *dst, qint32 nPixels) const +{ + if (src != dst) { + transform(src, dst, nPixels); + } else { + QByteArray buffer(srcColorSpace()->pixelSize() * nPixels, 0); + transform(src, reinterpret_cast(buffer.data()), nPixels); + memcpy(dst, buffer.data(), buffer.size()); + } +} + void KoColorConversionTransformation::setSrcColorSpace(const KoColorSpace* cs) const { Q_ASSERT(*d->srcColorSpace == *cs); diff --git a/libs/pigment/KoColorSpaceAbstract.h b/libs/pigment/KoColorSpaceAbstract.h --- a/libs/pigment/KoColorSpaceAbstract.h +++ b/libs/pigment/KoColorSpaceAbstract.h @@ -48,6 +48,9 @@ template class KoColorSpaceAbstract : public KoColorSpace { +public: + typedef _CSTrait ColorSpaceTraits; + public: KoColorSpaceAbstract(const QString &id, const QString &name) : KoColorSpace(id, name, new KoMixColorsOpImpl< _CSTrait>(), new KoConvolutionOpImpl< _CSTrait>()) { diff --git a/libs/pigment/KoColorSpaceEngine.h b/libs/pigment/KoColorSpaceEngine.h --- a/libs/pigment/KoColorSpaceEngine.h +++ b/libs/pigment/KoColorSpaceEngine.h @@ -49,6 +49,11 @@ virtual const KoColorProfile* addProfile(const QByteArray &data) = 0; virtual void removeProfile(const QString &filename) = 0; + /** + * \return true if the color space can be converted via this engine + */ + virtual bool supportsColorSpace(const QString& colorModelId, const QString& colorDepthId, const KoColorProfile *profile) const; + private: struct Private; Private* const d; diff --git a/libs/pigment/KoColorSpaceEngine.cpp b/libs/pigment/KoColorSpaceEngine.cpp --- a/libs/pigment/KoColorSpaceEngine.cpp +++ b/libs/pigment/KoColorSpaceEngine.cpp @@ -50,6 +50,15 @@ return d->name; } +bool KoColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const +{ + Q_UNUSED(colorModelId); + Q_UNUSED(colorDepthId); + Q_UNUSED(profile); + + return true; +} + KoColorSpaceEngineRegistry::KoColorSpaceEngineRegistry() { } diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h --- a/libs/pigment/KoColorSpaceRegistry.h +++ b/libs/pigment/KoColorSpaceRegistry.h @@ -290,6 +290,29 @@ */ const KoColorSpace * lab16(const KoColorProfile * profile); + /** + * Convenience method to get a standard profile for Rec.2020 linear light + * color space + */ + const KoColorProfile *p2020G10Profile() const; + + /** + * Convenience method to get a standard profile for Rec.2020 PQ color space + */ + const KoColorProfile *p2020PQProfile() const; + + /** + * Convenience method to get a standard profile for Rec. 709 linear light + * color space + */ + const KoColorProfile *p709G10Profile() const; + + /** + * Convenience method to get a standard profile for Rec. 709 sRGB-tone- + * response-curve profile + */ + const KoColorProfile *p709SRGBProfile() const; + /** * @return the list of available color models */ diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp --- a/libs/pigment/KoColorSpaceRegistry.cpp +++ b/libs/pigment/KoColorSpaceRegistry.cpp @@ -637,6 +637,26 @@ return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile); } +const KoColorProfile *KoColorSpaceRegistry::p2020G10Profile() const +{ + return profileByName("Rec2020-elle-V4-g10.icc"); +} + +const KoColorProfile *KoColorSpaceRegistry::p2020PQProfile() const +{ + return profileByName("High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF"); +} + +const KoColorProfile *KoColorSpaceRegistry::p709G10Profile() const +{ + return profileByName("sRGB-elle-V2-g10.icc"); +} + +const KoColorProfile *KoColorSpaceRegistry::p709SRGBProfile() const +{ + return profileByName("sRGB-elle-V2-srgbtrc.icc"); +} + QList KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const { QReadLocker l(&d->registrylock); diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -94,6 +94,7 @@ kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc + KisOcioConfiguration.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp @@ -167,6 +168,8 @@ opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp + opengl/KisOpenGLModeProber.cpp + opengl/KisScreenInformationAdapter.cpp kis_fps_decoration.cpp tool/KisToolChangesTracker.cpp @@ -398,7 +401,6 @@ input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp input/wintab/kis_tablet_support_win8.cpp - opengl/kis_opengl_win.cpp ) endif() diff --git a/libs/ui/KisOcioConfiguration.h b/libs/ui/KisOcioConfiguration.h new file mode 100644 --- /dev/null +++ b/libs/ui/KisOcioConfiguration.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISOCIOCONFIGURATION_H +#define KISOCIOCONFIGURATION_H + +#include + +class KisOcioConfiguration +{ +public: + enum Mode { + INTERNAL = 0, + OCIO_CONFIG, + OCIO_ENVIRONMENT + }; + +public: + Mode mode = INTERNAL; + QString configurationPath; + QString lutPath; + QString inputColorSpace; + QString displayDevice; + QString displayView; + QString look; +}; + +#endif // KISOCIOCONFIGURATION_H diff --git a/libs/ui/KisOcioConfiguration.cpp b/libs/ui/KisOcioConfiguration.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/KisOcioConfiguration.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisOcioConfiguration.h" + diff --git a/libs/ui/brushhud/kis_uniform_paintop_property_widget.h b/libs/ui/brushhud/kis_uniform_paintop_property_widget.h --- a/libs/ui/brushhud/kis_uniform_paintop_property_widget.h +++ b/libs/ui/brushhud/kis_uniform_paintop_property_widget.h @@ -50,10 +50,6 @@ class KisDoubleSliderSpinBox; class QCheckBox; -template class KisSliderBasedPaintOpProperty; -typedef KisSliderBasedPaintOpProperty KisIntSliderBasedPaintOpProperty; -typedef KisSliderBasedPaintOpProperty KisDoubleSliderBasedPaintOpProperty; - class KisUniformPaintOpPropertyIntSlider : public KisUniformPaintOpPropertyWidget { Q_OBJECT diff --git a/libs/ui/canvas/kis_abstract_canvas_widget.h b/libs/ui/canvas/kis_abstract_canvas_widget.h --- a/libs/ui/canvas/kis_abstract_canvas_widget.h +++ b/libs/ui/canvas/kis_abstract_canvas_widget.h @@ -31,6 +31,7 @@ class KisDisplayFilter; class KisDisplayColorConverter; class QBitArray; +class KoColorSpace; #include "kis_types.h" #include "kis_ui_types.h" @@ -63,13 +64,16 @@ /// set the specified display filter on the canvas virtual void setDisplayFilter(QSharedPointer displayFilter) = 0; + /// set/update the color space of the attached image + virtual void notifyImageColorSpaceChanged(const KoColorSpace *cs) = 0; + virtual void setWrapAroundViewingMode(bool value) = 0; // Called from KisCanvas2::channelSelectionChanged virtual void channelSelectionChanged(const QBitArray &channelFlags) = 0; // Called from KisCanvas2::slotSetDisplayProfile - virtual void setDisplayProfile(KisDisplayColorConverter *colorConverter) = 0; + virtual void setDisplayColorConverter(KisDisplayColorConverter *colorConverter) = 0; // Called from KisCanvas2::finishResizingImage virtual void finishResizingImage(qint32 w, qint32 h) = 0; diff --git a/libs/ui/canvas/kis_canvas2.h b/libs/ui/canvas/kis_canvas2.h --- a/libs/ui/canvas/kis_canvas2.h +++ b/libs/ui/canvas/kis_canvas2.h @@ -265,10 +265,6 @@ void channelSelectionChanged(); - /** - * Called whenever the display monitor profile resource changes - */ - void slotSetDisplayProfile(const KoColorProfile *profile); void startUpdateInPatches(const QRect &imageRect); void slotTrySwitchShapeManager(); @@ -308,6 +304,7 @@ void slotReferenceImagesChanged(); + void slotImageColorSpaceChanged(); public: bool isPopupPaletteVisible() const; @@ -334,6 +331,7 @@ void updateCanvasWidgetImpl(const QRect &rc = QRect()); void setCanvasWidget(KisAbstractCanvasWidget *widget); void resetCanvas(bool useOpenGL); + void setDisplayProfile(const KoColorProfile *profile); void notifyLevelOfDetailChange(); diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp --- a/libs/ui/canvas/kis_canvas2.cpp +++ b/libs/ui/canvas/kis_canvas2.cpp @@ -532,22 +532,25 @@ KisConfig cfg(true); QDesktopWidget dw; const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView())); + m_d->displayColorConverter.notifyOpenGLCanvasIsActive(useOpenGL && KisOpenGL::hasOpenGL()); m_d->displayColorConverter.setMonitorProfile(profile); + if (useOpenGL && !KisOpenGL::hasOpenGL()) { + warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; + useOpenGL = false; + } + + m_d->displayColorConverter.notifyOpenGLCanvasIsActive(useOpenGL); + if (useOpenGL) { - if (KisOpenGL::hasOpenGL()) { - createOpenGLCanvas(); - if (cfg.canvasState() == "OPENGL_FAILED") { - // Creating the opengl canvas failed, fall back - warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter."; - createQPainterCanvas(); - } - } else { - warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; + createOpenGLCanvas(); + if (cfg.canvasState() == "OPENGL_FAILED") { + // Creating the opengl canvas failed, fall back + warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter."; + m_d->displayColorConverter.notifyOpenGLCanvasIsActive(false); createQPainterCanvas(); } - } - else { + } else { createQPainterCanvas(); } @@ -561,6 +564,7 @@ { KisImageSP image = m_d->view->image(); + m_d->displayColorConverter.setImageColorSpace(image->colorSpace()); m_d->coordinatesConverter->setImage(image); m_d->toolProxy.initializeImage(image); @@ -573,6 +577,9 @@ connect(image, SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(startResizingImage()), Qt::DirectConnection); connect(image->undoAdapter(), SIGNAL(selectionChanged()), SLOT(slotTrySwitchShapeManager())); + connect(image, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged())); + connect(image, SIGNAL(sigProfileChanged(const KoColorProfile*)), SLOT(slotImageColorSpaceChanged())); + connectCurrentCanvas(); } @@ -613,6 +620,11 @@ void KisCanvas2::startUpdateInPatches(const QRect &imageRect) { + /** + * We don't do patched loading for openGL canvas, becasue it loads + * the tiles, which are bascially "patches". Therefore, big chunks + * of memory are never allocated. + */ if (m_d->currentCanvasIsOpenGL) { startUpdateCanvasProjection(imageRect); } else { @@ -646,6 +658,19 @@ return m_d->displayColorConverter.displayFilter(); } +void KisCanvas2::slotImageColorSpaceChanged() +{ + KisImageSP image = this->image(); + + m_d->view->viewManager()->blockUntilOperationsFinishedForced(image); + + m_d->displayColorConverter.setImageColorSpace(image->colorSpace()); + + image->barrierLock(); + m_d->canvasWidget->notifyImageColorSpaceChanged(image->colorSpace()); + image->unlock(); +} + KisDisplayColorConverter* KisCanvas2::displayColorConverter() const { return &m_d->displayColorConverter; @@ -1048,7 +1073,7 @@ m_d->vastScrolling = cfg.vastScrolling(); resetCanvas(cfg.useOpenGL()); - slotSetDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget()))); + setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget()))); initializeFpsDecoration(); } @@ -1060,7 +1085,7 @@ startUpdateInPatches(image->bounds()); } -void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile) +void KisCanvas2::setDisplayProfile(const KoColorProfile *monitorProfile) { if (m_d->displayColorConverter.monitorProfile() == monitorProfile) return; @@ -1069,7 +1094,7 @@ { KisImageSP image = this->image(); KisImageBarrierLocker l(image); - m_d->canvasWidget->setDisplayProfile(&m_d->displayColorConverter); + m_d->canvasWidget->setDisplayColorConverter(&m_d->displayColorConverter); } refetchDataFromImage(); diff --git a/libs/ui/canvas/kis_canvas_widget_base.h b/libs/ui/canvas/kis_canvas_widget_base.h --- a/libs/ui/canvas/kis_canvas_widget_base.h +++ b/libs/ui/canvas/kis_canvas_widget_base.h @@ -46,10 +46,6 @@ KoToolProxy *toolProxy() const override; - - /// set the specified display filter on the canvas - void setDisplayFilter(QSharedPointer displayFilter) override = 0; - /** * Draw the specified decorations on the view. */ diff --git a/libs/ui/canvas/kis_display_color_converter.h b/libs/ui/canvas/kis_display_color_converter.h --- a/libs/ui/canvas/kis_display_color_converter.h +++ b/libs/ui/canvas/kis_display_color_converter.h @@ -30,7 +30,7 @@ class KoColor; class KoColorProfile; class KoCanvasResourceProvider; - +class KoID; /** * Special helper class that provides primitives for converting colors when @@ -53,6 +53,9 @@ KisDisplayColorConverter(KoCanvasResourceProvider *resourceManager, QObject *parent); ~KisDisplayColorConverter() override; + void setImage(KisImageSP image); + void setImageColorSpace(const KoColorSpace *cs); + static KisDisplayColorConverter* dumbConverterInstance(); KoColorDisplayRendererInterface* displayRendererInterface() const; @@ -64,6 +67,11 @@ QColor toQColor(const KoColor &c) const; KoColor approximateFromRenderedQColor(const QColor &c) const; + bool canSkipDisplayConversion(const KoColorSpace *cs) const; + KoColor applyDisplayFiltering(const KoColor &srcColor, const KoID &bitDepthId) const; + void applyDisplayFilteringF32(KisFixedPaintDeviceSP device, const KoID &bitDepthId) const; + + /** * Converts the exactBounds() (!) of the \p srcDevice into QImage * properly rendered into display RGB space. Please note that the @@ -89,6 +97,10 @@ QSharedPointer displayFilter() const; const KoColorProfile* monitorProfile() const; + const KoColorProfile* openGLCanvasSurfaceProfile() const; + bool isHDRMode() const; + + void notifyOpenGLCanvasIsActive(bool value); Q_SIGNALS: void displayConfigurationChanged(); @@ -96,7 +108,8 @@ private: // is not possible to implement! KoColor toKoColor(const QColor &c); - + template + typename Policy::Result convertToDisplayImpl(const KoColor &srcColor, bool alreadyInDestinationF32 = false) const; private: Q_PRIVATE_SLOT(m_d, void slotCanvasResourceChanged(int key, const QVariant &v)); diff --git a/libs/ui/canvas/kis_display_color_converter.cpp b/libs/ui/canvas/kis_display_color_converter.cpp --- a/libs/ui/canvas/kis_display_color_converter.cpp +++ b/libs/ui/canvas/kis_display_color_converter.cpp @@ -40,6 +40,8 @@ #include "kis_config.h" #include "kis_paint_device.h" #include "kis_iterator_ng.h" +#include "kis_fixed_paint_device.h" +#include "opengl/KisOpenGLModeProber.h" Q_GLOBAL_STATIC(KisDisplayColorConverter, s_instance) @@ -51,14 +53,13 @@ resourceManager(_resourceManager), nodeColorSpace(0), paintingColorSpace(0), - monitorColorSpace(0), monitorProfile(0), renderingIntent(KoColorConversionTransformation::internalRenderingIntent()), conversionFlags(KoColorConversionTransformation::internalConversionFlags()), displayFilter(0), - intermediateColorSpace(0), displayRenderer(new DisplayRenderer(_q, _resourceManager)) { + useHDRMode = KisOpenGLModeProber::instance()->useHDRMode(); } KisDisplayColorConverter *const q; @@ -67,7 +68,67 @@ const KoColorSpace *nodeColorSpace; const KoColorSpace *paintingColorSpace; - const KoColorSpace *monitorColorSpace; + + const KoColorProfile* inputImageProfile = 0; + + const KoColorProfile* qtWidgetsProfile() const { + return useHDRMode ? KoColorSpaceRegistry::instance()->p709SRGBProfile() : monitorProfile; + } + + const KoColorProfile* openGLSurfaceProfile() const { + return useHDRMode && openGLCanvasIsActive ? KisOpenGLModeProber::instance()->rootSurfaceColorProfile() : monitorProfile; + } + + const KoColorProfile* ocioInputProfile() const { + return displayFilter && displayFilter->useInternalColorManagement() ? + openGLSurfaceProfile() : inputImageProfile; + } + + const KoColorProfile* ocioOutputProfile() const { + return openGLSurfaceProfile(); + } + + const KoColorSpace* ocioInputColorSpace() const { + return KoColorSpaceRegistry::instance()-> + colorSpace( + RGBAColorModelID.id(), + Float32BitsColorDepthID.id(), + ocioInputProfile()); + } + + const KoColorSpace* ocioOutputColorSpace() const { + return KoColorSpaceRegistry::instance()-> + colorSpace( + RGBAColorModelID.id(), + Float32BitsColorDepthID.id(), + ocioOutputProfile()); + } + + const KoColorSpace* qtWidgetsColorSpace() const { + return KoColorSpaceRegistry::instance()-> + colorSpace( + RGBAColorModelID.id(), + Integer8BitsColorDepthID.id(), + qtWidgetsProfile()); + } + + const KoColorSpace* openGLSurfaceColorSpace(const KoID &bitDepthId) const { + return KoColorSpaceRegistry::instance()-> + colorSpace( + RGBAColorModelID.id(), + bitDepthId.id(), + openGLSurfaceProfile()); + } + + const KoColorSpace* intermediateColorSpace() const { + // the color space where we apply exposure and + // gamma should always be linear + return KoColorSpaceRegistry::instance()-> + colorSpace( + RGBAColorModelID.id(), + Float32BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020G10Profile()); + } const KoColorProfile *monitorProfile; @@ -75,10 +136,13 @@ KoColorConversionTransformation::ConversionFlags conversionFlags; QSharedPointer displayFilter; - const KoColorSpace *intermediateColorSpace; KoColor intermediateFgColor; KisNodeSP connectedNode; + KisImageSP image; + + bool useHDRMode = false; + bool openGLCanvasIsActive = false; inline KoColor approximateFromQColor(const QColor &qcolor); inline QColor approximateToQColor(const KoColor &color); @@ -91,14 +155,6 @@ void setCurrentNode(KisNodeSP node); bool useOcio() const; - bool finalIsRgba(const KoColorSpace *cs) const; - - template - QColor floatArrayToQColor(const float *p); - - template - QImage convertToQImageDirect(KisPaintDeviceSP device); - class DisplayRenderer : public KoColorDisplayRendererInterface { public: DisplayRenderer(KisDisplayColorConverter *displayColorConverter, KoCanvasResourceProvider *resourceManager) @@ -169,7 +225,7 @@ connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(selectPaintingColorSpace())); - + m_d->inputImageProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); m_d->setCurrentNode(0); setMonitorProfile(0); setDisplayFilter(QSharedPointer(0)); @@ -180,17 +236,28 @@ { setDisplayFilter(QSharedPointer(0)); + m_d->inputImageProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); m_d->paintingColorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_d->setCurrentNode(0); setMonitorProfile(0); - } KisDisplayColorConverter::~KisDisplayColorConverter() { } +void KisDisplayColorConverter::setImageColorSpace(const KoColorSpace *cs) +{ + m_d->inputImageProfile = + cs->colorModelId() == RGBAColorModelID ? + cs->profile() : + KoColorSpaceRegistry::instance()->p709SRGBProfile(); + + emit displayConfigurationChanged(); +} + + KisDisplayColorConverter* KisDisplayColorConverter::dumbConverterInstance() { return s_instance; @@ -211,7 +278,7 @@ KIS_ASSERT_RECOVER_RETURN(displayFilter); KoColor color = srcColor; - color.convertTo(intermediateColorSpace); + color.convertTo(intermediateColorSpace()); displayFilter->approximateForwardTransformation(color.data(), 1); intermediateFgColor = color; } @@ -296,7 +363,11 @@ void KisDisplayColorConverter::setMonitorProfile(const KoColorProfile *monitorProfile) { - m_d->monitorColorSpace = KoColorSpaceRegistry::instance()->rgb8(monitorProfile); + if (m_d->useHDRMode) { + // we don't use ICCcolor management in HDR mode + monitorProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); + } + m_d->monitorProfile = monitorProfile; m_d->renderingIntent = renderingIntent(); @@ -317,19 +388,8 @@ } m_d->displayFilter = displayFilter; - m_d->intermediateColorSpace = 0; if (m_d->displayFilter) { - // choosing default profile, which is scRGB - const KoColorProfile *intermediateProfile = 0; - m_d->intermediateColorSpace = - KoColorSpaceRegistry::instance()-> - colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), intermediateProfile); - - KIS_ASSERT_RECOVER(m_d->intermediateColorSpace) { - m_d->intermediateColorSpace = m_d->monitorColorSpace; - } - m_d->updateIntermediateFgColor( m_d->resourceManager->foregroundColor()); } @@ -375,147 +435,140 @@ return m_d->monitorProfile; } -bool KisDisplayColorConverter::Private::finalIsRgba(const KoColorSpace *cs) const +const KoColorProfile* KisDisplayColorConverter::openGLCanvasSurfaceProfile() const { - /** - * In Krita RGB color spaces differ: 8/16bit are BGRA, 16f/32f-bit RGBA - */ - KoID colorDepthId = cs->colorDepthId(); - return colorDepthId == Float16BitsColorDepthID || - colorDepthId == Float32BitsColorDepthID; -} - -template -QColor KisDisplayColorConverter::Private::floatArrayToQColor(const float *p) { - if (flipToBgra) { - return QColor(KoColorSpaceMaths::scaleToA(p[0]), - KoColorSpaceMaths::scaleToA(p[1]), - KoColorSpaceMaths::scaleToA(p[2]), - KoColorSpaceMaths::scaleToA(p[3])); - } else { - return QColor(KoColorSpaceMaths::scaleToA(p[2]), - KoColorSpaceMaths::scaleToA(p[1]), - KoColorSpaceMaths::scaleToA(p[0]), - KoColorSpaceMaths::scaleToA(p[3])); - } + return m_d->openGLSurfaceProfile(); } +bool KisDisplayColorConverter::isHDRMode() const +{ + return m_d->useHDRMode; +} + +void KisDisplayColorConverter::notifyOpenGLCanvasIsActive(bool value) +{ + m_d->openGLCanvasIsActive = value; + emit displayConfigurationChanged(); +} + + QColor KisDisplayColorConverter::toQColor(const KoColor &srcColor) const { KoColor c(srcColor); - c.convertTo(m_d->paintingColorSpace); - if (!m_d->useOcio()) { - // we expect the display profile is rgb8, which is BGRA here - KIS_ASSERT_RECOVER(m_d->monitorColorSpace->pixelSize() == 4) { return Qt::red; }; + if (m_d->useOcio()) { + KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) { + return QColor(Qt::green); + } - c.convertTo(m_d->monitorColorSpace, m_d->renderingIntent, m_d->conversionFlags); + c.convertTo(m_d->ocioInputColorSpace()); + m_d->displayFilter->filter(c.data(), 1); + c.setProfile(m_d->ocioOutputProfile()); + } - const quint8 *p = c.data(); - return QColor(p[2], p[1], p[0], p[3]); - } else { - const KoColorSpace *srcCS = c.colorSpace(); - - if (m_d->displayFilter->useInternalColorManagement()) { - srcCS = KoColorSpaceRegistry::instance()->colorSpace( - RGBAColorModelID.id(), - Float32BitsColorDepthID.id(), - m_d->monitorProfile); - c.convertTo(srcCS, m_d->renderingIntent, m_d->conversionFlags); - } + // we expect the display profile is rgb8, which is BGRA here + KIS_ASSERT_RECOVER(m_d->qtWidgetsColorSpace()->pixelSize() == 4) { + return QColor(Qt::red); + } + + c.convertTo(m_d->qtWidgetsColorSpace(), m_d->renderingIntent, m_d->conversionFlags); + const quint8 *p = c.data(); + return QColor(p[2], p[1], p[0], p[3]); +} - int numChannels = srcCS->channelCount(); - QVector normalizedChannels(numChannels); - srcCS->normalisedChannelsValue(c.data(), normalizedChannels); - m_d->displayFilter->filter((quint8*)normalizedChannels.data(), 1); +KoColor KisDisplayColorConverter::applyDisplayFiltering(const KoColor &srcColor, + const KoID &bitDepthId) const +{ + KoColor c(srcColor); - const float *p = (const float *)normalizedChannels.constData(); + if (m_d->useOcio()) { + KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) { + return srcColor; + } - return m_d->finalIsRgba(srcCS) ? - m_d->floatArrayToQColor(p) : - m_d->floatArrayToQColor(p); + c.convertTo(m_d->ocioInputColorSpace()); + m_d->displayFilter->filter(c.data(), 1); + c.setProfile(m_d->ocioOutputProfile()); } + + c.convertTo(m_d->openGLSurfaceColorSpace(bitDepthId), m_d->renderingIntent, m_d->conversionFlags); + return c; +} + +bool KisDisplayColorConverter::canSkipDisplayConversion(const KoColorSpace *cs) const +{ + const KoColorProfile *displayProfile = m_d->openGLSurfaceProfile(); + + return !m_d->useOcio() && + cs->colorModelId() == RGBAColorModelID && + (!!cs->profile() == !!displayProfile) && + (!cs->profile() || + cs->profile()->uniqueId() == displayProfile->uniqueId()); } + KoColor KisDisplayColorConverter::approximateFromRenderedQColor(const QColor &c) const { return m_d->approximateFromQColor(c); } -template -QImage -KisDisplayColorConverter::Private::convertToQImageDirect(KisPaintDeviceSP device) +QImage KisDisplayColorConverter::toQImage(KisPaintDeviceSP srcDevice) const { - QRect bounds = device->exactBounds(); - if (bounds.isEmpty()) return QImage(); - - QImage image(bounds.size(), QImage::Format_ARGB32); + KisPaintDeviceSP device = srcDevice; - KisSequentialConstIterator it(device, bounds); - quint8 *dstPtr = image.bits(); + QRect bounds = srcDevice->exactBounds(); + if (bounds.isEmpty()) return QImage(); - const KoColorSpace *cs = device->colorSpace(); - int numChannels = cs->channelCount(); - QVector normalizedChannels(numChannels); - while (it.nextPixel()) { - cs->normalisedChannelsValue(it.rawDataConst(), normalizedChannels); - displayFilter->filter((quint8*)normalizedChannels.data(), 1); + if (m_d->useOcio()) { + KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) { + return QImage(); + } - const float *p = normalizedChannels.constData(); + device = new KisPaintDevice(*srcDevice); + device->convertTo(m_d->ocioInputColorSpace()); - if (flipToBgra) { - dstPtr[0] = KoColorSpaceMaths::scaleToA(p[2]); - dstPtr[1] = KoColorSpaceMaths::scaleToA(p[1]); - dstPtr[2] = KoColorSpaceMaths::scaleToA(p[0]); - dstPtr[3] = KoColorSpaceMaths::scaleToA(p[3]); - } else { - dstPtr[0] = KoColorSpaceMaths::scaleToA(p[0]); - dstPtr[1] = KoColorSpaceMaths::scaleToA(p[1]); - dstPtr[2] = KoColorSpaceMaths::scaleToA(p[2]); - dstPtr[3] = KoColorSpaceMaths::scaleToA(p[3]); + KisSequentialIterator it(device, bounds); + int numConseqPixels = it.nConseqPixels(); + while (it.nextPixels(numConseqPixels)) { + numConseqPixels = it.nConseqPixels(); + m_d->displayFilter->filter(it.rawData(), numConseqPixels); } - dstPtr += 4; + device->setProfile(m_d->ocioOutputProfile()); + } + + // we expect the display profile is rgb8, which is BGRA here + KIS_ASSERT_RECOVER(m_d->qtWidgetsColorSpace()->pixelSize() == 4) { + return QImage(); } - return image; + return device->convertToQImage(m_d->qtWidgetsProfile(), + bounds, + m_d->renderingIntent, m_d->conversionFlags); } -QImage KisDisplayColorConverter::toQImage(KisPaintDeviceSP srcDevice) const +void KisDisplayColorConverter::applyDisplayFilteringF32(KisFixedPaintDeviceSP device, + const KoID &bitDepthId) const { - KisPaintDeviceSP device = srcDevice; - if (*device->colorSpace() != *m_d->paintingColorSpace) { - device = new KisPaintDevice(*srcDevice); - - KUndo2Command *cmd = device->convertTo(m_d->paintingColorSpace); - delete cmd; - } - - if (!m_d->useOcio()) { - return device->convertToQImage(m_d->monitorProfile, m_d->renderingIntent, m_d->conversionFlags); - } else { - if (m_d->displayFilter->useInternalColorManagement()) { - if (device == srcDevice) { - device = new KisPaintDevice(*srcDevice); - } + /** + * This method is optimized for the case when device is already in 32f + * version of the pating color space. + */ - const KoColorSpace *srcCS = - KoColorSpaceRegistry::instance()->colorSpace( - RGBAColorModelID.id(), - Float32BitsColorDepthID.id(), - m_d->monitorProfile); + KIS_SAFE_ASSERT_RECOVER_RETURN(device->colorSpace()->colorDepthId() == Float32BitsColorDepthID); + KIS_SAFE_ASSERT_RECOVER_RETURN(device->colorSpace()->colorModelId() == RGBAColorModelID); + KIS_SAFE_ASSERT_RECOVER_RETURN(device->bounds().isValid()); - KUndo2Command *cmd = device->convertTo(srcCS, m_d->renderingIntent, m_d->conversionFlags); - delete cmd; - } + if (m_d->useOcio()) { + KIS_ASSERT_RECOVER_RETURN(m_d->ocioInputColorSpace()->pixelSize() == 16); - return m_d->finalIsRgba(device->colorSpace()) ? - m_d->convertToQImageDirect(device) : - m_d->convertToQImageDirect(device); + device->convertTo(m_d->ocioInputColorSpace()); + m_d->displayFilter->filter(device->data(), device->bounds().width() * device->bounds().height()); + device->setProfile(m_d->ocioOutputProfile()); } - return QImage(); + device->convertTo(m_d->openGLSurfaceColorSpace(bitDepthId)); } KoColor KisDisplayColorConverter::Private::approximateFromQColor(const QColor &qcolor) @@ -523,7 +576,7 @@ if (!useOcio()) { return KoColor(qcolor, paintingColorSpace); } else { - KoColor color(qcolor, intermediateColorSpace); + KoColor color(qcolor, intermediateColorSpace()); displayFilter->approximateInverseTransformation(color.data(), 1); color.convertTo(paintingColorSpace); return color; @@ -538,7 +591,7 @@ KoColor color(srcColor); if (useOcio()) { - color.convertTo(intermediateColorSpace); + color.convertTo(intermediateColorSpace()); displayFilter->approximateForwardTransformation(color.data(), 1); } diff --git a/libs/ui/canvas/kis_qpainter_canvas.h b/libs/ui/canvas/kis_qpainter_canvas.h --- a/libs/ui/canvas/kis_qpainter_canvas.h +++ b/libs/ui/canvas/kis_qpainter_canvas.h @@ -62,9 +62,10 @@ public: // Implement kis_abstract_canvas_widget interface void setDisplayFilter(QSharedPointer displayFilter) override; + void notifyImageColorSpaceChanged(const KoColorSpace *cs) override; void setWrapAroundViewingMode(bool value) override; void channelSelectionChanged(const QBitArray &channelFlags) override; - void setDisplayProfile(KisDisplayColorConverter *colorConverter) override; + void setDisplayColorConverter(KisDisplayColorConverter *colorConverter) override; void finishResizingImage(qint32 w, qint32 h) override; KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) override; QRect updateCanvasProjection(KisUpdateInfoSP info) override; diff --git a/libs/ui/canvas/kis_qpainter_canvas.cpp b/libs/ui/canvas/kis_qpainter_canvas.cpp --- a/libs/ui/canvas/kis_qpainter_canvas.cpp +++ b/libs/ui/canvas/kis_qpainter_canvas.cpp @@ -169,7 +169,7 @@ m_d->prescaledProjection->setChannelFlags(channelFlags); } -void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter) +void KisQPainterCanvas::setDisplayColorConverter(KisDisplayColorConverter *colorConverter) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(), @@ -185,6 +185,13 @@ canvas()->startUpdateInPatches(canvas()->image()->bounds()); } +void KisQPainterCanvas::notifyImageColorSpaceChanged(const KoColorSpace *cs) +{ + Q_UNUSED(cs); + // FIXME: on color space change the data is refetched multiple + // times by different actors! + canvas()->startUpdateInPatches(canvas()->image()->bounds()); +} void KisQPainterCanvas::setWrapAroundViewingMode(bool value) { diff --git a/libs/ui/canvas/kis_update_info.h b/libs/ui/canvas/kis_update_info.h --- a/libs/ui/canvas/kis_update_info.h +++ b/libs/ui/canvas/kis_update_info.h @@ -56,7 +56,7 @@ bool m_needsConversion; - const KoColorSpace *m_destinationColorSpace; + const KoColorSpace *m_destinationColorSpace = 0; KoColorConversionTransformation::Intent m_renderingIntent; KoColorConversionTransformation::ConversionFlags m_conversionFlags; }; diff --git a/libs/ui/dialogs/kis_dlg_preferences.h b/libs/ui/dialogs/kis_dlg_preferences.h --- a/libs/ui/dialogs/kis_dlg_preferences.h +++ b/libs/ui/dialogs/kis_dlg_preferences.h @@ -279,6 +279,7 @@ void setDefault(); protected Q_SLOTS: void slotUseOpenGLToggled(bool isChecked); + void slotPreferredSurfaceFormatChanged(int index); public: }; diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -21,6 +21,7 @@ #include "kis_dlg_preferences.h" +#include #include #include @@ -974,6 +975,38 @@ //--------------------------------------------------------------------------------------------------- #include "KoColor.h" +#include "opengl/KisOpenGLModeProber.h" +#include "opengl/KisScreenInformationAdapter.h" +#include +#include + +QString colorSpaceString(QSurfaceFormat::ColorSpace cs, int depth) +{ + const QString csString = +#ifdef HAVE_HDR + cs == QSurfaceFormat::bt2020PQColorSpace ? "Rec. 2020 PQ" : + cs == QSurfaceFormat::scRGBColorSpace ? "Rec. 709 Linear" : +#endif + cs == QSurfaceFormat::sRGBColorSpace ? "sRGB" : + cs == QSurfaceFormat::DefaultColorSpace ? "sRGB" : + "Unknown Color Space"; + + return QString("%1 (%2 bit)").arg(csString).arg(depth); +} + +int formatToIndex(KisConfig::RootSurfaceFormat fmt) +{ + return fmt == KisConfig::BT2020_PQ ? 1 : + fmt == KisConfig::BT709_G10 ? 2 : + 0; +} + +KisConfig::RootSurfaceFormat indexToFormat(int value) +{ + return value == 1 ? KisConfig::BT2020_PQ : + value == 2 ? KisConfig::BT709_G10 : + KisConfig::BT709_G22; +} DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) @@ -982,42 +1015,39 @@ const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); #ifdef Q_OS_WIN - const QString rendererAngleText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); - cmbRenderer->clear(); + const QString rendererOpenGLESText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); +#else + const QString rendererOpenGLESText = i18nc("canvas renderer", "OpenGL ES"); +#endif + lblCurrentRenderer->setText(KisOpenGL::hasOpenGLES() ? rendererOpenGLESText : rendererOpenGLText); + + cmbPreferredRenderer->clear(); QString qtPreferredRendererText; - if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererAngle) { - qtPreferredRendererText = rendererAngleText; + if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererOpenGLES) { + qtPreferredRendererText = rendererOpenGLESText; } else { qtPreferredRendererText = rendererOpenGLText; } - cmbRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); - cmbRenderer->setCurrentIndex(0); + cmbPreferredRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); + cmbPreferredRenderer->setCurrentIndex(0); if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) { - cmbRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); - if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { - cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); + cmbPreferredRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); + if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { + cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1); } } - if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererAngle) { - cmbRenderer->addItem(rendererAngleText, KisOpenGL::RendererAngle); - if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererAngle) { - cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); + +#ifdef Q_OS_WIN + if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererOpenGLES) { + cmbPreferredRenderer->addItem(rendererOpenGLESText, KisOpenGL::RendererOpenGLES); + if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererOpenGLES) { + cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1); } } -#else - lblRenderer->setEnabled(false); - cmbRenderer->setEnabled(false); - cmbRenderer->clear(); - cmbRenderer->addItem(rendererOpenGLText); - cmbRenderer->setCurrentIndex(0); #endif -#ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & - (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { -#else - if (!KisOpenGL::hasOpenGL()) { -#endif + (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); @@ -1039,6 +1069,65 @@ } } + lblCurrentDisplayFormat->setText(""); + lblCurrentRootSurfaceFormat->setText(""); + lblHDRWarning->setText(""); + cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::sRGBColorSpace, 8)); +#ifdef HAVE_HDR + cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::bt2020PQColorSpace, 10)); + cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::scRGBColorSpace, 16)); +#endif + cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22)); + slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex()); + + QOpenGLContext *context = QOpenGLContext::currentContext(); + + if (!context) { + context = QOpenGLContext::globalShareContext(); + } + + if (context) { + QScreen *screen = QGuiApplication::screenAt(rect().center()); + KisScreenInformationAdapter adapter(context); + if (screen && adapter.isValid()) { + KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen); + if (info.isValid()) { + QStringList toolTip; + + toolTip << i18n("Display Id: %1", info.screen->name()); + toolTip << i18n("Display Name: %1 %2", info.screen->manufacturer(), info.screen->model()); + toolTip << i18n("Min Luminance: %1", info.minLuminance); + toolTip << i18n("Max Luminance: %1", info.maxLuminance); + toolTip << i18n("Max Full Frame Luminance: %1", info.maxFullFrameLuminance); + toolTip << i18n("Red Primary: %1, %2", info.redPrimary[0], info.redPrimary[1]); + toolTip << i18n("Green Primary: %1, %2", info.greenPrimary[0], info.greenPrimary[1]); + toolTip << i18n("Blue Primary: %1, %2", info.bluePrimary[0], info.bluePrimary[1]); + toolTip << i18n("White Point: %1, %2", info.whitePoint[0], info.whitePoint[1]); + + lblCurrentDisplayFormat->setToolTip(toolTip.join('\n')); + lblCurrentDisplayFormat->setText(colorSpaceString(info.colorSpace, info.bitsPerColor)); + } else { + lblCurrentDisplayFormat->setToolTip(""); + lblCurrentDisplayFormat->setText(i18n("Unknown")); + } + } else { + lblCurrentDisplayFormat->setToolTip(""); + lblCurrentDisplayFormat->setText(i18n("Unknown")); + qWarning() << "Failed to fetch display info:" << adapter.errorString(); + } + + const QSurfaceFormat currentFormat = KisOpenGLModeProber::instance()->surfaceformatInUse(); + lblCurrentRootSurfaceFormat->setText(colorSpaceString(currentFormat.colorSpace(), currentFormat.redBufferSize())); + cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(cfg.rootSurfaceFormat())); + + connect(cmbPreferedRootSurfaceFormat, SIGNAL(currentIndexChanged(int)), SLOT(slotPreferredSurfaceFormatChanged(int))); + slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex()); + } + +#ifndef HAVE_HDR + grpHDRSettings->setVisible(false); +#endif + const QStringList openglWarnings = KisOpenGL::getOpenGLWarnings(); if (openglWarnings.isEmpty()) { lblOpenGLWarnings->setVisible(false); @@ -1099,13 +1188,9 @@ void DisplaySettingsTab::setDefault() { KisConfig cfg(true); - cmbRenderer->setCurrentIndex(0); -#ifdef Q_OS_WIN + cmbPreferredRenderer->setCurrentIndex(0); if (!(KisOpenGL::getSupportedOpenGLRenderers() & - (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { -#else - if (!KisOpenGL::hasOpenGL()) { -#endif + (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); @@ -1144,6 +1229,9 @@ gridColor.fromQColor(cfg.getPixelGridColor(true)); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold(true) * 100); + + cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22)); + slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex()); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) @@ -1153,6 +1241,28 @@ cmbFilterMode->setEnabled(isChecked); } +void DisplaySettingsTab::slotPreferredSurfaceFormatChanged(int index) +{ + Q_UNUSED(index); + + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (context) { + QScreen *screen = QGuiApplication::screenAt(rect().center()); + KisScreenInformationAdapter adapter(context); + if (adapter.isValid()) { + KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen); + if (info.isValid()) { + if (cmbPreferedRootSurfaceFormat->currentIndex() != formatToIndex(KisConfig::BT709_G22) && + info.colorSpace == QSurfaceFormat::sRGBColorSpace) { + lblHDRWarning->setText(i18n("WARNING: current display doesn't support HDR rendering")); + } else { + lblHDRWarning->setText(""); + } + } + } + } +} + //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { @@ -1453,23 +1563,20 @@ dialog->m_performanceSettings->save(); -#ifdef Q_OS_WIN { KisOpenGL::OpenGLRenderer renderer = static_cast( - dialog->m_displaySettings->cmbRenderer->itemData( - dialog->m_displaySettings->cmbRenderer->currentIndex()).toInt()); - KisOpenGL::setNextUserOpenGLRendererConfig(renderer); - const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); - QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); - kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); + dialog->m_displaySettings->cmbPreferredRenderer->itemData( + dialog->m_displaySettings->cmbPreferredRenderer->currentIndex()).toInt()); + KisOpenGL::setUserPreferredOpenGLRendererConfig(renderer); } -#endif + if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); + cfg.setRootSurfaceFormat(&kritarc, indexToFormat(dialog->m_displaySettings->cmbPreferedRootSurfaceFormat->currentIndex())); cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); diff --git a/libs/ui/forms/wdgdisplaysettings.ui b/libs/ui/forms/wdgdisplaysettings.ui --- a/libs/ui/forms/wdgdisplaysettings.ui +++ b/libs/ui/forms/wdgdisplaysettings.ui @@ -7,7 +7,7 @@ 0 0 598 - 546 + 595 @@ -38,40 +38,7 @@ true - - - - - 0 - 0 - - - - 0 - - - - Nearest Neighbour - - - - - Bilinear Filtering - - - - - Trilinear Filtering - - - - - High Quality Filtering - - - - - + @@ -90,7 +57,7 @@ - + @@ -106,7 +73,7 @@ - + @@ -122,24 +89,41 @@ - - + + - + 0 0 - - Renderer (needs restart): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 0 + + + Nearest Neighbour + + + + + Bilinear Filtering + + + + + Trilinear Filtering + + + + + High Quality Filtering + + - - + + 0 @@ -148,7 +132,7 @@ - + OpenGL Warnings @@ -161,6 +145,93 @@ + + + + + 0 + 0 + + + + Preferred Renderer (needs restart): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Unknown + + + + + + + Current Renderer: + + + + + + + + + + HDR + + + false + + + + + + Display Format: + + + + + + + Current Display Format + + + + + + + Current Output Format: + + + + + + + Current Surface Value + + + + + + + Preferred Output Format: + + + + + + + + + + HDR Warning.................................. + + + @@ -459,16 +530,16 @@ - - KisColorButton - QPushButton -
kis_color_button.h
-
KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
+ + KisColorButton + QPushButton +
kis_color_button.h
+
KisDoubleSliderSpinBox QWidget diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -33,6 +33,8 @@ class KoColorProfile; class KoColorSpace; class KisSnapConfig; +class QSettings; +class KisOcioConfiguration; class KRITAUI_EXPORT KisConfig { @@ -372,6 +374,9 @@ bool levelOfDetailEnabled(bool defaultValue = false) const; void setLevelOfDetailEnabled(bool value); + KisOcioConfiguration ocioConfiguration(bool defaultValue = false) const; + void setOcioConfiguration(const KisOcioConfiguration &cfg); + enum OcioColorManagementMode { INTERNAL = 0, OCIO_CONFIG, @@ -381,12 +386,6 @@ OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const; void setOcioColorManagementMode(OcioColorManagementMode mode) const; - QString ocioConfigurationPath(bool defaultValue = false) const; - void setOcioConfigurationPath(const QString &path) const; - - QString ocioLutPath(bool defaultValue = false) const; - void setOcioLutPath(const QString &path) const; - int ocioLutEdgeSize(bool defaultValue = false) const; void setOcioLutEdgeSize(int value); @@ -579,6 +578,17 @@ bool activateTransformToolAfterPaste(bool defaultValue = false) const; void setActivateTransformToolAfterPaste(bool value); + enum RootSurfaceFormat { + BT709_G22 = 0, + BT709_G10, + BT2020_PQ + }; + RootSurfaceFormat rootSurfaceFormat(bool defaultValue = false) const; + void setRootSurfaceFormat(RootSurfaceFormat value); + + static RootSurfaceFormat rootSurfaceFormat(QSettings *displayrc, bool defaultValue = false); + static void setRootSurfaceFormat(QSettings *displayrc, RootSurfaceFormat value); + bool useZip64(bool defaultValue = false) const; void setUseZip64(bool value); diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -49,6 +49,7 @@ #include #include +#include KisConfig::KisConfig(bool readOnly) : m_cfg( KSharedConfig::openConfig()->group("")) @@ -1364,36 +1365,46 @@ m_cfg.writeEntry("levelOfDetailEnabled", value); } -KisConfig::OcioColorManagementMode -KisConfig::ocioColorManagementMode(bool defaultValue) const +KisOcioConfiguration KisConfig::ocioConfiguration(bool defaultValue) const { - return (OcioColorManagementMode)(defaultValue ? INTERNAL - : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); -} + KisOcioConfiguration cfg; -void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const -{ - m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); -} + if (!defaultValue) { + cfg.mode = (KisOcioConfiguration::Mode)m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", 0); + cfg.configurationPath = m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString()); + cfg.lutPath = m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString()); + cfg.inputColorSpace = m_cfg.readEntry("Krita/Ocio/InputColorSpace", QString()); + cfg.displayDevice = m_cfg.readEntry("Krita/Ocio/DisplayDevice", QString()); + cfg.displayView = m_cfg.readEntry("Krita/Ocio/DisplayView", QString()); + cfg.look = m_cfg.readEntry("Krita/Ocio/DisplayLook", QString()); + } -QString KisConfig::ocioConfigurationPath(bool defaultValue) const -{ - return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString())); + return cfg; } -void KisConfig::setOcioConfigurationPath(const QString &path) const +void KisConfig::setOcioConfiguration(const KisOcioConfiguration &cfg) { - m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path); + m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) cfg.mode); + m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", cfg.configurationPath); + m_cfg.writeEntry("Krita/Ocio/OcioLutPath", cfg.lutPath); + m_cfg.writeEntry("Krita/Ocio/InputColorSpace", cfg.inputColorSpace); + m_cfg.writeEntry("Krita/Ocio/DisplayDevice", cfg.displayDevice); + m_cfg.writeEntry("Krita/Ocio/DisplayView", cfg.displayView); + m_cfg.writeEntry("Krita/Ocio/DisplayLook", cfg.look); } -QString KisConfig::ocioLutPath(bool defaultValue) const +KisConfig::OcioColorManagementMode +KisConfig::ocioColorManagementMode(bool defaultValue) const { - return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString())); + // FIXME: this option duplicates ocioConfiguration(), please deprecate it + return (OcioColorManagementMode)(defaultValue ? INTERNAL + : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } -void KisConfig::setOcioLutPath(const QString &path) const +void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { - m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path); + // FIXME: this option duplicates ocioConfiguration(), please deprecate it + m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const @@ -2004,6 +2015,45 @@ m_cfg.writeEntry("activateTransformToolAfterPaste", value); } +KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(bool defaultValue) const +{ + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); + + return rootSurfaceFormat(&kritarc, defaultValue); +} + +void KisConfig::setRootSurfaceFormat(KisConfig::RootSurfaceFormat value) +{ + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); + + setRootSurfaceFormat(&kritarc, value); +} + +KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(QSettings *displayrc, bool defaultValue) +{ + QString textValue = "bt709-g22"; + + if (!defaultValue) { + textValue = displayrc->value("rootSurfaceFormat", textValue).toString(); + } + + return textValue == "bt709-g10" ? BT709_G10 : + textValue == "bt2020-pq" ? BT2020_PQ : + BT709_G22; +} + +void KisConfig::setRootSurfaceFormat(QSettings *displayrc, KisConfig::RootSurfaceFormat value) +{ + const QString textValue = + value == BT709_G10 ? "bt709-g10" : + value == BT2020_PQ ? "bt2020-pq" : + "bt709-g22"; + + displayrc->setValue("rootSurfaceFormat", textValue); +} + bool KisConfig::useZip64(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("UseZip64", false); diff --git a/libs/ui/kis_png_converter.h b/libs/ui/kis_png_converter.h --- a/libs/ui/kis_png_converter.h +++ b/libs/ui/kis_png_converter.h @@ -55,6 +55,7 @@ , forceSRGB(false) , storeMetaData(false) , storeAuthor(false) + , saveAsHDR(false) , transparencyFillColor(Qt::white) {} @@ -69,6 +70,7 @@ bool forceSRGB; bool storeMetaData; bool storeAuthor; + bool saveAsHDR; QList filters; QColor transparencyFillColor; diff --git a/libs/ui/kis_png_converter.cpp b/libs/ui/kis_png_converter.cpp --- a/libs/ui/kis_png_converter.cpp +++ b/libs/ui/kis_png_converter.cpp @@ -70,7 +70,6 @@ namespace { - int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) { @@ -409,7 +408,6 @@ Q_UNUSED(png_ptr); } - KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod) { dbgFile << "Start decoding PNG File"; @@ -551,6 +549,7 @@ } } + bool loadedImageIsHDR = false; const KoColorProfile* profile = 0; if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { QByteArray profile_rawdata; @@ -565,6 +564,8 @@ dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user } } + + loadedImageIsHDR = strcmp(profile_name, "ITUR_2100_PQ_FULL") == 0; } else { dbgFile << "no embedded profile, will use the default profile"; @@ -595,8 +596,22 @@ } // Retrieve a pointer to the colorspace - const KoColorSpace* cs; - if (profile && profile->isSuitableForOutput()) { + KoColorConversionTransformation* transform = 0; + const KoColorSpace* cs = 0; + + if (loadedImageIsHDR && + csName.first == RGBAColorModelID.id() && + csName.second == Integer16BitsColorDepthID.id()) { + + const KoColorSpace *p2020PQCS = + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + Integer16BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020PQProfile()); + + cs = p2020PQCS; + + } else if (profile && profile->isSuitableForOutput()) { dbgFile << "image has embedded profile: " << profile->name() << "\n"; cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); } @@ -608,18 +623,18 @@ } else { cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); } + + //TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation + // Create the cmsTransform if needed + if (profile) { + transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + } } if (cs == 0) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } - //TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation - // Create the cmsTransform if needed - KoColorTransformation* transform = 0; - if (profile && !profile->isSuitableForOutput()) { - transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - } // Creating the KisImageSP if (m_image == 0) { @@ -719,24 +734,24 @@ do { quint16 *d = reinterpret_cast(it->rawData()); d[0] = *(src++); - if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) { d[1] = *(src++); } else { d[1] = quint16_MAX; } + if (transform) transform->transformInPlace(reinterpret_cast(d), reinterpret_cast(d), 1); } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); d[0] = (quint8)(stream.nextValue() * coeff); - if (transform) transform->transform(d, d, 1); if (hasalpha) { d[1] = (quint8)(stream.nextValue() * coeff); } else { d[1] = UCHAR_MAX; } + if (transform) transform->transformInPlace(d, d, 1); } while (it->nextPixel()); } // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" @@ -750,9 +765,9 @@ d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); - if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) d[3] = *(src++); else d[3] = quint16_MAX; + if (transform) transform->transformInPlace(reinterpret_cast(d), reinterpret_cast(d), 1); } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); @@ -761,9 +776,9 @@ d[2] = (quint8)(stream.nextValue() * coeff); d[1] = (quint8)(stream.nextValue() * coeff); d[0] = (quint8)(stream.nextValue() * coeff); - if (transform) transform->transform(d, d, 1); if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); else d[3] = UCHAR_MAX; + if (transform) transform->transformInPlace(d, d, 1); } while (it->nextPixel()); } break; @@ -903,13 +918,45 @@ if (device->colorSpace()->colorDepthId() == Float16BitsColorDepthID || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID) { - const KoColorSpace *dstcs = KoColorSpaceRegistry::instance()->colorSpace(device->colorSpace()->colorModelId().id(), Integer16BitsColorDepthID.id(), device->colorSpace()->profile()); - KisPaintDeviceSP tmp = new KisPaintDevice(dstcs); - KisPainter gc(tmp); - gc.bitBlt(imageRect.topLeft(), device, imageRect); - gc.end(); + + const KoColorSpace *dstCS = + KoColorSpaceRegistry::instance()->colorSpace( + device->colorSpace()->colorModelId().id(), + Integer16BitsColorDepthID.id(), + device->colorSpace()->profile()); + + if (options.saveAsHDR) { + dstCS = + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + Integer16BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020PQProfile()); + } + + KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); + tmp->makeCloneFromRough(device, imageRect); + delete tmp->convertTo(dstCS); + device = tmp; + + } else { + KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || + (device->colorSpace()->profile() && + device->colorSpace()->profile()->uniqueId() == + KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId())) { + options.saveAsHDR = false; + } + } + + + KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) { + options.forceSRGB = false; + } + + KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.tryToSaveAsIndexed) { + options.tryToSaveAsIndexed = false; } + QStringList colormodels = QStringList() << RGBAColorModelID.id() << GrayAColorModelID.id(); if (options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); @@ -1018,6 +1065,8 @@ int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(color_type >= 0, KisImageBuilder_RESULT_FAILURE); + png_set_IHDR(png_ptr, info_ptr, imageRect.width(), imageRect.height(), @@ -1038,6 +1087,39 @@ }*/ + /** TODO: Firefox still opens the image incorrectly if there is gAMA+cHRM tags + * present. According to the standard it should use iCCP tag with higher priority, + * but it doesn't: + * + * "When the iCCP chunk is present, PNG decoders that recognize it and are capable + * of colour management [ICC] shall ignore the gAMA and cHRM chunks and use + * the iCCP chunk instead and interpret it according to [ICC-1] and [ICC-1A]" + */ + +#if 0 + if (options.saveAsHDR) { + // https://www.w3.org/TR/PNG/#11gAMA +#if defined(PNG_GAMMA_SUPPORTED) + // the values are set in accurdance of HDR-PNG standard: + // https://www.w3.org/TR/png-hdr-pq/ + + png_set_gAMA_fixed(png_ptr, info_ptr, 15000); + dbgFile << "gAMA" << "(Rec 2100)"; +#endif + +#if defined PNG_cHRM_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + 31270, 32900, // white point + 70800, 29200, // red + 17000, 79700, // green + 13100, 4600 // blue + ); + dbgFile << "cHRM" << "(Rec 2100)"; +#endif + } +#endif + + // we should ensure we don't access non-existing palette object KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, KisImageBuilder_RESULT_FAILURE); @@ -1082,10 +1164,13 @@ if (!sRGB || options.saveSRGBProfile) { #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 - png_set_iCCP(png_ptr, info_ptr, (png_const_charp)"icc", PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); + const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL"; + png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); #else // older version of libpng has a problem with constness on the parameters - char typeString[] = "icc"; + char typeStringICC[] = "icc"; + char typeStringHDR[] = "ITUR_2100_PQ_FULL"; + char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR; png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size()); #endif } diff --git a/libs/ui/opengl/KisOpenGLModeProber.h b/libs/ui/opengl/KisOpenGLModeProber.h new file mode 100644 --- /dev/null +++ b/libs/ui/opengl/KisOpenGLModeProber.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 Alvin Wong + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KISOPENGLMODEPROBER_H +#define KISOPENGLMODEPROBER_H + +#include "kritaui_export.h" +#include "kis_config.h" +#include +#include + +class KoColorProfile; + +class KRITAUI_EXPORT KisOpenGLModeProber +{ +public: + class Result; + +public: + KisOpenGLModeProber(); + ~KisOpenGLModeProber(); + + static KisOpenGLModeProber* instance(); + + bool useHDRMode() const; + QSurfaceFormat surfaceformatInUse() const; + + const KoColorProfile *rootSurfaceColorProfile() const; + + boost::optional probeFormat(const QSurfaceFormat &format, + bool adjustGlobalState = true); + + static bool fuzzyCompareColorSpaces(const QSurfaceFormat::ColorSpace &lhs, + const QSurfaceFormat::ColorSpace &rhs); + +public: + static void initSurfaceFormatFromConfig(KisConfig::RootSurfaceFormat config, + QSurfaceFormat *format); + static bool isFormatHDR(const QSurfaceFormat &format); +}; + +class KisOpenGLModeProber::Result { +public: + Result(QOpenGLContext &context); + + int glMajorVersion() const { + return m_glMajorVersion; + } + + int glMinorVersion() const { + return m_glMinorVersion; + } + + bool supportsDeprecatedFunctions() const { + return m_supportsDeprecatedFunctions; + } + + bool isOpenGLES() const { + return m_isOpenGLES; + } + + QString rendererString() const { + return m_rendererString; + } + + QString driverVersionString() const { + return m_driverVersionString; + } + + bool isSupportedVersion() const { + return +#ifdef Q_OS_OSX + ((m_glMajorVersion * 100 + m_glMinorVersion) >= 302) +#else + (m_glMajorVersion >= 3 && (m_supportsDeprecatedFunctions || m_isOpenGLES)) || + ((m_glMajorVersion * 100 + m_glMinorVersion) == 201) +#endif + ; + } + + bool supportsLoD() const { + return (m_glMajorVersion * 100 + m_glMinorVersion) >= 300; + } + + bool hasOpenGL3() const { + return (m_glMajorVersion * 100 + m_glMinorVersion) >= 302; + } + + bool supportsFenceSync() const { + return m_glMajorVersion >= 3; + } + +#ifdef Q_OS_WIN + // This is only for detecting whether ANGLE is being used. + // For detecting generic OpenGL ES please check isOpenGLES + bool isUsingAngle() const { + return m_rendererString.startsWith("ANGLE", Qt::CaseInsensitive); + } +#endif + + QString shadingLanguageString() const + { + return m_shadingLanguageString; + } + + QString vendorString() const + { + return m_vendorString; + } + + QSurfaceFormat format() const + { + return m_format; + } + +private: + int m_glMajorVersion = 0; + int m_glMinorVersion = 0; + bool m_supportsDeprecatedFunctions = false; + bool m_isOpenGLES = false; + QString m_rendererString; + QString m_driverVersionString; + QString m_vendorString; + QString m_shadingLanguageString; + QSurfaceFormat m_format; +}; + +#endif // KISOPENGLMODEPROBER_H diff --git a/libs/ui/opengl/KisOpenGLModeProber.cpp b/libs/ui/opengl/KisOpenGLModeProber.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/opengl/KisOpenGLModeProber.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017 Alvin Wong + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisOpenGLModeProber.h" + +#include +#include +#include +#include +#include + +#include +Q_GLOBAL_STATIC(KisOpenGLModeProber, s_instance) + + +KisOpenGLModeProber::KisOpenGLModeProber() +{ +} + +KisOpenGLModeProber::~KisOpenGLModeProber() +{ + +} + +KisOpenGLModeProber *KisOpenGLModeProber::instance() +{ + return s_instance; +} + +bool KisOpenGLModeProber::useHDRMode() const +{ + return isFormatHDR(QSurfaceFormat::defaultFormat()); +} + +QSurfaceFormat KisOpenGLModeProber::surfaceformatInUse() const +{ + // TODO: use information provided by KisOpenGL instead + QOpenGLContext *sharedContext = QOpenGLContext::globalShareContext(); + QSurfaceFormat format = sharedContext ? sharedContext->format() : QSurfaceFormat::defaultFormat(); + return format; +} + +const KoColorProfile *KisOpenGLModeProber::rootSurfaceColorProfile() const +{ + const QSurfaceFormat::ColorSpace surfaceColorSpace = surfaceformatInUse().colorSpace(); + const KoColorProfile *profile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); + + if (surfaceColorSpace == QSurfaceFormat::sRGBColorSpace) { + // use the default one! +#ifdef HAVE_HDR + } else if (surfaceColorSpace == QSurfaceFormat::scRGBColorSpace) { + profile = KoColorSpaceRegistry::instance()->p709G10Profile(); + } else if (surfaceColorSpace == QSurfaceFormat::bt2020PQColorSpace) { + profile = KoColorSpaceRegistry::instance()->p2020PQProfile(); +#endif + } + + return profile; +} + +namespace { +struct AppAttributeSetter +{ + AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES) + : m_attribute(attribute), + m_oldValue(QCoreApplication::testAttribute(attribute)) + { + QCoreApplication::setAttribute(attribute, useOpenGLES); + } + + ~AppAttributeSetter() { + QCoreApplication::setAttribute(m_attribute, m_oldValue); + } + +private: + Qt::ApplicationAttribute m_attribute; + bool m_oldValue = false; +}; + +struct SurfaceFormatSetter +{ + SurfaceFormatSetter(const QSurfaceFormat &format) + : m_oldFormat(QSurfaceFormat::defaultFormat()) + { + QSurfaceFormat::setDefaultFormat(format); + } + + ~SurfaceFormatSetter() { + QSurfaceFormat::setDefaultFormat(m_oldFormat); + } + +private: + QSurfaceFormat m_oldFormat; +}; + +} + +boost::optional +KisOpenGLModeProber::probeFormat(const QSurfaceFormat &format, bool adjustGlobalState) +{ + QScopedPointer sharedContextSetter; + QScopedPointer glSetter; + QScopedPointer glesSetter; + QScopedPointer formatSetter; + QScopedPointer application; + + if (adjustGlobalState) { + sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false)); + + if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) { + glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES)); + glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES)); + } + + formatSetter.reset(new SurfaceFormatSetter(format)); + + int argc = 1; + QByteArray data("krita"); + char *argv = data.data(); + application.reset(new QApplication(argc, &argv)); + } + + QWindow surface; + surface.setFormat(format); + surface.setSurfaceType(QSurface::OpenGLSurface); + surface.create(); + QOpenGLContext context; + context.setFormat(format); + + + if (!context.create()) { + dbgOpenGL << "OpenGL context cannot be created"; + return boost::none; + } + if (!context.isValid()) { + dbgOpenGL << "OpenGL context is not valid while checking Qt's OpenGL status"; + return boost::none; + } + if (!context.makeCurrent(&surface)) { + dbgOpenGL << "OpenGL context cannot be made current"; + return boost::none; + } + + if (!fuzzyCompareColorSpaces(context.format().colorSpace(), format.colorSpace())) { + dbgOpenGL << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace(); + return boost::none; + } + + return Result(context); +} + +bool KisOpenGLModeProber::fuzzyCompareColorSpaces(const QSurfaceFormat::ColorSpace &lhs, const QSurfaceFormat::ColorSpace &rhs) +{ + return lhs == rhs || + ((lhs == QSurfaceFormat::DefaultColorSpace || + lhs == QSurfaceFormat::sRGBColorSpace) && + (rhs == QSurfaceFormat::DefaultColorSpace || + rhs == QSurfaceFormat::sRGBColorSpace)); +} + +void KisOpenGLModeProber::initSurfaceFormatFromConfig(KisConfig::RootSurfaceFormat config, + QSurfaceFormat *format) +{ +#ifdef HAVE_HDR + if (config == KisConfig::BT2020_PQ) { + + format->setRedBufferSize(10); + format->setGreenBufferSize(10); + format->setBlueBufferSize(10); + format->setAlphaBufferSize(2); + format->setColorSpace(QSurfaceFormat::bt2020PQColorSpace); + } else if (config == KisConfig::BT709_G10) { + format->setRedBufferSize(16); + format->setGreenBufferSize(16); + format->setBlueBufferSize(16); + format->setAlphaBufferSize(16); + format->setColorSpace(QSurfaceFormat::scRGBColorSpace); + } else +#else + if (config == KisConfig::BT2020_PQ) { + qWarning() << "WARNING: Bt.2020 PQ surface type is not supoprted by this build of Krita"; + } else if (config == KisConfig::BT709_G10) { + qWarning() << "WARNING: scRGB surface type is not supoprted by this build of Krita"; + } +#endif + + { + format->setRedBufferSize(8); + format->setGreenBufferSize(8); + format->setBlueBufferSize(8); + format->setAlphaBufferSize(8); + // TODO: check if we can use real sRGB space here + format->setColorSpace(QSurfaceFormat::DefaultColorSpace); + } +} + +bool KisOpenGLModeProber::isFormatHDR(const QSurfaceFormat &format) +{ +#ifdef HAVE_HDR + + bool isBt2020PQ = + format.colorSpace() == QSurfaceFormat::bt2020PQColorSpace && + format.redBufferSize() == 10 && + format.greenBufferSize() == 10 && + format.blueBufferSize() == 10 && + format.alphaBufferSize() == 2; + + bool isBt709G10 = + format.colorSpace() == QSurfaceFormat::scRGBColorSpace && + format.redBufferSize() == 16 && + format.greenBufferSize() == 16 && + format.blueBufferSize() == 16 && + format.alphaBufferSize() == 16; + + return isBt2020PQ || isBt709G10; +#else + return false; +#endif +} + +KisOpenGLModeProber::Result::Result(QOpenGLContext &context) { + if (!context.isValid()) { + return; + } + + QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used + + m_rendererString = QString(reinterpret_cast(funcs->glGetString(GL_RENDERER))); + m_driverVersionString = QString(reinterpret_cast(funcs->glGetString(GL_VERSION))); + m_vendorString = QString(reinterpret_cast(funcs->glGetString(GL_VENDOR))); + m_shadingLanguageString = QString(reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION))); + m_glMajorVersion = context.format().majorVersion(); + m_glMinorVersion = context.format().minorVersion(); + m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); + m_isOpenGLES = context.isOpenGLES(); + m_format = context.format(); +} diff --git a/libs/ui/opengl/KisScreenInformationAdapter.h b/libs/ui/opengl/KisScreenInformationAdapter.h new file mode 100644 --- /dev/null +++ b/libs/ui/opengl/KisScreenInformationAdapter.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISSCREENINFORMATIONADAPTER_H +#define KISSCREENINFORMATIONADAPTER_H + +#include "kritaui_export.h" +#include +#include + +class QScreen; +class QOpenGLContext; + +class KRITAUI_EXPORT KisScreenInformationAdapter +{ +public: + struct ScreenInfo { + QScreen *screen = 0; + int bitsPerColor = 0; + QSurfaceFormat::ColorSpace colorSpace = QSurfaceFormat::DefaultColorSpace; + qreal redPrimary[2] = {0, 0}; + qreal greenPrimary[2] = {0, 0}; + qreal bluePrimary[2] = {0, 0}; + qreal whitePoint[2] = {0, 0}; + qreal minLuminance = 0; + qreal maxLuminance = 0; + qreal maxFullFrameLuminance = 0; + + bool isValid() const { + return screen; + } + }; + +public: + KisScreenInformationAdapter(QOpenGLContext *context); + ~KisScreenInformationAdapter(); + + bool isValid() const; + QString errorString() const; + + ScreenInfo infoForScreen(QScreen *screen) const; + + +private: + struct Private; + const QScopedPointer m_d; +}; + +QDebug operator<<(QDebug, const KisScreenInformationAdapter::ScreenInfo &); + +#endif // KISSCREENINFORMATIONADAPTER_H diff --git a/libs/ui/opengl/KisScreenInformationAdapter.cpp b/libs/ui/opengl/KisScreenInformationAdapter.cpp new file mode 100644 --- /dev/null +++ b/libs/ui/opengl/KisScreenInformationAdapter.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisScreenInformationAdapter.h" + +#include "kis_debug.h" +#include + +#include +#include + +#include + +#ifdef Q_OS_WIN +#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 2)) +#include +#elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 0)) +#include +#elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 1)) +#include +#elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 2)) +#include +#elif (QT_VERSION == QT_VERSION_CHECK(5, 13, 0)) +#include +#endif + +#include +#include +#include +#include "EGL/egl.h" +#include "EGL/eglext.h" +#endif + +namespace { +struct EGLException { + EGLException() {} + EGLException(const QString &what) : m_what(what) {} + + QString what() const { + return m_what; + } + +private: + QString m_what; +}; + +template +void getProcAddressSafe(QOpenGLContext *context, const char *funcName, FuncType &func) +{ + func = reinterpret_cast(context->getProcAddress(funcName)); + if (!func) { + throw EGLException(QString("failed to fetch function %1").arg(funcName)); + } +} + +#ifdef Q_OS_WIN +typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name); +#endif +} + + +struct KisScreenInformationAdapter::Private +{ + void initialize(QOpenGLContext *context); + + QOpenGLContext *context; + QString errorString; + +#ifdef Q_OS_WIN + Microsoft::WRL::ComPtr dxgiAdapter; +#endif +}; + +KisScreenInformationAdapter::KisScreenInformationAdapter(QOpenGLContext *context) + : m_d(new Private) +{ + m_d->initialize(context); +} + +KisScreenInformationAdapter::~KisScreenInformationAdapter() +{ +} + +void KisScreenInformationAdapter::Private::initialize(QOpenGLContext *newContext) +{ + context = newContext; + errorString.clear(); + + try { + +#ifdef Q_OS_WIN + + if (!context->isOpenGLES()) { + throw EGLException("the context is not OpenGL ES"); + } + + PFNEGLQUERYSTRINGPROC queryString = nullptr; + getProcAddressSafe(context, "eglQueryString", queryString); + + const char* client_extensions = queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + const QList extensions = QByteArray(client_extensions).split(' '); + + if (!extensions.contains("EGL_ANGLE_platform_angle_d3d") || + !extensions.contains("EGL_ANGLE_device_creation_d3d11")) { + + throw EGLException("the context is not Angle + D3D11"); + } + + PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT = nullptr; + PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT = nullptr; + + getProcAddressSafe(context, "eglQueryDisplayAttribEXT", queryDisplayAttribEXT); + getProcAddressSafe(context, "eglQueryDeviceAttribEXT", queryDeviceAttribEXT); + + QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); + EGLDisplay display = reinterpret_cast(nativeInterface->nativeResourceForContext("egldisplay", context)); + + if (!display) { + throw EGLException( + QString("couldn't request EGLDisplay handle, display = 0x%1").arg(uintptr_t(display), 0, 16)); + } + + EGLAttrib value = 0; + EGLBoolean result = false; + + result = queryDisplayAttribEXT(display, EGL_DEVICE_EXT, &value); + + if (!result || value == EGL_NONE) { + throw EGLException( + QString("couldn't request EGLDeviceEXT handle, result = 0x%1, value = 0x%2") + .arg(result, 0, 16).arg(value, 0, 16)); + } + + EGLDeviceEXT device = reinterpret_cast(value); + + result = queryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &value); + + if (!result || value == EGL_NONE) { + throw EGLException( + QString("couldn't request ID3D11Device pointer, result = 0x%1, value = 0x%2") + .arg(result, 0, 16).arg(value, 0, 16)); + } + ID3D11Device *deviceD3D = reinterpret_cast(value); + + { + HRESULT result = 0; + + Microsoft::WRL::ComPtr dxgiDevice; + result = deviceD3D->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); + + if (FAILED(result)) { + throw EGLException( + QString("couldn't request IDXGIDevice pointer, result = 0x%1").arg(result, 0, 16)); + } + + Microsoft::WRL::ComPtr dxgiAdapter; + result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter1), (void**)&dxgiAdapter); + + if (FAILED(result)) { + throw EGLException( + QString("couldn't request IDXGIAdapter1 pointer, result = 0x%1").arg(result, 0, 16)); + } + + this->dxgiAdapter = dxgiAdapter; + } + +#else + throw EGLException("current platform doesn't support fetching display information"); +#endif + + } catch (EGLException &e) { + this->context = 0; + this->errorString = e.what(); +#ifdef Q_OS_WIN + this->dxgiAdapter.Reset(); +#endif + } +} + +bool KisScreenInformationAdapter::isValid() const +{ +#ifdef Q_OS_WIN + return m_d->context && m_d->dxgiAdapter; +#else + return false; +#endif +} + +QString KisScreenInformationAdapter::errorString() const +{ + return m_d->errorString; +} + +KisScreenInformationAdapter::ScreenInfo KisScreenInformationAdapter::infoForScreen(QScreen *screen) const +{ + ScreenInfo info; + +#ifdef Q_OS_WIN + + QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); + HMONITOR monitor = reinterpret_cast(nativeInterface->nativeResourceForScreen("handle", screen)); + + if (!monitor) { + qWarning("%s: failed to get HMONITOR handle for screen: screen = 0x%X, monitor = 0x%X", + __PRETTY_FUNCTION__, screen, monitor); + } + + UINT i = 0; + Microsoft::WRL::ComPtr currentOutput; + + while (m_d->dxgiAdapter->EnumOutputs(i, ¤tOutput) != DXGI_ERROR_NOT_FOUND) + { + + HRESULT result = 0; + Microsoft::WRL::ComPtr output6; + result = currentOutput.As(&output6); + + if (output6) { + DXGI_OUTPUT_DESC1 desc; + result = output6->GetDesc1(&desc); + + if (desc.Monitor == monitor) { + info.screen = screen; + info.bitsPerColor = desc.BitsPerColor; + info.redPrimary[0] = desc.RedPrimary[0]; + info.redPrimary[1] = desc.RedPrimary[1]; + info.greenPrimary[0] = desc.GreenPrimary[0]; + info.greenPrimary[1] = desc.GreenPrimary[1]; + info.bluePrimary[0] = desc.BluePrimary[0]; + info.bluePrimary[1] = desc.BluePrimary[1]; + info.whitePoint[0] = desc.WhitePoint[0]; + info.whitePoint[1] = desc.WhitePoint[1]; + info.minLuminance = desc.MinLuminance; + info.maxLuminance = desc.MaxLuminance; + info.maxFullFrameLuminance = desc.MaxFullFrameLuminance; + + info.colorSpace = QSurfaceFormat::DefaultColorSpace; + + if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { + info.colorSpace = QSurfaceFormat::sRGBColorSpace; + } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { +#ifdef HAVE_HDR + info.colorSpace = QSurfaceFormat::scRGBColorSpace; +#else + qWarning("WARNING: scRGB display color space is not supported by Qt's build"); +#endif + } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { +#ifdef HAVE_HDR + info.colorSpace = QSurfaceFormat::bt2020PQColorSpace; +#else + qWarning("WARNING: bt2020-pq display color space is not supported by Qt's build"); +#endif + } else { + qWarning("WARNING: unknown display color space! 0x%X", desc.ColorSpace); + } + + break; + } + } + + i++; + } + +#endif + Q_UNUSED(screen); + return info; +} + +QDebug operator<<(QDebug dbg, const KisScreenInformationAdapter::ScreenInfo &info) +{ + QDebugStateSaver saver(dbg); + + if (info.isValid()) { + dbg.nospace() << "ScreenInfo(" + << "screen " << info.screen + << ", bitsPerColor " << info.bitsPerColor + << ", colorSpace " << info.colorSpace + << ", redPrimary " << "(" << info.redPrimary[0] << ", " << info.redPrimary[1] << ")" + << ", greenPrimary " << "(" << info.greenPrimary[0] << ", " << info.greenPrimary[1] << ")" + << ", bluePrimary " << "(" << info.bluePrimary[0] << ", " << info.bluePrimary[1] << ")" + << ", whitePoint " << "(" << info.whitePoint[0] << ", " << info.whitePoint[1] << ")" + << ", minLuminance " << info.minLuminance + << ", maxLuminance " << info.maxLuminance + << ", maxFullFrameLuminance " << info.maxFullFrameLuminance + << ')'; + } else { + dbg.nospace() << "ScreenInfo()"; + } + + return dbg; +} diff --git a/libs/ui/opengl/kis_opengl.h b/libs/ui/opengl/kis_opengl.h --- a/libs/ui/opengl/kis_opengl.h +++ b/libs/ui/opengl/kis_opengl.h @@ -23,11 +23,15 @@ #include #include +#include +#include "kis_config.h" + #include "kritaui_export.h" class QOpenGLContext; class QString; class QStringList; +class QSurfaceFormat; /** * This class manages a shared OpenGL context and provides utility @@ -44,27 +48,27 @@ }; public: -#ifdef Q_OS_WIN enum OpenGLRenderer { RendererNone = 0x00, RendererAuto = 0x01, RendererDesktopGL = 0x02, - RendererAngle = 0x04, + RendererOpenGLES = 0x04, }; Q_DECLARE_FLAGS(OpenGLRenderers, OpenGLRenderer); - // Probe the Windows platform abstraction layer for OpenGL detection - static void probeWindowsQpaOpenGL(int argc, char **argv, QString userRendererConfigString); + static QSurfaceFormat selectSurfaceFormat(KisOpenGL::OpenGLRenderer preferredRenderer, + KisConfig::RootSurfaceFormat preferredRootSurfaceFormat, + bool enableDebug); + + static void setDefaultSurfaceFormat(const QSurfaceFormat &format); static OpenGLRenderer getCurrentOpenGLRenderer(); static OpenGLRenderer getQtPreferredOpenGLRenderer(); static OpenGLRenderers getSupportedOpenGLRenderers(); - static OpenGLRenderer getUserOpenGLRendererConfig(); - static OpenGLRenderer getNextUserOpenGLRendererConfig(); - static void setNextUserOpenGLRendererConfig(OpenGLRenderer renderer); + static OpenGLRenderer getUserPreferredOpenGLRendererConfig(); + static void setUserPreferredOpenGLRendererConfig(OpenGLRenderer renderer); static QString convertOpenGLRendererToConfig(OpenGLRenderer renderer); static OpenGLRenderer convertConfigToOpenGLRenderer(QString renderer); -#endif /// Request OpenGL version 3.2 static void initialize(); @@ -100,10 +104,11 @@ */ static bool needsPixmapCacheWorkaround(); - static void setDefaultFormat(bool enableDebug = false, bool debugSynchronous = false); + static void testingInitializeDefaultSurfaceFormat(); + static void setDebugSynchronous(bool value); private: - + static void fakeInitWindowsOpenGL(KisOpenGL::OpenGLRenderers supportedRenderers, KisOpenGL::OpenGLRenderer preferredByQt); KisOpenGL(); diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include "opengl/kis_opengl.h" #include "opengl/kis_opengl_p.h" @@ -37,10 +38,13 @@ #include #include +#include "KisOpenGLModeProber.h" #include - #include +#include "kis_assert.h" +#include +#include #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 @@ -50,18 +54,27 @@ namespace { - bool defaultFormatIsSet = false; - bool isDebugEnabled = false; - bool isDebugSynchronous = false; + // config option, set manually by main() + bool g_isDebugSynchronous = false; + + bool g_sanityDefaultFormatIsSet = false; - boost::optional openGLCheckResult; + boost::optional openGLCheckResult; - bool NeedsFenceWorkaround = false; - bool NeedsPixmapCacheWorkaround = false; + bool g_needsFenceWorkaround = false; + bool g_needsPixmapCacheWorkaround = false; - QString debugText("OpenGL Info\n **OpenGL not initialized**"); + QString g_surfaceFormatDetectionLog; + QString g_debugText("OpenGL Info\n **OpenGL not initialized**"); - QVector openglWarningStrings; + QVector g_openglWarningStrings; + KisOpenGL::OpenGLRenderers g_supportedRenderers; + KisOpenGL::OpenGLRenderer g_rendererPreferredByQt; + + void overrideSupportedRenderers(KisOpenGL::OpenGLRenderers supportedRenderers, KisOpenGL::OpenGLRenderer preferredByQt) { + g_supportedRenderers = supportedRenderers; + g_rendererPreferredByQt = preferredByQt; + } void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) { qDebug() << "OpenGL:" << debugMessage; @@ -85,65 +98,93 @@ void KisOpenGLPrivate::appendOpenGLWarningString(KLocalizedString warning) { - openglWarningStrings << warning; + g_openglWarningStrings << warning; } -bool KisOpenGLPrivate::isDefaultFormatSet() { - return defaultFormatIsSet; +void KisOpenGLPrivate::overrideOpenGLWarningString(QVector warnings) +{ + g_openglWarningStrings = warnings; } + void KisOpenGL::initialize() { if (openGLCheckResult) return; - KIS_SAFE_ASSERT_RECOVER(defaultFormatIsSet) { - qWarning() << "Default OpenGL format was not set before calling KisOpenGL::initialize. This might be a BUG!"; - setDefaultFormat(); - } - - // we need a QSurface active to get our GL functions from the context - QWindow surface; - surface.setSurfaceType( QSurface::OpenGLSurface ); - surface.create(); + KIS_SAFE_ASSERT_RECOVER_NOOP(g_sanityDefaultFormatIsSet); - QOpenGLContext context; - if (!context.create()) { - qDebug() << "OpenGL context cannot be created"; - KisUsageLogger::log("OpenGL context cannot be created"); - return; - } - if (!context.isValid()) { - qDebug() << "OpenGL context is not valid"; - KisUsageLogger::log("OpenGL context is not valid"); - return; - } - - if (!context.makeCurrent(&surface)) { - qDebug() << "OpenGL context cannot be made current"; - KisUsageLogger::log("OpenGL context cannot be made current"); - return; - } + openGLCheckResult = + KisOpenGLModeProber::instance()->probeFormat(QSurfaceFormat::defaultFormat(), false); - QOpenGLFunctions *funcs = context.functions(); - openGLCheckResult = OpenGLCheckResult(context); - debugText.clear(); - QDebug debugOut(&debugText); + g_debugText.clear(); + QDebug debugOut(&g_debugText); debugOut << "OpenGL Info\n"; - debugOut << "\n Vendor: " << reinterpret_cast(funcs->glGetString(GL_VENDOR)); + debugOut << "\n Vendor: " << openGLCheckResult->vendorString(); debugOut << "\n Renderer: " << openGLCheckResult->rendererString(); debugOut << "\n Version: " << openGLCheckResult->driverVersionString(); - debugOut << "\n Shading language: " << reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)); + debugOut << "\n Shading language: " << openGLCheckResult->shadingLanguageString(); debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat(); - debugOut << "\n Current format: " << context.format(); + debugOut << "\n Current format: " << openGLCheckResult->format(); debugOut.nospace(); debugOut << "\n Version: " << openGLCheckResult->glMajorVersion() << "." << openGLCheckResult->glMinorVersion(); debugOut.resetFormat(); debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions(); debugOut << "\n is OpenGL ES:" << openGLCheckResult->isOpenGLES(); - appendPlatformOpenGLDebugText(debugOut); - dbgOpenGL.noquote() << debugText; - KisUsageLogger::write(debugText); + debugOut << "\n\nQPA OpenGL Detection Info"; + debugOut << "\n supportsDesktopGL:" << bool(g_supportedRenderers & RendererDesktopGL); +#ifdef Q_OS_WIN + debugOut << "\n supportsAngleD3D11:" << bool(g_supportedRenderers & RendererOpenGLES); + debugOut << "\n isQtPreferAngle:" << bool(g_rendererPreferredByQt == RendererOpenGLES); +#else + debugOut << "\n supportsOpenGLES:" << bool(g_supportedRenderers & RendererOpenGLES); + debugOut << "\n isQtPreferOpenGLES:" << bool(g_rendererPreferredByQt == RendererOpenGLES); +#endif + debugOut << "\n== log ==\n"; + debugOut.noquote(); + debugOut << g_surfaceFormatDetectionLog; + debugOut.resetFormat(); + debugOut << "\n== end log =="; + + dbgOpenGL.noquote().nospace() << g_debugText; + KisUsageLogger::write(g_debugText); + + // Check if we have a bugged driver that needs fence workaround + bool isOnX11 = false; +#ifdef HAVE_X11 + isOnX11 = true; +#endif + + KisConfig cfg(true); + if ((isOnX11 && openGLCheckResult->rendererString().startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { + g_needsFenceWorkaround = true; + } + + /** + * NVidia + Qt's openGL don't play well together and one cannot + * draw a pixmap on a widget more than once in one rendering cycle. + * + * It can be workarounded by drawing strictly via QPixmapCache and + * only when the pixmap size in bigger than doubled size of the + * display framebuffer. That is for 8-bit HD display, you should have + * a cache bigger than 16 MiB. Don't ask me why. (DK) + * + * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 + * + * TODO: check if this workaround is still needed after merging + * Qt5+openGL3 branch. + */ + + if (openGLCheckResult->vendorString().toUpper().contains("NVIDIA")) { + g_needsPixmapCacheWorkaround = true; + + const QRect screenSize = QApplication::desktop()->screenGeometry(); + const int minCacheSize = 20 * 1024; + const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB + + QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); + } + } void KisOpenGL::initializeContext(QOpenGLContext *ctx) @@ -151,6 +192,8 @@ KisConfig cfg(true); initialize(); + const bool isDebugEnabled = ctx->format().testOption(QSurfaceFormat::DebugContext); + dbgUI << "OpenGL: Opening new context"; if (isDebugEnabled) { // Passing ctx for ownership management only, not specifying context. @@ -160,7 +203,7 @@ if (openglLogger->initialize()) { qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); - openglLogger->startLogging(isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); + openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); } else { qDebug() << "QOpenGLDebugLogger cannot be initialized."; @@ -183,55 +226,17 @@ QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); - - // Check if we have a bugged driver that needs fence workaround - bool isOnX11 = false; -#ifdef HAVE_X11 - isOnX11 = true; -#endif - - if ((isOnX11 && openGLCheckResult->rendererString().startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { - NeedsFenceWorkaround = true; - } - - - /** - * NVidia + Qt's openGL don't play well together and one cannot - * draw a pixmap on a widget more than once in one rendering cycle. - * - * It can be workarounded by drawing strictly via QPixmapCache and - * only when the pixmap size in bigger than doubled size of the - * display framebuffer. That is for 8-bit HD display, you should have - * a cache bigger than 16 MiB. Don't ask me why. (DK) - * - * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 - * - * TODO: check if this workaround is still needed after merging - * Qt5+openGL3 branch. - */ - - if (vendor.toUpper().contains("NVIDIA")) { - NeedsPixmapCacheWorkaround = true; - - const QRect screenSize = QApplication::desktop()->screenGeometry(); - const int minCacheSize = 20 * 1024; - const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB - - QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); - } - - } const QString &KisOpenGL::getDebugText() { initialize(); - return debugText; + return g_debugText; } QStringList KisOpenGL::getOpenGLWarnings() { QStringList strings; - Q_FOREACH (const KLocalizedString &item, openglWarningStrings) { + Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) { strings << item.toString(); } return strings; @@ -266,21 +271,87 @@ bool KisOpenGL::needsFenceWorkaround() { initialize(); - return NeedsFenceWorkaround; + return g_needsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); - return NeedsPixmapCacheWorkaround; + return g_needsPixmapCacheWorkaround; } -void KisOpenGL::setDefaultFormat(bool enableDebug, bool debugSynchronous) +void KisOpenGL::testingInitializeDefaultSurfaceFormat() { - if (defaultFormatIsSet) { - return; + setDefaultSurfaceFormat(selectSurfaceFormat(KisOpenGL::RendererAuto, KisConfig::BT709_G22, false)); +} + +void KisOpenGL::setDebugSynchronous(bool value) +{ + g_isDebugSynchronous = value; +} + +KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer() +{ + const QSurfaceFormat::RenderableType renderer = QSurfaceFormat::defaultFormat().renderableType(); + + return renderer == QSurfaceFormat::OpenGLES ? RendererOpenGLES : + renderer == QSurfaceFormat::OpenGL ? RendererDesktopGL : + RendererAuto; +} + +KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer() +{ + return g_rendererPreferredByQt; +} + +KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers() +{ + return g_supportedRenderers; +} + +KisOpenGL::OpenGLRenderer KisOpenGL::getUserPreferredOpenGLRendererConfig() +{ + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); + return convertConfigToOpenGLRenderer(kritarc.value("OpenGLRenderer", "auto").toString()); +} + +void KisOpenGL::setUserPreferredOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer) +{ + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); + kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); +} + +QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer) +{ + switch (renderer) { + case RendererDesktopGL: + return QStringLiteral("desktop"); + case RendererOpenGLES: + return QStringLiteral("angle"); + default: + return QStringLiteral("auto"); } - defaultFormatIsSet = true; +} + +KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer) +{ + if (renderer == "desktop") { + return RendererDesktopGL; + } else if (renderer == "angle") { + return RendererOpenGLES; + } else { + return RendererAuto; + } +} + +namespace { + +QSurfaceFormat generateSurfaceFormat(QSurfaceFormat::RenderableType renderer, + KisConfig::RootSurfaceFormat rootSurfaceFormat, + bool debugContext) +{ QSurfaceFormat format; #ifdef Q_OS_OSX format.setVersion(3, 2); @@ -293,14 +364,367 @@ #endif format.setDepthBufferSize(24); format.setStencilBufferSize(8); + + KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format); + + format.setRenderableType(renderer); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(0); // Disable vertical refresh syncing - isDebugEnabled = enableDebug; - if (enableDebug) { + if (debugContext) { format.setOption(QSurfaceFormat::DebugContext, true); - isDebugSynchronous = debugSynchronous; - qDebug() << "QOpenGLDebugLogger will be enabled, synchronous:" << debugSynchronous; } + + return format; +} + +bool isOpenGLRendererBlacklisted(const QString &rendererString, + const QString &driverVersionString, + QVector *warningMessage) +{ + bool isBlacklisted = false; + + // Special blacklisting of OpenGL/ANGLE is tracked on: + // https://phabricator.kde.org/T7411 + + // HACK: Specifically detect for Intel driver build number + // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics-drivers.html + if (rendererString.startsWith("Intel")) { + KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL."); + KLocalizedString grossIntelWarning = ki18n( + "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. " + "You may manually switch to OpenGL but it is not guaranteed to work properly." + ); + QRegularExpression regex("\\b\\d{2}\\.\\d{2}\\.\\d{2}\\.(\\d{4})\\b"); + QRegularExpressionMatch match = regex.match(driverVersionString); + if (match.hasMatch()) { + int driverBuild = match.captured(1).toInt(); + if (driverBuild > 4636 && driverBuild < 4729) { + // Make ANGLE the preferred renderer for Intel driver versions + // between build 4636 and 4729 (exclusive) due to an UI offset bug. + // See https://communities.intel.com/thread/116003 + // (Build 4636 is known to work from some test results) + qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer"; + isBlacklisted = true; + *warningMessage << knownBadIntelWarning; + } else if (driverBuild == 4358) { + // There are several reports on a bug where the canvas is not being + // updated properly which has debug info pointing to this build. + qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer"; + isBlacklisted = true; + *warningMessage << knownBadIntelWarning; + } else { + // Intel tends to randomly break OpenGL in some of their new driver + // builds, therefore we just shouldn't use OpenGL by default to + // reduce bug report noises. + qDebug() << "Detected Intel driver, making ANGLE the preferred renderer"; + isBlacklisted = true; + *warningMessage << grossIntelWarning; + } + } + } + + return isBlacklisted; +} + +boost::optional orderPreference(bool lhs, bool rhs) +{ + if (lhs == rhs) return boost::none; + if (lhs && !rhs) return true; + if (!lhs && rhs) return false; + return false; +} + +#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; } + +class FormatPositionLess +{ +public: + + FormatPositionLess() + { + } + + bool operator()(const QSurfaceFormat &lhs, const QSurfaceFormat &rhs) const { + KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredColorSpace != QSurfaceFormat::DefaultColorSpace); + + ORDER_BY(isPreferredColorSpace(lhs.colorSpace()), + isPreferredColorSpace(rhs.colorSpace())); + + + if (doPreferHDR()) { + ORDER_BY(isHDRFormat(lhs), isHDRFormat(rhs)); + } else { + ORDER_BY(!isHDRFormat(lhs), !isHDRFormat(rhs)); + } + + if (m_preferredRendererByUser != QSurfaceFormat::DefaultRenderableType) { + ORDER_BY(lhs.renderableType() == m_preferredRendererByUser, + rhs.renderableType() == m_preferredRendererByUser); + } + + ORDER_BY(!isBlacklisted(lhs), !isBlacklisted(rhs)); + + if (doPreferHDR() && + m_preferredRendererByHDR != QSurfaceFormat::DefaultRenderableType) { + + ORDER_BY(lhs.renderableType() == m_preferredRendererByHDR, + rhs.renderableType() == m_preferredRendererByHDR); + + } + + KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != QSurfaceFormat::DefaultRenderableType); + + ORDER_BY(lhs.renderableType() == m_preferredRendererByQt, + rhs.renderableType() == m_preferredRendererByQt); + + return false; + } + + +public: + void setPreferredColorSpace(const QSurfaceFormat::ColorSpace &preferredColorSpace) { + m_preferredColorSpace = preferredColorSpace; + } + + void setPreferredRendererByQt(const QSurfaceFormat::RenderableType &preferredRendererByQt) { + m_preferredRendererByQt = preferredRendererByQt; + } + + void setPreferredRendererByUser(const QSurfaceFormat::RenderableType &preferredRendererByUser) { + m_preferredRendererByUser = preferredRendererByUser; + } + + void setPreferredRendererByHDR(const QSurfaceFormat::RenderableType &preferredRendererByHDR) { + m_preferredRendererByHDR = preferredRendererByHDR; + } + + void setOpenGLBlacklisted(bool openGLBlacklisted) { + m_openGLBlacklisted = openGLBlacklisted; + } + + void setOpenGLESBlacklisted(bool openGLESBlacklisted) { + m_openGLESBlacklisted = openGLESBlacklisted; + } + + QSurfaceFormat::ColorSpace preferredColorSpace() const { + return m_preferredColorSpace; + } + + QSurfaceFormat::RenderableType preferredRendererByUser() const { + return m_preferredRendererByUser; + } + +private: + bool isHDRFormat(const QSurfaceFormat &f) const { +#ifdef HAVE_HDR + return f.colorSpace() == QSurfaceFormat::bt2020PQColorSpace || + f.colorSpace() == QSurfaceFormat::scRGBColorSpace; +#else + Q_UNUSED(f); + return false; +#endif + } + + bool isBlacklisted(const QSurfaceFormat &f) const { + KIS_SAFE_ASSERT_RECOVER_NOOP(f.renderableType() == QSurfaceFormat::OpenGL || + f.renderableType() == QSurfaceFormat::OpenGLES); + + return (f.renderableType() == QSurfaceFormat::OpenGL && m_openGLBlacklisted) || + (f.renderableType() == QSurfaceFormat::OpenGLES && m_openGLESBlacklisted); + } + + bool doPreferHDR() const { +#ifdef HAVE_HDR + return m_preferredColorSpace == QSurfaceFormat::bt2020PQColorSpace || + m_preferredColorSpace == QSurfaceFormat::scRGBColorSpace; +#else + return false; +#endif + } + + bool isPreferredColorSpace(const QSurfaceFormat::ColorSpace cs) const { + return KisOpenGLModeProber::fuzzyCompareColorSpaces(m_preferredColorSpace, cs); + return false; + } + +private: + QSurfaceFormat::ColorSpace m_preferredColorSpace = QSurfaceFormat::DefaultColorSpace; + QSurfaceFormat::RenderableType m_preferredRendererByQt = QSurfaceFormat::OpenGL; + QSurfaceFormat::RenderableType m_preferredRendererByUser = QSurfaceFormat::DefaultRenderableType; + QSurfaceFormat::RenderableType m_preferredRendererByHDR = QSurfaceFormat::DefaultRenderableType; + bool m_openGLBlacklisted = false; + bool m_openGLESBlacklisted = false; +}; + +struct DetectionDebug : public QDebug +{ + DetectionDebug(QString *string) + : QDebug(string), + m_string(string), + m_originalSize(string->size()) + {} + ~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << endl; } + + QString *m_string; + int m_originalSize; +}; +} + +#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog) + +QSurfaceFormat KisOpenGL::selectSurfaceFormat(KisOpenGL::OpenGLRenderer preferredRenderer, + KisConfig::RootSurfaceFormat preferredRootSurfaceFormat, + bool enableDebug) +{ + QVector warningMessages; + + using Info = boost::optional; + + QVector renderers({QSurfaceFormat::OpenGLES, QSurfaceFormat::OpenGL}); + +#ifdef HAVE_HDR + QVector formatSymbols({KisConfig::BT709_G22, KisConfig::BT709_G10, KisConfig::BT2020_PQ}); +#else + QVector formatSymbols({KisConfig::BT709_G22}); +#endif + + QVector preferredFormats; + Q_FOREACH (const QSurfaceFormat::RenderableType renderer, renderers) { + Q_FOREACH (const KisConfig::RootSurfaceFormat formatSymbol, formatSymbols) { + preferredFormats << generateSurfaceFormat(renderer, formatSymbol, enableDebug); + } + } + + QSurfaceFormat defaultFormat = generateSurfaceFormat(QSurfaceFormat::DefaultRenderableType, + KisConfig::BT709_G22, false); + Info info = KisOpenGLModeProber::instance()->probeFormat(defaultFormat); + + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(info, QSurfaceFormat()); + + FormatPositionLess compareOp; + compareOp.setPreferredRendererByQt(info->isOpenGLES() ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL); + +#ifdef HAVE_HDR + compareOp.setPreferredColorSpace( + preferredRootSurfaceFormat == KisConfig::BT709_G22 ? QSurfaceFormat::sRGBColorSpace : + preferredRootSurfaceFormat == KisConfig::BT709_G10 ? QSurfaceFormat::scRGBColorSpace : + QSurfaceFormat::bt2020PQColorSpace); +#else + Q_UNUSED(preferredRootSurfaceFormat); + compareOp.setPreferredColorSpace(QSurfaceFormat::sRGBColorSpace); +#endif + +#ifdef Q_OS_WIN + compareOp.setPreferredRendererByHDR(QSurfaceFormat::OpenGLES); +#endif + compareOp.setPreferredRendererByUser(preferredRenderer == KisOpenGL::RendererDesktopGL ? QSurfaceFormat::OpenGL : + preferredRenderer == KisOpenGL::RendererOpenGLES ? QSurfaceFormat::OpenGLES : + QSurfaceFormat::DefaultRenderableType); + compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm + + OpenGLRenderers supportedRenderers = RendererNone; + OpenGLRenderer preferredByQt = info->isOpenGLES() ? RendererOpenGLES : RendererDesktopGL; + + if (!info->isOpenGLES()) { + compareOp.setOpenGLBlacklisted(isOpenGLRendererBlacklisted(info->rendererString(), + info->driverVersionString(), + &warningMessages)); + + supportedRenderers |= RendererDesktopGL; + + info = KisOpenGLModeProber::instance()-> + probeFormat(generateSurfaceFormat(QSurfaceFormat::OpenGLES, + KisConfig::BT709_G22, false)); + if (info) { + supportedRenderers |= RendererOpenGLES; + } + } else { + supportedRenderers |= RendererOpenGLES; + + info = KisOpenGLModeProber::instance()-> + probeFormat(generateSurfaceFormat(QSurfaceFormat::OpenGL, + KisConfig::BT709_G22, false)); + + if (!info || info->isOpenGLES()) { + compareOp.setOpenGLBlacklisted(true); + } else { + compareOp.setOpenGLBlacklisted(isOpenGLRendererBlacklisted(info->rendererString(), + info->driverVersionString(), + &warningMessages)); + + supportedRenderers |= RendererDesktopGL; + } + } + + std::stable_sort(preferredFormats.begin(), preferredFormats.end(), compareOp); + + dbgDetection() << "Supported renderers:" << supportedRenderers; + + dbgDetection() << "Surface format preference list:"; + Q_FOREACH (const QSurfaceFormat &format, preferredFormats) { + dbgDetection() << "*" << format; + dbgDetection() << " " << format.renderableType(); + } + + QSurfaceFormat resultFormat = defaultFormat; + + Q_FOREACH (const QSurfaceFormat &format, preferredFormats) { + dbgDetection() <<"Probing format..." << format.colorSpace() << format.renderableType(); + + Info info = KisOpenGLModeProber::instance()->probeFormat(format); + + if (info && info->isSupportedVersion()) { + +#ifdef Q_OS_WIN + // HACK: Block ANGLE with Direct3D9 + // Direct3D9 does not give OpenGL ES 3.0 + // Some versions of ANGLE returns OpenGL version 3.0 incorrectly + + if (info->isUsingAngle() && + info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) { + + dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened."; + + continue; + } +#endif + + dbgDetection() << "Found format:" << format; + dbgDetection() << " " << format.renderableType(); + + resultFormat = format; + break; + } + } + + { + const bool colorSpaceIsCorrect = + KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(), + resultFormat.colorSpace()); + + const bool rendererIsCorrect = + compareOp.preferredRendererByUser() == QSurfaceFormat::DefaultRenderableType || + compareOp.preferredRendererByUser() == resultFormat.renderableType(); + + if (!rendererIsCorrect && colorSpaceIsCorrect) { + warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected."); + } else if (!colorSpaceIsCorrect) { + warningMessages << ki18n("Preferred output format is not supported by available renderers"); + } + + } + + overrideSupportedRenderers(supportedRenderers, preferredByQt); + overrideOpenGLWarningString(warningMessages); + + return resultFormat; +} + +void KisOpenGL::setDefaultSurfaceFormat(const QSurfaceFormat &format) +{ + KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet); + + g_sanityDefaultFormatIsSet = true; QSurfaceFormat::setDefaultFormat(format); } diff --git a/libs/ui/opengl/kis_opengl_canvas2.h b/libs/ui/opengl/kis_opengl_canvas2.h --- a/libs/ui/opengl/kis_opengl_canvas2.h +++ b/libs/ui/opengl/kis_opengl_canvas2.h @@ -83,9 +83,11 @@ public: // Implement kis_abstract_canvas_widget interface void setDisplayFilter(QSharedPointer displayFilter) override; + void notifyImageColorSpaceChanged(const KoColorSpace *cs) override; + void setWrapAroundViewingMode(bool value) override; void channelSelectionChanged(const QBitArray &channelFlags) override; - void setDisplayProfile(KisDisplayColorConverter *colorConverter) override; + void setDisplayColorConverter(KisDisplayColorConverter *colorConverter) override; void finishResizingImage(qint32 w, qint32 h) override; KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) override; QRect updateCanvasProjection(KisUpdateInfoSP info) override; diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp --- a/libs/ui/opengl/kis_opengl_canvas2.cpp +++ b/libs/ui/opengl/kis_opengl_canvas2.cpp @@ -43,6 +43,8 @@ #include #include #include +#include "KisOpenGLModeProber.h" +#include #ifndef Q_OS_OSX #include @@ -151,7 +153,7 @@ d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(image, - colorConverter->monitorProfile(), + colorConverter->openGLCanvasSurfaceProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); @@ -168,6 +170,10 @@ setAttribute(Qt::WA_InputMethodEnabled, false); setAttribute(Qt::WA_DontCreateNativeAncestors, true); + if (KisOpenGLModeProber::instance()->useHDRMode()) { + setTextureFormat(GL_RGBA16F); + } + setDisplayFilterImpl(colorConverter->displayFilter(), true); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); @@ -204,6 +210,16 @@ } } +void KisOpenGLCanvas2::notifyImageColorSpaceChanged(const KoColorSpace *cs) +{ + // FIXME: on color space change the data is refetched multiple + // times by different actors! + + if (d->openGLImageTextures->setImageColorSpace(cs)) { + canvas()->startUpdateInPatches(canvas()->image()->bounds()); + } +} + void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value) { d->wrapAroundMode = value; @@ -843,9 +859,17 @@ void KisOpenGLCanvas2::renderCanvasGL() { - // Draw the border (that is, clear the whole widget to the border color) - QColor widgetBackgroundColor = borderColor(); - glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0); + { + // Draw the border (that is, clear the whole widget to the border color) + QColor widgetBackgroundColor = borderColor(); + KoColor convertedBackgroudColor = + canvas()->displayColorConverter()->applyDisplayFiltering( + KoColor(widgetBackgroundColor, KoColorSpaceRegistry::instance()->rgb8()), + Float32BitsColorDepthID); + const float *pixel = reinterpret_cast(convertedBackgroudColor.data()); + glClearColor(pixel[0], pixel[1], pixel[2], 1.0); + } + glClear(GL_COLOR_BUFFER_BIT); if ((d->displayFilter && d->displayFilter->updateShader()) || @@ -879,9 +903,9 @@ } -void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter) +void KisOpenGLCanvas2::setDisplayColorConverter(KisDisplayColorConverter *colorConverter) { - d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(), + d->openGLImageTextures->setMonitorProfile(colorConverter->openGLCanvasSurfaceProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } diff --git a/libs/ui/opengl/kis_opengl_image_textures.h b/libs/ui/opengl/kis_opengl_image_textures.h --- a/libs/ui/opengl/kis_opengl_image_textures.h +++ b/libs/ui/opengl/kis_opengl_image_textures.h @@ -82,6 +82,12 @@ KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); + /** + * Update the textures when the color space of the image changes. + * @return true when a full data refetch should be initiated by the caller + */ + bool setImageColorSpace(const KoColorSpace *cs); + /** * Complete initialization can only happen once an OpenGL context has been created. * @param f Pointer to OpenGL functions. They must already be initialized. @@ -151,7 +157,7 @@ KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); - void createImageTextureTiles(); + void recreateImageTextureTiles(); void destroyImageTextureTiles(); @@ -171,13 +177,6 @@ KoColorConversionTransformation::Intent m_renderingIntent; KoColorConversionTransformation::ConversionFlags m_conversionFlags; - /** - * If the destination color space coincides with the one of the image, - * then effectively, there is no conversion happens. That is used - * for working with OCIO. - */ - const KoColorSpace *m_tilesDestinationColorSpace; - /** * Shows whether the internal color management should be enabled or not. * Please note that if you disable color management, *but* your image color diff --git a/libs/ui/opengl/kis_opengl_image_textures.cpp b/libs/ui/opengl/kis_opengl_image_textures.cpp --- a/libs/ui/opengl/kis_opengl_image_textures.cpp +++ b/libs/ui/opengl/kis_opengl_image_textures.cpp @@ -32,6 +32,7 @@ #include "kis_image.h" #include "kis_config.h" #include "KisPart.h" +#include "KisOpenGLModeProber.h" #ifdef HAVE_OPENEXR #include @@ -59,7 +60,6 @@ KisOpenGLImageTextures::KisOpenGLImageTextures() : m_image(0) , m_monitorProfile(0) - , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) @@ -83,7 +83,6 @@ , m_monitorProfile(monitorProfile) , m_renderingIntent(renderingIntent) , m_conversionFlags(conversionFlags) - , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) @@ -109,7 +108,7 @@ m_updateInfoBuilder.setTextureInfoPool(s_poolRegistry.getPool(m_texturesInfo.width, m_texturesInfo.height)); m_glFuncs->glGenTextures(1, &m_checkerTexture); - createImageTextureTiles(); + recreateImageTextureTiles(); KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds(), m_image); recalculateCache(info, false); @@ -175,13 +174,16 @@ } } -void KisOpenGLImageTextures::createImageTextureTiles() +void KisOpenGLImageTextures::recreateImageTextureTiles() { destroyImageTextureTiles(); updateTextureFormat(); - if (!m_tilesDestinationColorSpace) { + const KoColorSpace *tilesDestinationColorSpace = + m_updateInfoBuilder.destinationColorSpace(); + + if (!tilesDestinationColorSpace) { qDebug() << "No destination colorspace!!!!"; return; } @@ -194,7 +196,7 @@ m_numCols = lastCol + 1; // Default color is transparent black - const int pixelSize = m_tilesDestinationColorSpace->pixelSize(); + const int pixelSize = tilesDestinationColorSpace->pixelSize(); QByteArray emptyTileData((m_texturesInfo.width) * (m_texturesInfo.height) * pixelSize, 0); KisConfig config(true); @@ -339,7 +341,7 @@ void KisOpenGLImageTextures::slotImageSizeChanged(qint32 /*w*/, qint32 /*h*/) { - createImageTextureTiles(); + recreateImageTextureTiles(); } KisOpenGLUpdateInfoBuilder &KisOpenGLImageTextures::updateInfoBuilder() @@ -354,12 +356,16 @@ m_renderingIntent = renderingIntent; m_conversionFlags = conversionFlags; - m_updateInfoBuilder.setConversionOptions( - ConversionOptions(m_tilesDestinationColorSpace, - m_renderingIntent, - m_conversionFlags)); + recreateImageTextureTiles(); +} + +bool KisOpenGLImageTextures::setImageColorSpace(const KoColorSpace *cs) +{ + Q_UNUSED(cs); + // TODO: implement lazy update: do not re-upload textures when not needed - createImageTextureTiles(); + recreateImageTextureTiles(); + return true; } void KisOpenGLImageTextures::setChannelFlags(const QBitArray &channelFlags) @@ -436,7 +442,7 @@ if (needsFinalRegeneration) { m_internalColorManagementActive = value; - createImageTextureTiles(); + recreateImageTextureTiles(); // at this point the value of m_internalColorManagementActive might // have been forcely reverted to 'false' in case of some problems @@ -445,6 +451,43 @@ return needsFinalRegeneration; } +namespace { +void initializeRGBA16FTextures(QOpenGLContext *ctx, KisGLTexturesInfo &texturesInfo, KoID &destinationColorDepthId) +{ + if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { + texturesInfo.internalFormat = GL_RGBA16F; + dbgUI << "Using half (GLES or GL3)"; + } else if (ctx->hasExtension("GL_ARB_texture_float")) { + texturesInfo.internalFormat = GL_RGBA16F_ARB; + dbgUI << "Using ARB half"; + } + else if (ctx->hasExtension("GL_ATI_texture_float")) { + texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI; + dbgUI << "Using ATI half"; + } + + bool haveBuiltInOpenExr = false; +#ifdef HAVE_OPENEXR + haveBuiltInOpenExr = true; +#endif + + if (haveBuiltInOpenExr && (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3())) { + texturesInfo.type = GL_HALF_FLOAT; + destinationColorDepthId = Float16BitsColorDepthID; + dbgUI << "Pixel type half (GLES or GL3)"; + } else if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) { + texturesInfo.type = GL_HALF_FLOAT_ARB; + destinationColorDepthId = Float16BitsColorDepthID; + dbgUI << "Pixel type half"; + } else { + texturesInfo.type = GL_FLOAT; + destinationColorDepthId = Float32BitsColorDepthID; + dbgUI << "Pixel type float"; + } + texturesInfo.format = GL_RGBA; +} +} + void KisOpenGLImageTextures::updateTextureFormat() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -467,8 +510,9 @@ } } - KoID colorModelId = m_image->colorSpace()->colorModelId(); - KoID colorDepthId = m_image->colorSpace()->colorDepthId(); + const bool useHDRMode = KisOpenGLModeProber::instance()->useHDRMode(); + const KoID colorModelId = m_image->colorSpace()->colorModelId(); + const KoID colorDepthId = useHDRMode ? Float16BitsColorDepthID : m_image->colorSpace()->colorDepthId(); KoID destinationColorModelId = RGBAColorModelID; KoID destinationColorDepthId = Integer8BitsColorDepthID; @@ -477,38 +521,7 @@ if (colorModelId == RGBAColorModelID) { if (colorDepthId == Float16BitsColorDepthID) { - - if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { - m_texturesInfo.internalFormat = GL_RGBA16F; - dbgUI << "Using half (GLES or GL3)"; - } else if (ctx->hasExtension("GL_ARB_texture_float")) { - m_texturesInfo.internalFormat = GL_RGBA16F_ARB; - dbgUI << "Using ARB half"; - } - else if (ctx->hasExtension("GL_ATI_texture_float")) { - m_texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI; - dbgUI << "Using ATI half"; - } - - bool haveBuiltInOpenExr = false; -#ifdef HAVE_OPENEXR - haveBuiltInOpenExr = true; -#endif - - if (haveBuiltInOpenExr && (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3())) { - m_texturesInfo.type = GL_HALF_FLOAT; - destinationColorDepthId = Float16BitsColorDepthID; - dbgUI << "Pixel type half (GLES or GL3)"; - } else if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) { - m_texturesInfo.type = GL_HALF_FLOAT_ARB; - destinationColorDepthId = Float16BitsColorDepthID; - dbgUI << "Pixel type half"; - } else { - m_texturesInfo.type = GL_FLOAT; - destinationColorDepthId = Float32BitsColorDepthID; - dbgUI << "Pixel type float"; - } - m_texturesInfo.format = GL_RGBA; + initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); } else if (colorDepthId == Float32BitsColorDepthID) { if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { @@ -545,6 +558,9 @@ m_texturesInfo.format = GL_BGRA; destinationColorDepthId = Integer16BitsColorDepthID; dbgUI << "Using conversion to 16 bits rgba"; + } else if (colorDepthId == Float16BitsColorDepthID && KisOpenGL::hasOpenGLES()) { + // TODO: try removing opengl es limit + initializeRGBA16FTextures(ctx, m_texturesInfo, destinationColorDepthId); } // TODO: for ANGLE, see if we can convert to 16f to support 10-bit display } @@ -584,13 +600,13 @@ * would not be called when not needed (DK) */ - m_tilesDestinationColorSpace = + const KoColorSpace *tilesDestinationColorSpace = KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(), destinationColorDepthId.id(), profile); m_updateInfoBuilder.setConversionOptions( - ConversionOptions(m_tilesDestinationColorSpace, + ConversionOptions(tilesDestinationColorSpace, m_renderingIntent, m_conversionFlags)); } diff --git a/libs/ui/opengl/kis_opengl_p.h b/libs/ui/opengl/kis_opengl_p.h --- a/libs/ui/opengl/kis_opengl_p.h +++ b/libs/ui/opengl/kis_opengl_p.h @@ -102,6 +102,7 @@ #endif void appendOpenGLWarningString(KLocalizedString warning); +void overrideOpenGLWarningString(QVector warnings); bool isDefaultFormatSet(); diff --git a/libs/ui/opengl/kis_opengl_shader_loader.cpp b/libs/ui/opengl/kis_opengl_shader_loader.cpp --- a/libs/ui/opengl/kis_opengl_shader_loader.cpp +++ b/libs/ui/opengl/kis_opengl_shader_loader.cpp @@ -97,6 +97,10 @@ "#version 300 es\n" "precision mediump float;\n" "precision mediump sampler3D;\n"); + + // OpenColorIO doesn't support the new GLSL version yet. + fragSource.append("#define texture2D texture\n"); + fragSource.append("#define texture3D texture\n"); } else { fragSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n"); } diff --git a/libs/ui/opengl/kis_opengl_win.cpp b/libs/ui/opengl/kis_opengl_win.cpp deleted file mode 100644 --- a/libs/ui/opengl/kis_opengl_win.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2017 Alvin Wong - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "opengl/kis_opengl.h" -#include "opengl/kis_opengl_p.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -using namespace KisOpenGLPrivate; - -namespace -{ - -struct WindowsOpenGLStatus { - bool supportsDesktopGL = false; - bool supportsAngleD3D11 = false; - bool isQtPreferAngle = false; - bool overridePreferAngle = false; // override Qt to force ANGLE to be preferred -}; -WindowsOpenGLStatus windowsOpenGLStatus = {}; -KisOpenGL::OpenGLRenderer userRendererConfig; -KisOpenGL::OpenGLRenderer nextUserRendererConfig; -KisOpenGL::OpenGLRenderer currentRenderer; - -QStringList qpaDetectionLog; - -boost::optional checkQpaOpenGLStatus() { - QWindow surface; - surface.setSurfaceType(QSurface::OpenGLSurface); - surface.create(); - QOpenGLContext context; - if (!context.create()) { - qDebug() << "OpenGL context cannot be created"; - return boost::none; - } - if (!context.isValid()) { - qDebug() << "OpenGL context is not valid while checking Qt's OpenGL status"; - return boost::none; - } - if (!context.makeCurrent(&surface)) { - qDebug() << "OpenGL context cannot be made current"; - return boost::none; - } - return OpenGLCheckResult(context); -} - -bool checkIsSupportedDesktopGL(const OpenGLCheckResult &checkResult) { - if (checkResult.isUsingAngle()) { - qWarning() << "ANGLE was being used when desktop OpenGL was wanted, assuming no desktop OpenGL support"; - return false; - } - if (checkResult.isOpenGLES()) { - qWarning() << "Got OpenGL ES instead of desktop OpenGL, this shouldn't happen!"; - return false; - } - return checkResult.isSupportedVersion(); -} - -bool checkIsSupportedAngleD3D11(const OpenGLCheckResult &checkResult) { - if (!checkResult.isOpenGLES()) { - qWarning() << "Got desktop OpenGL instead of OpenGL ES, this shouldn't happen!"; - return false; - } - if (!checkResult.isUsingAngle()) { - // This can happen if someone tries to swap in SwiftShader, don't mind it. - qWarning() << "OpenGL ES context is not ANGLE. Continuing anyway..."; - } - // HACK: Block ANGLE with Direct3D9 - // Direct3D9 does not give OpenGL ES 3.0 - // Some versions of ANGLE returns OpenGL version 3.0 incorrectly - if (checkResult.rendererString().contains("Direct3D9", Qt::CaseInsensitive)) { - qWarning() << "ANGLE tried to use Direct3D9, Krita won't work with it"; - return false; - } - return checkResult.isSupportedVersion(); -} - -void specialOpenGLVendorFilter(WindowsOpenGLStatus &status, const OpenGLCheckResult &checkResult) { - if (!status.supportsAngleD3D11) { - return; - } - - // Special blacklisting of OpenGL/ANGLE is tracked on: - // https://phabricator.kde.org/T7411 - - // HACK: Specifically detect for Intel driver build number - // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics-drivers.html - if (checkResult.rendererString().startsWith("Intel")) { - KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL."); - KLocalizedString grossIntelWarning = ki18n( - "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. " - "You may manually switch to OpenGL but it is not guaranteed to work properly." - ); - QRegularExpression regex("\\b\\d{2}\\.\\d{2}\\.\\d{2}\\.(\\d{4})\\b"); - QRegularExpressionMatch match = regex.match(checkResult.driverVersionString()); - if (match.hasMatch()) { - int driverBuild = match.captured(1).toInt(); - if (driverBuild > 4636 && driverBuild < 4729) { - // Make ANGLE the preferred renderer for Intel driver versions - // between build 4636 and 4729 (exclusive) due to an UI offset bug. - // See https://communities.intel.com/thread/116003 - // (Build 4636 is known to work from some test results) - qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer"; - status.overridePreferAngle = true; - appendOpenGLWarningString(knownBadIntelWarning); - } else if (driverBuild == 4358) { - // There are several reports on a bug where the canvas is not being - // updated properly which has debug info pointing to this build. - qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer"; - status.overridePreferAngle = true; - appendOpenGLWarningString(knownBadIntelWarning); - } else { - // Intel tends to randomly break OpenGL in some of their new driver - // builds, therefore we just shouldn't use OpenGL by default to - // reduce bug report noises. - qDebug() << "Detected Intel driver, making ANGLE the preferred renderer"; - status.overridePreferAngle = true; - if (status.supportsDesktopGL) { - appendOpenGLWarningString(grossIntelWarning); - } - } - } - } -} - -} // namespace - -void KisOpenGLPrivate::appendPlatformOpenGLDebugText(QDebug &debugOut) { - debugOut << "\n\nQPA OpenGL Detection Info"; - debugOut << "\n supportsDesktopGL:" << windowsOpenGLStatus.supportsDesktopGL; - debugOut << "\n supportsAngleD3D11:" << windowsOpenGLStatus.supportsAngleD3D11; - debugOut << "\n isQtPreferAngle:" << windowsOpenGLStatus.isQtPreferAngle; - debugOut << "\n overridePreferAngle:" << windowsOpenGLStatus.overridePreferAngle; - debugOut << "\n== log ==\n"; - debugOut.noquote(); - debugOut << qpaDetectionLog.join('\n'); - debugOut.resetFormat(); - debugOut << "\n== end log =="; -} - -/** - * This function probes the Qt Platform Abstraction (QPA) for OpenGL diagnostics - * information. The code works under the assumption that the bundled Qt is built - * with `-opengl dynamic` and includes support for ANGLE. - * - * This function is written for Qt 5.9.1. On other versions it might not work - * as well. - */ -void KisOpenGL::probeWindowsQpaOpenGL(int argc, char **argv, QString userRendererConfigString) -{ - KIS_SAFE_ASSERT_RECOVER(isDefaultFormatSet()) { - qWarning() << "Default OpenGL format was not set before calling KisOpenGL::probeWindowsQpaOpenGL. This might be a BUG!"; - setDefaultFormat(); - } - - // Clear env var to prevent affecting tests - qunsetenv("QT_OPENGL"); - - boost::optional qpaDetectionResult; - - qDebug() << "Probing Qt OpenGL detection:"; - { - { - QGuiApplication app(argc, argv); - qpaDetectionResult = checkQpaOpenGLStatus(); - } - } - if (!qpaDetectionResult) { - qWarning() << "Could not initialize OpenGL context!"; - return; - } - qDebug() << "Done probing Qt OpenGL detection"; - - windowsOpenGLStatus.isQtPreferAngle = qpaDetectionResult->isOpenGLES(); - - boost::optional checkResultAngle, checkResultDesktopGL; - if (qpaDetectionResult->isOpenGLES()) { - checkResultAngle = qpaDetectionResult; - // We already checked ANGLE, now check desktop OpenGL - qputenv("QT_OPENGL", "desktop"); - qDebug() << "Checking desktop OpenGL..."; - { - QGuiApplication app(argc, argv); - checkResultDesktopGL = checkQpaOpenGLStatus(); - } - if (!checkResultDesktopGL) { - qWarning() << "Could not initialize OpenGL context!"; - } - qDebug() << "Done checking desktop OpenGL"; - qunsetenv("QT_OPENGL"); - } else { - checkResultDesktopGL = qpaDetectionResult; - // We already checked desktop OpenGL, now check ANGLE - qputenv("QT_OPENGL", "angle"); - qDebug() << "Checking ANGLE..."; - { - QGuiApplication app(argc, argv); - checkResultAngle = checkQpaOpenGLStatus(); - } - if (!checkResultAngle) { - qWarning() << "Could not initialize OpenGL context!"; - } - qDebug() << "Done checking ANGLE"; - qunsetenv("QT_OPENGL"); - } - - windowsOpenGLStatus.supportsDesktopGL = - checkResultDesktopGL && checkIsSupportedDesktopGL(*checkResultDesktopGL); - windowsOpenGLStatus.supportsAngleD3D11 = - checkResultAngle && checkIsSupportedAngleD3D11(*checkResultAngle); - - if (!windowsOpenGLStatus.supportsDesktopGL) { - appendOpenGLWarningString(ki18n("The graphics driver in use does not meet the OpenGL requirements.")); - } else if (windowsOpenGLStatus.isQtPreferAngle) { - appendOpenGLWarningString(ki18n("The graphics driver in use may not work well with OpenGL.")); - } - - // HACK: Filter specific buggy drivers not handled by Qt OpenGL buglist - if (checkResultDesktopGL) { - specialOpenGLVendorFilter(windowsOpenGLStatus, *checkResultDesktopGL); - } - - if (windowsOpenGLStatus.supportsAngleD3D11 - && (checkResultAngle->rendererString().contains("Software Adapter") - || checkResultAngle->rendererString().contains("Microsoft Basic Render Driver"))) { - appendOpenGLWarningString(ki18n( - "ANGLE is using a software Direct3D renderer, which is not hardware-accelerated and may be very slow. " - "This can happen if the graphics drivers are not properly installed, or when using a Remote Desktop session." - )); - } - - userRendererConfig = convertConfigToOpenGLRenderer(userRendererConfigString); - if ((userRendererConfig == RendererDesktopGL && !windowsOpenGLStatus.supportsDesktopGL) || - (userRendererConfig == RendererAngle && !windowsOpenGLStatus.supportsAngleD3D11)) { - // Set it to auto so we won't get stuck - userRendererConfig = RendererAuto; - } - nextUserRendererConfig = userRendererConfig; - switch (userRendererConfig) { - case RendererDesktopGL: - QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); - currentRenderer = RendererDesktopGL; - break; - case RendererAngle: - QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); - currentRenderer = RendererAngle; - break; - default: - if (windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { - currentRenderer = RendererAngle; - } else if (windowsOpenGLStatus.overridePreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { - QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); - currentRenderer = RendererAngle; - } else if (!windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsDesktopGL) { - currentRenderer = RendererDesktopGL; - } else { - currentRenderer = RendererNone; - } - break; - } -} - -KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer() -{ - return currentRenderer; -} - -KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer() -{ - return (windowsOpenGLStatus.isQtPreferAngle || windowsOpenGLStatus.overridePreferAngle) - ? RendererAngle : RendererDesktopGL; -} - -KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers() -{ - return RendererAuto | - (windowsOpenGLStatus.supportsDesktopGL ? RendererDesktopGL : static_cast(0)) | - (windowsOpenGLStatus.supportsAngleD3D11 ? RendererAngle : static_cast(0)); -} - -KisOpenGL::OpenGLRenderer KisOpenGL::getUserOpenGLRendererConfig() -{ - return userRendererConfig; -} - -KisOpenGL::OpenGLRenderer KisOpenGL::getNextUserOpenGLRendererConfig() -{ - return nextUserRendererConfig; -} - -void KisOpenGL::setNextUserOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer) -{ - nextUserRendererConfig = renderer; -} - -QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer) -{ - switch (renderer) { - case RendererDesktopGL: - return QStringLiteral("desktop"); - case RendererAngle: - return QStringLiteral("angle"); - default: - return QStringLiteral("auto"); - } -} - -KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer) -{ - if (renderer == "desktop") { - return RendererDesktopGL; - } else if (renderer == "angle") { - return RendererAngle; - } else { - return RendererAuto; - } -} diff --git a/libs/ui/tests/kis_derived_resources_test.cpp b/libs/ui/tests/kis_derived_resources_test.cpp --- a/libs/ui/tests/kis_derived_resources_test.cpp +++ b/libs/ui/tests/kis_derived_resources_test.cpp @@ -65,7 +65,7 @@ KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); - KisOpenGL::setDefaultFormat(false, false); + KisOpenGL::testingInitializeDefaultSurfaceFormat(); KisConfig cfg(false); cfg.setUseOpenGL(false); diff --git a/plugins/color/lcms2engine/IccColorSpaceEngine.h b/plugins/color/lcms2engine/IccColorSpaceEngine.h --- a/plugins/color/lcms2engine/IccColorSpaceEngine.h +++ b/plugins/color/lcms2engine/IccColorSpaceEngine.h @@ -40,6 +40,8 @@ KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const override; quint32 computeColorSpaceType(const KoColorSpace *cs) const; + + bool supportsColorSpace(const QString& colorModelId, const QString& colorDepthId, const KoColorProfile *profile) const override; private: struct Private; Private *const d; diff --git a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp --- a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp +++ b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp @@ -334,3 +334,9 @@ return depthType | modelType; } } + +bool IccColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const +{ + Q_UNUSED(colorDepthId); + return colorModelId != RGBAColorModelID.id() || !profile || profile->name() != "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF"; +} diff --git a/plugins/color/lcms2engine/LcmsEnginePlugin.cpp b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp --- a/plugins/color/lcms2engine/LcmsEnginePlugin.cpp +++ b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp @@ -65,6 +65,8 @@ #include "colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h" #include "colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h" +#include "LcmsRGBP2020PQColorSpace.h" + #include #ifdef HAVE_OPENEXR @@ -166,14 +168,14 @@ KoColorProfile *rgbProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreate_sRGBProfile()); registry->addProfile(rgbProfile); - registry->add(new RgbU8ColorSpaceFactory()); - registry->add(new RgbU16ColorSpaceFactory()); + registry->add(new LcmsRGBP2020PQColorSpaceFactoryWrapper()); + registry->add(new LcmsRGBP2020PQColorSpaceFactoryWrapper()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR - registry->add(new RgbF16ColorSpaceFactory()); + registry->add(new LcmsRGBP2020PQColorSpaceFactoryWrapper()); #endif #endif - registry->add(new RgbF32ColorSpaceFactory()); + registry->add(new LcmsRGBP2020PQColorSpaceFactoryWrapper()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory diff --git a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h new file mode 100644 --- /dev/null +++ b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LCMSRGBP2020PQCOLORSPACE_H +#define LCMSRGBP2020PQCOLORSPACE_H + +#include +#include +#include +#include + +#include "KoColorConversionTransformationFactory.h" + +#include + +template +struct ColorSpaceFromFactory { +}; + +template<> +struct ColorSpaceFromFactory { + typedef RgbU8ColorSpace type; +}; + +template<> +struct ColorSpaceFromFactory { + typedef RgbU16ColorSpace type; +}; + +template<> +struct ColorSpaceFromFactory { + typedef RgbF16ColorSpace type; +}; + +template<> +struct ColorSpaceFromFactory { + typedef RgbF32ColorSpace type; +}; + +/** + * Define a singly linked list of supported bit depth traits + */ +template struct NextTrait { using type = void; }; +template<> struct NextTrait { using type = KoBgrU16Traits; }; +template<> struct NextTrait { using type = KoRgbF16Traits; }; +template<> struct NextTrait { using type = KoRgbF32Traits; }; + +/** + * Recursively add bit-depths conversions to the color space. We add only + * **outgoing** conversions for every RGB color space. That is, every color + * space has exactly three outgoing edges for color conversion. + */ +template +void addInternalConversion(QList &list, CurrentTraits*) +{ + // general case: add a converter and recurse for the next traits + list << new LcmsScaleRGBP2020PQTransformationFactory(); + + using NextTraits = typename NextTrait::type; + addInternalConversion(list, static_cast(0)); +} + +template +void addInternalConversion(QList &list, typename ParentColorSpace::ColorSpaceTraits*) +{ + // exception: skip adding an edge to the same bit depth + + using CurrentTraits = typename ParentColorSpace::ColorSpaceTraits; + using NextTraits = typename NextTrait::type; + addInternalConversion(list, static_cast(0)); +} + +template +void addInternalConversion(QList &, void*) +{ + // stop recursion +} + +template +class LcmsRGBP2020PQColorSpaceFactoryWrapper : public BaseColorSpaceFactory +{ + typedef typename ColorSpaceFromFactory::type RelatedColorSpaceType; + + KoColorSpace *createColorSpace(const KoColorProfile *p) const override + { + return new RelatedColorSpaceType(this->name(), p->clone()); + } + + QList colorConversionLinks() const override + { + QList list; + + /** + * We explicitly disable direct conversions to/from integer color spaces, because + * they may cause the the conversion system to choose them as an intermediate + * color space for the conversion chain, e.g. + * p709-g10 F32 -> p2020-g10 U16 -> Rec2020-pq U16, which is incorrect and loses + * all the HDR data + */ + list << new LcmsFromRGBP2020PQTransformationFactory(); + list << new LcmsFromRGBP2020PQTransformationFactory(); + list << new LcmsToRGBP2020PQTransformationFactory(); + list << new LcmsToRGBP2020PQTransformationFactory(); + + // internally, we can convert to RGB U8 if needed + addInternalConversion(list, static_cast(0)); + + return list; + } +}; + +#endif // LCMSRGBP2020PQCOLORSPACE_H diff --git a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h new file mode 100644 --- /dev/null +++ b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H +#define LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H + +#include "KoAlwaysInline.h" +#include "KoColorModelStandardIds.h" +#include "KoColorSpaceMaths.h" +#include "KoColorModelStandardIdsUtils.h" +#include "KoColorConversionTransformationFactory.h" + +#include +#include +#include +#include + + +namespace +{ + +ALWAYS_INLINE float applySmpte2048Curve(float x) { + const float m1 = 2610.0 / 4096.0 / 4.0; + const float m2 = 2523.0 / 4096.0 * 128.0; + const float a1 = 3424.0 / 4096.0; + const float c2 = 2413.0 / 4096.0 * 32.0; + const float c3 = 2392.0 / 4096.0 * 32.0; + const float a4 = 1.0; + const float x_p = powf(0.008 * std::max(0.0f, x), m1); + const float res = powf((a1 + c2 * x_p) / (a4 + c3 * x_p), m2); + return res; +} + +ALWAYS_INLINE float removeSmpte2048Curve(float x) { + const float m1_r = 4096.0 * 4.0 / 2610.0; + const float m2_r = 4096.0 / 2523.0 / 128.0; + const float a1 = 3424.0 / 4096.0; + const float c2 = 2413.0 / 4096.0 * 32.0; + const float c3 = 2392.0 / 4096.0 * 32.0; + + const float x_p = powf(x, m2_r); + const float res = powf(qMax(0.0f, x_p - a1) / (c2 - c3 * x_p), m1_r); + return res * 125.0f; +} + +template +struct DstTraitsForSource { + typedef KoRgbF32Traits result; +}; + +template <> +struct DstTraitsForSource { + typedef KoRgbF16Traits result; +}; + +template <> +struct DstTraitsForSource { + typedef KoRgbF16Traits result; +}; + +template +struct RemoveSmpte2048Policy { + static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { + return + KoColorSpaceMaths::scaleToA( + removeSmpte2048Curve( + KoColorSpaceMaths::scaleToA( + value))); + } +}; + +template +struct ApplySmpte2048Policy { + static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { + return + KoColorSpaceMaths::scaleToA( + applySmpte2048Curve( + KoColorSpaceMaths::scaleToA( + value))); + } +}; + +template +struct NoopPolicy { + static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { + return KoColorSpaceMaths::scaleToA(value); + } +}; + +} + +template class Policy> +struct ApplyRgbShaper : public KoColorConversionTransformation +{ + ApplyRgbShaper(const KoColorSpace* srcCs, + const KoColorSpace* dstCs, + Intent renderingIntent, + ConversionFlags conversionFlags) + : KoColorConversionTransformation(srcCs, + dstCs, + renderingIntent, + conversionFlags) + { + } + + void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override { + KIS_ASSERT(src != dst); + + const typename SrcCSTraits::Pixel *srcPixel = reinterpret_cast(src); + typename DstCSTraits::Pixel *dstPixel = reinterpret_cast(dst); + + typedef typename SrcCSTraits::channels_type src_channel_type; + typedef typename DstCSTraits::channels_type dst_channel_type; + typedef Policy ConcretePolicy; + + for (int i = 0; i < nPixels; i++) { + dstPixel->red = ConcretePolicy::process(srcPixel->red); + dstPixel->green = ConcretePolicy::process(srcPixel->green); + dstPixel->blue = ConcretePolicy::process(srcPixel->blue); + dstPixel->alpha = + KoColorSpaceMaths::scaleToA( + srcPixel->alpha); + + srcPixel++; + dstPixel++; + } + } + +}; + +template::result> +class LcmsFromRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory +{ +public: + LcmsFromRGBP2020PQTransformationFactory() + : KoColorConversionTransformationFactory(RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", + RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "Rec2020-elle-V4-g10.icc") + { + } + + bool conserveColorInformation() const override { + return true; + } + + bool conserveDynamicRange() const override { + return + dstColorDepthId() == Float16BitsColorDepthID.id() || + dstColorDepthId() == Float32BitsColorDepthID.id() || + dstColorDepthId() == Float64BitsColorDepthID.id(); + } + + KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, + const KoColorSpace* dstColorSpace, + KoColorConversionTransformation::Intent renderingIntent, + KoColorConversionTransformation::ConversionFlags conversionFlags) const override + { + return new ApplyRgbShaper< + typename ParentColorSpace::ColorSpaceTraits, + DstColorSpaceTraits, + RemoveSmpte2048Policy>(srcColorSpace, + dstColorSpace, + renderingIntent, + conversionFlags); + } +}; + +template::result> +class LcmsToRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory +{ +public: + LcmsToRGBP2020PQTransformationFactory() + : KoColorConversionTransformationFactory(RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "Rec2020-elle-V4-g10.icc", + RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") + { + } + + bool conserveColorInformation() const override { + return true; + } + + bool conserveDynamicRange() const override { + return true; + } + + KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, + const KoColorSpace* dstColorSpace, + KoColorConversionTransformation::Intent renderingIntent, + KoColorConversionTransformation::ConversionFlags conversionFlags) const override + { + return new ApplyRgbShaper< + DstColorSpaceTraits, + typename ParentColorSpace::ColorSpaceTraits, + ApplySmpte2048Policy>(srcColorSpace, + dstColorSpace, + renderingIntent, + conversionFlags); + } +}; + +template +class LcmsScaleRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory +{ +public: + LcmsScaleRGBP2020PQTransformationFactory() + : KoColorConversionTransformationFactory(RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", + RGBAColorModelID.id(), + colorDepthIdForChannelType().id(), + "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") + { + KIS_SAFE_ASSERT_RECOVER_NOOP(srcColorDepthId() != dstColorDepthId()); + } + + bool conserveColorInformation() const override { + return true; + } + + bool conserveDynamicRange() const override { + return + srcColorDepthId() == Float16BitsColorDepthID.id() || + srcColorDepthId() == Float32BitsColorDepthID.id() || + srcColorDepthId() == Float64BitsColorDepthID.id(); + } + + KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, + const KoColorSpace* dstColorSpace, + KoColorConversionTransformation::Intent renderingIntent, + KoColorConversionTransformation::ConversionFlags conversionFlags) const override + { + return new ApplyRgbShaper< + typename ParentColorSpace::ColorSpaceTraits, + DstColorSpaceTraits, + NoopPolicy>(srcColorSpace, + dstColorSpace, + renderingIntent, + conversionFlags); + } +}; + +#endif // LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H diff --git a/plugins/color/lcms2engine/tests/CMakeLists.txt b/plugins/color/lcms2engine/tests/CMakeLists.txt --- a/plugins/color/lcms2engine/tests/CMakeLists.txt +++ b/plugins/color/lcms2engine/tests/CMakeLists.txt @@ -27,5 +27,6 @@ ecm_add_tests( TestKoLcmsColorProfile.cpp TestKoColorSpaceRegistry.cpp + TestLcmsRGBP2020PQColorSpace.cpp NAME_PREFIX "plugins-lcmsengine-" LINK_LIBRARIES kritawidgets kritapigment KF5::I18n Qt5::Test ${LCMS2_LIBRARIES}) diff --git a/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.h b/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.h new file mode 100644 --- /dev/null +++ b/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TESTLCMSRGBP2020PQCOLORSPACE_H +#define TESTLCMSRGBP2020PQCOLORSPACE_H +#include + +class TestLcmsRGBP2020PQColorSpace : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void test(); + void testInternalConversions(); + void testConvertToCmyk(); +}; + +#endif // TESTLCMSRGBP2020PQCOLORSPACE_H diff --git a/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.cpp b/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.cpp new file mode 100644 --- /dev/null +++ b/plugins/color/lcms2engine/tests/TestLcmsRGBP2020PQColorSpace.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "TestLcmsRGBP2020PQColorSpace.h" + +#include +#include "sdk/tests/kistest.h" + +#include "kis_debug.h" + +#include "KoColorProfile.h" +#include "KoColorSpaceRegistry.h" +#include "KoColor.h" +#include "KoColorModelStandardIds.h" + +inline QString truncated(QString value) { + value.truncate(24); + return value; +} + +enum SourceType { + SDR, + HDR, + HDR_PQ +}; + +void testRoundTrip(const KoColorSpace *srcCS, const KoColorSpace *dstCS, SourceType sourceIsPQ) +{ + qDebug() << "Testing:" << srcCS->id() << truncated(srcCS->profile()->name()) + << "->" + << dstCS->id() << truncated(dstCS->profile()->name()); + + KoColor srcColor(srcCS); + KoColor dstColor(dstCS); + + QVector refChannels; + + if (sourceIsPQ == HDR) { + refChannels << 2.8; // R + refChannels << 1.8; // G + refChannels << 0.8; // B + refChannels << 0.9; // A + } else if (sourceIsPQ == HDR_PQ) { + refChannels << 0.9; // R (PQ) + refChannels << 0.7; // G (PQ) + refChannels << 0.1; // B (PQ) + refChannels << 0.9; // A + } else if (sourceIsPQ == SDR) { + refChannels << 0.15; // R + refChannels << 0.17; // G + refChannels << 0.19; // B + refChannels << 0.90; // A + } + + srcCS->fromNormalisedChannelsValue(srcColor.data(), refChannels); + + srcCS->convertPixelsTo(srcColor.data(), dstColor.data(), dstCS, 1, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags()); + + dstCS->convertPixelsTo(dstColor.data(), srcColor.data(), srcCS, 1, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags()); + + QVector result(4); + srcCS->normalisedChannelsValue(srcColor.data(), result); + + QList channels = srcCS->channels(); + + // 5% tolerance for CMYK, 4% for 8-bit, and 1% for everything else + const float tolerance = + dstCS->colorModelId() == CMYKAColorModelID ? 0.05 : + (dstCS->colorDepthId() == Integer8BitsColorDepthID || + srcCS->colorDepthId() == Integer8BitsColorDepthID) ? 0.04 : + 0.01; + + bool roundTripIsCorrect = true; + for (int i = 0; i < 4; i++) { + roundTripIsCorrect &= qAbs(refChannels[i] - result[i]) < tolerance; + } + + if (!roundTripIsCorrect) { + for (int i = 0; i < 4; i++) { + qDebug() << channels[i]->name() << "ref" << refChannels[i] << "result" << result[i]; + } + } + + QVERIFY(roundTripIsCorrect); +} + +void testRoundTrip(const KoID &linearColorDepth, const KoID &pqColorDepth, SourceType sourceIsPQ) +{ + const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); + const KoColorProfile *p2020G10Profile = KoColorSpaceRegistry::instance()->p2020G10Profile(); + + const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), linearColorDepth.id(), p2020G10Profile); + const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), pqColorDepth.id(), p2020PQProfile);; + + if (sourceIsPQ == HDR_PQ) { + std::swap(srcCS, dstCS); + } + + testRoundTrip(srcCS, dstCS, sourceIsPQ); +} + +void TestLcmsRGBP2020PQColorSpace::test() +{ + const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); + const KoColorProfile *p2020G10Profile = KoColorSpaceRegistry::instance()->p2020G10Profile(); + const KoColorProfile *p709G10Profile = KoColorSpaceRegistry::instance()->p709G10Profile(); + + QVERIFY(p2020PQProfile); + QVERIFY(p2020G10Profile); + QVERIFY(p709G10Profile); + + QVector linearModes; + linearModes << Float16BitsColorDepthID; + linearModes << Float32BitsColorDepthID; + + QVector pqModes; + pqModes << Integer8BitsColorDepthID; + pqModes << Integer16BitsColorDepthID; + pqModes << Float16BitsColorDepthID; + pqModes << Float32BitsColorDepthID; + + Q_FOREACH(const KoID &src, linearModes) { + Q_FOREACH(const KoID &dst, pqModes) { + testRoundTrip(src, dst, HDR); + } + } + + Q_FOREACH(const KoID &src, linearModes) { + Q_FOREACH(const KoID &dst, pqModes) { + testRoundTrip(src, dst, HDR_PQ); + } + } +} + +void TestLcmsRGBP2020PQColorSpace::testInternalConversions() +{ + const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); + + QVector pqModes; + pqModes << Integer16BitsColorDepthID; + pqModes << Float16BitsColorDepthID; + pqModes << Float32BitsColorDepthID; + + Q_FOREACH(const KoID &src, pqModes) { + Q_FOREACH(const KoID &dst, pqModes) { + if (src == dst) continue; + + const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), src.id(), p2020PQProfile); + const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), dst.id(), p2020PQProfile); + + testRoundTrip(srcCS, dstCS, HDR_PQ); + } + } +} + +void TestLcmsRGBP2020PQColorSpace::testConvertToCmyk() +{ + const KoColorProfile *p2020PQProfile = KoColorSpaceRegistry::instance()->p2020PQProfile(); + + const KoColorSpace *srcCS = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Integer16BitsColorDepthID.id(), p2020PQProfile); + const KoColorSpace *dstCS = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), 0); + + testRoundTrip(srcCS, dstCS, SDR); +} + +KISTEST_MAIN(TestLcmsRGBP2020PQColorSpace) diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,5 +1,7 @@ add_subdirectory(defaultdockers) -add_subdirectory(smallcolorselector) +if(HAVE_OPENEXR) + add_subdirectory(smallcolorselector) +endif() add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp @@ -194,8 +194,6 @@ void KisColorSelector::reset() { - KisColorSelectorBase::reset(); - if (m_mainComponent) { m_mainComponent->setDirty(); } @@ -203,6 +201,8 @@ if (m_subComponent) { m_subComponent->setDirty(); } + + KisColorSelectorBase::reset(); } void KisColorSelector::paintEvent(QPaintEvent* e) diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_component.h b/plugins/dockers/advancedcolorselector/kis_color_selector_component.h --- a/plugins/dockers/advancedcolorselector/kis_color_selector_component.h +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_component.h @@ -54,7 +54,7 @@ void setConfiguration(Parameter param, Type type); /// set the color, blibs etc - virtual void setColor(const KoColor& color) = 0; + virtual void setColor(const KoColor& color); /// force subsequent redraw of the component void setDirty(); @@ -93,6 +93,11 @@ /// values for the subclasses are provided in component coordinates, eg (0,0) is top left of component virtual bool containsPointInComponentCoords(int x, int y) const; + /// a subclass can implement this method to note that the point, although it is in + /// containsPointInComponentCoords area, still cannot be selected as a color (e.g. + /// it is masked out). Default implementation always returns true. + virtual bool allowsColorSelectionAtPoint(const QPoint &pt) const; + // Workaround for Bug 287001 void setLastMousePosition(int x, int y); @@ -120,6 +125,7 @@ int m_height; bool m_dirty; const KoColorSpace* m_lastColorSpace; + KoColor m_lastSelectedColor; }; #endif // KIS_COLOR_SELECTOR_COMPONENT_H diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp @@ -77,9 +77,11 @@ int newX=qBound(0, (x-m_x), width()); int newY=qBound(0, (y-m_y), height()); - selectColor(newX, newY); - m_lastX=newX; - m_lastY=newY; + if (allowsColorSelectionAtPoint(QPoint(x, y))) { + m_lastSelectedColor = selectColor(newX, newY); + m_lastX=newX; + m_lastY=newY; + } } const KoColorSpace* KisColorSelectorComponent::colorSpace() const @@ -92,6 +94,7 @@ void KisColorSelectorComponent::setDirty() { m_dirty = true; + setColor(m_lastSelectedColor); } void KisColorSelectorComponent::setGamutMask(KoGamutMask *gamutMask) @@ -133,6 +136,11 @@ return false; } +bool KisColorSelectorComponent::allowsColorSelectionAtPoint(const QPoint &pt) const +{ + return true; +} + KoColor KisColorSelectorComponent::currentColor() { return selectColor(m_lastX, m_lastY); @@ -230,6 +238,11 @@ m_type = type; } +void KisColorSelectorComponent::setColor(const KoColor &color) +{ + m_lastSelectedColor = color; +} + void KisColorSelectorComponent::setLastMousePosition(int x, int y) { // prevent movement due to rounding errors diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ring.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_ring.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector_ring.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ring.cpp @@ -117,7 +117,7 @@ m_lastHue=hue; emit update(); - return KoColor(); + return m_parent->converter()->fromHsvF(hue, 1.0, 1.0); } void KisColorSelectorRing::setColor(const KoColor &color) @@ -133,6 +133,8 @@ } emit update(); + + KisColorSelectorComponent::setColor(color); } void KisColorSelectorRing::paintCache() diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_simple.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_simple.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector_simple.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_simple.cpp @@ -257,6 +257,7 @@ emit update(); //Workaround for bug 317648 setLastMousePosition((m_lastClickPos.x()*width()), (m_lastClickPos.y()*height())); + KisColorSelectorComponent::setColor(color); } void KisColorSelectorSimple::paint(QPainter* painter) diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_triangle.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_triangle.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector_triangle.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_triangle.cpp @@ -136,6 +136,7 @@ emit paramChanged(-1, s, v, -1, -1, -1, -1, -1, -1); emit update(); + KisColorSelectorComponent::setColor(color); } int KisColorSelectorTriangle::triangleWidth() const diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.h b/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.h --- a/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.h +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.h @@ -43,14 +43,13 @@ protected: KoColor selectColor(int x, int y) override; void paint(QPainter*) override; - void mouseEvent(int x, int y) override; private: friend class Acs::PixelCacheRenderer; KoColor colorAt(int x, int y, bool forceValid = false); private: - bool coordIsClear(int x, int y); + bool allowsColorSelectionAtPoint(const QPoint &pt) const override; QPointF m_lastClickPos; QImage m_pixelCache; QPoint m_pixelCacheOffset; diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.cpp --- a/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_wheel.cpp @@ -177,6 +177,8 @@ setLastMousePosition(pos.x(), pos.y()); } + + KisColorSelectorComponent::setColor(color); } void KisColorSelectorWheel::paint(QPainter* painter) @@ -257,18 +259,6 @@ } } -void KisColorSelectorWheel::mouseEvent(int x, int y) -{ - int newX=qBound(0, (x-m_x), width()); - int newY=qBound(0, (y-m_y), height()); - - if (coordIsClear(newX,newY)) { - selectColor(newX, newY); - m_lastX=newX; - m_lastY=newY; - } -} - KoColor KisColorSelectorWheel::colorAt(int x, int y, bool forceValid) { KoColor color(Qt::transparent, m_parent->colorSpace()); @@ -327,15 +317,9 @@ return color; } -bool KisColorSelectorWheel::coordIsClear(int x, int y) +bool KisColorSelectorWheel::allowsColorSelectionAtPoint(const QPoint &pt) const { - bool retval = false; - if (m_gamutMaskOn && m_currentGamutMask) { - bool isClear = m_currentGamutMask->coordIsClear(m_toRenderArea.map(QPointF(x,y)), *m_viewConverter, m_maskPreviewActive); - retval = (isClear) ? true : false; - } else { - retval = true; - } - - return retval; + return !m_gamutMaskOn || !m_currentGamutMask || + m_currentGamutMask->coordIsClear(m_toRenderArea.map(QPointF(pt)), + *m_viewConverter, m_maskPreviewActive); } diff --git a/plugins/dockers/lut/lutdocker_dock.cpp b/plugins/dockers/lut/lutdocker_dock.cpp --- a/plugins/dockers/lut/lutdocker_dock.cpp +++ b/plugins/dockers/lut/lutdocker_dock.cpp @@ -17,6 +17,7 @@ */ #include "lutdocker_dock.h" +#include #include @@ -56,6 +57,8 @@ #include "ocio_display_filter.h" #include "black_white_point_chooser.h" +#include "KisOcioConfiguration.h" +#include OCIO::ConstConfigRcPtr defaultRawProfile() @@ -107,12 +110,12 @@ connect(m_chkUseOcio, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings())); connect(m_colorManagement, SIGNAL(currentIndexChanged(int)), SLOT(slotColorManagementModeChanged())); - m_txtConfigurationPath->setText(cfg.ocioConfigurationPath()); - m_bnSelectConfigurationFile->setToolTip(i18n("Select custom configuration file.")); connect(m_bnSelectConfigurationFile,SIGNAL(clicked()), SLOT(selectOcioConfiguration())); - m_txtLut->setText(cfg.ocioLutPath()); + KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); + m_txtConfigurationPath->setText(ocioOptions.configurationPath); + m_txtLut->setText(ocioOptions.lutPath); m_bnSelectLut->setToolTip(i18n("Select LUT file")); connect(m_bnSelectLut, SIGNAL(clicked()), SLOT(selectLut())); @@ -235,7 +238,19 @@ bool LutDockerDock::canChangeExposureAndGamma() const { - return m_chkUseOcio->isChecked() && m_ocioConfig; + if (!m_chkUseOcio->isChecked() || !m_ocioConfig) return false; + + const bool externalColorManagementEnabled = + m_colorManagement->currentIndex() != (int)KisOcioConfiguration::INTERNAL; + + const bool exposureManagementEnabled = + externalColorManagementEnabled +#ifdef HAVE_HDR + || KisOpenGLModeProber::instance()->surfaceformatInUse().colorSpace() == QSurfaceFormat::scRGBColorSpace +#endif + ; + + return exposureManagementEnabled; } qreal LutDockerDock::currentExposure() const @@ -345,14 +360,14 @@ if (!canDoExternalColorCorrection) { KisSignalsBlocker colorManagementBlocker(m_colorManagement); Q_UNUSED(colorManagementBlocker); - m_colorManagement->setCurrentIndex((int) KisConfig::INTERNAL); + m_colorManagement->setCurrentIndex((int) KisOcioConfiguration::INTERNAL); } - bool ocioEnabled = m_chkUseOcio->isChecked(); + const bool ocioEnabled = m_chkUseOcio->isChecked(); m_colorManagement->setEnabled(ocioEnabled && canDoExternalColorCorrection); - bool externalColorManagementEnabled = - m_colorManagement->currentIndex() != (int)KisConfig::INTERNAL; + const bool externalColorManagementEnabled = + m_colorManagement->currentIndex() != (int)KisOcioConfiguration::INTERNAL; m_lblInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled); @@ -363,7 +378,24 @@ m_lblLook->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbLook->setEnabled(ocioEnabled && externalColorManagementEnabled); - bool enableConfigPath = m_colorManagement->currentIndex() == (int) KisConfig::OCIO_CONFIG; + const bool exposureManagementEnabled = canChangeExposureAndGamma(); + + m_exposureDoubleWidget->setEnabled(exposureManagementEnabled); + m_gammaDoubleWidget->setEnabled(exposureManagementEnabled); + m_lblExposure->setEnabled(exposureManagementEnabled); + m_lblGamma->setEnabled(exposureManagementEnabled); + + QString exposureToolTip; + + if (!exposureManagementEnabled) { + exposureToolTip = i18nc("@info:tooltip", "Exposure and Gamma corrections are disabled in Internal mode. Switch to OCIO mode to use them"); + } + m_exposureDoubleWidget->setToolTip(exposureToolTip); + m_gammaDoubleWidget->setToolTip(exposureToolTip); + m_lblExposure->setToolTip(exposureToolTip); + m_lblGamma->setToolTip(exposureToolTip); + + bool enableConfigPath = m_colorManagement->currentIndex() == (int) KisOcioConfiguration::OCIO_CONFIG; lblConfig->setEnabled(ocioEnabled && enableConfigPath); m_txtConfigurationPath->setEnabled(ocioEnabled && enableConfigPath); @@ -396,15 +428,15 @@ displayFilter->displayDevice = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex()); displayFilter->view = m_ocioConfig->getView(displayFilter->displayDevice, m_cmbView->currentIndex()); displayFilter->look = m_ocioConfig->getLookNameByIndex(m_cmbLook->currentIndex()); - displayFilter->gamma = m_gammaDoubleWidget->value(); - displayFilter->exposure = m_exposureDoubleWidget->value(); + displayFilter->gamma = m_gammaDoubleWidget->isEnabled() ? m_gammaDoubleWidget->value() : 1.0; + displayFilter->exposure = m_exposureDoubleWidget->isEnabled() ? m_exposureDoubleWidget->value() : 0.0; displayFilter->swizzle = (OCIO_CHANNEL_SWIZZLE)m_cmbComponents->currentIndex(); displayFilter->blackPoint = m_bwPointChooser->blackPoint(); displayFilter->whitePoint = m_bwPointChooser->whitePoint(); displayFilter->forceInternalColorManagement = - m_colorManagement->currentIndex() == (int)KisConfig::INTERNAL; + m_colorManagement->currentIndex() == (int)KisOcioConfiguration::INTERNAL; displayFilter->setLockCurrentColorVisualRepresentation(m_btnConvertCurrentColor->isChecked()); @@ -419,10 +451,18 @@ void LutDockerDock::writeControls() { - KisConfig cfg(true); - + KisOcioConfiguration ocioOptions; + ocioOptions.mode = (KisOcioConfiguration::Mode)m_colorManagement->currentIndex(); + ocioOptions.configurationPath = m_txtConfigurationPath->text(); + ocioOptions.lutPath = m_txtLut->text(); + ocioOptions.inputColorSpace = m_cmbInputColorSpace->itemHighlighted(); + ocioOptions.displayDevice = m_cmbDisplayDevice->itemHighlighted(); + ocioOptions.displayView = m_cmbView->itemHighlighted(); + ocioOptions.look = m_cmbLook->itemHighlighted(); + + KisConfig cfg(false); cfg.setUseOcio(m_chkUseOcio->isChecked()); - cfg.setOcioColorManagementMode((KisConfig::OcioColorManagementMode) m_colorManagement->currentIndex()); + cfg.setOcioConfiguration(ocioOptions); cfg.setOcioLockColorVisualRepresentation(m_btnConvertCurrentColor->isChecked()); } @@ -445,8 +485,6 @@ QFile f(filename); if (f.exists()) { m_txtConfigurationPath->setText(filename); - KisConfig cfg(false); - cfg.setOcioConfigurationPath(filename); writeControls(); resetOcioConfiguration(); } @@ -455,16 +493,17 @@ void LutDockerDock::resetOcioConfiguration() { KisConfig cfg(true); + KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); m_ocioConfig.reset(); try { - if (cfg.ocioColorManagementMode() == KisConfig::INTERNAL) { + if (ocioOptions.mode == KisOcioConfiguration::INTERNAL) { m_ocioConfig = defaultRawProfile(); - } else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_ENVIRONMENT) { + } else if (ocioOptions.mode == KisOcioConfiguration::OCIO_ENVIRONMENT) { m_ocioConfig = OCIO::Config::CreateFromEnv(); } - else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_CONFIG) { - QString configFile = cfg.ocioConfigurationPath(); + else if (ocioOptions.mode == KisOcioConfiguration::OCIO_CONFIG) { + QString configFile = ocioOptions.configurationPath; if (QFile::exists(configFile)) { m_ocioConfig = OCIO::Config::CreateFromFile(configFile.toUtf8()); @@ -495,10 +534,12 @@ KIS_ASSERT_RECOVER_RETURN(m_ocioConfig); + KisConfig cfg(true); + KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); + { // Color Management Mode - KisConfig cfg(true); KisSignalsBlocker modeBlocker(m_colorManagement); - m_colorManagement->setCurrentIndex((int) cfg.ocioColorManagementMode()); + m_colorManagement->setCurrentIndex((int) ocioOptions.mode); } { // Exposure @@ -537,10 +578,13 @@ itemsList << QString::fromUtf8(colorSpace->getName()); } + KisSignalsBlocker inputCSBlocker(m_cmbInputColorSpace); + if (itemsList != m_cmbInputColorSpace->originalTexts()) { - KisSignalsBlocker inputCSBlocker(m_cmbInputColorSpace); m_cmbInputColorSpace->resetOriginalTexts(itemsList); } + + m_cmbInputColorSpace->setCurrent(ocioOptions.inputColorSpace); } { // Display Device @@ -550,15 +594,17 @@ itemsList << QString::fromUtf8(m_ocioConfig->getDisplay(i)); } + KisSignalsBlocker displayDeviceLocker(m_cmbDisplayDevice); + if (itemsList != m_cmbDisplayDevice->originalTexts()) { - KisSignalsBlocker displayDeviceLocker(m_cmbDisplayDevice); m_cmbDisplayDevice->resetOriginalTexts(itemsList); } + + m_cmbDisplayDevice->setCurrent(ocioOptions.displayDevice); } { // Lock Current Color KisSignalsBlocker locker(m_btnConvertCurrentColor); - KisConfig cfg(true); m_btnConvertCurrentColor->setChecked(cfg.ocioLockColorVisualRepresentation()); } @@ -572,10 +618,13 @@ } itemsList << i18nc("Item to indicate no look transform being selected","None"); + KisSignalsBlocker LookComboLocker(m_cmbLook); + if (itemsList != m_cmbLook->originalTexts()) { - KisSignalsBlocker LookComboLocker(m_cmbLook); m_cmbLook->resetOriginalTexts(itemsList); } + + m_cmbLook->setCurrent(ocioOptions.look); } updateDisplaySettings(); } @@ -593,6 +642,10 @@ for (int j = 0; j < numViews; ++j) { m_cmbView->addSqueezedItem(QString::fromUtf8(m_ocioConfig->getView(display, j))); } + + KisConfig cfg(true); + KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); + m_cmbView->setCurrent(ocioOptions.displayView); } void LutDockerDock::selectLut() @@ -608,8 +661,7 @@ QFile f(filename); if (f.exists() && filename != m_txtLut->text()) { m_txtLut->setText(filename); - KisConfig cfg(false); - cfg.setOcioLutPath(filename); + writeControls(); updateDisplaySettings(); } } diff --git a/plugins/dockers/lut/ocio_display_filter.cpp b/plugins/dockers/lut/ocio_display_filter.cpp --- a/plugins/dockers/lut/ocio_display_filter.cpp +++ b/plugins/dockers/lut/ocio_display_filter.cpp @@ -267,7 +267,12 @@ bool OcioDisplayFilter::updateShader() { - if (KisOpenGL::hasOpenGL3()) { + if (KisOpenGL::hasOpenGLES()) { + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + if (f) { + return updateShaderImpl(f); + } + } else if (KisOpenGL::hasOpenGL3()) { QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions(); if (f) { return updateShaderImpl(f); diff --git a/plugins/dockers/lut/wdglut.ui b/plugins/dockers/lut/wdglut.ui --- a/plugins/dockers/lut/wdglut.ui +++ b/plugins/dockers/lut/wdglut.ui @@ -130,7 +130,7 @@ - + Exposure: @@ -261,7 +261,7 @@ - + Gamma: diff --git a/plugins/dockers/smallcolorselector/CMakeLists.txt b/plugins/dockers/smallcolorselector/CMakeLists.txt --- a/plugins/dockers/smallcolorselector/CMakeLists.txt +++ b/plugins/dockers/smallcolorselector/CMakeLists.txt @@ -1,4 +1,18 @@ -set(KRITA_smallCOLORSELECTOR_SOURCES smallcolorselector.cc smallcolorselector_dock.cc kis_small_color_widget.cc ) +set(KRITA_smallCOLORSELECTOR_SOURCES + smallcolorselector.cc + smallcolorselector_dock.cc + kis_small_color_widget.cc + KisGLImageF16.cpp + KisGLImageWidget.cpp + KisClickableGLImageWidget.cpp + ) + +set(small_color_selector_QRCS + ${CMAKE_CURRENT_SOURCE_DIR}/kis_gl_image_widget.qrc + CACHE INTERNAL "small_color_selector_QRCS" +) +qt5_add_resources(KRITA_smallCOLORSELECTOR_SOURCES ${small_color_selector_QRCS}) + add_library(kritasmallcolorselector MODULE ${KRITA_smallCOLORSELECTOR_SOURCES}) target_link_libraries(kritasmallcolorselector kritaui) install(TARGETS kritasmallcolorselector DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.h b/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISCLICKABLEGLIMAGEWIDGET_H +#define KISCLICKABLEGLIMAGEWIDGET_H + +#include +#include + + +class KisClickableGLImageWidget : public KisGLImageWidget +{ + Q_OBJECT +public: + struct HandlePaintingStrategy { + virtual void drawHandle(QPainter *p, const QPointF &normalizedPoint, const QRect &rect, bool useOpacity) = 0; + virtual ~HandlePaintingStrategy() {} + }; + + struct VerticalLineHandleStrategy : public HandlePaintingStrategy { + void drawHandle(QPainter *p, const QPointF &normalizedPoint, const QRect &rect, bool useOpacity) override; + }; + + struct CircularHandleStrategy : public HandlePaintingStrategy { + void drawHandle(QPainter *p, const QPointF &normalizedPoint, const QRect &rect, bool useOpacity) override; + }; + +public: + KisClickableGLImageWidget(QWidget *parent = nullptr); + KisClickableGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, + QWidget *parent = nullptr); + ~KisClickableGLImageWidget(); + + void setHandlePaintingStrategy(HandlePaintingStrategy *strategy); + void setUseHandleOpacity(bool value); + + QPointF normalizedPos() const; + void setNormalizedPos(const QPointF &pos, bool update = true); + + void paintEvent(QPaintEvent *event) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +Q_SIGNALS: + void selected(const QPointF &normalizedPos); + +private: + QPointF normalizePoint(const QPointF &pos) const; + +private: + QPointF m_normalizedClickPoint; + QScopedPointer m_handleStrategy; + bool m_useHandleOpacity = true; +}; + +#endif // KISCLICKABLEGLIMAGEWIDGET_H diff --git a/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.cpp b/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisClickableGLImageWidget.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisClickableGLImageWidget.h" + +#include +#include +#include "kis_algebra_2d.h" +#include + +KisClickableGLImageWidget::KisClickableGLImageWidget(QWidget *parent) + : KisGLImageWidget(parent) +{ +} + +KisClickableGLImageWidget::KisClickableGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, QWidget *parent) + : KisGLImageWidget(colorSpace, parent) +{ +} + +KisClickableGLImageWidget::~KisClickableGLImageWidget() +{ +} + +void KisClickableGLImageWidget::setHandlePaintingStrategy(HandlePaintingStrategy *strategy) +{ + m_handleStrategy.reset(strategy); +} + +void KisClickableGLImageWidget::setUseHandleOpacity(bool value) +{ + m_useHandleOpacity = value; + update(); +} + +QPointF KisClickableGLImageWidget::normalizedPos() const +{ + return m_normalizedClickPoint; +} + +void KisClickableGLImageWidget::setNormalizedPos(const QPointF &pos, bool update) +{ + m_normalizedClickPoint = KisAlgebra2D::clampPoint(pos, QRectF(0,0,1.0,1.0)); + if (update) { + this->update(); + } +} + +void KisClickableGLImageWidget::paintEvent(QPaintEvent *event) +{ + KisGLImageWidget::paintEvent(event); + + if (m_handleStrategy) { + QPainter p(this); + m_handleStrategy->drawHandle(&p, m_normalizedClickPoint, rect(), m_useHandleOpacity); + } +} + +void KisClickableGLImageWidget::mousePressEvent(QMouseEvent *event) +{ + KisGLImageWidget::mousePressEvent(event); + + if (!event->isAccepted()) { + event->accept(); + m_normalizedClickPoint = normalizePoint(event->localPos()); + emit selected(m_normalizedClickPoint); + + if (m_handleStrategy) { + update(); + } + } +} + +void KisClickableGLImageWidget::mouseReleaseEvent(QMouseEvent *event) +{ + KisGLImageWidget::mouseReleaseEvent(event); + + if (!event->isAccepted()) { + event->accept(); + m_normalizedClickPoint = normalizePoint(event->localPos()); + emit selected(m_normalizedClickPoint); + + if (m_handleStrategy) { + update(); + } + } +} + +void KisClickableGLImageWidget::mouseMoveEvent(QMouseEvent *event) +{ + KisGLImageWidget::mouseMoveEvent(event); + + if (!event->isAccepted()) { + event->accept(); + m_normalizedClickPoint = normalizePoint(event->localPos()); + emit selected(m_normalizedClickPoint); + + if (m_handleStrategy) { + update(); + } + } +} + +QPointF KisClickableGLImageWidget::normalizePoint(const QPointF &pos) const +{ + const QPointF croppedPoint = KisAlgebra2D::clampPoint(pos, rect()); + return QPointF(croppedPoint.x() / width(), croppedPoint.y() / height()); +} + + +namespace { +QPen outerHandlePen(bool useOpacity) { + // opacity works inexpectedly in HDR mode, so let the user switch it off + return QPen(QColor(0, 0, 0, useOpacity ? 180 : 255), 0); +} +QPen innerHandlePen(bool useOpacity) { + // opacity works inexpectedly in HDR mode, so let the user switch it off + return QPen(QColor(255, 255, 255, useOpacity ? 180 : 255), 0); +} +} + +void KisClickableGLImageWidget::VerticalLineHandleStrategy::drawHandle(QPainter *p, const QPointF &normalizedPoint, const QRect &rect, bool useOpacity) +{ + const QPointF pos = KisAlgebra2D::relativeToAbsolute(normalizedPoint, rect); + const int x = std::floor(pos.x()); + + p->setPen(outerHandlePen(useOpacity)); + p->drawLine(x, rect.top(), x, rect.bottom()); + p->setPen(innerHandlePen(useOpacity)); + p->drawLine(x + 1, rect.top(), x + 1, rect.bottom()); +} + +void KisClickableGLImageWidget::CircularHandleStrategy::drawHandle(QPainter *p, const QPointF &normalizedPoint, const QRect &rect, bool useOpacity) +{ + const QPointF pos = KisAlgebra2D::relativeToAbsolute(normalizedPoint, rect); + + p->setRenderHint(QPainter::Antialiasing); + p->setPen(outerHandlePen(useOpacity)); + p->drawEllipse(pos, 5, 5); + + p->setPen(innerHandlePen(useOpacity)); + p->drawEllipse(pos, 4, 4); +} diff --git a/plugins/dockers/smallcolorselector/KisGLImageF16.h b/plugins/dockers/smallcolorselector/KisGLImageF16.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisGLImageF16.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISGLIMAGEF16_H +#define KISGLIMAGEF16_H + +#include +#include +#include + +class QSize; + +class KisGLImageF16 : public boost::equality_comparable +{ +public: + KisGLImageF16(); + KisGLImageF16(const QSize &size, bool clearPixels = false); + KisGLImageF16(int width, int height, bool clearPixels = false); + KisGLImageF16(const KisGLImageF16 &rhs); + KisGLImageF16& operator=(const KisGLImageF16 &rhs); + + friend bool operator==(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs); + + ~KisGLImageF16(); + + void clearPixels(); + void resize(const QSize &size, bool clearPixels = false); + + const half* constData() const; + half* data(); + + QSize size() const; + int width() const; + int height() const; + + bool isNull() const; + +private: + struct Private; + QSharedDataPointer m_d; +}; + +#endif // KISGLIMAGEF16_H diff --git a/plugins/dockers/smallcolorselector/KisGLImageF16.cpp b/plugins/dockers/smallcolorselector/KisGLImageF16.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisGLImageF16.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisGLImageF16.h" + +#include +#include + +struct KisGLImageF16::Private : public QSharedData +{ + QSize size; + QByteArray data; +}; + + + +KisGLImageF16::KisGLImageF16() + : m_d(new Private) +{ +} + +KisGLImageF16::KisGLImageF16(const QSize &size, bool clearPixels) + : m_d(new Private) +{ + resize(size, clearPixels); +} + +KisGLImageF16::KisGLImageF16(int width, int height, bool clearPixels) + : KisGLImageF16(QSize(width, height), clearPixels) +{ +} + +KisGLImageF16::KisGLImageF16(const KisGLImageF16 &rhs) + : m_d(rhs.m_d) +{ +} + +KisGLImageF16 &KisGLImageF16::operator=(const KisGLImageF16 &rhs) +{ + m_d = rhs.m_d; + return *this; +} + +bool operator==(const KisGLImageF16 &lhs, const KisGLImageF16 &rhs) +{ + return lhs.m_d == rhs.m_d; +} + +KisGLImageF16::~KisGLImageF16() +{ +} + +void KisGLImageF16::clearPixels() +{ + if (!m_d->data.isEmpty()) { + m_d->data.fill(0); + } +} + +void KisGLImageF16::resize(const QSize &size, bool clearPixels) +{ + const int pixelSize = 2 * 4; + + m_d->size = size; + m_d->data.resize(size.width() * size.height() * pixelSize); + + if (clearPixels) { + m_d->data.fill(0); + } +} + +const half *KisGLImageF16::constData() const +{ + Q_ASSERT(!m_d->data.isNull()); + return reinterpret_cast(m_d->data.data()); +} + +half *KisGLImageF16::data() +{ + m_d->data.detach(); + Q_ASSERT(!m_d->data.isNull()); + + return reinterpret_cast(m_d->data.data()); +} + +QSize KisGLImageF16::size() const +{ + return m_d->size; +} + +int KisGLImageF16::width() const +{ + return m_d->size.width(); +} + +int KisGLImageF16::height() const +{ + return m_d->size.height(); +} + +bool KisGLImageF16::isNull() const +{ + return m_d->data.isNull(); +} diff --git a/plugins/dockers/smallcolorselector/KisGLImageWidget.h b/plugins/dockers/smallcolorselector/KisGLImageWidget.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisGLImageWidget.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISGLIMAGEWIDGET_H +#define KISGLIMAGEWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +class KisGLImageWidget : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT +public: + KisGLImageWidget(QWidget *parent = nullptr); + KisGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, + QWidget *parent = nullptr); + + void initializeGL(); + void paintGL(); + + void loadImage(const KisGLImageF16 &image); + + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + + QSize sizeHint() const; + +public Q_SLOTS: + +private Q_SLOTS: + void slotOpenGLContextDestroyed(); + +private: + void updateVerticesBuffer(const QRect &rect); + +private: + KisGLImageF16 m_sourceImage; + + QScopedPointer m_shader; + QOpenGLVertexArrayObject m_vao; + QOpenGLBuffer m_verticesBuffer; + QOpenGLBuffer m_textureVerticesBuffer; + QOpenGLTexture m_texture; + + bool m_havePendingTextureUpdate = false; +}; + +#endif // KISGLIMAGEWIDGET_H diff --git a/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp b/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisGLImageWidget.h" + +#include +#include +#include +#include "kis_debug.h" +#include + +#include "KisGLImageF16.h" + +namespace { +inline void rectToVertices(QVector3D* vertices, const QRectF &rc) +{ + vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); + vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); + vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); + vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); + vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); + vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); +} + +inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) +{ + texCoords[0] = QVector2D(rc.left(), rc.bottom()); + texCoords[1] = QVector2D(rc.left(), rc.top()); + texCoords[2] = QVector2D(rc.right(), rc.bottom()); + texCoords[3] = QVector2D(rc.left(), rc.top()); + texCoords[4] = QVector2D(rc.right(), rc.top()); + texCoords[5] = QVector2D(rc.right(), rc.bottom()); +} +} + +KisGLImageWidget::KisGLImageWidget(QWidget *parent) + : KisGLImageWidget(QSurfaceFormat::sRGBColorSpace, parent) +{ +} + +KisGLImageWidget::KisGLImageWidget(QSurfaceFormat::ColorSpace colorSpace, + QWidget *parent) + : QOpenGLWidget(parent), + m_texture(QOpenGLTexture::Target2D) +{ + setTextureFormat(GL_RGBA16F); + +#ifdef HAVE_HDR + setTextureColorSpace(colorSpace); +#endif + + setUpdateBehavior(QOpenGLWidget::NoPartialUpdate); +} + +void KisGLImageWidget::initializeGL() +{ + initializeOpenGLFunctions(); + + connect(context(), SIGNAL(aboutToBeDestroyed()), SLOT(slotOpenGLContextDestroyed())); + m_shader.reset(new QOpenGLShaderProgram); + + QFile vertexShaderFile(QString(":/") + "kis_gl_image_widget.vert"); + vertexShaderFile.open(QIODevice::ReadOnly); + QString vertSource = vertexShaderFile.readAll(); + + QFile fragShaderFile(QString(":/") + "kis_gl_image_widget.frag"); + fragShaderFile.open(QIODevice::ReadOnly); + QString fragSource = fragShaderFile.readAll(); + + if (context()->isOpenGLES()) { + const char *versionHelper = "#define USE_OPENGLES\n"; + vertSource.prepend(versionHelper); + fragSource.prepend(versionHelper); + + const char *versionDefinition = "#version 100\n"; + vertSource.prepend(versionDefinition); + fragSource.prepend(versionDefinition); + } else { + const char *versionDefinition = "#version 330 core\n"; + vertSource.prepend(versionDefinition); + fragSource.prepend(versionDefinition); + } + + if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource)) { + qDebug() << "Could not add vertex code"; + return; + } + + if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) { + qDebug() << "Could not add fragment code"; + return; + } + + if (!m_shader->link()) { + qDebug() << "Could not link"; + return; + } + + if (!m_shader->bind()) { + qDebug() << "Could not bind"; + return; + } + + m_shader->release(); + + + m_vao.create(); + m_vao.bind(); + + m_verticesBuffer.create(); + updateVerticesBuffer(this->rect()); + + QVector textureVertices(6); + rectToTexCoords(textureVertices.data(), QRect(0.0, 0.0, 1.0, 1.0)); + + m_textureVerticesBuffer.create(); + m_textureVerticesBuffer.bind(); + m_textureVerticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + m_textureVerticesBuffer.allocate(2 * 3 * sizeof(QVector2D)); + m_verticesBuffer.write(0, textureVertices.data(), m_textureVerticesBuffer.size()); + m_textureVerticesBuffer.release(); + + m_vao.release(); + + + if (!m_sourceImage.isNull()) { + loadImage(m_sourceImage); + } +} + +void KisGLImageWidget::slotOpenGLContextDestroyed() +{ + this->makeCurrent(); + + m_shader.reset(); + m_texture.destroy(); + m_verticesBuffer.destroy(); + m_textureVerticesBuffer.destroy(); + m_vao.destroy(); + m_havePendingTextureUpdate = false; + + this->doneCurrent(); +} + +void KisGLImageWidget::updateVerticesBuffer(const QRect &rect) +{ + if (!m_vao.isCreated() || !m_verticesBuffer.isCreated()) return; + + QVector vertices(6); + rectToVertices(vertices.data(), rect); + + m_verticesBuffer.bind(); + m_verticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + m_verticesBuffer.allocate(2 * 3 * sizeof(QVector3D)); + m_verticesBuffer.write(0, vertices.data(), m_verticesBuffer.size()); + m_verticesBuffer.release(); +} + + +void KisGLImageWidget::paintGL() +{ + const QColor bgColor = palette().background().color(); + // TODO: fix conversion to the destination surface space + //glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), 1.0f); + glClearColor(0.3, 0.2, 0.8, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + + + if (m_havePendingTextureUpdate) { + m_havePendingTextureUpdate = false; + + if (!m_texture.isCreated() || + m_sourceImage.width() != m_texture.width() || + m_sourceImage.height() != m_texture.height()) { + + if (m_texture.isCreated()) { + m_texture.destroy(); + } + + m_texture.setFormat(QOpenGLTexture::RGBA16F); + m_texture.setSize(m_sourceImage.width(), m_sourceImage.height()); + m_texture.allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float16); + m_texture.setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); + m_texture.setMagnificationFilter(QOpenGLTexture::Linear); + m_texture.setWrapMode(QOpenGLTexture::ClampToEdge); + } + + m_texture.setData(QOpenGLTexture::RGBA, QOpenGLTexture::Float16, m_sourceImage.constData()); + } + + if (!m_texture.isCreated()) return; + + m_vao.bind(); + m_shader->bind(); + + { + QMatrix4x4 projectionMatrix; + projectionMatrix.setToIdentity(); + projectionMatrix.ortho(0, width(), height(), 0, -1, 1); + QMatrix4x4 viewProjectionMatrix; + + // use a QTransform to scale, translate, rotate your view + QTransform transform; // TODO: noop! + viewProjectionMatrix = projectionMatrix * QMatrix4x4(transform); + + m_shader->setUniformValue("viewProjectionMatrix", viewProjectionMatrix); + } + + m_shader->enableAttributeArray("vertexPosition"); + m_verticesBuffer.bind(); + m_shader->setAttributeBuffer("vertexPosition", GL_FLOAT, 0, 3); + + m_shader->enableAttributeArray("texturePosition"); + m_textureVerticesBuffer.bind(); + m_shader->setAttributeBuffer("texturePosition", GL_FLOAT, 0, 2); + + glActiveTexture(GL_TEXTURE0); + m_texture.bind(); + + // draw 2 triangles = 6 vertices starting at offset 0 in the buffer + glDrawArrays(GL_TRIANGLES, 0, 6); + + m_verticesBuffer.release(); + m_textureVerticesBuffer.release(); + m_texture.release(); + m_shader->release(); + m_vao.release(); +} + +void KisGLImageWidget::loadImage(const KisGLImageF16 &image) +{ + if (m_sourceImage != image) { + m_sourceImage = image; + } + + m_havePendingTextureUpdate = true; + + + updateGeometry(); + update(); +} + +void KisGLImageWidget::paintEvent(QPaintEvent *event) +{ + QOpenGLWidget::paintEvent(event); +} + +void KisGLImageWidget::resizeEvent(QResizeEvent *event) +{ + updateVerticesBuffer(QRect(QPoint(),event->size())); + QOpenGLWidget::resizeEvent(event); +} + +QSize KisGLImageWidget::sizeHint() const +{ + return m_sourceImage.size(); +} + diff --git a/plugins/dockers/smallcolorselector/kis_gl_image_widget.frag b/plugins/dockers/smallcolorselector/kis_gl_image_widget.frag new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/kis_gl_image_widget.frag @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef USE_OPENGLES +#define INATTR in +#define OUTATTR out +#define DECLARE_OUT_VAR out vec4 f_fragColor; +#define OUT_VAR f_fragColor +#define highp +#define texture2D texture +#else +#define INATTR varying +#define DECLARE_OUT_VAR +#define OUT_VAR gl_FragColor +#endif +// vertices datas +INATTR highp vec4 textureCoordinates; +uniform sampler2D f_tileTexture; +DECLARE_OUT_VAR + +void main() +{ + // get the fragment color from the tile texture + highp vec4 color = texture2D(f_tileTexture, textureCoordinates.st); + OUT_VAR = vec4(color); +} diff --git a/plugins/dockers/smallcolorselector/kis_gl_image_widget.qrc b/plugins/dockers/smallcolorselector/kis_gl_image_widget.qrc new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/kis_gl_image_widget.qrc @@ -0,0 +1,6 @@ + + + kis_gl_image_widget.frag + kis_gl_image_widget.vert + + diff --git a/plugins/dockers/smallcolorselector/kis_gl_image_widget.vert b/plugins/dockers/smallcolorselector/kis_gl_image_widget.vert new file mode 100644 --- /dev/null +++ b/plugins/dockers/smallcolorselector/kis_gl_image_widget.vert @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef USE_OPENGLES +#define INATTR in +#define OUTATTR out +#define highp +#else +#define INATTR attribute +#define OUTATTR varying +#endif +uniform mat4 viewProjectionMatrix; +INATTR highp vec3 vertexPosition; +INATTR highp vec2 texturePosition; +OUTATTR highp vec4 textureCoordinates; +void main() +{ + textureCoordinates = vec4(texturePosition.x, texturePosition.y, 0.0, 1.0); + gl_Position = viewProjectionMatrix * vec4(vertexPosition.x, vertexPosition.y, 0.0, 1.0); +} diff --git a/plugins/dockers/smallcolorselector/kis_small_color_widget.h b/plugins/dockers/smallcolorselector/kis_small_color_widget.h --- a/plugins/dockers/smallcolorselector/kis_small_color_widget.h +++ b/plugins/dockers/smallcolorselector/kis_small_color_widget.h @@ -20,6 +20,10 @@ #include +class KoColor; +class KisDisplayColorConverter; +class KisGLImageWidget; + class KisSmallColorWidget : public QWidget { Q_OBJECT @@ -27,28 +31,43 @@ KisSmallColorWidget(QWidget* parent); ~KisSmallColorWidget() override; public: - void paintEvent(QPaintEvent * event) override; void resizeEvent(QResizeEvent * event) override; - void mouseReleaseEvent(QMouseEvent * event) override; - void mousePressEvent(QMouseEvent * event) override; - void mouseMoveEvent(QMouseEvent * event) override; + + void setDisplayColorConverter(KisDisplayColorConverter *converter); + public: - int hue() const; - int value() const; - int saturation() const; - QColor color() const; + public Q_SLOTS: - void setHue(int h); - void setHSV(int h, int s, int v); - void setQColor(const QColor&); + void setHue(qreal h); + void setHSV(qreal h, qreal s, qreal v, bool notifyChanged = true); + void setColor(const KoColor &color); + + void slotUpdatePalettes(); + void updateSVPalette(); + Q_SIGNALS: - void colorChanged(const QColor&); + void colorChanged(const KoColor&); + + void sigTellColorChangedInternal(); + +private Q_SLOTS: + void slotHueSliderChanged(const QPointF &pos); + void slotValueSliderChanged(const QPointF &pos); + void slotInitiateUpdateDynamicRange(int maxLuminance); + void slotDisplayConfigurationChanged(); + void slotTellColorChanged(); + +private: + void updateDynamicRange(int maxLuminance); + private: - void tellColorChanged(); - void updateParameters(); - void generateRubber(); - void generateSquare(); - void selectColorAt(int _x, int _y); + + void updateHuePalette(); + + template + void uploadPaletteData(KisGLImageWidget *widget, const QSize &size); + + private: struct Private; Private* const d; diff --git a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc --- a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc +++ b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc @@ -16,243 +16,501 @@ */ #include "kis_small_color_widget.h" -#include -#include -#include #include +#include "kis_slider_spin_box.h" +#include +#include "kis_signal_compressor.h" +#include #include +#include + +#include "kis_debug.h" +#include "kis_assert.h" + +#include +#include "KisGLImageF16.h" +#include "KisGLImageWidget.h" +#include "KisClickableGLImageWidget.h" +#include "kis_display_color_converter.h" +#include "kis_signal_auto_connection.h" +#include "kis_signal_compressor_with_param.h" + +#include +#include +#include "kis_fixed_paint_device.h" +#include -enum CurrentHandle { - NoHandle, - HueHandle, - ValueSaturationHandle -}; struct KisSmallColorWidget::Private { - QPixmap rubberPixmap; - QPixmap squarePixmap; - double rectangleWidthProportion; - int rectangleHeight; - int rectangleWidth; - int rubberWidth; - int rubberHeight; - int margin; - int hue; - int value; - int saturation; + qreal hue; // 0 ... 1.0 + qreal value; // 0 ... 1.0 + qreal saturation; // 0 ... 1.0 bool updateAllowed; - double squareHandleSize; - CurrentHandle handle; - int lastX, lastY; - QTimer updateTimer; + KisClickableGLImageWidget *hueWidget; + KisClickableGLImageWidget *valueWidget; + KisSignalCompressor *repaintCompressor; + KisSignalCompressor *resizeUpdateCompressor; + KisSignalCompressor *valueSliderUpdateCompressor; + KisSignalCompressor *colorChangedSignalCompressor; + KisSignalCompressorWithParam *dynamicRangeCompressor; + int huePreferredHeight = 32; + KisSliderSpinBox *dynamicRange = 0; + qreal currentRelativeDynamicRange = 1.0; + KisDisplayColorConverter *displayColorConverter = KisDisplayColorConverter::dumbConverterInstance(); + KisSignalAutoConnectionsStore colorConverterConnections; + bool hasHDR = false; + bool hasHardwareHDR = false; + + qreal effectiveRelativeDynamicRange() const { + return hasHDR ? currentRelativeDynamicRange : 1.0; + } + + const KoColorSpace *outputColorSpace() { + return + KoColorSpaceRegistry::instance()-> + colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), + displayColorConverter->openGLCanvasSurfaceProfile()); + } + + const KoColorSpace *generationColorSpace() { + const KoColorSpace *result = displayColorConverter->paintingColorSpace(); + + if (!result || result->colorModelId() != RGBAColorModelID) { + result = outputColorSpace(); + } else if (result->colorDepthId() != Float32BitsColorDepthID) { + result = KoColorSpaceRegistry::instance()-> + colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), result->profile()); + } + + // PQ color space we deliniearize into linear one + if (result->colorModelId() == RGBAColorModelID && + result->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()) { + + result = KoColorSpaceRegistry::instance()-> + colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020G10Profile()); + } + + return result; + } }; -KisSmallColorWidget::KisSmallColorWidget(QWidget* parent) : QWidget(parent), d(new Private) +KisSmallColorWidget::KisSmallColorWidget(QWidget* parent) + : QWidget(parent), + d(new Private) { - setMinimumHeight(50); - d->hue = 0; + d->hue = 0.0; d->value = 0; d->saturation = 0; d->updateAllowed = true; - d->handle = NoHandle; - updateParameters(); - d->lastX = -1; - d->lastY = -1; - d->updateTimer.setInterval(1); - d->updateTimer.setSingleShot(true); - connect(&(d->updateTimer), SIGNAL(timeout()), this, SLOT(update())); -} -KisSmallColorWidget::~KisSmallColorWidget() -{ - delete d; -} + d->repaintCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_ACTIVE, this); + connect(d->repaintCompressor, SIGNAL(timeout()), SLOT(update())); -int KisSmallColorWidget::hue() const -{ - return d->hue; -} + d->resizeUpdateCompressor = new KisSignalCompressor(200, KisSignalCompressor::FIRST_ACTIVE, this); + connect(d->resizeUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdatePalettes())); -int KisSmallColorWidget::value() const -{ - return d->value; -} + d->valueSliderUpdateCompressor = new KisSignalCompressor(100, KisSignalCompressor::FIRST_ACTIVE, this); + connect(d->valueSliderUpdateCompressor, SIGNAL(timeout()), SLOT(updateSVPalette())); -int KisSmallColorWidget::saturation() const -{ - return d->saturation; + d->colorChangedSignalCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_ACTIVE, this); + connect(d->colorChangedSignalCompressor, SIGNAL(timeout()), SLOT(slotTellColorChanged())); + + { + using namespace std::placeholders; + std::function callback( + std::bind(&KisSmallColorWidget::updateDynamicRange, this, _1)); + d->dynamicRangeCompressor = new KisSignalCompressorWithParam(50, callback); + + } + + const QSurfaceFormat::ColorSpace colorSpace = QSurfaceFormat::DefaultColorSpace; + + d->hueWidget = new KisClickableGLImageWidget(colorSpace, this); + d->hueWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + d->hueWidget->setHandlePaintingStrategy(new KisClickableGLImageWidget::VerticalLineHandleStrategy); + connect(d->hueWidget, SIGNAL(selected(const QPointF&)), SLOT(slotHueSliderChanged(const QPointF&))); + + d->valueWidget = new KisClickableGLImageWidget(colorSpace, this); + d->valueWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + d->valueWidget->setHandlePaintingStrategy(new KisClickableGLImageWidget::CircularHandleStrategy); + connect(d->valueWidget, SIGNAL(selected(const QPointF&)), SLOT(slotValueSliderChanged(const QPointF&))); + + d->hasHardwareHDR = KisOpenGLModeProber::instance()->useHDRMode(); + + if (d->hasHardwareHDR) { + d->dynamicRange = new KisSliderSpinBox(this); + d->dynamicRange->setRange(80, 10000); + d->dynamicRange->setExponentRatio(3.0); + d->dynamicRange->setSingleStep(1); + d->dynamicRange->setPageStep(100); + d->dynamicRange->setSuffix("cd/m²"); + d->dynamicRange->setValue(80.0 * d->currentRelativeDynamicRange); + connect(d->dynamicRange, SIGNAL(valueChanged(int)), SLOT(slotInitiateUpdateDynamicRange(int))); + } + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(d->hueWidget, 0); + layout->addWidget(d->valueWidget, 1); + + if (d->dynamicRange) { + layout->addSpacing(16); + layout->addWidget(d->dynamicRange, 0); + } + setLayout(layout); + + slotUpdatePalettes(); } -QColor KisSmallColorWidget::color() const +KisSmallColorWidget::~KisSmallColorWidget() { - int r, g, b; - hsv_to_rgb(d->hue, d->saturation, d->value, &r, &g, &b); - return QColor(r, g, b); + delete d; } -void KisSmallColorWidget::setHue(int h) +void KisSmallColorWidget::setHue(qreal h) { - h = qBound(0, h, 360); + h = qBound(0.0, h, 1.0); d->hue = h; - tellColorChanged(); - generateSquare(); - d->updateTimer.start(); + d->colorChangedSignalCompressor->start(); + d->valueSliderUpdateCompressor->start(); + d->repaintCompressor->start(); } -void KisSmallColorWidget::setHSV(int h, int s, int v) +void KisSmallColorWidget::setHSV(qreal h, qreal s, qreal v, bool notifyChanged) { - h = qBound(0, h, 360); - s = qBound(0, s, 255); - v = qBound(0, v, 255); - bool newH = (d->hue != h); + h = qBound(0.0, h, 1.0); + s = qBound(0.0, s, 1.0); + v = qBound(0.0, v, 1.0); + bool newH = !qFuzzyCompare(d->hue, h); d->hue = h; d->value = v; d->saturation = s; - tellColorChanged(); + // TODO: remove and make acyclic! + if (notifyChanged) { + d->colorChangedSignalCompressor->start(); + } if(newH) { - generateSquare(); + d->valueSliderUpdateCompressor->start(); } - d->updateTimer.start(); + d->repaintCompressor->start(); } -void KisSmallColorWidget::setQColor(const QColor& c) +void KisSmallColorWidget::setColor(const KoColor &color) { - if (d->updateAllowed) { - int hue; - rgb_to_hsv(c.red(), c.green(), c.blue(), &hue, &d->saturation, &d->value); - if (hue >= 0 && hue <= 360) { - d->hue = hue; + if (!d->updateAllowed) return; + + KIS_SAFE_ASSERT_RECOVER(!d->dynamicRange || d->hasHDR == d->dynamicRange->isEnabled()) { + slotDisplayConfigurationChanged(); + } + + KIS_SAFE_ASSERT_RECOVER_RETURN(!d->hasHDR || d->hasHardwareHDR); + + const KoColorSpace *cs = d->generationColorSpace(); + KIS_SAFE_ASSERT_RECOVER_RETURN(cs); + + KoColor newColor(color); + newColor.convertTo(cs); + + QVector channels(4); + cs->normalisedChannelsValue(newColor.data(), channels); + + float r, g, b; + + if (cs->colorDepthId() == Integer8BitsColorDepthID) { + r = channels[2]; + g = channels[1]; + b = channels[0]; + } else { + r = channels[0]; + g = channels[1]; + b = channels[2]; + } + + if (d->hasHDR) { + qreal rangeCoeff = d->effectiveRelativeDynamicRange(); + + if (rangeCoeff < r || rangeCoeff < g || rangeCoeff < b) { + rangeCoeff = std::max({r, g, b}) * 1.10f; + + const int newMaxLuminance = qRound(80.0 * rangeCoeff); + updateDynamicRange(newMaxLuminance); + d->dynamicRange->setValue(newMaxLuminance); } - generateSquare(); - d->updateTimer.start(); + + r /= rangeCoeff; + g /= rangeCoeff; + b /= rangeCoeff; + } else { + r = qBound(0.0f, r, 1.0f); + g = qBound(0.0f, g, 1.0f); + b = qBound(0.0f, b, 1.0f); } + + float denormHue, saturation, value; + RGBToHSV(r, g, b, &denormHue, &saturation, &value); + + d->hueWidget->setNormalizedPos(QPointF(denormHue / 360.0, 0.0)); + d->valueWidget->setNormalizedPos(QPointF(saturation, 1.0 - value)); + + setHSV(denormHue / 360.0, saturation, value, false); } -void KisSmallColorWidget::tellColorChanged() +void KisSmallColorWidget::slotUpdatePalettes() { - d->updateAllowed = false; - emit(colorChanged(color())); - d->updateAllowed = true; + updateHuePalette(); + updateSVPalette(); +} + +namespace { +struct FillHPolicy { + static inline void getRGB(qreal hue, float xPortionCoeff, float yPortionCoeff, + int x, int y, float *r, float *g, float *b) { + + HSVToRGB(xPortionCoeff * x * 360.0f, 1.0, 1.0, r, g, b); + } +}; + +struct FillSVPolicy { + static inline void getRGB(qreal hue, float xPortionCoeff, float yPortionCoeff, + int x, int y, float *r, float *g, float *b) { + + HSVToRGB(hue * 360.0, xPortionCoeff * x, 1.0 - yPortionCoeff * y, r, g, b); + } +}; } -void KisSmallColorWidget::paintEvent(QPaintEvent * event) +template +void KisSmallColorWidget::uploadPaletteData(KisGLImageWidget *widget, const QSize &size) { - Q_UNUSED(event); - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - p.drawPixmap(0, 0, d->rubberPixmap); - p.drawPixmap(width() - d->rectangleWidth, 0 , d->squarePixmap); - // Draw Hue handle - p.save(); - p.setPen(QPen(Qt::white, 1.0)); - p.translate((d->hue * d->rubberWidth) / 360.0 , 0.0); - p.drawRect(QRectF(-1.5, 0 , 3.0, height())); - p.restore(); - // Draw Saturation / Value handle - p.setPen(QPen(Qt::white, 1.0)); - p.setBrush(color()); - p.translate(d->saturation * d->rectangleWidth / 255.0 + width() - d->rectangleWidth, - d->rectangleHeight-(d->value * d->rectangleHeight / 255.0)); - p.drawEllipse(QRectF(-d->squareHandleSize * 0.5, -d->squareHandleSize * 0.5, d->squareHandleSize, d->squareHandleSize)); - p.end(); + if (size.isEmpty()) return; + + KisGLImageF16 image(size); + const float xPortionCoeff = 1.0 / image.width(); + const float yPortionCoeff = 1.0 / image.height(); + const float rangeCoeff = d->effectiveRelativeDynamicRange(); + + const KoColorSpace *generationColorSpace = d->generationColorSpace(); + + if (d->displayColorConverter->canSkipDisplayConversion(generationColorSpace)) { + half *pixelPtr = image.data(); + + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + Imf::Rgba &pxl = reinterpret_cast(*pixelPtr); + + float r, g, b; + FillPolicy::getRGB(d->hue, xPortionCoeff, yPortionCoeff, x, y, + &r, &g, &b); + + pxl.r = r * rangeCoeff; + pxl.g = g * rangeCoeff; + pxl.b = b * rangeCoeff; + pxl.a = 1.0; + + pixelPtr += 4; + } + } + + } else { + KIS_SAFE_ASSERT_RECOVER_RETURN(d->displayColorConverter); + + KisFixedPaintDeviceSP device = new KisFixedPaintDevice(generationColorSpace); + device->setRect(QRect(QPoint(), image.size())); + device->reallocateBufferWithoutInitialization(); + float *devicePtr = reinterpret_cast(device->data()); + + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + FillPolicy::getRGB(d->hue, xPortionCoeff, yPortionCoeff, x, y, + devicePtr, devicePtr + 1, devicePtr + 2); + + devicePtr[0] *= rangeCoeff; + devicePtr[1] *= rangeCoeff; + devicePtr[2] *= rangeCoeff; + devicePtr[3] = 1.0; + + devicePtr += 4; + } + } + + d->displayColorConverter->applyDisplayFilteringF32(device, Float32BitsColorDepthID); + + half *imagePtr = image.data(); + devicePtr = reinterpret_cast(device->data()); + + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + imagePtr[0] = devicePtr[0]; + imagePtr[1] = devicePtr[1]; + imagePtr[2] = devicePtr[2]; + imagePtr[3] = devicePtr[3]; + + devicePtr += 4; + imagePtr += 4; + } + } + } + + widget->loadImage(image); } -void KisSmallColorWidget::resizeEvent(QResizeEvent * event) +void KisSmallColorWidget::updateHuePalette() { - QWidget::resizeEvent(event); - setMaximumHeight(50); - updateParameters(); - generateRubber(); - generateSquare(); + uploadPaletteData(d->hueWidget, QSize(d->hueWidget->width(), d->huePreferredHeight)); } -void KisSmallColorWidget::updateParameters() +void KisSmallColorWidget::updateSVPalette() { - d->margin = 5; - d->rectangleWidthProportion = 0.3; - d->rectangleWidth = qMax((int)(width() * d->rectangleWidthProportion) , height()); - d->rectangleHeight = height(); - d->rubberWidth = width() - d->rectangleWidth - d->margin; - d->rubberHeight = height(); - d->squareHandleSize = 10.0; + const int maxSize = 256; + QSize newSize = d->valueWidget->size(); + newSize.rwidth() = qMin(maxSize, newSize.width()); + newSize.rheight() = qMin(maxSize, newSize.height()); + + uploadPaletteData(d->valueWidget, newSize); } -void KisSmallColorWidget::generateRubber() +void KisSmallColorWidget::slotHueSliderChanged(const QPointF &pos) { - QImage image(d->rubberWidth, d->rubberHeight, QImage::Format_RGB32); - for (int y = 0; y < d->rubberHeight; y++) { - for (int x = 0; x < d->rubberWidth; x++) { - int h = (x * 360) / d->rubberWidth ; - int r, g, b; - hsv_to_rgb(h, 255, 255, &r, &g, &b); - image.setPixel(x, y, qRgb(r, g, b)); - } + const qreal newHue = pos.x(); + + if (!qFuzzyCompare(newHue, d->hue)) { + setHue(newHue); } - d->rubberPixmap = QPixmap::fromImage(image); } -void KisSmallColorWidget::generateSquare() +void KisSmallColorWidget::slotValueSliderChanged(const QPointF &pos) { - QImage image(d->rectangleWidth, d->rectangleHeight, QImage::Format_RGB32); - for (int y = 0; y < d->rectangleHeight; ++y) { - int v = 255-((y * 255) / d->rectangleHeight); - uint* data = reinterpret_cast(image.scanLine(y)); - for (int x = 0; x < d->rectangleWidth; ++x, ++data) { - int s = (x * 255) / d->rectangleWidth; - int r, g, b; - hsv_to_rgb(hue(), s, v, &r, &g, &b); - *data = qRgb(r, g, b); - } + const qreal newSaturation = pos.x(); + const qreal newValue = 1.0 - pos.y(); + + if (!qFuzzyCompare(newSaturation, d->saturation) || + !qFuzzyCompare(newValue, d->value)) { + + setHSV(d->hue, newSaturation, newValue); } - d->squarePixmap = QPixmap::fromImage(image); } -void KisSmallColorWidget::mouseReleaseEvent(QMouseEvent * event) +void KisSmallColorWidget::slotInitiateUpdateDynamicRange(int maxLuminance) { - if (event->button() == Qt::LeftButton) { - selectColorAt(event->x(), event->y()); - d->handle = NoHandle; - } else { - QWidget::mouseReleaseEvent(event); - } + d->dynamicRangeCompressor->start(maxLuminance); } -void KisSmallColorWidget::mousePressEvent(QMouseEvent * event) +void KisSmallColorWidget::updateDynamicRange(int maxLuminance) { - if (event->button() == Qt::LeftButton) { - d->handle = NoHandle; - selectColorAt(event->x(), event->y()); - } else { - QWidget::mousePressEvent(event); + const qreal oldRange = d->currentRelativeDynamicRange; + const qreal newRange = qreal(maxLuminance) / 80.0; + + if (qFuzzyCompare(oldRange, newRange)) return; + + float r, g, b; + float denormHue = d->hue * 360.0; + float saturation = d->saturation; + float value = d->value; + + HSVToRGB(denormHue, saturation, value, &r, &g, &b); + + const qreal transformCoeff = oldRange / newRange; + + r = qBound(0.0, r * transformCoeff, 1.0); + g = qBound(0.0, g * transformCoeff, 1.0); + b = qBound(0.0, b * transformCoeff, 1.0); + + RGBToHSV(r, g, b, &denormHue, &saturation, &value); + + d->currentRelativeDynamicRange = newRange; + slotUpdatePalettes(); + setHSV(denormHue / 360.0, saturation, value, false); + d->hueWidget->setNormalizedPos(QPointF(denormHue / 360.0, 0)); + d->valueWidget->setNormalizedPos(QPointF(saturation, 1.0 - value)); +} + +void KisSmallColorWidget::setDisplayColorConverter(KisDisplayColorConverter *converter) +{ + d->colorConverterConnections.clear(); + + if (!converter) { + converter = KisDisplayColorConverter::dumbConverterInstance(); + } + + d->displayColorConverter = converter; + + if (d->displayColorConverter) { + d->colorConverterConnections.addConnection( + d->displayColorConverter, SIGNAL(displayConfigurationChanged()), + this, SLOT(slotDisplayConfigurationChanged())); } + + slotDisplayConfigurationChanged(); } -void KisSmallColorWidget::mouseMoveEvent(QMouseEvent * event) +void KisSmallColorWidget::slotDisplayConfigurationChanged() { - if (event->buttons() & Qt::LeftButton) { - selectColorAt(event->x(), event->y()); - } else { - QWidget::mouseMoveEvent(event); + d->hasHDR = false; + + if (d->hasHardwareHDR) { + const KoColorSpace *cs = d->displayColorConverter->paintingColorSpace(); + + d->hasHDR = cs->colorModelId() == RGBAColorModelID && + (cs->colorDepthId() == Float16BitsColorDepthID || + cs->colorDepthId() == Float32BitsColorDepthID || + cs->colorDepthId() == Float64BitsColorDepthID || + cs->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()); + } + + if (d->dynamicRange) { + d->dynamicRange->setEnabled(d->hasHDR); } + d->hueWidget->setUseHandleOpacity(!d->hasHDR); + d->valueWidget->setUseHandleOpacity(!d->hasHDR); + + slotUpdatePalettes(); + // TODO: also set the currently selected color again } -void KisSmallColorWidget::selectColorAt(int _x, int _y) +void KisSmallColorWidget::slotTellColorChanged() { - if (d->lastX == _x && d->lastY == _y) - { - return; + d->updateAllowed = false; + + float r, g, b; + HSVToRGB(d->hue * 360.0, d->saturation, d->value, &r, &g, &b); + + if (d->hasHDR) { + const float rangeCoeff = d->effectiveRelativeDynamicRange(); + + r *= rangeCoeff; + g *= rangeCoeff; + b *= rangeCoeff; } - d->lastX = _x; - d->lastY = _y; - if ((_x < d->rubberWidth && d->handle == NoHandle) || d->handle == HueHandle) { - d->handle = HueHandle; - setHue((_x * 360.0) / d->rubberWidth); - d->updateTimer.start(); - } else if ((_x > width() - d->rectangleWidth && d->handle == NoHandle) || d->handle == ValueSaturationHandle) { - d->handle = ValueSaturationHandle; - setHSV(d->hue, (_x - width() + d->rectangleWidth) * 255 / d->rectangleWidth, 255-((_y * 255) / d->rectangleHeight)); - d->updateTimer.start(); + + const KoColorSpace *cs = d->generationColorSpace(); + KIS_SAFE_ASSERT_RECOVER_RETURN(cs); + + QVector values(4); + + if (cs->colorDepthId() == Integer8BitsColorDepthID) { + values[0] = b; + values[1] = g; + values[2] = r; + values[3] = 1.0f; + } else { + values[0] = r; + values[1] = g; + values[2] = b; + values[3] = 1.0f; } + + KoColor c(cs); + cs->fromNormalisedChannelsValue(c.data(), values); + emit colorChanged(c); + + d->updateAllowed = true; +} + +void KisSmallColorWidget::resizeEvent(QResizeEvent * event) +{ + QWidget::resizeEvent(event); + update(); + d->resizeUpdateCompressor->start(); } diff --git a/plugins/dockers/smallcolorselector/smallcolorselector_dock.h b/plugins/dockers/smallcolorselector/smallcolorselector_dock.h --- a/plugins/dockers/smallcolorselector/smallcolorselector_dock.h +++ b/plugins/dockers/smallcolorselector/smallcolorselector_dock.h @@ -24,6 +24,7 @@ #include #include +class KoColor; class KisSmallColorWidget; class SmallColorSelectorDock : public QDockWidget, public KoCanvasObserverBase @@ -36,7 +37,7 @@ void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override { m_canvas = 0; setEnabled(false); } public Q_SLOTS: - void colorChangedProxy(const QColor&); + void colorChangedProxy(const KoColor &); void canvasResourceChanged(int, const QVariant&); private: KisSmallColorWidget* m_smallColorWidget; diff --git a/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc b/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc --- a/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc +++ b/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc @@ -18,7 +18,7 @@ #include "smallcolorselector_dock.h" #include -#include +#include "kis_canvas2.h" #include "kis_small_color_widget.h" #include "kis_canvas_resource_provider.h" @@ -34,12 +34,17 @@ QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); m_smallColorWidget = new KisSmallColorWidget(this); - layout->addWidget(m_smallColorWidget); - layout->addStretch(1); + layout->addWidget(m_smallColorWidget, 1); + page->setLayout(layout); + setWidget(page); - m_smallColorWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - connect(m_smallColorWidget, SIGNAL(colorChanged(QColor)), - this, SLOT(colorChangedProxy(QColor))); + + m_smallColorWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + connect(m_smallColorWidget, SIGNAL(colorChanged(KoColor)), + this, SLOT(colorChangedProxy(KoColor))); + + connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), + m_smallColorWidget, SLOT(update())); setWindowTitle(i18n("Small Color Selector")); } @@ -50,26 +55,30 @@ if (m_canvas) { m_canvas->disconnectCanvasObserver(this); - m_smallColorWidget->setQColor(Qt::black); + m_smallColorWidget->setColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); + m_smallColorWidget->setDisplayColorConverter(0); } m_canvas = canvas; if (m_canvas && m_canvas->resourceManager()) { connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); - m_smallColorWidget->setQColor(m_canvas->resourceManager()->foregroundColor().toQColor()); + + KisCanvas2 *kisCanvas = dynamic_cast(canvas); + m_smallColorWidget->setDisplayColorConverter(kisCanvas->displayColorConverter()); + m_smallColorWidget->setColor(m_canvas->resourceManager()->foregroundColor()); } } -void SmallColorSelectorDock::colorChangedProxy(const QColor& c) +void SmallColorSelectorDock::colorChangedProxy(const KoColor& c) { if (m_canvas) - m_canvas->resourceManager()->setForegroundColor(KoColor(c , KoColorSpaceRegistry::instance()->rgb8())); + m_canvas->resourceManager()->setForegroundColor(c); } void SmallColorSelectorDock::canvasResourceChanged(int key, const QVariant& v) { if (key == KoCanvasResourceProvider::ForegroundColor) { - m_smallColorWidget->setQColor(v.value().toQColor()); + m_smallColorWidget->setColor(v.value()); } } diff --git a/plugins/extensions/pykrita/kritarunner/main.cpp b/plugins/extensions/pykrita/kritarunner/main.cpp --- a/plugins/extensions/pykrita/kritarunner/main.cpp +++ b/plugins/extensions/pykrita/kritarunner/main.cpp @@ -34,7 +34,7 @@ qsrand(time(0)); KLocalizedString::setApplicationDomain("kritarunner"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); - KisOpenGL::setDefaultFormat(); + KisOpenGL::testingInitializeDefaultSurfaceFormat(); // first create the application so we can create a pixmap diff --git a/plugins/impex/png/kis_png_export.h b/plugins/impex/png/kis_png_export.h --- a/plugins/impex/png/kis_png_export.h +++ b/plugins/impex/png/kis_png_export.h @@ -30,17 +30,14 @@ Q_OBJECT public: - KisWdgOptionsPNG(QWidget *parent) - : KisConfigWidget(parent) - { - setupUi(this); - } + KisWdgOptionsPNG(QWidget *parent); void setConfiguration(const KisPropertiesConfigurationSP config) override; KisPropertiesConfigurationSP configuration() const override; private Q_SLOTS: void on_alpha_toggled(bool checked); + void slotUseHDRChanged(bool value); }; class KisPNGExport : public KisImportExportFilter diff --git a/plugins/impex/png/kis_png_export.cc b/plugins/impex/png/kis_png_export.cc --- a/plugins/impex/png/kis_png_export.cc +++ b/plugins/impex/png/kis_png_export.cc @@ -73,6 +73,7 @@ options.forceSRGB = configuration->getBool("forceSRGB", true); options.storeAuthor = configuration->getBool("storeAuthor", false); options.storeMetaData = configuration->getBool("storeMetaData", false); + options.saveAsHDR = configuration->getBool("saveAsHDR", false); vKisAnnotationSP_it beginIt = image->beginAnnotations(); vKisAnnotationSP_it endIt = image->endAnnotations(); @@ -119,6 +120,7 @@ cfg->setProperty("transparencyFillcolor", v); cfg->setProperty("saveSRGBProfile", false); cfg->setProperty("forceSRGB", true); + cfg->setProperty("saveAsHDR", false); cfg->setProperty("storeMetaData", false); cfg->setProperty("storeAuthor", false); @@ -142,6 +144,14 @@ addSupportedColorModels(supportedColorModels, "PNG"); } +KisWdgOptionsPNG::KisWdgOptionsPNG(QWidget *parent) + : KisConfigWidget(parent) +{ + setupUi(this); + + connect(chkSaveAsHDR, SIGNAL(toggled(bool)), this, SLOT(slotUseHDRChanged(bool))); +} + void KisWdgOptionsPNG::setConfiguration(const KisPropertiesConfigurationSP cfg) { // the export manager should have prepared some info for us! @@ -181,6 +191,9 @@ //chkForceSRGB->setEnabled(!sRGB); chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false)); + chkSaveAsHDR->setChecked(cfg->getBool("saveAsHDR", false)); + slotUseHDRChanged(chkSaveAsHDR->isChecked()); + chkAuthor->setChecked(cfg->getBool("storeAuthor", false)); chkMetaData->setChecked(cfg->getBool("storeMetaData", false)); @@ -198,12 +211,14 @@ bool alpha = this->alpha->isChecked(); bool interlace = interlacing->isChecked(); int compression = (int)compressionLevel->value(); - bool tryToSaveAsIndexed = this->tryToSaveAsIndexed->isChecked(); - bool saveSRGB = chkSRGB->isChecked(); - bool forceSRGB = chkForceSRGB->isChecked(); + bool saveAsHDR = chkSaveAsHDR->isChecked(); + bool tryToSaveAsIndexed = !saveAsHDR && this->tryToSaveAsIndexed->isChecked(); + bool saveSRGB = !saveAsHDR && chkSRGB->isChecked(); + bool forceSRGB = !saveAsHDR && chkForceSRGB->isChecked(); bool storeAuthor = chkAuthor->isChecked(); bool storeMetaData = chkMetaData->isChecked(); + QVariant transparencyFillcolor; transparencyFillcolor.setValue(bnTransparencyFillColor->color()); @@ -212,6 +227,7 @@ cfg->setProperty("compression", compression); cfg->setProperty("interlaced", interlace); cfg->setProperty("transparencyFillcolor", transparencyFillcolor); + cfg->setProperty("saveAsHDR", saveAsHDR); cfg->setProperty("saveSRGBProfile", saveSRGB); cfg->setProperty("forceSRGB", forceSRGB); cfg->setProperty("storeAuthor", storeAuthor); @@ -225,5 +241,12 @@ bnTransparencyFillColor->setEnabled(!checked); } +void KisWdgOptionsPNG::slotUseHDRChanged(bool value) +{ + tryToSaveAsIndexed->setDisabled(value); + chkForceSRGB->setDisabled(value); + chkSRGB->setDisabled(value); +} + #include "kis_png_export.moc" diff --git a/plugins/impex/png/kis_wdg_options_png.ui b/plugins/impex/png/kis_wdg_options_png.ui --- a/plugins/impex/png/kis_wdg_options_png.ui +++ b/plugins/impex/png/kis_wdg_options_png.ui @@ -35,57 +35,83 @@ 6 - + + + <html><head/><body><p><span style=" font-weight:600;">PNG</span> files have <span style=" font-style:italic;">two</span> options to save <span style=" font-weight:600;">sRGB</span> information: as a tag or as an explicit profile. For use with in websites, <span style=" font-style:italic;">disable</span> this option. For interchange with other applications, <span style=" font-style:italic;">enable</span> this option.</p></body></html> + - Force convert to sRGB + Embed sRGB profile - - + + + + + 0 + 0 + + + + + 50 + 0 + + - Indexed PNG images are smaller. If you enabled this option, your image will be analyzed to see whether it is possible to save as an indexed PNG. + <html><head/><body><p>Background color to replace transparent pixels with.</p></body></html> + + + + + + + Disable to get smaller files if your image has no transparency + + + <p>The Portable Network Graphics (PNG) file format allows transparency in your image to be stored by saving an alpha channel. +You can uncheck the box if you are not using transparency and you want to make the resulting file smaller .<br>Always saving the alpha channel is recommended.</p> - Save as indexed PNG, if possible + Store alpha channel (transparency) true - - - - <p>Adjust the compression time. Better compression takes longer. -<br>Note: the compression level does not change the quality of the result.</p> + + + + <html><head/><body><p>Save author nickname and the first contact information of the author profile into the png, if possible.</p></body></html> - Small File Size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Sign with author data - - - - Note: the compression level does not change the quality of the result + + + + Force convert to sRGB + + + + <p>Adjust the compression time. Better compression takes longer. <br>Note: the compression level does not change the quality of the result.</p> - Compression (Lossless): + Small File Size - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + <html><head/><body><p>Store information like keywords, title and subject and license, if possible.</p></body></html> @@ -95,29 +121,37 @@ - - + + - Note: the compression level does not change the quality of the result + Indexed PNG images are smaller. If you enabled this option, your image will be analyzed to see whether it is possible to save as an indexed PNG. - - <p>Adjust the compression time. Better compression takes longer. -<br>Note: the compression level does not change the quality of the result.</p> + + Save as indexed PNG, if possible + + + true - - + + + + Note: the compression level does not change the quality of the result + <p>Adjust the compression time. Better compression takes longer. <br>Note: the compression level does not change the quality of the result.</p> - Large file size + Compression (Lossless): + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - + Transparent color: @@ -127,73 +161,46 @@ - - - - Use interlacing when publishing on the Internet. - + + - <p>Interlacing is useful if you intend to publish your image on the Internet.<br> -Enabling interlacing will cause the image to be displayed by the browser even while downloading.</p> + <p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level does not change the quality of the result.</p> - Interlacing + Large file size - - + + - <html><head/><body><p><span style=" font-weight:600;">PNG</span> files have <span style=" font-style:italic;">two</span> options to save <span style=" font-weight:600;">sRGB</span> information: as a tag or as an explicit profile. For use with in websites, <span style=" font-style:italic;">disable</span> this option. For interchange with other applications, <span style=" font-style:italic;">enable</span> this option.</p></body></html> - - - Embed sRGB profile - - - - - - - - 0 - 0 - - - - - 50 - 0 - + Note: the compression level does not change the quality of the result - - <html><head/><body><p>Background color to replace transparent pixels with.</p></body></html> + + <p>Adjust the compression time. Better compression takes longer. +<br>Note: the compression level does not change the quality of the result.</p> - - + + - Disable to get smaller files if your image has no transparency + Use interlacing when publishing on the Internet. - <p>The Portable Network Graphics (PNG) file format allows transparency in your image to be stored by saving an alpha channel. -You can uncheck the box if you are not using transparency and you want to make the resulting file smaller .<br>Always saving the alpha channel is recommended.</p> + <p>Interlacing is useful if you intend to publish your image on the Internet.<br> +Enabling interlacing will cause the image to be displayed by the browser even while downloading.</p> - Store alpha channel (transparency) - - - true + Interlacing - - - - <html><head/><body><p>Save author nickname and the first contact information of the author profile into the png, if possible.</p></body></html> - + + - Sign with author data + Save as HDR image (Rec. 2020 PQ) @@ -218,17 +225,17 @@ + + KisColorButton + QPushButton +
kis_color_button.h
+
KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
- - KisColorButton - QPushButton -
kis_color_button.h
-
diff --git a/plugins/impex/png/tests/kis_png_test.h b/plugins/impex/png/tests/kis_png_test.h --- a/plugins/impex/png/tests/kis_png_test.h +++ b/plugins/impex/png/tests/kis_png_test.h @@ -26,6 +26,7 @@ Q_OBJECT private Q_SLOTS: void testFiles(); + void testSaveHDR(); }; #endif diff --git a/plugins/impex/png/tests/kis_png_test.cpp b/plugins/impex/png/tests/kis_png_test.cpp --- a/plugins/impex/png/tests/kis_png_test.cpp +++ b/plugins/impex/png/tests/kis_png_test.cpp @@ -36,5 +36,120 @@ TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", QStringList(), QString(), 1); } +void roudTripHdrImage(const KoColorSpace *savingColorSpace) +{ + qDebug() << "Test saving" << savingColorSpace->id() << savingColorSpace->profile()->name(); + + const KoColorSpace * scRGBF32 = + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + Float32BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p709G10Profile()); + + KoColor fillColor(scRGBF32); + float *pixelPtr = reinterpret_cast(fillColor.data()); + + pixelPtr[0] = 2.7; + pixelPtr[1] = 1.6; + pixelPtr[2] = 0.8; + pixelPtr[3] = 0.9; + + { + QScopedPointer doc(KisPart::instance()->createDocument()); + + KisImageSP image = new KisImage(0, 3, 3, scRGBF32, "png test"); + KisPaintLayerSP paintLayer0 = new KisPaintLayer(image, "paint0", OPACITY_OPAQUE_U8); + paintLayer0->paintDevice()->fill(image->bounds(), fillColor); + image->addNode(paintLayer0, image->root()); + + // convert image color space before saving + image->convertImageColorSpace(savingColorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + image->waitForDone(); + + KisImportExportManager manager(doc.data()); + doc->setFileBatchMode(true); + doc->setCurrentImage(image); + + KisPropertiesConfigurationSP exportConfiguration = new KisPropertiesConfiguration(); + exportConfiguration->setProperty("saveAsHDR", true); + exportConfiguration->setProperty("saveSRGBProfile", false); + exportConfiguration->setProperty("forceSRGB", false); + doc->exportDocumentSync(QUrl::fromLocalFile("test.png"), "image/png", exportConfiguration); + } + + { + QScopedPointer doc(KisPart::instance()->createDocument()); + KisImportExportManager manager(doc.data()); + doc->setFileBatchMode(true); + + KisImportExportFilter::ConversionStatus loadingStatus = + manager.importDocument("test.png", QString()); + + QCOMPARE(loadingStatus, KisImportExportFilter::OK); + + KisImageSP image = doc->image(); + image->initialRefreshGraph(); + + KoColor resultColor; + +// qDebug() << ppVar(image->colorSpace()) << image->colorSpace()->profile()->name(); +// image->projection()->pixel(1, 1, &resultColor); +// qDebug() << ppVar(resultColor); + + image->convertImageColorSpace(scRGBF32, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + image->waitForDone(); + + image->projection()->pixel(1, 1, &resultColor); +// qDebug() << ppVar(resultColor); + + const float tolerance = savingColorSpace->colorDepthId() == Integer8BitsColorDepthID ? 0.02 : 0.01; + bool resultIsValid = true; + float *resultPtr = reinterpret_cast(resultColor.data()); + for (int i = 0; i < 4; i++) { + resultIsValid &= qAbs(resultPtr[i] - pixelPtr[i]) < tolerance; + } + + if (!resultIsValid) { + qDebug() << ppVar(fillColor) << ppVar(resultColor); + } + QVERIFY(resultIsValid); + } +} + +void KisPngTest::testSaveHDR() +{ + QVector colorDepthIds; + colorDepthIds << Float16BitsColorDepthID; + colorDepthIds << Float32BitsColorDepthID; + + QVector profiles; + profiles << KoColorSpaceRegistry::instance()->p709G10Profile(); + profiles << KoColorSpaceRegistry::instance()->p2020G10Profile(); + profiles << KoColorSpaceRegistry::instance()->p2020PQProfile(); + + + Q_FOREACH(const KoID &depth, colorDepthIds) { + Q_FOREACH(const KoColorProfile *profile, profiles) { + roudTripHdrImage( + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + depth.id(), + profile)); + } + } + + roudTripHdrImage( + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + Integer16BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020PQProfile())); + + roudTripHdrImage( + KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), + Integer8BitsColorDepthID.id(), + KoColorSpaceRegistry::instance()->p2020PQProfile())); +} + KISTEST_MAIN(KisPngTest) diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -188,6 +188,9 @@ displayPickedColor(); } +#include "kis_canvas2.h" +#include "kis_display_color_converter.h" + void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); @@ -205,6 +208,7 @@ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename())); } } + } struct PickedChannel { @@ -244,6 +248,17 @@ item->setText(0, pc.name); item->setText(1, pc.valueText); } + + KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); + KoColor newColor = kritaCanvas->displayColorConverter()->applyDisplayFiltering(m_pickedColor, Float32BitsColorDepthID); + QVector values(4); + newColor.colorSpace()->normalisedChannelsValue(newColor.data(), values); + + for (int i = 0; i < values.size(); i++) { + QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); + item->setText(0, QString("DisplayCh%1").arg(i)); + item->setText(1, QString::number(values[i])); + } } } diff --git a/plugins/tools/basictools/wdgcolorpicker.ui b/plugins/tools/basictools/wdgcolorpicker.ui --- a/plugins/tools/basictools/wdgcolorpicker.ui +++ b/plugins/tools/basictools/wdgcolorpicker.ui @@ -97,6 +97,12 @@
+ + + 0 + 1 + + 0