diff --git a/3rdparty/ext_pyqt/CMakeLists.txt b/3rdparty/ext_pyqt/CMakeLists.txt index ed65304773..2ef225701c 100644 --- a/3rdparty/ext_pyqt/CMakeLists.txt +++ b/3rdparty/ext_pyqt/CMakeLists.txt @@ -1,54 +1,54 @@ SET(PREFIX_ext_pyqt "${EXTPREFIX}" ) if (UNIX) SET(PYTHON_EXECUTABLE_PATH ${PREFIX_ext_pyqt}/bin/python3) if(NOT EXISTS ${PYTHON_EXECUTABLE_PATH}) message("WARNING: using system python3!") SET(PYTHON_EXECUTABLE_PATH python3) endif() ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://www.riverbankcomputing.com/static/Downloads/PyQt5/PyQt5_gpl-5.12.zip - URL_MD5 102062aff7435c03114e02c456dfd01f + URL https://www.riverbankcomputing.com/static/Downloads/PyQt5/PyQt5_gpl-5.12.1.zip + URL_MD5 0b2912828a4d59e13d86decdce1687e6 CONFIGURE_COMMAND ${PYTHON_EXECUTABLE_PATH} /configure.py --confirm-license --qmake ${PREFIX_ext_pyqt}/bin/qmake --sip ${PREFIX_ext_pyqt}/bin/sip --sip-incdir ${PREFIX_ext_pyqt}/include --sipdir ${PREFIX_ext_pyqt}/share/sip BUILD_COMMAND make INSTALL_COMMAND make install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ) elseif(MINGW) list(APPEND _PYQT_conf --confirm-license --target-py-version 3.6 --bindir ${PREFIX_ext_pyqt}/bin --qt ${PREFIX_ext_pyqt} --sip ${PREFIX_ext_pyqt}/bin/sip.exe --sip-incdir ${PREFIX_ext_pyqt}/include --spec win32-g++ --verbose --sipdir ${PREFIX_ext_pyqt}/share/sip --destdir ${PREFIX_ext_pyqt}/lib/krita-python-libs --stubsdir ${PREFIX_ext_pyqt}/lib/krita-python-libs/PyQt5 --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools --disable QtSql --disable QtTest --disable QtWinExtras --disable QtHelp ) ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://www.riverbankcomputing.com/static/Downloads/PyQt5/PyQt5_gpl-5.12.tar.gz - URL_MD5 757161fc19a2da788962fbc5d18480c0 + URL https://www.riverbankcomputing.com/static/Downloads/PyQt5/PyQt5_gpl-5.12.1.tar.gz + URL_MD5 67508b652098d2e05c4c2b5baeb170cc PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/pyqt-configure-fix.patch CONFIGURE_COMMAND ${PYTHON_EXECUTABLE} /configure.py ${_PYQT_conf} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} CXXFLAGS=-D_hypot=hypot LDFLAGS=${SECURITY_SHARED_LINKER_FLAGS} INSTALL_COMMAND mingw32-make install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ) endif() diff --git a/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch b/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch index fa6dc7cb79..60aee3488f 100644 --- a/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch +++ b/3rdparty/ext_qt/0005-cumulative-patch-for-hdr.patch @@ -1,4574 +1,3598 @@ -From 94a1fa7324a4c4b65f28426755428cd4ded01cfb Mon Sep 17 00:00:00 2001 +From 3a16c206f86ddd97c4ef6c89bbeb444b7a16a89a 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 +Subject: [PATCH 1/6] Implement openGL surface color space selection in Angle -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 +WARNING: this patch actually means that the library must be build on + the system with at least DXGI 1.4 (DirectX 12 API) present + in SDK. Mingw64 7.3 supports that. 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 +6) As a fallback, SwapChain uses old DXGI_SWAP_EFFECT_DISCARD, because + flip modes are not available on Windows 7 and such old systems. + +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: I68204a5db6bbd7066a83a8d1d021ce76cd1cf6f6 --- - 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(-) + src/3rdparty/angle/src/common/platform.h | 14 +- + 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 | 16 +- + .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 4 +- + .../renderer/d3d/d3d11/SwapChain11.cpp | 91 ++- + .../libANGLE/renderer/d3d/d3d11/SwapChain11.h | 4 +- + .../d3d/d3d11/win32/NativeWindow11Win32.cpp | 19 +- + .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 4 +- + .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 3 +- + .../angle/src/libANGLE/validationEGL.cpp | 53 ++ + ...-surface-color-space-selection-in-An.patch | 596 ++++++++++++++++++ + 15 files changed, 831 insertions(+), 20 deletions(-) + create mode 100644 src/angle/patches/0013-Implement-openGL-surface-color-space-selection-in-An.patch +diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h +index fb251da579..2e17994557 100644 +--- a/src/3rdparty/angle/src/common/platform.h ++++ b/src/3rdparty/angle/src/common/platform.h +@@ -59,12 +59,14 @@ + # endif + + # if defined(ANGLE_ENABLE_D3D11) +-#include +-#include +-#include +-#include +-#include +-#include ++# include ++# include ++# include ++# include ++# include ++# include ++# include // WARNING: This is actually D3D12! ++# include + # endif + + #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) 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 +index b0ef9abddc..f0e497b52f 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) +@@ -1002,6 +1004,10 @@ void Renderer11::populateRenderer11DeviceCaps() 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 +@@ -1241,6 +1247,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; ++ // 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() -@@ -1436,10 +1449,11 @@ SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, +@@ -1436,10 +1447,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 +index dcfd06484d..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,6 +18,8 @@ +@@ -18,6 +18,11 @@ #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" #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h" -@@ -56,12 +58,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, +@@ -56,12 +61,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, +@@ -620,10 +627,92 @@ 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 +index 5394e3d3e7..af52c41d00 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, +@@ -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) + { +@@ -158,9 +161,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, +@@ -176,7 +179,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, +@@ -191,6 +194,16 @@ 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. ++ * NOTE1: 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 ++ * NOTE2: Flip modes are not supported on Windows 7 and the like, ++ * so use a legacy mode as a fallback + */ -+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_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 +index b583273641..8e158f6432 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, +@@ -719,8 +719,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 +index 13a3a9e280..858d7ee929 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."); ++ return EglBadAttribute() << "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."); ++ return EglBadAttribute() << "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."); ++ return EglBadAttribute() << "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); ++ return EglBadAttribute() << "Unknown EGL color space requested"; + } + 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()); - } +@@ -977,6 +1003,33 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri + } + break; --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; ++ case EGL_GL_COLORSPACE: + -+ 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; -+ } ++ if (!displayExtensions.colorspaceSRGB) ++ { ++ return EglBadAttribute() << "EGL_KHR_gl_colorspace is not supported on this platform."; ++ } + -+ // TODO: check if the attribute is actually suportef by the implementation -+ const EGLint attributes[] = { -+ EGL_GL_COLORSPACE, eglColorSpace, -+ EGL_NONE -+ }; ++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) ++ { ++ if (!displayExtensions.colorspaceSCRGBLinear) ++ { ++ return EglBadAttribute() << "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 EglBadAttribute() << "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 EglBadAttribute() << "Unknown EGL color space requested"; ++ } ++ break; + - 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."; + default: + return EglBadAttribute(); } - 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 +diff --git a/src/angle/patches/0013-Implement-openGL-surface-color-space-selection-in-An.patch b/src/angle/patches/0013-Implement-openGL-surface-color-space-selection-in-An.patch +new file mode 100644 +index 0000000000..dfbe362690 +--- /dev/null ++++ b/src/angle/patches/0013-Implement-openGL-surface-color-space-selection-in-An.patch +@@ -0,0 +1,596 @@ ++From 05082a2affad3428e2ba4475a5c083e81a7730ab Mon Sep 17 00:00:00 2001 ++From: Dmitry Kazakov ++Date: Sat, 8 Dec 2018 15:35:43 +0300 ++Subject: [PATCH] Implement openGL surface color space selection in Angle + ++WARNING: this patch actually means that the library must be build on ++ the system with at least DXGI 1.4 (DirectX 12 API) present ++ in SDK. Mingw64 7.3 supports that. + ++1) D3D11 implementation of angle now supports GL_RGB10_A2 format + - 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, ++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 + -+ 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; ++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. + -+ 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 ++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 :) + -+ // disable color management if at least one of the color -+ // spaces is declared as default -+ if (srcColorSpace == QSurfaceFormat::DefaultColorSpace || -+ dstColorSpace == QSurfaceFormat::DefaultColorSpace) { ++6) As a fallback, SwapChain uses old DXGI_SWAP_EFFECT_DISCARD, because ++ flip modes are not available on Windows 7 and such old systems. + -+ return 0; -+ } ++Notes: + -+ // disable color management if source and destination color -+ // spaces are the same -+ if (srcColorSpace == dstColorSpace) { -+ return 0; -+ } ++eglCreatePixmapSurface() is not implemented in Angle, so the support is ++not added. + -+ ColorSpaceConversion conversion(srcColorSpace, dstColorSpace); -+ return supportedColorSpaceConversions.indexOf(conversion); -+} ++eglCreatePlatformWindowSurface() and eglCreatePlatformPixmapSurface() ++do not have support for color spaces according to the extension wording ++(and they are also not supported by Angle :) ) + -+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(-) ++Change-Id: I68204a5db6bbd7066a83a8d1d021ce76cd1cf6f6 ++--- ++ src/3rdparty/angle/src/common/platform.h | 14 +-- ++ 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 | 16 +++- ++ .../libANGLE/renderer/d3d/d3d11/Renderer11.h | 4 +- ++ .../renderer/d3d/d3d11/SwapChain11.cpp | 91 ++++++++++++++++++- ++ .../libANGLE/renderer/d3d/d3d11/SwapChain11.h | 4 +- ++ .../d3d/d3d11/win32/NativeWindow11Win32.cpp | 19 +++- ++ .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 4 +- ++ .../libANGLE/renderer/d3d/d3d9/Renderer9.h | 3 +- ++ .../angle/src/libANGLE/validationEGL.cpp | 53 +++++++++++ ++ 14 files changed, 235 insertions(+), 20 deletions(-) + +diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h -+index fb251da579..89359f954e 100644 ++index fb251da579..2e17994557 100644 +--- a/src/3rdparty/angle/src/common/platform.h ++++ b/src/3rdparty/angle/src/common/platform.h -+@@ -59,12 +59,22 @@ ++@@ -59,12 +59,14 @@ + # 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 // WARNING: This is actually D3D12! ++# 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 ++index b0ef9abddc..f0e497b52f 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) ++@@ -1002,6 +1004,10 @@ void Renderer11::populateRenderer11DeviceCaps() + 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 ++@@ -1241,6 +1247,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; +++ // 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() -+@@ -1436,10 +1449,11 @@ SwapChainD3D *Renderer11::createSwapChain(NativeWindowD3D *nativeWindow, ++@@ -1436,10 +1447,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 ++index dcfd06484d..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,6 +18,8 @@ ++@@ -18,6 +18,11 @@ + #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" + #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h" -+@@ -56,12 +58,14 @@ SwapChain11::SwapChain11(Renderer11 *renderer, ++@@ -56,12 +61,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, ++@@ -620,10 +627,92 @@ 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 ++index 5394e3d3e7..af52c41d00 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, ++@@ -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) ++ { ++@@ -158,9 +161,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, ++@@ -176,7 +179,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, ++@@ -191,6 +194,16 @@ 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. +++ * NOTE1: 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 +++ * NOTE2: Flip modes are not supported on Windows 7 and the like, +++ * so use a legacy mode as a fallback ++ */ -++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; ++ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_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 ++index 13a3a9e280..858d7ee929 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."); +++ return EglBadAttribute() << "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."); +++ return EglBadAttribute() << "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."); +++ return EglBadAttribute() << "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); +++ return EglBadAttribute() << "Unknown EGL color space requested"; ++ } ++ break; + + default: + return EglBadAttribute(); ++@@ -977,6 +1003,33 @@ Error ValidateCreatePbufferSurface(Display *display, Config *config, const Attri ++ } ++ break; ++ +++ case EGL_GL_COLORSPACE: +++ +++ if (!displayExtensions.colorspaceSRGB) +++ { +++ return EglBadAttribute() << "EGL_KHR_gl_colorspace is not supported on this platform."; +++ } +++ +++ if (value == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) +++ { +++ if (!displayExtensions.colorspaceSCRGBLinear) +++ { +++ return EglBadAttribute() << "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 EglBadAttribute() << "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 EglBadAttribute() << "Unknown EGL color space requested"; +++ } +++ break; +++ ++ default: ++ return EglBadAttribute(); ++ } +-- +2.20.1.windows.1 + +-- +2.20.1.windows.1 + + +From 9442d48679b33ac8e7b3ef7e16b7b13097808f26 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Wed, 13 Feb 2019 16:56:11 +0300 +Subject: [PATCH 2/6] Implement color space selection for QSurfaceFormat + +With the patch one can select color space of openGL surface +which is used a a root surface of the underlying native window. + +This feature is needed, e.g. when the user wants to render HDR +content on screen. In such a case OS should be instructed about +how to treat the graphical data in the application framebuffer. + +The easiest approach is to call QSurfaceFormat::setDefaultFormat() +before creating the first application window. In such a case the +root surface will (may) be in the requested format. + +Supported color spaces/formats: + +1) sRGB, SDR +2) scRGB (Rec 709, gamma 1.0), HDR +3) Rec 2020 PQ, HDR + +Please take into account that in real life the user should select +proper bit depth for each color space, otherwise the system will +refuse to create the surface: + +1) sRGB --- 8 bit or 10 bit +2) scRGB --- 16 bit only +3) Rec 2020 PQ --- 10 bit only + +Please note that color space selection is supported only on +platforms with DXGI 1.4 and higher. + +Change-Id: I5f4945db9798d542f19c8ff1af1effa34f7745fd +--- + src/gui/kernel/qsurfaceformat.cpp | 11 ++++ + src/gui/kernel/qsurfaceformat.h | 4 +- + src/gui/opengl/qopenglframebufferobject.cpp | 7 ++- + .../platforms/windows/qwindowseglcontext.cpp | 57 +++++++++++++++++-- + .../platforms/windows/qwindowseglcontext.h | 6 +- + .../platforms/windows/qwindowsopenglcontext.h | 2 +- + .../platforms/windows/qwindowswindow.cpp | 8 ++- + 7 files changed, 83 insertions(+), 12 deletions(-) + +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 063e81150e..4cd745eac6 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 = nullptr; +@@ -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, +@@ -297,11 +305,48 @@ 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; ++ 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; ++ } ++ ++ 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), nullptr); ++ static_cast(nativeWindow), ++ attributes.constData()); + if (surface == EGL_NO_SURFACE) { + *err = libEGL.eglGetError(); + qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); +@@ -349,6 +394,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 +424,6 @@ QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EG + \internal + \ingroup qt-lighthouse-win + */ +- + QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share) +@@ -483,6 +528,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..9f7742e6fb 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) +@@ -121,7 +122,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); +@@ -135,6 +136,9 @@ private: + EGLDisplay *display, EGLint *major, EGLint *minor); + + const EGLDisplay m_display; ++ bool m_hasSRGBColorSpaceSupport; ++ bool m_hasSCRGBColorSpaceSupport; ++ bool m_hasBt2020PQColorSpaceSupport; + }; + + class QWindowsEGLContext : public QWindowsOpenGLContext +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 c5d57be2ad..910d8dd209 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.cpp ++++ b/src/plugins/platforms/windows/qwindowswindow.cpp +@@ -2738,9 +2738,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 2039f3cb4e267232160a844739e500e025b3818e Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Thu, 22 Nov 2018 15:47:48 +0300 +Subject: [PATCH 3/6] 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. + +The patch also implements QOpenGLWidget::setTextureColorSpace(), +which notifies the compositor about the color space of non-native +openGL widgets, so that the data could be converted correctly. + +TODO: should we implement the same for QOpenGLWindow:: + setTextureColorSpace()? + +Note: +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. + +Change-Id: Icbf599952c93cc04de417d0c3790a65282741655 +--- + src/gui/opengl/qopengltextureblitter.cpp | 222 ++++++++++++++++-- + src/gui/opengl/qopengltextureblitter.h | 12 +- + src/gui/painting/qplatformbackingstore.cpp | 16 +- + 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 +- + 9 files changed, 281 insertions(+), 26 deletions(-) + +diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp +index b65df9dc82..5f6dbff292 100644 +--- a/src/gui/opengl/qopengltextureblitter.cpp ++++ b/src/gui/opengl/qopengltextureblitter.cpp +@@ -131,14 +131,85 @@ 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 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" ++ " 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 || 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" ++ " 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" ++ "#endif\n" ++ "#if defined SRGB_TO_BT2020PQ || defined SCRGB_TO_BT2020PQ\n" ++ "highp vec4 scRgbToBt2020pq(highp vec4 value)\n" ++ "{\n" ++ " 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" ++ "#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);" +- " tmpFragColor.a *= opacity;" +- " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" ++ " 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" ++ " 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 = tmpFragColor;" + "}"; + + static const char fragment_shader_external_oes[] = +@@ -187,6 +258,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 +285,29 @@ public: + }; + + enum ProgramIndex { +- TEXTURE_2D, +- TEXTURE_EXTERNAL_OES ++ 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 + }; + + 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::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); + +@@ -214,6 +315,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 +341,48 @@ 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 = ++ QOpenGLTextureBlitterPrivate::ProgramIndex( ++ int(QOpenGLTextureBlitterPrivate::TEXTURE_2D) + colorSpaceConversion); ++ return index; ++ } + case GL_TEXTURE_EXTERNAL_OES: + return QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES; + default: +@@ -261,7 +393,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 +425,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 +439,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 +540,28 @@ 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 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; +@@ -455,6 +609,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 +640,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 +673,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 +696,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 afb4613ba5..8541e582e9 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,6 +308,7 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, + if (srgb && canUseSrgb) + funcs->glEnable(GL_FRAMEBUFFER_SRGB); + ++ blitter->rebind(GL_TEXTURE_2D, textures->colorSpace(idx), window->format().colorSpace()); + blitter->blit(textures->textureId(idx), target, source); + + if (srgb && canUseSrgb) +@@ -433,6 +442,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. +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 7e4ea2cc0c..1ff5af426b 100644 +--- a/src/widgets/kernel/qwidget_p.h ++++ b/src/widgets/kernel/qwidget_p.h +@@ -655,6 +655,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 9ce6a1f443545d7615053dcfd642c0ab9ad86478 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Tue, 4 Dec 2018 20:11:34 +0300 +Subject: [PATCH 4/6] Return QScreen's HMONITOR handle via + QPlatformNativeInterface + +It is needed to be able to fetch extra information about the display via +DXGI interface. + +Change-Id: Id83982eb07ade157719e430d0abcc2613409a343 +--- + .../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 32eec322e8..05d6ac9201 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 nullptr; + } + ++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 a161dc46e9..8a5f0e6577 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 df143f54ae6134edecd9f76818df3510a10dfcf1 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Sun, 10 Feb 2019 22:55:59 +0300 +Subject: [PATCH 5/6] 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 +the 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 +++++ + tests/manual/manual.pro | 2 +- + 14 files changed, 1217 insertions(+), 1 deletion(-) + 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..b57c657046 +--- /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 data ++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..e517ef8579 +--- /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_MACOS ++ 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(); + -+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 ++ QImage qimage(size, QImage::Format_ARGB32); ++ quint8 *qimagePixelPtr = qimage.bits(); + -+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(-) ++ for (int y = 0; y < size.height(); y++) { ++ for (int x = 0; x < size.width(); x++) { ++ const qfloat16 *srcPxl = pixelPtr; ++ quint8 *dstPxl = qimagePixelPtr; + -+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 ++ 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]); + -+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 ++ pixelPtr += 4; ++ qimagePixelPtr += 4; ++ } ++ } + -+"Flip" modes are not available on older versions of DirectX, -+so it is not safe to request it as a fallback case. ++ return qimage; ++} + -+Change-Id: I3f00e64e5a11a8c5ef2dab9b27a5d9e677f1ed58 -+--- -+ .../renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp | 12 +++++++----- -+ 1 file changed, 7 insertions(+), 5 deletions(-) ++void Window::updateSurfaceInfo() ++{ ++ const QSurfaceFormat format = m_imageWidget->context()->format(); + -+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 ++ 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); + -+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 ++ 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$ ++** ++****************************************************************************/ + -+Notes: ++#ifndef WINDOW_H ++#define WINDOW_H + -+eglCreatePixmapSurface() is not implemented in Angle, so the support is -+not added. ++#include + -+eglCreatePlatformWindowSurface() and eglCreatePlatformPixmapSurface() -+do not have support for color spaces according to the extension wording -+(and they are also not supported by Angle :) ) ++class QAction; + -+Change-Id: Ic780a96c6a7e98fba7524fbabc6043ea2de435b0 -+--- -+ .../angle/src/libANGLE/validationEGL.cpp | 27 +++++++++++++++++++ -+ 1 file changed, 27 insertions(+) ++class GLWidget; ++class KisGLImageWidget; ++class KisGLImageF16; ++class QLabel; + -+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 ++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 +diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro +index ab00a5ef60..b202ed0431 100644 +--- a/tests/manual/manual.pro ++++ b/tests/manual/manual.pro +@@ -59,7 +59,7 @@ qtabbar + + qtConfig(opengl) { + SUBDIRS += qopengltextureblitter +- qtConfig(egl): SUBDIRS += qopenglcontext ++ qtConfig(egl): SUBDIRS += qopenglcontext hdr-qopenglwidget + } + + win32: SUBDIRS -= network_remote_stresstest network_stresstest +-- +2.20.1.windows.1 + + +From abd67442a2aec3ece2025e1154aa3c58599e2471 Mon Sep 17 00:00:00 2001 +From: Dmitry Kazakov +Date: Thu, 6 Dec 2018 16:16:27 +0300 +Subject: [PATCH 6/6] 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. + +Change-Id: Id3ffbd2018a8e68844d174328dd1c4ceb7fa01d3 +--- + 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 diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt index 603606a338..2e10d904fc 100644 --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,224 +1,226 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) list(APPEND _QT_conf -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 -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-qml-debug -no-ssl -no-libproxy -no-system-proxies -no-icu -no-mtdev -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth -skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard # -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg # -opensource -confirm-license # -release -platform win32-g++ -prefix ${EXTPREFIX_qt} QMAKE_LFLAGS_APP+=${SECURITY_EXE_LINKER_FLAGS} QMAKE_LFLAGS_SHLIB+=${SECURITY_SHARED_LINKER_FLAGS} QMAKE_LFLAGS_SONAME+=${SECURITY_SHARED_LINKER_FLAGS} ) if (QT_ENABLE_DEBUG_INFO) # Set the option to build Qt with debugging info enabled list(APPEND _QT_conf -force-debug-info) endif(QT_ENABLE_DEBUG_INFO) if (QT_ENABLE_DYNAMIC_OPENGL) list(APPEND _QT_conf -opengl dynamic -angle) else (QT_ENABLE_DYNAMIC_OPENGL) list(APPEND _QT_conf -opengl desktop -no-angle) endif (QT_ENABLE_DYNAMIC_OPENGL) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz URL_MD5 99c2eb46e533371798b4ca2d1458e065 - PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-disable-wintab.patch + + 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}/disable-winink.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} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install UPDATE_COMMAND "" # Use a short name to reduce the chance of exceeding path length limit SOURCE_DIR s BINARY_DIR b DEPENDS ext_patch ) elseif (NOT APPLE) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz URL_MD5 99c2eb46e533371798b4ca2d1458e065 PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0010-Fix-tablet-jitter-on-X11.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0011-Add-an-ID-for-recognition-of-UGEE-tablets.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto' CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -openssl-linked -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 -skip qtmultimedia INSTALL_DIR ${EXTPREFIX_qt} BUILD_COMMAND $(MAKE) INSTALL_COMMAND $(MAKE) install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) else( APPLE ) # XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need # to detect it manually here. if (NOT XCODE_VERSION) execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE xcodebuild_version OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_FILE /dev/null ) string(REGEX MATCH "Xcode ([0-9]+([.][0-9]+)*)" version_match ${xcodebuild_version}) if (version_match) message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}") set(XCODE_VERSION ${CMAKE_MATCH_1}) else() # If detecting Xcode version failed, set a crazy high version so we default # to the newest. set(XCODE_VERSION 99) message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.") endif(version_match) endif(NOT XCODE_VERSION) # ------------------------------------------------------------------------------- # Verify the Xcode installation on Mac OS like Qt5.7 does/will # If not stop now, the system isn't configured correctly for Qt. # No reason to even proceed. # ------------------------------------------------------------------------------- set(XCSELECT_OUTPUT) find_program(XCSELECT_PROGRAM "xcode-select") if(XCSELECT_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}") set(XCSELECT_COMMAND ${XCSELECT_PROGRAM} "--print-path") execute_process( COMMAND ${XCSELECT_COMMAND} RESULT_VARIABLE XCSELECT_COMMAND_RESULT OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCSELECT_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}") # message(STATUS "${XCSELECT_COMMAND_STR}") message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}") endif() else() message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version") endif() # Belts and suspenders # Beyond all the Xcode and Qt version checking, the proof of the pudding # lies in the success/failure of this command: xcrun --find xcrun. # On failure a patch is necessary, otherwise we're ok # So hard check xcrun now... set(XCRUN_OUTPUT) find_program(XCRUN_PROGRAM "xcrun") if(XCRUN_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}") set(XCRUN_COMMAND ${XCRUN_PROGRAM} "--find xcrun") execute_process( COMMAND ${XCRUN_COMMAND} RESULT_VARIABLE XCRUN_COMMAND_RESULT OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCRUN_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}") # message(STATUS "${XCRUN_COMMAND_STR}") message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}") endif() else() message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}") endif() # # Now configure ext_qt accordingly # if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0))) # Fix Xcode xcrun related issue. # NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9 # NOTE: but no one's holding their breath. set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND}} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/mac_standardpaths_qtbug-61159.diff COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch #COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch ) message(STATUS "${EXTPREFIX_qt}:Additional patches injected.") else() # No extra patches will be applied # NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8. # NOTE: that is uncharted territory and (hopefully) a very unlikely scenario... set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch ) endif() # Qt is big - try and parallelize if at all possible include(ProcessorCount) ProcessorCount(NUM_CORES) if(NOT NUM_CORES EQUAL 0) if (NUM_CORES GREATER 2) # be nice... MATH( EXPR NUM_CORES "${NUM_CORES} - 2" ) endif() set(PARALLEL_MAKE "make;-j${NUM_CORES}") message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}") else() set(PARALLEL_MAKE "make") endif() ExternalProject_Add(ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} LOG_DOWNLOAD ON LOG_UPDATE ON LOG_CONFIGURE ON LOG_BUILD ON LOG_TEST ON LOG_INSTALL ON BUILD_IN_SOURCE ON URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz - URL_MD5=99c2eb46e533371798b4ca2d1458e065 + URL_MD5 99c2eb46e533371798b4ca2d1458e065 CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto' INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -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 -openssl-linked -prefix ${EXTPREFIX_qt} BUILD_COMMAND ${PARALLEL_MAKE} INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) endif() diff --git a/3rdparty/ext_qt/disable-winink.patch b/3rdparty/ext_qt/disable-winink.patch new file mode 100644 index 0000000000..e69de29bb2 diff --git a/3rdparty/ext_sip/CMakeLists.txt b/3rdparty/ext_sip/CMakeLists.txt index bd57c2d659..4a57c053ad 100644 --- a/3rdparty/ext_sip/CMakeLists.txt +++ b/3rdparty/ext_sip/CMakeLists.txt @@ -1,45 +1,45 @@ SET(PREFIX_ext_sip "${EXTPREFIX}" ) if (UNIX) SET(PYTHON_EXECUTABLE_PATH ${PREFIX_ext_sip}/bin/python3) if(NOT EXISTS ${PYTHON_EXECUTABLE_PATH}) message("WARNING: using system python3!") SET(PYTHON_EXECUTABLE_PATH python3) endif() ExternalProject_Add( ext_sip DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://www.riverbankcomputing.com/static/Downloads/sip/sip-4.19.14.tar.gz - URL_MD5 bba62b2ea7e16193c7dfcfadb8dd0d05 + URL https://www.riverbankcomputing.com/static/Downloads/sip/sip-4.19.15.zip + URL_MD5 4a1a4760cfabef15d68f45a6920974c2 CONFIGURE_COMMAND ${PYTHON_EXECUTABLE_PATH} /configure.py -b ${PREFIX_ext_sip}/bin -d ${PREFIX_ext_sip}/sip -e ${PREFIX_ext_sip}/include --sipdir ${PREFIX_ext_sip}/sip --target-py-version 3.5 BUILD_COMMAND make INSTALL_COMMAND make install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ) elseif (MINGW) list(APPEND _SIP_conf --platform win32-g++ -b ${PREFIX_ext_sip}/bin -d ${PREFIX_ext_sip}/lib/krita-python-libs -e ${PREFIX_ext_sip}/include --sipdir ${PREFIX_ext_sip}/share/sip --target-py-version 3.6 ) ExternalProject_Add( ext_sip DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://www.riverbankcomputing.com/static/Downloads/sip/sip-4.19.14.tar.gz - URL_MD5 bba62b2ea7e16193c7dfcfadb8dd0d05 + URL https://www.riverbankcomputing.com/static/Downloads/sip/sip-4.19.15.tar.gz + URL_MD5 236578d2199da630ae1251671b9a7bfe CONFIGURE_COMMAND ${PYTHON_EXECUTABLE} /configure.py ${_SIP_conf} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} LDFLAGS=${SECURITY_SHARED_LINKER_FLAGS} INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 5872ec1d94..de8f0dcd34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,772 +1,772 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) set(MIN_FRAMEWORKS_VERSION 5.18.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.11 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_definitions(-Wno-suggest-override) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.2.0-pre-alpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 2) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") 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") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.6) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent Sql ) if (WIN32) - option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) + option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." OFF) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING ) if (${Qt5_VERSION} VERSION_GREATER "5.8.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800) elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700) else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600) endif() add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.19.13") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for quazip ## find_package(QuaZip 0.6) set_package_properties(QuaZip PROPERTIES DESCRIPTION "A library for reading and writing zip files" URL "https://stachenov.github.io/quazip/" TYPE REQUIRED PURPOSE "Needed for reading and writing KRA and ORA files" ) ## ## Test for Atomics ## include(CheckAtomic) ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) add_subdirectory(benchmarks) add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd index 2fa9c6a6cf..efd0dbcb86 100644 --- a/build-tools/windows/build.cmd +++ b/build-tools/windows/build.cmd @@ -1,789 +1,789 @@ @echo off setlocal enabledelayedexpansion goto begin :: Subroutines :find_on_path out_variable file_name set %1=%~f$PATH:2 goto :EOF :get_dir_path out_variable file_path set %1=%~dp2 goto :EOF :get_full_path out_variable file_path setlocal set FULL_PATH=%~f2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) else ( if exist "%FULL_PATH%\" ( set FULL_PATH= ) ) endlocal & set "%1=%FULL_PATH%" goto :EOF :get_full_path_dir out_variable file_path setlocal set FULL_PATH=%~dp2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) endlocal & set "%1=%FULL_PATH%" goto :EOF :prompt_for_string out_variable prompt set /p %1=%~2^> goto :EOF :prompt_for_positive_integer out_variable prompt setlocal call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" set USER_INPUT=0 set /a RESULT=%USER_INPUT% if not %RESULT% GTR 0 ( set RESULT= ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_file out_variable prompt setlocal :prompt_for_file__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path RESULT "%USER_INPUT%" if "%RESULT%" == "" ( echo Input does not point to valid file! set USER_INPUT= goto prompt_for_file__retry ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_dir out_variable prompt setlocal :prompt_for_dir__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path_dir RESULT "%USER_INPUT%\" if "%RESULT%" == "" ( echo Input does not point to valid dir! set USER_INPUT= goto prompt_for_dir__retry ) endlocal & set "%1=%RESULT%" goto :EOF :usage echo Usage: echo %~n0 [--no-interactive] [ OPTIONS ... ] echo. echo Basic options: echo --no-interactive Run without interactive prompts echo When not specified, the script will prompt echo for some of the parameters. echo --jobs ^ Set parallel jobs count when building echo Defaults to no. of logical cores echo --skip-deps Skips (re)building of deps echo --skip-krita Skips (re)building of Krita echo. echo Path options: echo --src-dir ^ Specify Krita source dir echo If unspecified, this will be determined from echo the script location. echo --download-dir ^ Specify deps download dir echo Can be omitted if --skip-deps is used echo --deps-build-dir ^ Specify deps build dir echo Can be omitted if --skip-deps is used echo --deps-install-dir ^ Specify deps install dir echo --krita-build-dir ^ Specify Krita build dir echo Can be omitted if --skip-krita is used echo --krita-install-dir ^ Specify Krita install dir echo Can be omitted if --skip-krita is used echo. goto :EOF :usage_and_exit call :usage exit /b :usage_and_fail call :usage exit /b 100 :: ---------------------------- :begin echo Krita build script for Windows echo. :: command-line args parsing set ARG_NO_INTERACTIVE= set ARG_JOBS= set ARG_SKIP_DEPS= set ARG_SKIP_KRITA= set ARG_SRC_DIR= set ARG_DOWNLOAD_DIR= set ARG_DEPS_BUILD_DIR= set ARG_DEPS_INSTALL_DIR= set ARG_KRITA_BUILD_DIR= set ARG_KRITA_INSTALL_DIR= :args_parsing_loop set CURRENT_MATCHED= if not "%1" == "" ( if "%1" == "--no-interactive" ( set ARG_NO_INTERACTIVE=1 set CURRENT_MATCHED=1 ) if "%1" == "--jobs" ( if not "%ARG_JOBS%" == "" ( echo ERROR: Arg --jobs specified more than once 1>&2 echo. goto usage_and_fail ) set /a "ARG_JOBS=%2" if not !ARG_JOBS! GTR 0 ( echo ERROR: Arg --jobs is not a positive integer 1>&2 echo. goto usage_and_fail ) shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--skip-deps" ( set ARG_SKIP_DEPS=1 set CURRENT_MATCHED=1 ) if "%1" == "--skip-krita" ( set ARG_SKIP_KRITA=1 set CURRENT_MATCHED=1 ) if "%1" == "--src-dir" ( if not "%ARG_SRC_DIR%" == "" ( echo ERROR: Arg --src-dir specified more than once 1>&2 echo. goto usage_and_fail ) if not exist "%~f2\" ( echo ERROR: Arg --src-dir does not point to a directory 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_SRC_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--download-dir" ( if not "%ARG_DOWNLOAD_DIR%" == "" ( echo ERROR: Arg --download-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --download-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DOWNLOAD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-build-dir" ( if not "%ARG_DEPS_BUILD_DIR%" == "" ( echo ERROR: Arg --deps-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-install-dir" ( if not "%ARG_DEPS_INSTALL_DIR%" == "" ( echo ERROR: Arg --deps-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-build-dir" ( if not "%ARG_KRITA_BUILD_DIR%" == "" ( echo ERROR: Arg --krita-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-install-dir" ( if not "%ARG_KRITA_INSTALL_DIR%" == "" ( echo ERROR: Arg --krita-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--help" ( goto usage_and_exit ) if not "!CURRENT_MATCHED!" == "1" ( echo ERROR: Unknown option %1 1>&2 echo. goto usage_and_fail ) shift /1 goto args_parsing_loop ) if "%ARG_NO_INTERACTIVE%" == "1" ( echo Non-interactive mode ) else ( echo Interactive mode :: Trick to pause on exit call :real_begin pause exit /b !ERRORLEVEL! ) :real_begin echo. if "%ARG_SKIP_DEPS%" == "1" ( if "%ARG_SKIP_KRITA%" == "1" ( echo ERROR: You cannot skip both deps and Krita 1>&2 echo. exit /b 102 ) echo Building of deps will be skipped. ) else ( if "%ARG_SKIP_KRITA%" == "1" ( echo Building of Krita will be skipped. ) else ( echo Both deps and Krita will be built. ) ) :: Check environment config if "%CMAKE_EXE%" == "" ( call :find_on_path CMAKE_EXE cmake.exe if "!CMAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" ) if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) else ( echo Found CMake on PATH: !CMAKE_EXE! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) ) ) ) echo CMake: %CMAKE_EXE% if "%SEVENZIP_EXE%" == "" ( call :find_on_path SEVENZIP_EXE 7z.exe if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe" if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe" ) if "!SEVENZIP_EXE!" == "" ( echo 7-Zip not found ) ) ) if "%SEVENZIP_EXE%" == "" ( echo 7-Zip: %SEVENZIP_EXE% ) if "%MINGW_BIN_DIR%" == "" ( call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" ) if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) else ( call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" echo Found mingw on PATH: !MINGW_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) ) ) ) echo mingw-w64: %MINGW_BIN_DIR% if "%PYTHON_BIN_DIR%" == "" ( call :find_on_path PYTHON_BIN_DIR_PYTHON_EXE python.exe if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" ) if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) else ( call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" echo Found Python on PATH: !PYTHON_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) ) ) ) echo Python: %PYTHON_BIN_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_windows_sdk_dir_check if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if exist "%WindowsSdkDir%\" ( 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\" ) ) ) popd ) set QT_ENABLE_DYNAMIC_OPENGL=ON if not "%HAVE_FXC_EXE%" == "1" ( set WindowsSdkDir= echo Windows SDK 10 with fxc.exe not found echo Qt will *not* be built with ANGLE ^(dynamic OpenGL^) support. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 102 ) ) set QT_ENABLE_DYNAMIC_OPENGL=OFF ) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir% :skip_windows_sdk_dir_check if not "%ARG_JOBS%" == "" ( set "PARALLEL_JOBS=%ARG_JOBS%" ) if "%PARALLEL_JOBS%" == "" ( echo Number of logical CPU cores detected: %NUMBER_OF_PROCESSORS% echo Enabling %NUMBER_OF_PROCESSORS% parallel jobs set PARALLEL_JOBS=%NUMBER_OF_PROCESSORS% if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_positive_integer PARALLEL_JOBS "Provide no. of parallel jobs" if "!PARALLEL_JOBS!" == "" ( echo ERROR: Invalid job count! 1>&2 exit /b 102 ) ) ) ) echo Parallel jobs count: %PARALLEL_JOBS% if not "%ARG_SRC_DIR%" == "" ( set "KRITA_SRC_DIR=%ARG_SRC_DIR%" ) if "%KRITA_SRC_DIR%" == "" ( :: Check whether this looks like to be in the source tree set "_temp=%~dp0" if "!_temp:~-21!" == "\build-tools\windows\" ( if exist "!_temp:~0,-21!\CMakeLists.txt" ( if exist "!_temp:~0,-21!\3rdparty\CMakeLists.txt" ( set "KRITA_SRC_DIR=!_temp:~0,-21!\" echo Script is running inside Krita src dir ) ) ) ) if "%KRITA_SRC_DIR%" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir" ) if "!KRITA_SRC_DIR!" == "" ( echo ERROR: Krita src dir not found! 1>&2 exit /b 102 ) ) echo Krita src: %KRITA_SRC_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_deps_args_check if not "%ARG_DOWNLOAD_DIR%" == "" ( set "DEPS_DOWNLOAD_DIR=%ARG_DOWNLOAD_DIR%" ) if "%DEPS_DOWNLOAD_DIR%" == "" ( set DEPS_DOWNLOAD_DIR=%CD%\d\ echo Using default deps download dir: !DEPS_DOWNLOAD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_DOWNLOAD_DIR "Provide path of depps download dir" ) ) if "!DEPS_DOWNLOAD_DIR!" == "" ( echo ERROR: Deps download dir not set! 1>&2 exit /b 102 ) ) echo Deps download dir: %DEPS_DOWNLOAD_DIR% if not "%ARG_DEPS_BUILD_DIR%" == "" ( set "DEPS_BUILD_DIR=%ARG_DEPS_BUILD_DIR%" ) if "%DEPS_BUILD_DIR%" == "" ( set DEPS_BUILD_DIR=%CD%\b_deps\ echo Using default deps build dir: !DEPS_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_BUILD_DIR "Provide path of deps build dir" ) ) if "!DEPS_BUILD_DIR!" == "" ( echo ERROR: Deps build dir not set! 1>&2 exit /b 102 ) ) echo Deps build dir: %DEPS_BUILD_DIR% :skip_deps_args_check if not "%ARG_DEPS_INSTALL_DIR%" == "" ( set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%" ) if "%DEPS_INSTALL_DIR%" == "" ( set DEPS_INSTALL_DIR=%CD%\i_deps\ echo Using default deps install dir: !DEPS_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir" ) ) if "!DEPS_INSTALL_DIR!" == "" ( echo ERROR: Deps install dir not set! 1>&2 exit /b 102 ) ) echo Deps install dir: %DEPS_INSTALL_DIR% if "%ARG_SKIP_KRITA%" == "1" goto skip_krita_args_check if not "%ARG_KRITA_BUILD_DIR%" == "" ( set "KRITA_BUILD_DIR=%ARG_KRITA_BUILD_DIR%" ) if "%KRITA_BUILD_DIR%" == "" ( set KRITA_BUILD_DIR=%CD%\b\ echo Using default Krita build dir: !KRITA_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_BUILD_DIR "Provide path of Krita build dir" ) ) if "!KRITA_BUILD_DIR!" == "" ( echo ERROR: Krita build dir not set! 1>&2 exit /b 102 ) ) echo Krita build dir: %KRITA_BUILD_DIR% if not "%ARG_KRITA_INSTALL_DIR%" == "" ( set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%" ) if "%KRITA_INSTALL_DIR%" == "" ( set KRITA_INSTALL_DIR=%CD%\i\ echo Using default Krita install dir: !KRITA_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir" ) ) if "!KRITA_INSTALL_DIR!" == "" ( echo ERROR: Krita install dir not set! 1>&2 exit /b 102 ) ) echo Krita install dir: %KRITA_INSTALL_DIR% :skip_krita_args_check echo. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is the above ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 1 ) echo. ) :: Initialize clean PATH set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\ set PATH=%MINGW_BIN_DIR%;%PYTHON_BIN_DIR%;%PATH% echo Creating dirs... if NOT "%ARG_SKIP_DEPS%" == "1" ( mkdir %DEPS_DOWNLOAD_DIR% if errorlevel 1 ( if not exist "%DEPS_DOWNLOAD_DIR%\" ( echo ERROR: Cannot create deps download dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_BUILD_DIR% if errorlevel 1 ( if not exist "%DEPS_BUILD_DIR%\" ( echo ERROR: Cannot create deps build dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_INSTALL_DIR% if errorlevel 1 ( if not exist "%DEPS_INSTALL_DIR%\" ( echo ERROR: Cannot create deps install dir! 1>&2 exit /b 103 ) ) ) if NOT "%ARG_SKIP_KRITA%" == "1" ( mkdir %KRITA_BUILD_DIR% if errorlevel 1 ( if not exist "%KRITA_BUILD_DIR%\" ( echo ERROR: Cannot create Krita build dir! 1>&2 exit /b 103 ) ) mkdir %KRITA_INSTALL_DIR% if errorlevel 1 ( if not exist "%KRITA_INSTALL_DIR%\" ( echo ERROR: Cannot create Krita install dir! 1>&2 exit /b 103 ) ) ) echo. set CMAKE_BUILD_TYPE=RelWithDebInfo set QT_ENABLE_DEBUG_INFO=OFF :: Paths for CMake set "BUILDDIR_DOWNLOAD_CMAKE=%DEPS_DOWNLOAD_DIR:\=/%" set "BUILDDIR_DOWNLOAD_CMAKE=%BUILDDIR_DOWNLOAD_CMAKE: =\ %" set "BUILDDIR_DEPS_INSTALL_CMAKE=%DEPS_INSTALL_DIR:\=/%" set "BUILDDIR_DEPS_INSTALL_CMAKE=%BUILDDIR_DEPS_INSTALL_CMAKE: =\ %" set "BUILDDIR_KRITA_INSTALL_CMAKE=%KRITA_INSTALL_DIR:\=/%" set "BUILDDIR_KRITA_INSTALL_CMAKE=%BUILDDIR_KRITA_INSTALL_CMAKE: =\ %" set PATH=%DEPS_INSTALL_DIR%\bin;%PATH% if not "%GETTEXT_SEARCH_PATH%" == "" ( set PATH=!PATH!;!GETTEXT_SEARCH_PATH! ) if "%ARG_SKIP_DEPS%" == "1" goto skip_build_deps pushd %DEPS_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter deps build dir! 1>&2 exit /b 104 ) echo Running CMake for deps... "%CMAKE_EXE%" "%KRITA_SRC_DIR%\3rdparty" ^ -DSUBMAKE_JOBS=%PARALLEL_JOBS% ^ -DQT_ENABLE_DEBUG_INFO=%QT_ENABLE_DEBUG_INFO% ^ -DQT_ENABLE_DYNAMIC_OPENGL=%QT_ENABLE_DYNAMIC_OPENGL% ^ -DEXTERNALS_DOWNLOAD_DIR=%BUILDDIR_DOWNLOAD_CMAKE% ^ -DINSTALL_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% if errorlevel 1 ( echo ERROR: CMake configure failed! 1>&2 exit /b 104 ) echo. set EXT_TARGETS=patch png2ico zlib lzma gettext qt boost eigen3 exiv2 fftw3 ilmbase set EXT_TARGETS=%EXT_TARGETS% jpeg lcms2 ocio openexr png tiff gsl vc libraw set EXT_TARGETS=%EXT_TARGETS% giflib freetype poppler kwindowsystem drmingw gmic set EXT_TARGETS=%EXT_TARGETS% python sip pyqt set EXT_TARGETS=%EXT_TARGETS% quazip for %%a in (%EXT_TARGETS%) do ( echo Building ext_%%a... "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a if errorlevel 1 ( echo ERROR: Building of ext_%%a failed! 1>&2 exit /b 105 ) ) echo. echo ******** Built deps ******** popd :skip_build_deps if "%ARG_SKIP_KRITA%" == "1" goto skip_build_krita pushd %KRITA_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter Krita build dir! 1>&2 exit /b 104 ) echo Running CMake for Krita... echo "%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^ -DBoost_DEBUG=OFF ^ -DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^ -DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^ -DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -DFOUNDATION_BUILD=ON ^ -DHAVE_HDR=ON ^ - -DUSE_QT_TABLET_WINDOWS=ON ^ + -DUSE_QT_TABLET_WINDOWS=OFF ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% "%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^ -DBoost_DEBUG=OFF ^ -DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^ -DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^ -DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -DFOUNDATION_BUILD=ON ^ - -DUSE_QT_TABLET_WINDOWS=ON ^ + -DUSE_QT_TABLET_WINDOWS=OFF ^ -DHAVE_HDR=ON ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% if errorlevel 1 ( echo ERROR: CMake configure failed! 1>&2 exit /b 104 ) echo. echo Building Krita... "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS% if errorlevel 1 ( echo ERROR: Building of Krita failed! 1>&2 exit /b 105 ) echo. echo ******** Built Krita ******** popd :skip_build_krita echo Krita build completed! diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc index 68290d568d..bec887e335 100644 --- a/libs/image/kis_paint_device.cc +++ b/libs/image/kis_paint_device.cc @@ -1,2215 +1,2215 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * 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 "kis_paint_device.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_random_sub_accessor.h" #include "kis_selection.h" #include "kis_node.h" #include "kis_datamanager.h" #include "kis_paint_device_writer.h" #include "kis_selection_component.h" #include "kis_pixel_selection.h" #include "kis_repeat_iterators_pixel.h" #include "kis_fixed_paint_device.h" #include "tiles3/kis_hline_iterator.h" #include "tiles3/kis_vline_iterator.h" #include "tiles3/kis_random_accessor.h" #include "kis_default_bounds.h" #include "kis_lod_transform.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_cache.h" #include "kis_paint_device_data.h" #include "kis_paint_device_frames_interface.h" #include "kis_transform_worker.h" #include "kis_filter_strategy.h" #include "krita_utils.h" struct KisPaintDeviceSPStaticRegistrar { KisPaintDeviceSPStaticRegistrar() { qRegisterMetaType("KisPaintDeviceSP"); } }; static KisPaintDeviceSPStaticRegistrar __registrar; struct KisPaintDevice::Private { /** * Used when the paint device is loading to ensure no lod/animation * interferes the process. */ static const KisDefaultBoundsSP transitionalDefaultBounds; public: class KisPaintDeviceStrategy; class KisPaintDeviceWrappedStrategy; Private(KisPaintDevice *paintDevice); ~Private(); KisPaintDevice *q; KisNodeWSP parent; QScopedPointer contentChannel; KisDefaultBoundsBaseSP defaultBounds; QScopedPointer basicStrategy; QScopedPointer wrappedStrategy; QMutex m_wrappedStrategyMutex; QScopedPointer framesInterface; bool isProjectionDevice; KisPaintDeviceStrategy* currentStrategy(); void init(const KoColorSpace *cs, const quint8 *defaultPixel); KUndo2Command* convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); bool assignProfile(const KoColorProfile * profile); inline const KoColorSpace* colorSpace() const { return currentData()->colorSpace(); } inline KisDataManagerSP dataManager() const { return currentData()->dataManager(); } inline qint32 x() const { return currentData()->x(); } inline qint32 y() const { return currentData()->y(); } inline void setX(qint32 x) { currentData()->setX(x); } inline void setY(qint32 y) { currentData()->setY(y); } inline KisPaintDeviceCache* cache() { return currentData()->cache(); } inline KisIteratorCompleteListener* cacheInvalidator() { return currentData()->cacheInvalidator(); } void cloneAllDataObjects(Private *rhs, bool copyFrames) { m_lodData.reset(); m_externalFrameData.reset(); if (!m_frames.isEmpty()) { m_frames.clear(); } if (!copyFrames) { if (m_data) { m_data->prepareClone(rhs->currentNonLodData(), true); } else { m_data = toQShared(new KisPaintDeviceData(rhs->currentNonLodData(), true)); } } else { if (m_data && !rhs->m_data) { m_data.clear(); } else if (!m_data && rhs->m_data) { m_data = toQShared(new KisPaintDeviceData(rhs->m_data.data(), true)); } else if (m_data && rhs->m_data) { m_data->prepareClone(rhs->m_data.data(), true); } if (!rhs->m_frames.isEmpty()) { FramesHash::const_iterator it = rhs->m_frames.constBegin(); FramesHash::const_iterator end = rhs->m_frames.constEnd(); for (; it != end; ++it) { DataSP data = toQShared(new KisPaintDeviceData(it.value().data(), true)); m_frames.insert(it.key(), data); } } m_nextFreeFrameId = rhs->m_nextFreeFrameId; } if (rhs->m_lodData) { m_lodData.reset(new KisPaintDeviceData(rhs->m_lodData.data(), true)); } } void prepareClone(KisPaintDeviceSP src) { prepareCloneImpl(src, src->m_d->currentData()); Q_ASSERT(fastBitBltPossible(src)); } bool fastBitBltPossible(KisPaintDeviceSP src) { return fastBitBltPossibleImpl(src->m_d->currentData()); } int currentFrameId() const { KIS_ASSERT_RECOVER(contentChannel) { return -1; } return !defaultBounds->currentLevelOfDetail() ? contentChannel->frameIdAt(defaultBounds->currentTime()) : -1; } KisDataManagerSP frameDataManager(int frameId) const { DataSP data = m_frames[frameId]; return data->dataManager(); } void invalidateFrameCache(int frameId) { DataSP data = m_frames[frameId]; return data->cache()->invalidate(); } private: typedef KisPaintDeviceData Data; typedef QSharedPointer DataSP; typedef QHash FramesHash; class FrameInsertionCommand : public KUndo2Command { public: FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand) : KUndo2Command(parentCommand), m_hash(hash), m_data(data), m_frameId(frameId), m_insert(insert) { } void redo() override { doSwap(m_insert); } void undo() override { doSwap(!m_insert); } private: void doSwap(bool insert) { if (insert) { m_hash->insert(m_frameId, m_data); } else { DataSP deletedData = m_hash->take(m_frameId); } } private: FramesHash *m_hash; DataSP m_data; int m_frameId; bool m_insert; }; public: int getNextFrameId() { int frameId = 0; while (m_frames.contains(frameId = m_nextFreeFrameId++)); KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId)); return frameId; } int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER(parentCommand) { return -1; } DataSP data; bool initialFrame = false; if (m_frames.isEmpty()) { /** * Here we move the contents of the paint device to the * new frame and clear m_data to make the "background" for * the areas where there is no frame at all. */ data = toQShared(new Data(m_data.data(), true)); m_data->dataManager()->clear(); m_data->cache()->invalidate(); initialFrame = true; } else if (copy) { DataSP srcData = m_frames[copySrc]; data = toQShared(new Data(srcData.data(), true)); } else { DataSP srcData = m_frames.begin().value(); data = toQShared(new Data(srcData.data(), false)); } if (!initialFrame && !copy) { data->setX(offset.x()); data->setY(offset.y()); } int frameId = getNextFrameId(); KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, data, frameId, true, parentCommand); cmd->redo(); return frameId; } void deleteFrame(int frame, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame)); KIS_ASSERT_RECOVER_RETURN(parentCommand); DataSP deletedData = m_frames[frame]; KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, deletedData, frame, false, parentCommand); cmd->redo(); } QRect frameBounds(int frameId) { DataSP data = m_frames[frameId]; QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); return extent; } QPoint frameOffset(int frameId) const { DataSP data = m_frames[frameId]; return QPoint(data->x(), data->y()); } void setFrameOffset(int frameId, const QPoint &offset) { DataSP data = m_frames[frameId]; data->setX(offset.x()); data->setY(offset.y()); } const QList frameIds() const { return m_frames.keys(); } bool readFrame(QIODevice *stream, int frameId) { bool retval = false; DataSP data = m_frames[frameId]; retval = data->dataManager()->read(stream); data->cache()->invalidate(); return retval; } bool writeFrame(KisPaintDeviceWriter &store, int frameId) { DataSP data = m_frames[frameId]; return data->dataManager()->write(store); } void setFrameDefaultPixel(const KoColor &defPixel, int frameId) { DataSP data = m_frames[frameId]; KoColor color(defPixel); color.convertTo(data->colorSpace()); data->dataManager()->setDefaultPixel(color.data()); } KoColor frameDefaultPixel(int frameId) const { DataSP data = m_frames[frameId]; return KoColor(data->dataManager()->defaultPixel(), data->colorSpace()); } void fetchFrame(int frameId, KisPaintDeviceSP targetDevice); void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrameData(DataSP srcData, DataSP dstData); struct LodDataStructImpl; LodDataStruct* createLodDataStruct(int lod); void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect); void uploadLodDataStruct(LodDataStruct *dst); QRegion regionForLodSyncing() const; void updateLodDataManager(KisDataManager *srcDataManager, KisDataManager *dstDataManager, const QPoint &srcOffset, const QPoint &dstOffset, const QRect &originalRect, int lod); void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod); void tesingFetchLodDevice(KisPaintDeviceSP targetDevice); private: qint64 estimateDataSize(Data *data) const { const QRect &rc = data->dataManager()->extent(); return rc.width() * rc.height() * data->colorSpace()->pixelSize(); } public: void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const { imageData = 0; temporaryData = 0; lodData = 0; if (m_data) { imageData += estimateDataSize(m_data.data()); } if (m_lodData) { lodData += estimateDataSize(m_lodData.data()); } if (m_externalFrameData) { temporaryData += estimateDataSize(m_externalFrameData.data()); } Q_FOREACH (DataSP value, m_frames.values()) { imageData += estimateDataSize(value.data()); } } private: QRegion syncWholeDevice(Data *srcData); inline DataSP currentFrameData() const { DataSP data; const int numberOfFrames = contentChannel->keyframeCount(); if (numberOfFrames > 1) { int frameId = contentChannel->frameIdAt(defaultBounds->currentTime()); if (frameId == -1) { data = m_data; } else { KIS_ASSERT_RECOVER(m_frames.contains(frameId)) { return m_frames.begin().value(); } data = m_frames[frameId]; } } else if (numberOfFrames == 1) { data = m_frames.begin().value(); } else { data = m_data; } return data; } inline Data* currentNonLodData() const { Data *data = m_data.data(); if (contentChannel) { data = currentFrameData().data(); } else if (isProjectionDevice && defaultBounds->externalFrameActive()) { if (!m_externalFrameData) { QMutexLocker l(&m_dataSwitchLock); if (!m_externalFrameData) { m_externalFrameData.reset(new Data(m_data.data(), false)); } } data = m_externalFrameData.data(); } return data; } inline void ensureLodDataPresent() const { if (!m_lodData) { Data *srcData = currentNonLodData(); QMutexLocker l(&m_dataSwitchLock); if (!m_lodData) { m_lodData.reset(new Data(srcData, false)); } } } inline Data* currentData() const { Data *data; if (defaultBounds->currentLevelOfDetail()) { ensureLodDataPresent(); data = m_lodData.data(); } else { data = currentNonLodData(); } return data; } void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData) { currentData()->prepareClone(srcData); q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace())); q->setDefaultBounds(src->defaultBounds()); } bool fastBitBltPossibleImpl(Data *srcData) { return x() == srcData->x() && y() == srcData->y() && *colorSpace() == *srcData->colorSpace(); } QList allDataObjects() const { QList dataObjects; if (m_frames.isEmpty()) { dataObjects << m_data.data(); } dataObjects << m_lodData.data(); dataObjects << m_externalFrameData.data(); Q_FOREACH (DataSP value, m_frames.values()) { dataObjects << value.data(); } return dataObjects; } void transferFromData(Data *data, KisPaintDeviceSP targetDevice); struct Q_DECL_HIDDEN StrategyPolicy; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialConstIterator; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialIterator; private: friend class KisPaintDeviceFramesInterface; private: DataSP m_data; mutable QScopedPointer m_lodData; mutable QScopedPointer m_externalFrameData; mutable QMutex m_dataSwitchLock; FramesHash m_frames; int m_nextFreeFrameId; }; const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds(); #include "kis_paint_device_strategies.h" KisPaintDevice::Private::Private(KisPaintDevice *paintDevice) : q(paintDevice), basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)), isProjectionDevice(false), m_data(new Data(paintDevice)), m_nextFreeFrameId(0) { } KisPaintDevice::Private::~Private() { m_frames.clear(); } KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy() { if (!defaultBounds->wrapAroundMode()) { return basicStrategy.data(); } const QRect wrapRect = defaultBounds->bounds(); if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) { QMutexLocker locker(&m_wrappedStrategyMutex); if (!wrappedStrategy) { wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this)); } else if (wrappedStrategy->wrapRect() != wrapRect) { wrappedStrategy->setWrapRect(wrapRect); } } return wrappedStrategy.data(); } struct KisPaintDevice::Private::StrategyPolicy { StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy, KisDataManager *dataManager, qint32 offsetX, qint32 offsetY) : m_strategy(strategy), m_dataManager(dataManager), m_offsetX(offsetX), m_offsetY(offsetY) { } KisHLineConstIteratorSP createConstIterator(const QRect &rect) { return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } KisHLineIteratorSP createIterator(const QRect &rect) { return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } int pixelSize() const { return m_dataManager->pixelSize(); } KisPaintDeviceStrategy *m_strategy; KisDataManager *m_dataManager; int m_offsetX; int m_offsetY; }; struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct { LodDataStructImpl(Data *_lodData) : lodData(_lodData) {} QScopedPointer lodData; }; QRegion KisPaintDevice::Private::regionForLodSyncing() const { Data *srcData = currentNonLodData(); return srcData->dataManager()->region().translated(srcData->x(), srcData->y()); } KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod) { KIS_SAFE_ASSERT_RECOVER_NOOP(newLod > 0); Data *srcData = currentNonLodData(); Data *lodData = new Data(srcData, false); LodDataStruct *lodStruct = new LodDataStructImpl(lodData); int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod); int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod); /** * We compare color spaces as pure pointers, because they must be * exactly the same, since they come from the common source. */ if (lodData->levelOfDetail() != newLod || lodData->colorSpace() != srcData->colorSpace() || lodData->x() != expectedX || lodData->y() != expectedY) { lodData->prepareClone(srcData); lodData->setLevelOfDetail(newLod); lodData->setX(expectedX); lodData->setY(expectedY); // FIXME: different kind of synchronization } //QRegion dirtyRegion = syncWholeDevice(srcData); lodData->cache()->invalidate(); return lodStruct; } void KisPaintDevice::Private::updateLodDataManager(KisDataManager *srcDataManager, KisDataManager *dstDataManager, const QPoint &srcOffset, const QPoint &dstOffset, const QRect &originalRect, int lod) { const int srcStepSize = 1 << lod; KIS_ASSERT_RECOVER_RETURN(lod > 0); const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod); const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod); if (!srcRect.isValid() || !dstRect.isValid()) return; KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width()); const int pixelSize = srcDataManager->pixelSize(); int rowsAccumulated = 0; int columnsAccumulated = 0; KoMixColorsOp *mixOp = colorSpace()->mixColorsOp(); QScopedArrayPointer blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]); quint8 *blendDataPtr = blendData.data(); int blendDataOffset = 0; const int srcCellSize = srcStepSize * srcStepSize; const int srcCellStride = srcCellSize * pixelSize; const int srcStepStride = srcStepSize * pixelSize; const int srcColumnStride = (srcStepSize - 1) * srcStepStride; QScopedArrayPointer weights(new qint16[srcCellSize]); { const qint16 averageWeight = qCeil(255.0 / srcCellSize); const qint16 extraWeight = averageWeight * srcCellSize - 255; KIS_ASSERT_RECOVER_NOOP(extraWeight == 1); for (int i = 0; i < srcCellSize - 1; i++) { weights[i] = averageWeight; } weights[srcCellSize - 1] = averageWeight - extraWeight; } InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcDataManager, srcOffset.x(), srcOffset.y()), srcRect); InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), dstDataManager, dstOffset.x(), dstOffset.y()), dstRect); int rowsRemaining = srcRect.height(); while (rowsRemaining > 0) { int colsRemaining = srcRect.width(); while (colsRemaining > 0 && srcIntIt.nextPixel()) { memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize); blendDataPtr += pixelSize; columnsAccumulated++; if (columnsAccumulated >= srcStepSize) { blendDataPtr += srcColumnStride; columnsAccumulated = 0; } colsRemaining--; } rowsAccumulated++; if (rowsAccumulated >= srcStepSize) { // blend and write the final data blendDataPtr = blendData.data(); int colsRemaining = dstRect.width(); while (colsRemaining > 0 && dstIntIt.nextPixel()) { mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData()); blendDataPtr += srcCellStride; colsRemaining--; } // reset counters rowsAccumulated = 0; blendDataPtr = blendData.data(); blendDataOffset = 0; } else { blendDataOffset += srcStepStride; blendDataPtr = blendData.data() + blendDataOffset; } rowsRemaining--; } } void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect) { LodDataStructImpl *dst = dynamic_cast(_dst); KIS_SAFE_ASSERT_RECOVER_RETURN(dst); Data *lodData = dst->lodData.data(); Data *srcData = currentNonLodData(); const int lod = lodData->levelOfDetail(); updateLodDataManager(srcData->dataManager().data(), lodData->dataManager().data(), QPoint(srcData->x(), srcData->y()), QPoint(lodData->x(), lodData->y()), originalRect, lod); } void KisPaintDevice::Private::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod) { KIS_SAFE_ASSERT_RECOVER_RETURN(fastBitBltPossible(dst)); Data *srcData = currentNonLodData(); updateLodDataManager(srcData->dataManager().data(), dst->dataManager().data(), QPoint(srcData->x(), srcData->y()), QPoint(dst->x(), dst->y()), originalRect, lod); } void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst) { LodDataStructImpl *dst = dynamic_cast(_dst); KIS_SAFE_ASSERT_RECOVER_RETURN(dst); KIS_SAFE_ASSERT_RECOVER_RETURN( dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail()); ensureLodDataPresent(); m_lodData->prepareClone(dst->lodData.data()); m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent()); } void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice) { QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); targetDevice->m_d->prepareCloneImpl(q, data); targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent); } void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { DataSP data = m_frames[frameId]; transferFromData(data.data(), targetDevice); } void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_frames[srcFrameId]; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_data; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData) { if (srcData->colorSpace() != dstData->colorSpace() && *srcData->colorSpace() != *dstData->colorSpace()) { KUndo2Command tempCommand; srcData = toQShared(new Data(srcData.data(), true)); srcData->convertDataColorSpace(dstData->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags(), &tempCommand); } dstData->dataManager()->clear(); dstData->cache()->invalidate(); const QRect rect = srcData->dataManager()->extent(); dstData->dataManager()->bitBltRough(srcData->dataManager(), rect); dstData->setX(srcData->x()); dstData->setY(srcData->y()); } void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { Data *data = m_lodData.data(); Q_ASSERT(data); transferFromData(data, targetDevice); } KUndo2Command* KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { class DeviceChangeColorSpaceCommand : public KUndo2Command { public: DeviceChangeColorSpaceCommand(KisPaintDeviceSP device) : m_firstRun(true), m_device(device) { } void emitNotifications() { m_device->emitColorSpaceChanged(); m_device->setDirty(); } void redo() override { KUndo2Command::redo(); if (!m_firstRun) { m_firstRun = false; return; } emitNotifications(); } void undo() override { KUndo2Command::undo(); emitNotifications(); } private: bool m_firstRun; KisPaintDeviceSP m_device; }; KUndo2Command *parentCommand = new DeviceChangeColorSpaceCommand(q); QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand); } if (!parentCommand->childCount()) { delete parentCommand; parentCommand = 0; } else { q->emitColorSpaceChanged(); } return parentCommand; } bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile) { if (!profile) return false; const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); if (!dstColorSpace) return false; QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->assignColorSpace(dstColorSpace); } q->emitProfileChanged(); // no undo information is provided here return true; } void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel) { QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel); data->init(cs, dataManager); } } KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, new KisDefaultBounds(), 0, name); } KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, defaultBounds, parent, name); } void KisPaintDevice::init(const KoColorSpace *colorSpace, KisDefaultBoundsBaseSP defaultBounds, KisNodeWSP parent, const QString& name) { Q_ASSERT(colorSpace); setObjectName(name); // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; if (!defaultBounds) { // Reuse transitionalDefaultBounds here. Change if you change // semantics of transitionalDefaultBounds defaultBounds = m_d->transitionalDefaultBounds; } QScopedArrayPointer defaultPixel(new quint8[colorSpace->pixelSize()]); colorSpace->fromQColor(Qt::transparent, defaultPixel.data()); m_d->init(colorSpace, defaultPixel.data()); Q_ASSERT(m_d->colorSpace()); setDefaultBounds(defaultBounds); setParentNode(parent); } KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode) : QObject() , KisShared() , m_d(new Private(this)) { if (this != &rhs) { // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; // copy data objects with or without frames m_d->cloneAllDataObjects(rhs.m_d, copyMode == KritaUtils::CopyAllFrames); if (copyMode == KritaUtils::CopyAllFrames && rhs.m_d->framesInterface) { KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface); KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this)); } setDefaultBounds(rhs.m_d->defaultBounds); setParentNode(newParentNode); } } KisPaintDevice::~KisPaintDevice() { delete m_d; } void KisPaintDevice::setProjectionDevice(bool value) { m_d->isProjectionDevice = value; } void KisPaintDevice::prepareClone(KisPaintDeviceSP src) { m_d->prepareClone(src); Q_ASSERT(fastBitBltPossible(src)); } void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = rect & src->extent(); fastBitBlt(src, optimizedRect); } void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = minimalRect & src->extent(); fastBitBltRough(src, optimizedRect); } void KisPaintDevice::setDirty() { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(); } void KisPaintDevice::setDirty(const QRect & rc) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rc); } void KisPaintDevice::setDirty(const QRegion & region) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(region); } void KisPaintDevice::setDirty(const QVector rects) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rects); } void KisPaintDevice::requestTimeSwitch(int time) { if (m_d->parent.isValid()) { m_d->parent->requestTimeSwitch(time); } } int KisPaintDevice::sequenceNumber() const { return m_d->cache()->sequenceNumber(); } void KisPaintDevice::estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const { m_d->estimateMemoryStats(imageData, temporaryData, lodData); } void KisPaintDevice::setParentNode(KisNodeWSP parent) { m_d->parent = parent; } // for testing purposes only KisNodeWSP KisPaintDevice::parentNode() const { return m_d->parent; } void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds) { m_d->defaultBounds = defaultBounds; m_d->cache()->invalidate(); } KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const { return m_d->defaultBounds; } void KisPaintDevice::moveTo(const QPoint &pt) { m_d->currentStrategy()->move(pt); m_d->cache()->invalidate(); } QPoint KisPaintDevice::offset() const { return QPoint(x(), y()); } void KisPaintDevice::moveTo(qint32 x, qint32 y) { moveTo(QPoint(x, y)); } void KisPaintDevice::setX(qint32 x) { moveTo(QPoint(x, m_d->y())); } void KisPaintDevice::setY(qint32 y) { moveTo(QPoint(m_d->x(), y)); } qint32 KisPaintDevice::x() const { return m_d->x(); } qint32 KisPaintDevice::y() const { return m_d->y(); } void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const { QRect rc = extent(); x = rc.x(); y = rc.y(); w = rc.width(); h = rc.height(); } QRect KisPaintDevice::extent() const { return m_d->currentStrategy()->extent(); } QRegion KisPaintDevice::region() const { return m_d->currentStrategy()->region(); } QRect KisPaintDevice::nonDefaultPixelArea() const { return m_d->cache()->nonDefaultPixelArea(); } QRect KisPaintDevice::exactBounds() const { return m_d->cache()->exactBounds(); } QRect KisPaintDevice::exactBoundsAmortized() const { return m_d->cache()->exactBoundsAmortized(); } namespace Impl { struct CheckFullyTransparent { CheckFullyTransparent(const KoColorSpace *colorSpace) : m_colorSpace(colorSpace) { } bool isPixelEmpty(const quint8 *pixelData) { return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8; } private: const KoColorSpace *m_colorSpace; }; struct CheckNonDefault { CheckNonDefault(int pixelSize, const quint8 *defaultPixel) : m_pixelSize(pixelSize), m_defaultPixel(defaultPixel) { } bool isPixelEmpty(const quint8 *pixelData) { return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0; } private: int m_pixelSize; const quint8 *m_defaultPixel; }; template QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp) { if (startRect == endRect) return startRect; // the passed extent might have weird invalid structure that // can overflow integer precision when calling startRect.right() if (!startRect.isValid()) return QRect(); // Solution n°2 int x, y, w, h; int boundLeft, boundTop, boundRight, boundBottom; int endDirN, endDirE, endDirS, endDirW; startRect.getRect(&x, &y, &w, &h); if (endRect.isEmpty()) { endDirS = startRect.bottom(); endDirN = startRect.top(); endDirE = startRect.right(); endDirW = startRect.left(); startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } else { endDirS = endRect.top() - 1; endDirN = endRect.bottom() + 1; endDirE = endRect.left() - 1; endDirW = endRect.right() + 1; endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } // XXX: a small optimization is possible by using H/V line iterators in the first // and third cases, at the cost of making the code a bit more complex KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG(x, y); bool found = false; { for (qint32 y2 = y; y2 <= endDirS; ++y2) { for (qint32 x2 = x; x2 < x + w || found; ++ x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundTop = y2; found = true; break; } } if (found) break; } } /** * If the first pass hasn't found any opaque pixel, there is no * reason to check that 3 more times. They will not appear in the * meantime. Just return an empty bounding rect. */ if (!found && endRect.isEmpty()) { return QRect(); } found = false; for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) { for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundBottom = y2; found = true; break; } } if (found) break; } found = false; { for (qint32 x2 = x; x2 <= endDirE ; ++x2) { for (qint32 y2 = y; y2 < y + h || found; ++y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundLeft = x2; found = true; break; } } if (found) break; } } found = false; // Look for right edge ) { for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) { for (qint32 y2 = y + h - 1; y2 >= y || found; --y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundRight = x2; found = true; break; } } if (found) break; } } return QRect(boundLeft, boundTop, boundRight - boundLeft + 1, boundBottom - boundTop + 1); } } QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const { QRect startRect = extent(); QRect endRect; quint8 defaultOpacity = defaultPixel().opacityU8(); if (defaultOpacity != OPACITY_TRANSPARENT_U8) { if (!nonDefaultOnly) { /** * We will calculate exact bounds only outside of the * image bounds, and that'll be nondefault area only. */ endRect = defaultBounds()->bounds(); nonDefaultOnly = true; } else { startRect = region().boundingRect(); } } if (nonDefaultOnly) { const KoColor defaultPixel = this->defaultPixel(); Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } else { Impl::CheckFullyTransparent compareOp(m_d->colorSpace()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } return endRect; } QRegion KisPaintDevice::regionExact() const { QRegion resultRegion; QVector rects = region().rects(); const KoColor defaultPixel = this->defaultPixel(); Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data()); Q_FOREACH (const QRect &rc1, rects) { const int patchSize = 64; QVector smallerRects = KritaUtils::splitRectIntoPatches(rc1, QSize(patchSize, patchSize)); Q_FOREACH (const QRect &rc2, smallerRects) { const QRect result = Impl::calculateExactBoundsImpl(this, rc2, QRect(), compareOp); if (!result.isEmpty()) { resultRegion += result; } } } return resultRegion; } void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h) { crop(QRect(x, y, w, h)); } void KisPaintDevice::crop(const QRect &rect) { m_d->currentStrategy()->crop(rect); } void KisPaintDevice::purgeDefaultPixels() { KisDataManagerSP dm = m_d->dataManager(); dm->purge(dm->extent()); } void KisPaintDevice::setDefaultPixel(const KoColor &defPixel) { KoColor color(defPixel); color.convertTo(colorSpace()); m_d->dataManager()->setDefaultPixel(color.data()); m_d->cache()->invalidate(); } KoColor KisPaintDevice::defaultPixel() const { return KoColor(m_d->dataManager()->defaultPixel(), colorSpace()); } void KisPaintDevice::clear() { m_d->dataManager()->clear(); m_d->cache()->invalidate(); } void KisPaintDevice::clear(const QRect & rc) { m_d->currentStrategy()->clear(rc); } void KisPaintDevice::fill(const QRect & rc, const KoColor &color) { KIS_ASSERT_RECOVER_RETURN(*color.colorSpace() == *colorSpace()); m_d->currentStrategy()->fill(rc, color.data()); } void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel) { m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel); } bool KisPaintDevice::write(KisPaintDeviceWriter &store) { return m_d->dataManager()->write(store); } bool KisPaintDevice::read(QIODevice *stream) { bool retval; retval = m_d->dataManager()->read(stream); m_d->cache()->invalidate(); return retval; } void KisPaintDevice::emitColorSpaceChanged() { emit colorSpaceChanged(m_d->colorSpace()); } void KisPaintDevice::emitProfileChanged() { emit profileChanged(m_d->colorSpace()->profile()); } KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { KUndo2Command *command = m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags); return command; } bool KisPaintDevice::setProfile(const KoColorProfile * profile) { return m_d->assignProfile(profile); } KisDataManagerSP KisPaintDevice::dataManager() const { return m_d->dataManager(); } void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile, qint32 offsetX, qint32 offsetY) { QImage image = _image; if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } // Don't convert if not no profile is given and both paint dev and qimage are rgba. if (!profile && colorSpace()->id() == "RGBA") { writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height()); } else { try { quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()]; KoColorSpaceRegistry::instance() ->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile) ->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); writeBytes(dstData, offsetX, offsetY, image.width(), image.height()); delete[] dstData; } catch (const std::bad_alloc&) { warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes"; return; } } m_d->cache()->invalidate(); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { qint32 x1; qint32 y1; qint32 w; qint32 h; QRect rc = exactBounds(); x1 = rc.x(); y1 = rc.y(); w = rc.width(); h = rc.height(); return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, const QRect &rc, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { return convertToQImage(dstProfile, rc.x(), rc.y(), rc.width(), rc.height(), renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (w < 0) return QImage(); if (h < 0) return QImage(); quint8 *data = 0; try { data = new quint8 [w * h * pixelSize()]; } catch (const std::bad_alloc&) { warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize(); //delete[] data; // data is not allocated, so don't free it return QImage(); } Q_CHECK_PTR(data); // XXX: Is this really faster than converting line by line and building the QImage directly? // This copies potentially a lot of data. readBytes(data, x1, y1, w, h); QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags); delete[] data; return image; } inline bool moveBy(KisSequentialConstIterator& iter, int numPixels) { int pos = 0; while (pos < numPixels) { int step = std::min(iter.nConseqPixels(), numPixels - pos); if (!iter.nextPixels(step)) return false; pos += step; } return true; } static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect) { KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace()); qint32 pixelSize = srcDev->pixelSize(); KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG(0, 0); KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0); for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) { qint32 iY = srcY0 + (y * srcHeight) / h; for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) { qint32 iX = srcX0 + (x * srcWidth) / w; srcIter->moveTo(iX, iY); dstIter->moveTo(x, y); memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize); } } return thumbnail; } QSize fixThumbnailSize(QSize size) { if (!size.width() && size.height()) { size.setWidth(1); } if (size.width() && !size.height()) { size.setHeight(1); } return size; } KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const { QSize thumbnailSize(w, h); QRect imageRect = rect.isValid() ? rect : extent(); if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) { thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio); } thumbnailSize = fixThumbnailSize(thumbnailSize); //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image if (imageRect.isEmpty() || thumbnailSize.isEmpty()) { return new KisPaintDevice(colorSpace()); } int srcWidth, srcHeight; int srcX0, srcY0; imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight); if (!outputRect.isValid()) { outputRect = QRect(0, 0, w, h); } KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(), thumbnailSize.width(), thumbnailSize.height(), outputRect); return thumbnail; } KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect, QRect outputTileRect) const { QSize thumbnailSize(w, h); qreal oversampleAdjusted = qMax(oversample, 1.); QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize; QRect outputRect; QRect imageRect = rect.isValid() ? rect : extent(); qint32 hstart = thumbnailOversampledSize.height(); if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) { thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio); } thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize); //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) { return new KisPaintDevice(colorSpace()); } oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height()); if (outputTileRect.isValid()) { //compensating output rectangle for oversampling outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight()); outputRect = outputRect.intersected(outputTileRect); } KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(), thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect); if (oversample != 1. && oversampleAdjusted != 1.) { KoDummyUpdater updater; KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &updater, KisFilterStrategyRegistry::instance()->value("Bilinear")); worker.run(); } return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QSize size = fixThumbnailSize(QSize(w, h)); KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect); QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags); return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QSize size = fixThumbnailSize(QSize(w, h)); return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags); } KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createVLineIteratorNG(x, y, w); } KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w); } KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const { return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator()); } KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const { return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator()); } KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG(qint32 x, qint32 y) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createRandomAccessorNG(x, y); } KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG(qint32 x, qint32 y) const { return m_d->currentStrategy()->createRandomConstAccessorNG(x, y); } KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const { KisPaintDevice* pd = const_cast(this); return new KisRandomSubAccessor(pd); } void KisPaintDevice::clearSelection(KisSelectionSP selection) { const KoColorSpace *colorSpace = m_d->colorSpace(); - QRect r = selection->selectedExactRect() & m_d->defaultBounds->bounds(); + const QRect r = selection->selectedExactRect(); if (r.isValid()) { { KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width()); KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width()); const KoColor defaultPixel = this->defaultPixel(); bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8); for (qint32 y = 0; y < r.height(); y++) { do { // XXX: Optimize by using stretches colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1); if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) { memcpy(devIt->rawData(), defaultPixel.data(), colorSpace->pixelSize()); } } while (devIt->nextPixel() && selectionIt->nextPixel()); devIt->nextRow(); selectionIt->nextRow(); } } // purge() must be executed **after** all iterators have been destroyed! m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y())); setDirty(r); } } bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; colorSpace()->toQColor(pix, c); return true; } bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; kc->setColor(pix, m_d->colorSpace()); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c) { KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); colorSpace()->fromQColor(c, iter->rawData()); m_d->cache()->invalidate(); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc) { const quint8 * pix; KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); if (kc.colorSpace() != m_d->colorSpace()) { KoColor kc2(kc, m_d->colorSpace()); pix = kc2.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } else { pix = kc.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } m_d->cache()->invalidate(); return true; } bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src) { return m_d->fastBitBltPossible(src); } void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBlt(src, rect); } void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltOldData(src, rect); } void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRough(src, rect); } void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRoughOldData(src, rect); } void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const { readBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const { m_d->currentStrategy()->readBytes(data, rect); } void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) { writeBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect) { m_d->currentStrategy()->writeBytes(data, rect); } QVector KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const { return m_d->currentStrategy()->readPlanarBytes(x, y, w, h); } void KisPaintDevice::writePlanarBytes(QVector planes, qint32 x, qint32 y, qint32 w, qint32 h) { m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h); } quint32 KisPaintDevice::pixelSize() const { quint32 _pixelSize = m_d->colorSpace()->pixelSize(); Q_ASSERT(_pixelSize > 0); return _pixelSize; } quint32 KisPaintDevice::channelCount() const { quint32 _channelCount = m_d->colorSpace()->channelCount(); Q_ASSERT(_channelCount > 0); return _channelCount; } KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id) { Q_ASSERT(!m_d->framesInterface); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); Q_ASSERT(!m_d->contentChannel); m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds)); // Raster channels always have at least one frame (representing a static image) KUndo2Command tempParentCommand; m_d->contentChannel->addKeyframe(0, &tempParentCommand); return m_d->contentChannel.data(); } KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const { if (m_d->contentChannel) { return m_d->contentChannel.data(); } return 0; } const KoColorSpace* KisPaintDevice::colorSpace() const { Q_ASSERT(m_d->colorSpace() != 0); return m_d->colorSpace(); } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const { KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace()); device->setDefaultBounds(defaultBounds()); return device; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const { KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource); clone->setDefaultBounds(defaultBounds()); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const { KisPaintDeviceSP clone = new KisPaintDevice(colorSpace()); clone->setDefaultBounds(defaultBounds()); clone->makeCloneFromRough(cloneSource, roughRect); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const { return new KisFixedPaintDevice(compositionSourceColorSpace()); } const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const { return colorSpace(); } QVector KisPaintDevice::channelSizes() const { QVector sizes; QList channels = colorSpace()->channels(); std::sort(channels.begin(), channels.end()); Q_FOREACH (KoChannelInfo * channelInfo, channels) { sizes.append(channelInfo->size()); } return sizes; } KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject() { KisDataManager::releaseInternalPools(); } KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject() { return new MemoryReleaseObject(); } KisPaintDevice::LodDataStruct::~LodDataStruct() { } QRegion KisPaintDevice::regionForLodSyncing() const { return m_d->regionForLodSyncing(); } KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod) { return m_d->createLodDataStruct(lod); } void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect) { m_d->updateLodDataStruct(dst, srcRect); } void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst) { m_d->uploadLodDataStruct(dst); } void KisPaintDevice::generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod) { m_d->generateLodCloneDevice(dst, originalRect, lod); } KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface() { return m_d->framesInterface.data(); } /******************************************************************/ /* KisPaintDeviceFramesInterface */ /******************************************************************/ KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice) : q(parentDevice) { } QList KisPaintDeviceFramesInterface::frames() { return q->m_d->frameIds(); } int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { return q->m_d->createFrame(copy, copySrc, offset, parentCommand); } void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand) { return q->m_d->deleteFrame(frame, parentCommand); } void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { q->m_d->fetchFrame(frameId, targetDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(dstFrameId, srcDevice); } QRect KisPaintDeviceFramesInterface::frameBounds(int frameId) { return q->m_d->frameBounds(frameId); } QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const { return q->m_d->frameOffset(frameId); } void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); q->m_d->setFrameDefaultPixel(defPixel, frameId); } KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return KoColor(Qt::red, q->m_d->colorSpace()); } return q->m_d->frameDefaultPixel(frameId); } bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->writeFrame(store, frameId); } bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->readFrame(stream, frameId); } int KisPaintDeviceFramesInterface::currentFrameId() const { return q->m_d->currentFrameId(); } KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return q->m_d->dataManager(); } return q->m_d->frameDataManager(frameId); } void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->invalidateFrameCache(frameId); } void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->setFrameOffset(frameId, offset); } KisPaintDeviceFramesInterface::TestingDataObjects KisPaintDeviceFramesInterface::testingGetDataObjects() const { TestingDataObjects objects; objects.m_data = q->m_d->m_data.data(); objects.m_lodData = q->m_d->m_lodData.data(); objects.m_externalFrameData = q->m_d->m_externalFrameData.data(); typedef KisPaintDevice::Private::FramesHash FramesHash; FramesHash::const_iterator it = q->m_d->m_frames.constBegin(); FramesHash::const_iterator end = q->m_d->m_frames.constEnd(); for (; it != end; ++it) { objects.m_frames.insert(it.key(), it.value().data()); } objects.m_currentData = q->m_d->currentData(); return objects; } QList KisPaintDeviceFramesInterface::testingGetDataObjectsList() const { return q->m_d->allDataObjects(); } void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { m_d->tesingFetchLodDevice(targetDevice); } diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index a48e8ce96e..caa3be5aa4 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1655 +1,1663 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 Boudewijn Rempt * * 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 "kis_dlg_preferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoID.h" #include #include #include #include #include #include #include #include "KisProofingConfiguration.h" #include "KoColorConversionTransformation.h" #include "kis_action_registry.h" #include #include #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "kis_action_registry.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_color_manager.h" #include "kis_config.h" #include "kis_cursor.h" #include "kis_image_config.h" #include "kis_preference_set_registry.h" #include "widgets/kis_cmb_idlist.h" #include #include "kis_file_name_requester.h" #include #include #include #include "slider_and_spin_box_sync.h" // for the performance update #include #include #include "input/config/kis_input_configuration_page.h" #include "input/wintab/drawpile_tablettester/tablettester.h" #ifdef Q_OS_WIN #include "config_use_qt_tablet_windows.h" # ifndef USE_QT_TABLET_WINDOWS # include # endif #endif struct BackupSuffixValidator : public QValidator { BackupSuffixValidator(QObject *parent) : QValidator(parent) , invalidCharacters(QStringList() << "0" << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9" << "/" << "\\" << ":" << ";" << " ") {} ~BackupSuffixValidator() override {} const QStringList invalidCharacters; State validate(QString &line, int &/*pos*/) const override { Q_FOREACH(const QString invalidChar, invalidCharacters) { if (line.contains(invalidChar)) { return Invalid; } } return Acceptable; } }; GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg(true); // // Cursor Tab // m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline()); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor()); cursorColorBtutton->setColor(cursorColor); // // Window Tab // m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor()); m_mdiColor->setColor(mdiColor); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", true).toBool()); chkUsageLogging->setChecked(kritarc.value("LogUsage", true).toBool()); m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); // // Tools tab // m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); cmbFlowMode->setCurrentIndex((int)!cfg.readEntry("useCreamyAlphaDarken", true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste()); m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled()); m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag")); m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag")); m_cmbKineticScrollingGesture->addItem(i18n("On Middle-Click Drag")); //m_cmbKineticScrollingGesture->addItem(i18n("On Right Click Drag")); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); m_kineticScrollingSensitivitySlider->setRange(0, 100); m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity()); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars()); // // File handling // int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); chkHideAutosaveFiles->setChecked(cfg.readEntry("autosavefileshidden", true)); m_chkCompressKra->setChecked(cfg.compressKra()); chkZip64->setChecked(cfg.useZip64()); m_backupFileCheckBox->setChecked(cfg.backupFile()); cmbBackupFileLocation->setCurrentIndex(cfg.readEntry("backupfilelocation", 0)); txtBackupFileSuffix->setText(cfg.readEntry("backupfilesuffix", "~")); QValidator *validator = new BackupSuffixValidator(txtBackupFileSuffix); txtBackupFileSuffix->setValidator(validator); intNumBackupFiles->setValue(cfg.readEntry("numberofbackupfiles", 1)); // // Miscellaneous // cmbStartupSession->addItem(i18n("Open default window")); cmbStartupSession->addItem(i18n("Load previous session")); cmbStartupSession->addItem(i18n("Show session manager")); cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup()); chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); m_undoStackSize->setValue(cfg.undoStackLimit()); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); chkShowRootLayer->setChecked(cfg.showRootLayer()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); bool dontUseNative = true; #ifdef Q_OS_UNIX if (qgetenv("XDG_CURRENT_DESKTOP") == "KDE") { dontUseNative = false; } #endif #ifdef Q_OS_WIN dontUseNative = false; #endif m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", dontUseNative)); intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000)); // // Resources // m_urlCacheDbLocation->setMode(KoFileDialog::OpenDirectory); m_urlCacheDbLocation->setConfigurationName("cachedb_location"); m_urlCacheDbLocation->setFileName(cfg.readEntry(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); m_urlResourceFolder->setMode(KoFileDialog::OpenDirectory); m_urlResourceFolder->setConfigurationName("resource_directory"); m_urlResourceFolder->setFileName(cfg.readEntry(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); } void GeneralTab::setDefault() { KisConfig cfg(true); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); chkHideAutosaveFiles->setChecked(true); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); cmbBackupFileLocation->setCurrentIndex(0); txtBackupFileSuffix->setText("~"); intNumBackupFiles->setValue(1); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline(true)); m_chkNativeFileDialog->setChecked(false); intMaxBrushSize->setValue(1000); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor(true)); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); chkZip64->setChecked(cfg.useZip64(true)); m_chkHiDPI->setChecked(false); m_chkSingleApplication->setChecked(true); m_chkHiDPI->setChecked(true); chkUsageLogging->setChecked(true); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); cmbFlowMode->setCurrentIndex(0); m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled(true)); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture(true)); m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity(true)); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); m_urlCacheDbLocation->setFileName(cfg.readEntry(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); m_urlResourceFolder->setFileName(cfg.readEntry(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } KisConfig::SessionOnStartup GeneralTab::sessionOnStartup() const { return (KisConfig::SessionOnStartup)cmbStartupSession->currentIndex(); } bool GeneralTab::saveSessionOnQuit() const { return chkSaveSessionOnQuit->isChecked(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::useZip64() { return chkZip64->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::kineticScrollingEnabled() { return m_groupBoxKineticScrollingSettings->isChecked(); } int GeneralTab::kineticScrollingGesture() { return m_cmbKineticScrollingGesture->currentIndex(); } int GeneralTab::kineticScrollingSensitivity() { return m_kineticScrollingSensitivitySlider->value(); } bool GeneralTab::kineticScrollingHiddenScrollbars() { return m_chkKineticScrollingHideScrollbars->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } #include "kactioncollection.h" #include "KisActionsSnapshot.h" ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); m_snapshot.reset(new KisActionsSnapshot); KActionCollection *collection = KisPart::instance()->currentMainwindow()->actionCollection(); Q_FOREACH (QAction *action, collection->actions()) { m_snapshot->addAction(action->objectName(), action); } QMap sortedCollections = m_snapshot->actionCollections(); for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) { m_page->addCollection(it.value(), it.key()); } } ShortcutSettingsTab::~ShortcutSettingsTab() { } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::cancelChanges() { m_page->undo(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg(true); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileLabels << lbl; KisSqueezedComboBox *cmb = new KisSqueezedComboBox(); cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); monitorProfileGrid->addRow(lbl, cmb); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors()); KisImageConfig cfgImage(true); KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); m_page->sldAdaptationState->setMaximum(20); m_page->sldAdaptationState->setMinimum(0); m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) { qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName(); continue; } iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName()); } KisConfig cfg(true); refillMonitorProfiles(KoID("RGBA")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { KisConfig cfg(true); if (useSystemProfile) { QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { refillMonitorProfiles(KoID("RGBA")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); refillMonitorProfiles(KoID("RGBA")); KisConfig cfg(true); KisImageConfig cfgImage(true); KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_page->sldAdaptationState->setValue(0); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } QMap profileList; Q_FOREACH(const KoColorProfile *profile, KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId.id())) { profileList[profile->name()] = profile; } Q_FOREACH (const KoColorProfile *profile, profileList.values()) { //qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId.id())); } } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN #ifndef USE_QT_TABLET_WINDOWS if (KisTabletSupportWin8::isAvailable()) { KisConfig cfg(true); m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true)); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true)); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); } #else m_page->grpTabletApi->setVisible(false); #endif #endif } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg(true); KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN #ifndef USE_QT_TABLET_WINDOWS if (KisTabletSupportWin8::isAvailable()) { m_page->radioWintab->setChecked(!cfg.useWin8PointerInput()); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput()); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); m_page->grpTabletApi->setVisible(false); } #else m_page->grpTabletApi->setVisible(false); #endif #else m_page->grpTabletApi->setVisible(false); #endif connect(m_page->btnTabletTest, SIGNAL(clicked()), SLOT(slotTabletTest())); } void TabletSettingsTab::slotTabletTest() { TabletTestDialog tabletTestDialog(this); tabletTestDialog.exec(); } //--------------------------------------------------------------------------------------------------- #include "kis_acyclic_signal_connector.h" int getTotalRAM() { return KisImageConfig(true).totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg(true); const double totalRAM = cfg.totalRAM(); lblTotalMemory->setText(KFormat().formatByteSize(totalRAM * 1024 * 1024, 0, KFormat::IECBinaryDialect, KFormat::UnitMegaByte)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&KisIntParseSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); sliderThreadsLimit->setRange(1, QThread::idealThreadCount()); sliderFrameClonesLimit->setRange(1, QThread::idealThreadCount()); sliderFpsLimit->setRange(20, 100); sliderFpsLimit->setSuffix(i18n(" fps")); connect(sliderThreadsLimit, SIGNAL(valueChanged(int)), SLOT(slotThreadsLimitChanged(int))); connect(sliderFrameClonesLimit, SIGNAL(valueChanged(int)), SLOT(slotFrameClonesLimitChanged(int))); intCachedFramesSizeLimit->setRange(1, 10000); intCachedFramesSizeLimit->setSuffix(i18n(" px")); intCachedFramesSizeLimit->setSingleStep(1); intCachedFramesSizeLimit->setPageStep(1000); intRegionOfInterestMargin->setRange(1, 100); intRegionOfInterestMargin->setSuffix(i18n(" %")); intRegionOfInterestMargin->setSingleStep(1); intRegionOfInterestMargin->setPageStep(10); connect(chkCachedFramesSizeLimit, SIGNAL(toggled(bool)), intCachedFramesSizeLimit, SLOT(setEnabled(bool))); connect(chkUseRegionOfInterest, SIGNAL(toggled(bool)), intRegionOfInterestMargin, SLOT(setEnabled(bool))); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg(true); sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault); m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault); sliderThreadsLimit->setValue(m_lastUsedThreadsLimit); sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit); sliderFpsLimit->setValue(cfg.fpsLimit(requestDefault)); { KisConfig cfg2(true); chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault)); chkBrushSpeedLogging->setChecked(cfg2.enableBrushSpeedLogging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); chkBackgroundCacheGeneration->setChecked(cfg2.calculateAnimationCacheInBackground(requestDefault)); } if (cfg.useOnDiskAnimationCacheSwapping(requestDefault)) { optOnDisk->setChecked(true); } else { optInMemory->setChecked(true); } chkCachedFramesSizeLimit->setChecked(cfg.useAnimationCacheFrameSizeLimit(requestDefault)); intCachedFramesSizeLimit->setValue(cfg.animationCacheFrameSizeLimit(requestDefault)); intCachedFramesSizeLimit->setEnabled(chkCachedFramesSizeLimit->isChecked()); chkUseRegionOfInterest->setChecked(cfg.useAnimationCacheRegionOfInterest(requestDefault)); intRegionOfInterestMargin->setValue(cfg.animationCacheRegionOfInterestMargin(requestDefault) * 100.0); intRegionOfInterestMargin->setEnabled(chkUseRegionOfInterest->isChecked()); } void PerformanceTab::save() { KisImageConfig cfg(false); cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); cfg.setMaxNumberOfThreads(sliderThreadsLimit->value()); cfg.setFrameRenderingClones(sliderFrameClonesLimit->value()); cfg.setFpsLimit(sliderFpsLimit->value()); { KisConfig cfg2(true); cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked()); cfg2.setEnableBrushSpeedLogging(chkBrushSpeedLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); cfg2.setCalculateAnimationCacheInBackground(chkBackgroundCacheGeneration->isChecked()); } cfg.setUseOnDiskAnimationCacheSwapping(optOnDisk->isChecked()); cfg.setUseAnimationCacheFrameSizeLimit(chkCachedFramesSizeLimit->isChecked()); cfg.setAnimationCacheFrameSizeLimit(intCachedFramesSizeLimit->value()); cfg.setUseAnimationCacheRegionOfInterest(chkUseRegionOfInterest->isChecked()); cfg.setAnimationCacheRegionOfInterestMargin(intRegionOfInterestMargin->value() / 100.0); } void PerformanceTab::selectSwapDir() { KisImageConfig cfg(true); QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); if (swapDir.isEmpty()) { return; } lblSwapFileLocation->setText(swapDir); } void PerformanceTab::slotThreadsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value)); m_lastUsedThreadsLimit = value; } void PerformanceTab::slotFrameClonesLimitChanged(int value) { KisSignalsBlocker b(sliderThreadsLimit); sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value)); m_lastUsedClonesLimit = value; } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" #include "opengl/KisOpenGLModeProber.h" #include "opengl/KisScreenInformationAdapter.h" #include #include QString colorSpaceString(KisSurfaceColorSpace cs, int depth) { const QString csString = #ifdef HAVE_HDR cs == KisSurfaceColorSpace::bt2020PQColorSpace ? "Rec. 2020 PQ" : cs == KisSurfaceColorSpace::scRGBColorSpace ? "Rec. 709 Linear" : #endif cs == KisSurfaceColorSpace::sRGBColorSpace ? "sRGB" : cs == KisSurfaceColorSpace::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) { KisConfig cfg(true); const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); #ifdef Q_OS_WIN 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::RendererOpenGLES) { qtPreferredRendererText = rendererOpenGLESText; } else { qtPreferredRendererText = rendererOpenGLText; } cmbPreferredRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); cmbPreferredRenderer->setCurrentIndex(0); if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) { cmbPreferredRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->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); } } #endif if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); // Don't show the high quality filtering mode if it's not available if (!KisOpenGL::supportsLoD()) { cmbFilterMode->removeItem(3); } } lblCurrentDisplayFormat->setText(""); lblCurrentRootSurfaceFormat->setText(""); lblHDRWarning->setText(""); cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(KisSurfaceColorSpace::sRGBColorSpace, 8)); #ifdef HAVE_HDR cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(KisSurfaceColorSpace::bt2020PQColorSpace, 10)); cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(KisSurfaceColorSpace::scRGBColorSpace, 16)); #endif cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22)); slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex()); QOpenGLContext *context = QOpenGLContext::currentContext(); if (!context) { context = QOpenGLContext::globalShareContext(); } if (context) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) QScreen *screen = QGuiApplication::screenAt(rect().center()); +#else + QScreen *screen = 0; +#endif 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(); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) KisSurfaceColorSpace colorSpace = currentFormat.colorSpace(); #else KisSurfaceColorSpace colorSpace = KisSurfaceColorSpace::DefaultColorSpace; #endif lblCurrentRootSurfaceFormat->setText(colorSpaceString(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); } else { QString text(" "); text.append(i18n("Warning(s):")); text.append("
    "); Q_FOREACH (const QString &warning, openglWarnings) { text.append("
  • "); text.append(warning.toHtmlEscaped()); text.append("
  • "); } text.append("
"); lblOpenGLWarnings->setText(text); lblOpenGLWarnings->setVisible(true); } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KisImageConfig imageCfg(false); KoColor c; c.fromQColor(imageCfg.selectionOverlayMaskColor()); c.setOpacity(1.0); btnSelectionOverlayColor->setColor(c); sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2); sldSelectionOverlayOpacity->setSingleStep(0.05); sldSelectionOverlayOpacity->setValue(imageCfg.selectionOverlayMaskColor().alphaF()); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1()); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2()); colorChecks2->setColor(ck2); KoColor cb(KoColorSpaceRegistry::instance()->rgb8()); cb.fromQColor(cfg.canvasBorderColor()); canvasBorder->setColor(cb); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor()); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100); } void DisplaySettingsTab::setDefault() { KisConfig cfg(true); cmbPreferredRenderer->setCurrentIndex(0); if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1(true)); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2(true)); colorChecks2->setColor(ck2); KoColor cvb(KoColorSpaceRegistry::instance()->rgb8()); cvb.fromQColor(cfg.canvasBorderColor(true)); canvasBorder->setColor(cvb); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); 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) { chkUseTextureBuffer->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); } void DisplaySettingsTab::slotPreferredSurfaceFormatChanged(int index) { Q_UNUSED(index); QOpenGLContext *context = QOpenGLContext::currentContext(); if (context) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) QScreen *screen = QGuiApplication::screenAt(rect().center()); +#else + QScreen *screen = 0; +#endif KisScreenInformationAdapter adapter(context); if (adapter.isValid()) { KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen); if (info.isValid()) { if (cmbPreferedRootSurfaceFormat->currentIndex() != formatToIndex(KisConfig::BT709_G22) && info.colorSpace == KisSurfaceColorSpace::sRGBColorSpace) { lblHDRWarning->setText(i18n("WARNING: current display doesn't support HDR rendering")); } else { lblHDRWarning->setText(""); } } } } } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg(true); chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg(true); chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Configure Krita")); setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); setFaceType(KPageDialog::Tree); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("go-home")); m_pages << page; addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); m_pages << page; addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); m_pages << page; // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); m_pages << page; addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); m_pages << page; addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); m_pages << page; addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); m_pages << page; addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); m_pages << page; addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); m_pages << page; QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); restoreDefaultsButton->setText(i18nc("@action:button", "Restore Defaults")); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); KisConfig cfg(true); QString currentPageName = cfg.readEntry("KisDlgPreferences/CurrentPage"); Q_FOREACH(KPageWidgetItem *page, m_pages) { if (page->objectName() == currentPageName) { setCurrentPage(page); break; } } } KisDlgPreferences::~KisDlgPreferences() { KisConfig cfg(true); cfg.writeEntry("KisDlgPreferences/CurrentPage", currentPage()->objectName()); } void KisDlgPreferences::showEvent(QShowEvent *event){ KPageDialog::showEvent(event); button(QDialogButtonBox::Cancel)->setAutoDefault(false); button(QDialogButtonBox::Ok)->setAutoDefault(false); button(QDialogButtonBox::RestoreDefaults)->setAutoDefault(false); button(QDialogButtonBox::Cancel)->setDefault(false); button(QDialogButtonBox::Ok)->setDefault(false); button(QDialogButtonBox::RestoreDefaults)->setDefault(false); } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "general") { m_general->setDefault(); } else if (currentPage()->objectName() == "shortcuts") { m_shortcutSettings->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg(false); cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setForceAlwaysFullSizedOutline(!dialog->m_general->m_changeBrushOutline->isChecked()); cfg.setSessionOnStartup(dialog->m_general->sessionOnStartup()); cfg.setSaveSessionOnQuit(dialog->m_general->saveSessionOnQuit()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry("DontUseNativeFileDialog", !dialog->m_general->m_chkNativeFileDialog->isChecked()); cfg.writeEntry("maximumBrushSize", dialog->m_general->intMaxBrushSize->value()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.writeEntry("autosavefileshidden", dialog->m_general->chkHideAutosaveFiles->isChecked()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.writeEntry("backupfilelocation", dialog->m_general->cmbBackupFileLocation->currentIndex()); cfg.writeEntry("backupfilesuffix", dialog->m_general->txtBackupFileSuffix->text()); cfg.writeEntry("numberofbackupfiles", dialog->m_general->intNumBackupFiles->value()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); cfg.setUseZip64(dialog->m_general->useZip64()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("EnableHiDPI", dialog->m_general->m_chkHiDPI->isChecked()); kritarc.setValue("EnableSingleApplication", dialog->m_general->m_chkSingleApplication->isChecked()); kritarc.setValue("LogUsage", dialog->m_general->chkUsageLogging->isChecked()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.writeEntry("useCreamyAlphaDarken", (bool)!dialog->m_general->cmbFlowMode->currentIndex()); cfg.setKineticScrollingEnabled(dialog->m_general->kineticScrollingEnabled()); cfg.setKineticScrollingGesture(dialog->m_general->kineticScrollingGesture()); cfg.setKineticScrollingSensitivity(dialog->m_general->kineticScrollingSensitivity()); cfg.setKineticScrollingHideScrollbars(dialog->m_general->kineticScrollingHiddenScrollbars()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked()); cfg.setActivateTransformToolAfterPaste(dialog->m_general->chkEnableTranformToolAfterPaste->isChecked()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); cfg.writeEntry(KisResourceCacheDb::dbLocationKey, dialog->m_general->m_urlCacheDbLocation->fileName()); cfg.writeEntry(KisResourceLocator::resourceLocationKey, dialog->m_general->m_urlResourceFolder->fileName()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); KisImageConfig cfgImage(false); cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(), dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(), dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(), dialog->m_colorSettings->m_page->gamutAlarm->color(), (double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setForcePaletteColors(dialog->m_colorSettings->m_page->chkForcePaletteColor->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); #ifdef Q_OS_WIN #ifndef USE_QT_TABLET_WINDOWS if (KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(dialog->m_tabletSettings->m_page->radioWin8PointerInput->isChecked()); } #endif #endif dialog->m_performanceSettings->save(); { KisOpenGL::OpenGLRenderer renderer = static_cast( dialog->m_displaySettings->cmbPreferredRenderer->itemData( dialog->m_displaySettings->cmbPreferredRenderer->currentIndex()).toInt()); KisOpenGL::setUserPreferredOpenGLRendererConfig(renderer); } 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()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color(); c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value()); cfgImage.setSelectionOverlayMaskColor(c.toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); cfg.setCursorMainColor(dialog->m_general->cursorColorBtutton->color().toQColor()); cfg.setPixelGridColor(dialog->m_displaySettings->pixelGridColorButton->color().toQColor()); cfg.setPixelGridDrawingThreshold(dialog->m_displaySettings->pixelGridDrawingThresholdBox->value() / 100); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/flake/kis_shape_selection.cpp b/libs/ui/flake/kis_shape_selection.cpp index f53c802529..d675959ca9 100644 --- a/libs/ui/flake/kis_shape_selection.cpp +++ b/libs/ui/flake/kis_shape_selection.cpp @@ -1,390 +1,400 @@ /* * Copyright (c) 2010 Sven Langkamp * Copyright (c) 2011 Jan Hambrecht * * 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 "kis_shape_selection.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_shape_selection_model.h" #include "kis_shape_selection_canvas.h" #include "kis_take_all_shapes_command.h" #include "kis_image_view_converter.h" #include "kis_shape_layer.h" #include KisShapeSelection::KisShapeSelection(KoShapeControllerBase *shapeControllerBase, KisImageWSP image, KisSelectionWSP selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(image, selection, this)) , m_image(image) , m_shapeControllerBase(shapeControllerBase) { Q_ASSERT(m_image); setShapeId("KisShapeSelection"); setSelectable(false); m_converter = new KisImageViewConverter(image); m_canvas = new KisShapeSelectionCanvas(shapeControllerBase); m_canvas->shapeManager()->addShape(this); m_model->setObjectName("KisShapeSelectionModel"); m_model->moveToThread(image->thread()); m_canvas->setObjectName("KisShapeSelectionCanvas"); m_canvas->moveToThread(image->thread()); connect(this, SIGNAL(sigMoveShapes(QPointF)), SLOT(slotMoveShapes(QPointF))); } KisShapeSelection::~KisShapeSelection() { m_model->setShapeSelection(0); delete m_canvas; delete m_converter; } KisShapeSelection::KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(rhs.m_image, selection, this)) { m_image = rhs.m_image; m_shapeControllerBase = rhs.m_shapeControllerBase; m_converter = new KisImageViewConverter(m_image); m_canvas = new KisShapeSelectionCanvas(m_shapeControllerBase); m_canvas->shapeManager()->addShape(this); Q_FOREACH (KoShape *shape, rhs.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } this->addShape(clonedShape); } } KisSelectionComponent* KisShapeSelection::clone(KisSelection* selection) { return new KisShapeSelection(*this, selection); } bool KisShapeSelection::saveSelection(KoStore * store) const { const QSizeF sizeInPx = m_image->bounds().size(); const QSizeF sizeInPt(sizeInPx.width() / m_image->xRes(), sizeInPx.height() / m_image->yRes()); return KisShapeLayer::saveShapesToStore(store, this->shapes(), sizeInPt); } bool KisShapeSelection::loadSelection(KoStore* store) { QSizeF fragmentSize; // unused! // FIXME: we handle xRes() only! KIS_SAFE_ASSERT_RECOVER_NOOP(qFuzzyCompare(m_image->xRes(), m_image->yRes())); const qreal resolutionPPI = 72.0 * m_image->xRes(); QList shapes; if (store->open("content.svg")) { KoStoreDevice storeDev(store); storeDev.open(QIODevice::ReadOnly); shapes = KisShapeLayer::createShapesFromSvg(&storeDev, "", m_image->bounds(), resolutionPPI, m_canvas->shapeController()->resourceManager(), &fragmentSize); store->close(); Q_FOREACH (KoShape *shape, shapes) { addShape(shape); } return true; } KoOdfReadStore odfStore(store); QString errorMessage; odfStore.loadAndParse(errorMessage); if (!errorMessage.isEmpty()) { dbgKrita << errorMessage; return false; } KoXmlElement contents = odfStore.contentDoc().documentElement(); // dbgKrita <<"Start loading OASIS document..." << contents.text(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement(); KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body")); if (body.isNull()) { dbgKrita << "No office:body found!"; //setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) ); return false; } body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing"); if (body.isNull()) { dbgKrita << "No office:drawing found!"; //setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) ); return false; } KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page")); if (page.isNull()) { dbgKrita << "No office:drawing found!"; //setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) ); return false; } KoXmlElement * master = 0; if (odfStore.styles().masterPages().contains("Standard")) master = odfStore.styles().masterPages().value("Standard"); else if (odfStore.styles().masterPages().contains("Default")) master = odfStore.styles().masterPages().value("Default"); else if (! odfStore.styles().masterPages().empty()) master = odfStore.styles().masterPages().begin().value(); if (master) { const KoXmlElement *style = odfStore.styles().findStyle( master->attributeNS(KoXmlNS::style, "page-layout-name", QString())); KoPageLayout pageLayout; pageLayout.loadOdf(*style); setSize(QSizeF(pageLayout.width, pageLayout.height)); } else { dbgKrita << "No master page found!"; return false; } KoOdfLoadingContext context(odfStore.styles(), odfStore.store()); KoShapeLoadingContext shapeContext(context, 0); KoXmlElement layerElement; forEachElement(layerElement, context.stylesReader().layerSet()) { if (!loadOdf(layerElement, shapeContext)) { dbgKrita << "Could not load vector layer!"; return false; } } KoXmlElement child; forEachElement(child, page) { KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext); if (shape) { addShape(shape); } } return true; } void KisShapeSelection::setUpdatesEnabled(bool enabled) { m_model->setUpdatesEnabled(enabled); } bool KisShapeSelection::updatesEnabled() const { return m_model->updatesEnabled(); } KUndo2Command* KisShapeSelection::resetToEmpty() { return new KisTakeAllShapesCommand(this, true); } bool KisShapeSelection::isEmpty() const { return !m_model->count(); } QPainterPath KisShapeSelection::outlineCache() const { return m_outline; } bool KisShapeSelection::outlineCacheValid() const { return true; } void KisShapeSelection::recalculateOutlineCache() { QList shapesList = shapes(); QPainterPath outline; Q_FOREACH (KoShape * shape, shapesList) { QTransform shapeMatrix = shape->absoluteTransformation(0); outline = outline.united(shapeMatrix.map(shape->outline())); } QTransform resolutionMatrix; resolutionMatrix.scale(m_image->xRes(), m_image->yRes()); m_outline = resolutionMatrix.map(outline); } void KisShapeSelection::paintComponent(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &) { Q_UNUSED(painter); Q_UNUSED(converter); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection) { Q_ASSERT(projection); Q_ASSERT(m_image); QRectF boundingRect = outlineCache().boundingRect(); renderSelection(projection, boundingRect.toAlignedRect()); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection, const QRect& r) { Q_ASSERT(projection); renderSelection(projection, r); } -void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& r) +void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect) { KIS_SAFE_ASSERT_RECOVER_RETURN(projection); KIS_SAFE_ASSERT_RECOVER_RETURN(m_image); const qint32 MASK_IMAGE_WIDTH = 256; const qint32 MASK_IMAGE_HEIGHT = 256; + const QPainterPath selectionOutline = outlineCache(); + + if (*projection->defaultPixel().data() == OPACITY_TRANSPARENT_U8) { + projection->clear(requestedRect); + } else { + KoColor transparentColor(Qt::transparent, projection->colorSpace()); + projection->fill(requestedRect, transparentColor); + } + const QRect r = requestedRect & selectionOutline.boundingRect().toAlignedRect(); + QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32); QPainter maskPainter(&polygonMaskImage); maskPainter.setRenderHint(QPainter::Antialiasing, true); // Break the mask up into chunks so we don't have to allocate a potentially very large QImage. for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) { for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) { maskPainter.fillRect(polygonMaskImage.rect(), Qt::black); maskPainter.translate(-x, -y); - maskPainter.fillPath(outlineCache(), Qt::white); + maskPainter.fillPath(selectionOutline, Qt::white); maskPainter.translate(x, y); qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH); qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT); KisSequentialIterator it(projection, QRect(x, y, rectWidth, rectHeight)); while (it.nextPixel()) { (*it.rawData()) = qRed(polygonMaskImage.pixel(it.x() - x, it.y() - y)); } } } } KoShapeManager* KisShapeSelection::shapeManager() const { return m_canvas->shapeManager(); } KisShapeSelectionFactory::KisShapeSelectionFactory() : KoShapeFactoryBase("KisShapeSelection", "selection shape container") { setHidden(true); } void KisShapeSelection::moveX(qint32 x) { const QPointF diff(x / m_image->xRes(), 0); emit sigMoveShapes(diff); } void KisShapeSelection::moveY(qint32 y) { const QPointF diff(0, y / m_image->yRes()); emit sigMoveShapes(diff); } void KisShapeSelection::slotMoveShapes(const QPointF &diff) { Q_FOREACH (KoShape* shape, shapeManager()->shapes()) { if (shape != this) { QPointF pos = shape->position(); shape->setPosition(pos + diff); } } } // TODO same code as in vector layer, refactor! KUndo2Command* KisShapeSelection::transform(const QTransform &transform) { QList shapes = m_canvas->shapeManager()->shapes(); if(shapes.isEmpty()) return 0; QTransform realTransform = m_converter->documentToView() * transform * m_converter->viewToDocument(); QList oldTransformations; QList newTransformations; // this code won't work if there are shapes, that inherit the transformation from the parent container. // the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that. Q_FOREACH (const KoShape* shape, shapes) { QTransform oldTransform = shape->transformation(); oldTransformations.append(oldTransform); if (dynamic_cast(shape)) { newTransformations.append(oldTransform); } else { QTransform globalTransform = shape->absoluteTransformation(0); QTransform localTransform = globalTransform * realTransform * globalTransform.inverted(); newTransformations.append(localTransform*oldTransform); } } return new KoShapeTransformCommand(shapes, oldTransformations, newTransformations); } diff --git a/libs/ui/flake/kis_shape_selection.h b/libs/ui/flake/kis_shape_selection.h index 6e4a696db8..3b2e8530e1 100644 --- a/libs/ui/flake/kis_shape_selection.h +++ b/libs/ui/flake/kis_shape_selection.h @@ -1,140 +1,140 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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_SHAPE_SELECTION_H #define KIS_SHAPE_SELECTION_H #include #include #include #include #include #include #include class KoStore; class KoShapeManager; class KisShapeSelectionCanvas; class KisShapeSelectionModel; class KisImageViewConverter; class KUndo2Command; /** * The marker class. * It is added to the shape's user data to show this shape * is a part of a shape selection */ class KisShapeSelectionMarker : public KoShapeUserData { KoShapeUserData* clone() const override { return new KisShapeSelectionMarker(*this); } }; class KRITAUI_EXPORT KisShapeSelection : public QObject, public KoShapeLayer, public KisSelectionComponent { Q_OBJECT KisShapeSelection(const KisShapeSelection& rhs); public: KisShapeSelection(KoShapeControllerBase *shapeControllerBase, KisImageWSP image, KisSelectionWSP selection); ~KisShapeSelection() override; KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection); KisSelectionComponent* clone(KisSelection* selection) override; bool saveSelection(KoStore * store) const; bool loadSelection(KoStore * store); /** * Renders the shapes to a selection. This method should only be called * by KisSelection to update it's projection. * * @param projection the target selection */ void renderToProjection(KisPaintDeviceSP projection) override; void renderToProjection(KisPaintDeviceSP projection, const QRect& r) override; KUndo2Command* resetToEmpty() override; bool isEmpty() const override; QPainterPath outlineCache() const override; bool outlineCacheValid() const override; void recalculateOutlineCache() override; KoShapeManager *shapeManager() const; void moveX(qint32 x) override; void moveY(qint32 y) override; KUndo2Command* transform(const QTransform &transform) override; Q_SIGNALS: void sigMoveShapes(const QPointF &diff); private Q_SLOTS: void slotMoveShapes(const QPointF &diff); protected: void paintComponent(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &paintcontext) override; private: friend class KisTakeAllShapesCommand; void setUpdatesEnabled(bool enabled); bool updatesEnabled() const; private: - void renderSelection(KisPaintDeviceSP projection, const QRect& r); + void renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect); KisImageWSP m_image; QPainterPath m_outline; KisImageViewConverter *m_converter; KisShapeSelectionCanvas *m_canvas; KisShapeSelectionModel *m_model; KoShapeControllerBase *m_shapeControllerBase; friend class KisShapeSelectionModel; }; class KRITAUI_EXPORT KisShapeSelectionFactory : public KoShapeFactoryBase { public: KisShapeSelectionFactory(); ~KisShapeSelectionFactory() override {} KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override { Q_UNUSED(documentResources); return 0; } bool supports(const KoXmlElement & e, KoShapeLoadingContext &context) const override { Q_UNUSED(e); Q_UNUSED(context); return false; } }; #endif diff --git a/libs/ui/kis_animation_importer.cpp b/libs/ui/kis_animation_importer.cpp index ba3db9c7c4..f43cb43677 100644 --- a/libs/ui/kis_animation_importer.cpp +++ b/libs/ui/kis_animation_importer.cpp @@ -1,130 +1,130 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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 "kis_animation_importer.h" #include #include "KoColorSpace.h" #include #include #include "KisPart.h" #include "KisDocument.h" #include "kis_image.h" #include "kis_undo_adapter.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include "kis_raster_keyframe_channel.h" #include "commands/kis_image_layer_add_command.h" struct KisAnimationImporter::Private { KisImageSP image; KisDocument *document; bool stop; KoUpdaterPtr updater; }; KisAnimationImporter::KisAnimationImporter(KisImageSP image, KoUpdaterPtr updater) : m_d(new Private()) { m_d->document = 0; m_d->image = image; m_d->stop = false; m_d->updater = updater; } KisAnimationImporter::KisAnimationImporter(KisDocument* document) : m_d(new Private()) { m_d->document= document; m_d->image = document->image(); m_d->stop = false; } KisAnimationImporter::~KisAnimationImporter() {} KisImportExportFilter::ConversionStatus KisAnimationImporter::import(QStringList files, int firstFrame, int step) { Q_ASSERT(step > 0); m_d->image->lock(); KisUndoAdapter *undo = m_d->image->undoAdapter(); undo->beginMacro(kundo2_i18n("Import animation")); QScopedPointer importDoc(KisPart::instance()->createDocument()); importDoc->setFileBatchMode(true); KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; int frame = firstFrame; int filesProcessed = 0; if (m_d->updater) { - m_d->updater->setRange(0, files.size() - 1); + m_d->updater->setRange(0, files.size()); } KisRasterKeyframeChannel *contentChannel = 0; Q_FOREACH(QString file, files) { bool successfullyLoaded = importDoc->openUrl(QUrl::fromLocalFile(file), KisDocument::DontAddToRecent); if (!successfullyLoaded) { status = KisImportExportFilter::InternalError; break; } if (frame == firstFrame) { const KoColorSpace *cs = importDoc->image()->colorSpace(); KisPaintLayerSP paintLayer = new KisPaintLayer(m_d->image, m_d->image->nextLayerName(), OPACITY_OPAQUE_U8, cs); undo->addCommand(new KisImageLayerAddCommand(m_d->image, paintLayer, m_d->image->rootLayer(), m_d->image->rootLayer()->childCount())); paintLayer->enableAnimation(); contentChannel = qobject_cast(paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true)); } if (m_d->updater) { if (m_d->updater->interrupted()) { m_d->stop = true; } else { - m_d->updater->setValue(filesProcessed); + m_d->updater->setValue(filesProcessed + 1); // the updater doesn't call that automatically, // it is "threaded" by default qApp->processEvents(); } } if (m_d->stop) { status = KisImportExportFilter::ProgressCancelled; break; } contentChannel->importFrame(frame, importDoc->image()->projection(), NULL); frame += step; filesProcessed++; } undo->endMacro(); m_d->image->unlock(); return status; } void KisAnimationImporter::cancel() { m_d->stop = true; } diff --git a/libs/ui/opengl/KisScreenInformationAdapter.cpp b/libs/ui/opengl/KisScreenInformationAdapter.cpp index a7435cd419..8efb71cb27 100644 --- a/libs/ui/opengl/KisScreenInformationAdapter.cpp +++ b/libs/ui/opengl/KisScreenInformationAdapter.cpp @@ -1,306 +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 defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 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 +#if defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 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 = KisSurfaceColorSpace::DefaultColorSpace; if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { info.colorSpace = KisSurfaceColorSpace::sRGBColorSpace; } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { #ifdef HAVE_HDR info.colorSpace = KisSurfaceColorSpace::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 = KisSurfaceColorSpace::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.cpp b/libs/ui/opengl/kis_opengl.cpp index 07cf6b940b..4714c7eff3 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,741 +1,743 @@ /* * Copyright (c) 2007 Adrian Page * * 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 #include "opengl/kis_opengl.h" #include "opengl/kis_opengl_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisOpenGLModeProber.h" #include #include #include "kis_assert.h" #include #include #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif using namespace KisOpenGLPrivate; namespace { // config option, set manually by main() bool g_isDebugSynchronous = false; bool g_sanityDefaultFormatIsSet = false; boost::optional openGLCheckResult; bool g_needsFenceWorkaround = false; bool g_needsPixmapCacheWorkaround = false; QString g_surfaceFormatDetectionLog; QString g_debugText("OpenGL Info\n **OpenGL not initialized**"); 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; } } KisOpenGLPrivate::OpenGLCheckResult::OpenGLCheckResult(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_glMajorVersion = context.format().majorVersion(); m_glMinorVersion = context.format().minorVersion(); m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); m_isOpenGLES = context.isOpenGLES(); } void KisOpenGLPrivate::appendOpenGLWarningString(KLocalizedString warning) { g_openglWarningStrings << warning; } void KisOpenGLPrivate::overrideOpenGLWarningString(QVector warnings) { g_openglWarningStrings = warnings; } void KisOpenGL::initialize() { if (openGLCheckResult) return; KIS_SAFE_ASSERT_RECOVER_NOOP(g_sanityDefaultFormatIsSet); openGLCheckResult = KisOpenGLModeProber::instance()->probeFormat(QSurfaceFormat::defaultFormat(), false); g_debugText.clear(); QDebug debugOut(&g_debugText); debugOut << "OpenGL Info\n"; debugOut << "\n Vendor: " << openGLCheckResult->vendorString(); debugOut << "\n Renderer: " << openGLCheckResult->rendererString(); debugOut << "\n Version: " << openGLCheckResult->driverVersionString(); debugOut << "\n Shading language: " << openGLCheckResult->shadingLanguageString(); debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat(); 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(); 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) { 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. // QOpenGLDebugLogger only function on the current active context. // FIXME: Do we need to make sure ctx is the active context? QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx); if (openglLogger->initialize()) { qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); } else { qDebug() << "QOpenGLDebugLogger cannot be initialized."; delete openglLogger; } } // Double check we were given the version we requested QSurfaceFormat format = ctx->format(); QOpenGLFunctions *f = ctx->functions(); f->initializeOpenGLFunctions(); QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(openGLCheckResult->rendererString().toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); } const QString &KisOpenGL::getDebugText() { initialize(); return g_debugText; } QStringList KisOpenGL::getOpenGLWarnings() { QStringList strings; Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) { strings << item.toString(); } return strings; } // XXX Temporary function to allow LoD on OpenGL3 without triggering // all of the other 3.2 functionality, can be removed once we move to Qt5.7 bool KisOpenGL::supportsLoD() { initialize(); return openGLCheckResult->supportsLoD(); } bool KisOpenGL::hasOpenGL3() { initialize(); return openGLCheckResult->hasOpenGL3(); } bool KisOpenGL::hasOpenGLES() { initialize(); return openGLCheckResult->isOpenGLES(); } bool KisOpenGL::supportsFenceSync() { initialize(); return openGLCheckResult->supportsFenceSync(); } bool KisOpenGL::needsFenceWorkaround() { initialize(); return g_needsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); return g_needsPixmapCacheWorkaround; } void KisOpenGL::testingInitializeDefaultSurfaceFormat() { 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"); } } 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); format.setProfile(QSurfaceFormat::CoreProfile); #else // XXX This can be removed once we move to Qt5.7 format.setVersion(3, 0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOptions(QSurfaceFormat::DeprecatedFunctions); #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 if (debugContext) { format.setOption(QSurfaceFormat::DebugContext, true); } 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 != KisSurfaceColorSpace::DefaultColorSpace); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) ORDER_BY(isPreferredColorSpace(lhs.colorSpace()), isPreferredColorSpace(rhs.colorSpace())); +#endif 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 KisSurfaceColorSpace &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; } KisSurfaceColorSpace 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() == KisSurfaceColorSpace::bt2020PQColorSpace || f.colorSpace() == KisSurfaceColorSpace::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 == KisSurfaceColorSpace::bt2020PQColorSpace || m_preferredColorSpace == KisSurfaceColorSpace::scRGBColorSpace; #else return false; #endif } bool isPreferredColorSpace(const KisSurfaceColorSpace cs) const { return KisOpenGLModeProber::fuzzyCompareColorSpaces(m_preferredColorSpace, cs); return false; } private: KisSurfaceColorSpace m_preferredColorSpace = KisSurfaceColorSpace::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 ? KisSurfaceColorSpace::sRGBColorSpace : preferredRootSurfaceFormat == KisConfig::BT709_G10 ? KisSurfaceColorSpace::scRGBColorSpace : KisSurfaceColorSpace::bt2020PQColorSpace); #else Q_UNUSED(preferredRootSurfaceFormat); compareOp.setPreferredColorSpace(KisSurfaceColorSpace::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) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) dbgDetection() <<"Probing format..." << format.colorSpace() << format.renderableType(); #else dbgDetection() <<"Probing format..." << format.renderableType(); #endif 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 = #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(), resultFormat.colorSpace()); #else true; #endif 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); } bool KisOpenGL::hasOpenGL() { return openGLCheckResult->isSupportedVersion(); } diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp index 0d96789461..e5533d1391 100644 --- a/libs/ui/opengl/kis_opengl_canvas2.cpp +++ b/libs/ui/opengl/kis_opengl_canvas2.cpp @@ -1,984 +1,986 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006-2013 * Copyright (C) 2015 Michael Abrahams * * 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. */ #define GL_GLEXT_PROTOTYPES #include "opengl/kis_opengl_canvas2.h" #include "opengl/kis_opengl_canvas2_p.h" #include "opengl/kis_opengl_shader_loader.h" #include "opengl/kis_opengl_canvas_debugger.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_coordinates_converter.h" #include "canvas/kis_display_filter.h" #include "canvas/kis_display_color_converter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include "KisOpenGLModeProber.h" #include #ifndef Q_OS_OSX #include #endif #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #define PROGRAM_VERTEX_ATTRIBUTE 0 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 static bool OPENGL_SUCCESS = false; struct KisOpenGLCanvas2::Private { public: ~Private() { delete displayShader; delete checkerShader; delete solidColorShader; Sync::deleteSync(glSyncObject); } bool canvasInitialized{false}; KisOpenGLImageTexturesSP openGLImageTextures; KisOpenGLShaderLoader shaderLoader; KisShaderProgram *displayShader{0}; KisShaderProgram *checkerShader{0}; KisShaderProgram *solidColorShader{0}; bool displayShaderCompiledWithDisplayFilterSupport{false}; GLfloat checkSizeScale; bool scrollCheckers; QSharedPointer displayFilter; KisOpenGL::FilterMode filterMode; bool proofingConfigIsUpdated=false; GLsync glSyncObject{0}; bool wrapAroundMode{false}; // Stores a quad for drawing the canvas QOpenGLVertexArrayObject quadVAO; QOpenGLBuffer quadBuffers[2]; // Stores data for drawing tool outlines QOpenGLVertexArrayObject outlineVAO; QOpenGLBuffer lineBuffer; QVector3D vertices[6]; QVector2D texCoords[6]; #ifndef Q_OS_OSX QOpenGLFunctions_2_1 *glFn201; #endif qreal pixelGridDrawingThreshold; bool pixelGridEnabled; QColor gridColor; QColor cursorColor; bool lodSwitchInProgress = false; int xToColWithWrapCompensation(int x, const QRect &imageRect) { int firstImageColumn = openGLImageTextures->xToCol(imageRect.left()); int lastImageColumn = openGLImageTextures->xToCol(imageRect.right()); int colsPerImage = lastImageColumn - firstImageColumn + 1; int numWraps = floor(qreal(x) / imageRect.width()); int remainder = x - imageRect.width() * numWraps; return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder); } int yToRowWithWrapCompensation(int y, const QRect &imageRect) { int firstImageRow = openGLImageTextures->yToRow(imageRect.top()); int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom()); int rowsPerImage = lastImageRow - firstImageRow + 1; int numWraps = floor(qreal(y) / imageRect.height()); int remainder = y - imageRect.height() * numWraps; return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder); } }; KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter) : QOpenGLWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , d(new Private()) { KisConfig cfg(false); cfg.setCanvasState("OPENGL_STARTED"); d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(image, colorConverter->openGLCanvasSurfaceProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); setAcceptDrops(true); setAutoFillBackground(false); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_NoSystemBackground, true); #ifdef Q_OS_OSX setAttribute(Qt::WA_AcceptTouchEvents, false); #else setAttribute(Qt::WA_AcceptTouchEvents, true); #endif setAttribute(Qt::WA_InputMethodEnabled, false); setAttribute(Qt::WA_DontCreateNativeAncestors, true); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) if (KisOpenGLModeProber::instance()->useHDRMode()) { setTextureFormat(GL_RGBA16F); } +#endif setDisplayFilterImpl(colorConverter->displayFilter(), true); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotPixelGridModeChanged())); slotConfigChanged(); slotPixelGridModeChanged(); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); } KisOpenGLCanvas2::~KisOpenGLCanvas2() { delete d; } void KisOpenGLCanvas2::setDisplayFilter(QSharedPointer displayFilter) { setDisplayFilterImpl(displayFilter, false); } void KisOpenGLCanvas2::setDisplayFilterImpl(QSharedPointer displayFilter, bool initializing) { bool needsInternalColorManagement = !displayFilter || displayFilter->useInternalColorManagement(); bool needsFullRefresh = d->openGLImageTextures->setInternalColorManagementActive(needsInternalColorManagement); d->displayFilter = displayFilter; if (!initializing && needsFullRefresh) { canvas()->startUpdateInPatches(canvas()->image()->bounds()); } else if (!initializing) { canvas()->updateCanvas(); } } 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; update(); } 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()); } void KisOpenGLCanvas2::initializeGL() { KisOpenGL::initializeContext(context()); initializeOpenGLFunctions(); #ifndef Q_OS_OSX if (!KisOpenGL::hasOpenGLES()) { d->glFn201 = context()->versionFunctions(); if (!d->glFn201) { warnUI << "Cannot obtain QOpenGLFunctions_2_1, glLogicOp cannot be used"; } } else { d->glFn201 = nullptr; } #endif KisConfig cfg(true); d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); d->openGLImageTextures->initGL(context()->functions()); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); initializeShaders(); // If we support OpenGL 3.2, then prepare our VAOs and VBOs for drawing if (KisOpenGL::hasOpenGL3()) { d->quadVAO.create(); d->quadVAO.bind(); glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE); glEnableVertexAttribArray(PROGRAM_TEXCOORD_ATTRIBUTE); // Create the vertex buffer object, it has 6 vertices with 3 components d->quadBuffers[0].create(); d->quadBuffers[0].setUsagePattern(QOpenGLBuffer::StaticDraw); d->quadBuffers[0].bind(); d->quadBuffers[0].allocate(d->vertices, 6 * 3 * sizeof(float)); glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0); // Create the texture buffer object, it has 6 texture coordinates with 2 components d->quadBuffers[1].create(); d->quadBuffers[1].setUsagePattern(QOpenGLBuffer::StaticDraw); d->quadBuffers[1].bind(); d->quadBuffers[1].allocate(d->texCoords, 6 * 2 * sizeof(float)); glVertexAttribPointer(PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0); // Create the outline buffer, this buffer will store the outlines of // tools and will frequently change data d->outlineVAO.create(); d->outlineVAO.bind(); glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE); // The outline buffer has a StreamDraw usage pattern, because it changes constantly d->lineBuffer.create(); d->lineBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); d->lineBuffer.bind(); glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0); } Sync::init(context()); d->canvasInitialized = true; } /** * Loads all shaders and reports compilation problems */ void KisOpenGLCanvas2::initializeShaders() { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized); delete d->checkerShader; delete d->solidColorShader; d->checkerShader = 0; d->solidColorShader = 0; try { d->checkerShader = d->shaderLoader.loadCheckerShader(); d->solidColorShader = d->shaderLoader.loadSolidColorShader(); } catch (const ShaderLoaderException &e) { reportFailedShaderCompilation(e.what()); } initializeDisplayShader(); } void KisOpenGLCanvas2::initializeDisplayShader() { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized); bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering; delete d->displayShader; d->displayShader = 0; try { d->displayShader = d->shaderLoader.loadDisplayShader(d->displayFilter, useHiQualityFiltering); d->displayShaderCompiledWithDisplayFilterSupport = d->displayFilter; } catch (const ShaderLoaderException &e) { reportFailedShaderCompilation(e.what()); } } /** * Displays a message box telling the user that * shader compilation failed and turns off OpenGL. */ void KisOpenGLCanvas2::reportFailedShaderCompilation(const QString &context) { KisConfig cfg(false); qDebug() << "Shader Compilation Failure: " << context; QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.", context), QMessageBox::Close); cfg.setUseOpenGL(false); cfg.setCanvasState("OPENGL_FAILED"); } void KisOpenGLCanvas2::resizeGL(int width, int height) { coordinatesConverter()->setCanvasWidgetSize(QSize(width, height)); paintGL(); } void KisOpenGLCanvas2::paintGL() { if (!OPENGL_SUCCESS) { KisConfig cfg(false); cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED"); } KisOpenglCanvasDebugger::instance()->nofityPaintRequested(); renderCanvasGL(); if (d->glSyncObject) { Sync::deleteSync(d->glSyncObject); } d->glSyncObject = Sync::getSync(); QPainter gc(this); renderDecorations(&gc); gc.end(); if (!OPENGL_SUCCESS) { KisConfig cfg(false); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); OPENGL_SUCCESS = true; } } void KisOpenGLCanvas2::paintToolOutline(const QPainterPath &path) { if (!d->solidColorShader->bind()) { return; } // setup the mvp transformation QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(coordinatesConverter()->flakeToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix); if (!KisOpenGL::hasOpenGLES()) { glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glEnable(GL_COLOR_LOGIC_OP); #ifndef Q_OS_OSX if (d->glFn201) { d->glFn201->glLogicOp(GL_XOR); } #else glLogicOp(GL_XOR); #endif } else { glEnable(GL_BLEND); glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ZERO, GL_ONE, GL_ONE); } d->solidColorShader->setUniformValue( d->solidColorShader->location(Uniform::FragmentColor), QVector4D(d->cursorColor.redF(), d->cursorColor.greenF(), d->cursorColor.blueF(), 1.0f)); // Paint the tool outline if (KisOpenGL::hasOpenGL3()) { d->outlineVAO.bind(); d->lineBuffer.bind(); } // Convert every disjointed subpath to a polygon and draw that polygon QList subPathPolygons = path.toSubpathPolygons(); for (int i = 0; i < subPathPolygons.size(); i++) { const QPolygonF& polygon = subPathPolygons.at(i); QVector vertices; vertices.resize(polygon.count()); for (int j = 0; j < polygon.count(); j++) { QPointF p = polygon.at(j); vertices[j].setX(p.x()); vertices[j].setY(p.y()); } if (KisOpenGL::hasOpenGL3()) { d->lineBuffer.allocate(vertices.constData(), 3 * vertices.size() * sizeof(float)); } else { d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData()); } glDrawArrays(GL_LINE_STRIP, 0, vertices.size()); } if (KisOpenGL::hasOpenGL3()) { d->lineBuffer.release(); d->outlineVAO.release(); } if (!KisOpenGL::hasOpenGLES()) { glDisable(GL_COLOR_LOGIC_OP); } else { glDisable(GL_BLEND); } d->solidColorShader->release(); } bool KisOpenGLCanvas2::isBusy() const { const bool isBusyStatus = Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; KisOpenglCanvasDebugger::instance()->nofitySyncStatus(isBusyStatus); return isBusyStatus; } void KisOpenGLCanvas2::setLodResetInProgress(bool value) { d->lodSwitchInProgress = value; } void KisOpenGLCanvas2::drawCheckers() { if (!d->checkerShader) { return; } KisCoordinatesConverter *converter = coordinatesConverter(); QTransform textureTransform; QTransform modelTransform; QRectF textureRect; QRectF modelRect; QRectF viewportRect = !d->wrapAroundMode ? converter->imageRectInViewportPixels() : converter->widgetToViewport(this->rect()); if (!canvas()->renderingLimit().isEmpty()) { const QRect vrect = converter->imageToViewport(canvas()->renderingLimit()).toAlignedRect(); viewportRect &= vrect; } converter->getOpenGLCheckersInfo(viewportRect, &textureTransform, &modelTransform, &textureRect, &modelRect, d->scrollCheckers); textureTransform *= QTransform::fromScale(d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE); if (!d->checkerShader->bind()) { qWarning() << "Could not bind checker shader"; return; } QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(modelTransform); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::ModelViewProjection), modelMatrix); QMatrix4x4 textureMatrix(textureTransform); d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::TextureMatrix), textureMatrix); //Setup the geometry for rendering if (KisOpenGL::hasOpenGL3()) { rectToVertices(d->vertices, modelRect); d->quadBuffers[0].bind(); d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float)); rectToTexCoords(d->texCoords, textureRect); d->quadBuffers[1].bind(); d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float)); } else { rectToVertices(d->vertices, modelRect); d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); } // render checkers glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture()); glDrawArrays(GL_TRIANGLES, 0, 6); glBindTexture(GL_TEXTURE_2D, 0); d->checkerShader->release(); glBindBuffer(GL_ARRAY_BUFFER, 0); } void KisOpenGLCanvas2::drawGrid() { if (!d->solidColorShader->bind()) { return; } QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); d->solidColorShader->setUniformValue( d->solidColorShader->location(Uniform::FragmentColor), QVector4D(d->gridColor.redF(), d->gridColor.greenF(), d->gridColor.blueF(), 0.5f)); if (KisOpenGL::hasOpenGL3()) { d->outlineVAO.bind(); d->lineBuffer.bind(); } QRectF widgetRect(0,0, width(), height()); QRectF widgetRectInImagePixels = coordinatesConverter()->documentToImage(coordinatesConverter()->widgetToDocument(widgetRect)); QRect wr = widgetRectInImagePixels.toAlignedRect(); if (!d->wrapAroundMode) { wr &= d->openGLImageTextures->storedImageBounds(); } QPoint topLeftCorner = wr.topLeft(); QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1); QVector grid; for (int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) { grid.append(QVector3D(i, topLeftCorner.y(), 0)); grid.append(QVector3D(i, bottomRightCorner.y(), 0)); } for (int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) { grid.append(QVector3D(topLeftCorner.x(), i, 0)); grid.append(QVector3D(bottomRightCorner.x(), i, 0)); } if (KisOpenGL::hasOpenGL3()) { d->lineBuffer.allocate(grid.constData(), 3 * grid.size() * sizeof(float)); } else { d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, grid.constData()); } glDrawArrays(GL_LINES, 0, grid.size()); if (KisOpenGL::hasOpenGL3()) { d->lineBuffer.release(); d->outlineVAO.release(); } d->solidColorShader->release(); glDisable(GL_BLEND); } void KisOpenGLCanvas2::drawImage() { if (!d->displayShader) { return; } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); KisCoordinatesConverter *converter = coordinatesConverter(); d->displayShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(converter->imageToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->displayShader->setUniformValue(d->displayShader->location(Uniform::ModelViewProjection), modelMatrix); QMatrix4x4 textureMatrix; textureMatrix.setToIdentity(); d->displayShader->setUniformValue(d->displayShader->location(Uniform::TextureMatrix), textureMatrix); QRectF widgetRect(0,0, width(), height()); QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect)); const QRect renderingLimit = canvas()->renderingLimit(); if (!renderingLimit.isEmpty()) { widgetRectInImagePixels &= renderingLimit; } qreal scaleX, scaleY; converter->imagePhysicalScale(&scaleX, &scaleY); d->displayShader->setUniformValue(d->displayShader->location(Uniform::ViewportScale), (GLfloat) scaleX); d->displayShader->setUniformValue(d->displayShader->location(Uniform::TexelSize), (GLfloat) d->openGLImageTextures->texelSize()); QRect ir = d->openGLImageTextures->storedImageBounds(); QRect wr = widgetRectInImagePixels.toAlignedRect(); if (!d->wrapAroundMode) { // if we don't want to paint wrapping images, just limit the // processing area, and the code will handle all the rest wr &= ir; } int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); int minColumn = d->openGLImageTextures->xToCol(ir.left()); int maxColumn = d->openGLImageTextures->xToCol(ir.right()); int minRow = d->openGLImageTextures->yToRow(ir.top()); int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); int imageColumns = maxColumn - minColumn + 1; int imageRows = maxRow - minRow + 1; for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { int effectiveCol = col; int effectiveRow = row; QPointF tileWrappingTranslation; if (effectiveCol > maxColumn || effectiveCol < minColumn) { int translationStep = floor(qreal(col) / imageColumns); int originCol = translationStep * imageColumns; effectiveCol = col - originCol; tileWrappingTranslation.rx() = translationStep * ir.width(); } if (effectiveRow > maxRow || effectiveRow < minRow) { int translationStep = floor(qreal(row) / imageRows); int originRow = translationStep * imageRows; effectiveRow = row - originRow; tileWrappingTranslation.ry() = translationStep * ir.height(); } KisTextureTile *tile = d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow); if (!tile) { warnUI << "OpenGL: Trying to paint texture tile but it has not been created yet."; continue; } /* * We create a float rect here to workaround Qt's * "history reasons" in calculation of right() * and bottom() coordinates of integer rects. */ QRectF textureRect; QRectF modelRect; if (renderingLimit.isEmpty()) { textureRect = tile->tileRectInTexturePixels(); modelRect = tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y()); } else { const QRect limitedTileRect = tile->tileRectInImagePixels() & renderingLimit; textureRect = tile->imageRectInTexturePixels(limitedTileRect); modelRect = limitedTileRect.translated(tileWrappingTranslation.x(), tileWrappingTranslation.y()); } //Setup the geometry for rendering if (KisOpenGL::hasOpenGL3()) { rectToVertices(d->vertices, modelRect); d->quadBuffers[0].bind(); d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float)); rectToTexCoords(d->texCoords, textureRect); d->quadBuffers[1].bind(); d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float)); } else { rectToVertices(d->vertices, modelRect); d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); } if (d->displayFilter) { glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture()); d->displayShader->setUniformValue(d->displayShader->location(Uniform::Texture1), 1); } glActiveTexture(GL_TEXTURE0); const int currentLodPlane = tile->bindToActiveTexture(d->lodSwitchInProgress); if (d->displayShader->location(Uniform::FixedLodLevel) >= 0) { d->displayShader->setUniformValue(d->displayShader->location(Uniform::FixedLodLevel), (GLfloat) currentLodPlane); } if (currentLodPlane > 0) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch(d->filterMode) { case KisOpenGL::NearestFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; case KisOpenGL::BilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; case KisOpenGL::TrilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; case KisOpenGL::HighQualityFiltering: if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } break; } } glDrawArrays(GL_TRIANGLES, 0, 6); } } glBindTexture(GL_TEXTURE_2D, 0); d->displayShader->release(); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisable(GL_BLEND); } void KisOpenGLCanvas2::slotConfigChanged() { KisConfig cfg(true); d->checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize()); d->scrollCheckers = cfg.scrollCheckers(); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels()); d->filterMode = (KisOpenGL::FilterMode) cfg.openGLFilteringMode(); d->cursorColor = cfg.getCursorMainColor(); notifyConfigChanged(); } void KisOpenGLCanvas2::slotPixelGridModeChanged() { KisConfig cfg(true); d->pixelGridDrawingThreshold = cfg.getPixelGridDrawingThreshold(); d->pixelGridEnabled = cfg.pixelGridEnabled(); d->gridColor = cfg.getPixelGridColor(); update(); } QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisOpenGLCanvas2::renderCanvasGL() { { // 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()) || (bool(d->displayFilter) != d->displayShaderCompiledWithDisplayFilterSupport)) { KIS_SAFE_ASSERT_RECOVER_NOOP(d->canvasInitialized); d->canvasInitialized = false; // TODO: check if actually needed? initializeDisplayShader(); d->canvasInitialized = true; } if (KisOpenGL::hasOpenGL3()) { d->quadVAO.bind(); } drawCheckers(); drawImage(); if ((coordinatesConverter()->effectiveZoom() > d->pixelGridDrawingThreshold - 0.00001) && d->pixelGridEnabled) { drawGrid(); } if (KisOpenGL::hasOpenGL3()) { d->quadVAO.release(); } } void KisOpenGLCanvas2::renderDecorations(QPainter *painter) { QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect(); drawDecorations(*painter, boundingRect); } void KisOpenGLCanvas2::setDisplayColorConverter(KisDisplayColorConverter *colorConverter) { d->openGLImageTextures->setMonitorProfile(colorConverter->openGLCanvasSurfaceProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } void KisOpenGLCanvas2::channelSelectionChanged(const QBitArray &channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); } void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h) { if (d->canvasInitialized) { d->openGLImageTextures->slotImageSizeChanged(w, h); } } KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); if (canvas()->proofingConfigUpdated()) { d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); canvas()->setProofingConfigUpdated(false); } return d->openGLImageTextures->updateCache(rc, d->openGLImageTextures->image()); } QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info) { // See KisQPainterCanvas::updateCanvasProjection for more info bool isOpenGLUpdateInfo = dynamic_cast(info.data()); if (isOpenGLUpdateInfo) { d->openGLImageTextures->recalculateCache(info, d->lodSwitchInProgress); } return QRect(); // FIXME: Implement dirty rect for OpenGL } QVector KisOpenGLCanvas2::updateCanvasProjection(const QVector &infoObjects) { #ifdef Q_OS_OSX /** * On OSX openGL defferent (shared) contexts have different execution queues. * It means that the textures uploading and their painting can be easily reordered. * To overcome the issue, we should ensure that the textures are uploaded in the * same openGL context as the painting is done. */ QOpenGLContext *oldContext = QOpenGLContext::currentContext(); QSurface *oldSurface = oldContext ? oldContext->surface() : 0; this->makeCurrent(); #endif QVector result = KisCanvasWidgetBase::updateCanvasProjection(infoObjects); #ifdef Q_OS_OSX if (oldContext) { oldContext->makeCurrent(oldSurface); } else { this->doneCurrent(); } #endif return result; } bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } KisOpenGLImageTexturesSP KisOpenGLCanvas2::openGLImageTextures() const { return d->openGLImageTextures; } diff --git a/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp b/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp index 22c9ff95ab..72114d01c7 100644 --- a/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp +++ b/plugins/dockers/smallcolorselector/KisGLImageWidget.cpp @@ -1,281 +1,283 @@ /* * 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 #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(KisSurfaceColorSpace::sRGBColorSpace, parent) { } KisGLImageWidget::KisGLImageWidget(KisSurfaceColorSpace colorSpace, QWidget *parent) : QOpenGLWidget(parent), m_texture(QOpenGLTexture::Target2D) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) setTextureFormat(GL_RGBA16F); +#endif #ifdef HAVE_HDR setTextureColorSpace(colorSpace); #endif setUpdateBehavior(QOpenGLWidget::NoPartialUpdate); } KisGLImageWidget::~KisGLImageWidget() { // force releasing the reourses on destruction slotOpenGLContextDestroyed(); } 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 = KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\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/impex/psd/psd_layer_record.cpp b/plugins/impex/psd/psd_layer_record.cpp index 314355fad6..0cee72b08a 100644 --- a/plugins/impex/psd/psd_layer_record.cpp +++ b/plugins/impex/psd/psd_layer_record.cpp @@ -1,760 +1,762 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * 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 "psd_layer_record.h" #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include #include "psd_utils.h" #include "psd_header.h" #include "compression.h" #include #include #include #include #include #include #include #include "psd_pixel_utils.h" #include // Just for pretty debug messages QString channelIdToChannelType(int channelId, psd_color_mode colormode) { switch(channelId) { case -3: return "Real User Supplied Layer Mask (when both a user mask and a vector mask are present"; case -2: return "User Supplied Layer Mask"; case -1: return "Transparency mask"; case 0: switch(colormode) { case Bitmap: case Indexed: return QString("bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return "gray"; case RGB: case RGB48: return "red"; case Lab: case Lab48: return "L"; case CMYK: case CMYK64: return "cyan"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 1: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return "green"; case Lab: case Lab48: return "a"; case CMYK: case CMYK64: return "Magenta"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 2: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return "blue"; case Lab: case Lab48: return "b"; case CMYK: case CMYK64: return "yellow"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; case 3: switch(colormode) { case Bitmap: case Indexed: return QString("WARNING bitmap or indexed: %1").arg(channelId); case Grayscale: case Gray16: return QString("WARNING: %1").arg(channelId); case RGB: case RGB48: return QString("alpha: %1").arg(channelId); case Lab: case Lab48: return QString("alpha: %1").arg(channelId); case CMYK: case CMYK64: return "Key"; case MultiChannel: case DeepMultichannel: return QString("multichannel channel %1").arg(channelId); case DuoTone: case Duotone16: return QString("duotone channel %1").arg(channelId); default: return QString("unknown: %1").arg(channelId); }; default: return QString("unknown: %1").arg(channelId); }; } PSDLayerRecord::PSDLayerRecord(const PSDHeader& header) : top(0) , left(0) , bottom(0) , right(0) , nChannels(0) , opacity(0) , clipping(0) , transparencyProtected(false) , visible(true) , irrelevant(false) , layerName("UNINITIALIZED") , infoBlocks(header) , m_transparencyMaskSizeOffset(0) , m_header(header) { } bool PSDLayerRecord::read(QIODevice* io) { dbgFile << "Going to read layer record. Pos:" << io->pos(); if (!psdread(io, &top) || !psdread(io, &left) || !psdread(io, &bottom) || !psdread(io, &right) || !psdread(io, &nChannels)) { error = "could not read layer record"; return false; } dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels; Q_ASSERT(top <= bottom); Q_ASSERT(left <= right); Q_ASSERT(nChannels > 0); if (nChannels < 1) { error = QString("Not enough channels. Got: %1").arg(nChannels); return false; } if (nChannels > MAX_CHANNELS) { error = QString("Too many channels. Got: %1").arg(nChannels); return false; } for (int i = 0; i < nChannels; ++i) { if (io->atEnd()) { error = "Could not read enough data for channels"; return false; } ChannelInfo* info = new ChannelInfo; if (!psdread(io, &info->channelId)) { error = "could not read channel id"; delete info; return false; } bool r; if (m_header.version == 1) { quint32 channelDataLength; r = psdread(io, &channelDataLength); info->channelDataLength = (quint64)channelDataLength; } else { r = psdread(io, &info->channelDataLength); } if (!r) { error = "Could not read length for channel data"; delete info; return false; } dbgFile << "\tchannel" << i << "id" << channelIdToChannelType(info->channelId, m_header.colormode) << "length" << info->channelDataLength << "start" << info->channelDataStart << "offset" << info->channelOffset << "channelInfoPosition" << info->channelInfoPosition; channelInfoRecords << info; } if (!psd_read_blendmode(io, blendModeKey)) { error = QString("Could not read blend mode key. Got: %1").arg(blendModeKey); return false; } dbgFile << "\tBlend mode" << blendModeKey << "pos" << io->pos(); if (!psdread(io, &opacity)) { error = "Could not read opacity"; return false; } dbgFile << "\tOpacity" << opacity << io->pos(); if (!psdread(io, &clipping)) { error = "Could not read clipping"; return false; } dbgFile << "\tclipping" << clipping << io->pos(); quint8 flags; if (!psdread(io, &flags)) { error = "Could not read flags"; return false; } dbgFile << "\tflags" << flags << io->pos(); transparencyProtected = flags & 1 ? true : false; dbgFile << "\ttransparency protected" << transparencyProtected; visible = flags & 2 ? false : true; dbgFile << "\tvisible" << visible; if (flags & 8) { irrelevant = flags & 16 ? true : false; } else { irrelevant = false; } dbgFile << "\tirrelevant" << irrelevant; dbgFile << "\tfiller at " << io->pos(); quint8 filler; if (!psdread(io, &filler) || filler != 0) { error = "Could not read padding"; return false; } dbgFile << "\tGoing to read extra data length" << io->pos(); quint32 extraDataLength; if (!psdread(io, &extraDataLength) || io->bytesAvailable() < extraDataLength) { error = QString("Could not read extra layer data: %1 at pos %2").arg(extraDataLength).arg(io->pos()); return false; } dbgFile << "\tExtra data length" << extraDataLength; if (extraDataLength > 0) { dbgFile << "Going to read extra data field. Bytes available: " << io->bytesAvailable() << "pos" << io->pos(); + + // See https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_22582 quint32 layerMaskLength = 1; // invalid... if (!psdread(io, &layerMaskLength) || io->bytesAvailable() < layerMaskLength || !(layerMaskLength == 0 || layerMaskLength == 20 || layerMaskLength == 36)) { error = QString("Could not read layer mask length: %1").arg(layerMaskLength); return false; } memset(&layerMask, 0, sizeof(LayerMaskData)); if (layerMaskLength == 20 || layerMaskLength == 36) { if (!psdread(io, &layerMask.top) || !psdread(io, &layerMask.left) || !psdread(io, &layerMask.bottom) || !psdread(io, &layerMask.right) || !psdread(io, &layerMask.defaultColor) || !psdread(io, &flags)) { error = "could not read mask record"; return false; } } if (layerMaskLength == 20) { quint16 padding; if (!psdread(io, &padding)) { error = "Could not read layer mask padding"; return false; } } - + // If it's 36, that is, bit four of the flags is set, we also need to read the 'real' flags, background and rectangle if (layerMaskLength == 36 ) { if (!psdread(io, &flags) || !psdread(io, &layerMask.defaultColor) || !psdread(io, &layerMask.top) || !psdread(io, &layerMask.left) || !psdread(io, &layerMask.bottom) || !psdread(io, &layerMask.top)) { error = "could not read 'real' mask record"; return false; } } layerMask.positionedRelativeToLayer = flags & 1 ? true : false; layerMask.disabled = flags & 2 ? true : false; layerMask.invertLayerMaskWhenBlending = flags & 4 ? true : false; dbgFile << "\tRead layer mask/adjustment layer data. Length of block:" << layerMaskLength << "pos" << io->pos(); // layer blending thingies quint32 blendingDataLength; if (!psdread(io, &blendingDataLength) || io->bytesAvailable() < blendingDataLength) { error = "Could not read extra blending data."; return false; } //dbgFile << "blending block data length" << blendingDataLength << ", pos" << io->pos(); blendingRanges.data = io->read(blendingDataLength); if ((quint32)blendingRanges.data.size() != blendingDataLength) { error = QString("Got %1 bytes for the blending range block, needed %2").arg(blendingRanges.data.size(), blendingDataLength); } /* // XXX: reading this block correctly failed, I have more channel ranges than I'd expected. if (!psdread(io, &blendingRanges.blackValues[0]) || !psdread(io, &blendingRanges.blackValues[1]) || !psdread(io, &blendingRanges.whiteValues[0]) || !psdread(io, &blendingRanges.whiteValues[1]) || !psdread(io, &blendingRanges.compositeGrayBlendDestinationRange)) { error = "Could not read blending black/white values"; return false; } for (int i = 0; i < nChannels; ++i) { quint32 src; quint32 dst; if (!psdread(io, &src) || !psdread(io, &dst)) { error = QString("could not read src/dst range for channel %1").arg(i); return false; } dbgFile << "\tread range " << src << "to" << dst << "for channel" << i; blendingRanges.sourceDestinationRanges << QPair(src, dst); } */ dbgFile << "\tGoing to read layer name at" << io->pos(); quint8 layerNameLength; if (!psdread(io, &layerNameLength)) { error = "Could not read layer name length"; return false; } dbgFile << "\tlayer name length unpadded" << layerNameLength << "pos" << io->pos(); layerNameLength = ((layerNameLength + 1 + 3) & ~0x03) - 1; dbgFile << "\tlayer name length padded" << layerNameLength << "pos" << io->pos(); layerName = io->read(layerNameLength); dbgFile << "\tlayer name" << layerName << io->pos(); if (!infoBlocks.read(io)) { error = infoBlocks.error; return false; } if (infoBlocks.keys.contains("luni") && !infoBlocks.unicodeLayerName.isEmpty()) { layerName = infoBlocks.unicodeLayerName; } } return valid(); } void PSDLayerRecord::write(QIODevice* io, KisPaintDeviceSP layerContentDevice, KisNodeSP onlyTransparencyMask, const QRect &maskRect, psd_section_type sectionType, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat) { dbgFile << "writing layer info record" << "at" << io->pos(); m_layerContentDevice = layerContentDevice; m_onlyTransparencyMask = onlyTransparencyMask; m_onlyTransparencyMaskRect = maskRect; dbgFile << "saving layer record for " << layerName << "at pos" << io->pos(); dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels; Q_ASSERT(left <= right); Q_ASSERT(top <= bottom); Q_ASSERT(nChannels > 0); try { const QRect layerRect(left, top, right - left, bottom - top); KisAslWriterUtils::writeRect(layerRect, io); { quint16 realNumberOfChannels = nChannels + bool(m_onlyTransparencyMask); SAFE_WRITE_EX(io, realNumberOfChannels); } Q_FOREACH (ChannelInfo *channel, channelInfoRecords) { SAFE_WRITE_EX(io, (quint16)channel->channelId); channel->channelInfoPosition = io->pos(); // to be filled in when we know how big channel block is const quint32 fakeChannelSize = 0; SAFE_WRITE_EX(io, fakeChannelSize); } if (m_onlyTransparencyMask) { const quint16 userSuppliedMaskChannelId = -2; SAFE_WRITE_EX(io, userSuppliedMaskChannelId); m_transparencyMaskSizeOffset = io->pos(); const quint32 fakeTransparencyMaskSize = 0; SAFE_WRITE_EX(io, fakeTransparencyMaskSize); } // blend mode dbgFile << ppVar(blendModeKey) << ppVar(io->pos()); KisAslWriterUtils::writeFixedString("8BIM", io); KisAslWriterUtils::writeFixedString(blendModeKey, io); SAFE_WRITE_EX(io, opacity); SAFE_WRITE_EX(io, clipping); // unused // visibility and protection quint8 flags = 0; if (transparencyProtected) flags |= 1; if (!visible) flags |= 2; if (irrelevant) { flags |= (1 << 3) | (1 << 4); } SAFE_WRITE_EX(io, flags); { quint8 padding = 0; SAFE_WRITE_EX(io, padding); } { // extra fields with their own length tag KisAslWriterUtils::OffsetStreamPusher extraDataSizeTag(io); if (m_onlyTransparencyMask) { { const quint32 layerMaskDataSize = 20; // support simple case only SAFE_WRITE_EX(io, layerMaskDataSize); } KisAslWriterUtils::writeRect(m_onlyTransparencyMaskRect, io); { // NOTE: in PSD the default color of the mask is stored in 1 byte value! // Even when the mask is actually 16/32 bit! I have no idea how it is // actually treated in this case. KIS_ASSERT_RECOVER_NOOP(m_onlyTransparencyMask->paintDevice()->pixelSize() == 1); const quint8 defaultPixel = *m_onlyTransparencyMask->paintDevice()->defaultPixel().data(); SAFE_WRITE_EX(io, defaultPixel); } { const quint8 maskFlags = 0; // nothing serious SAFE_WRITE_EX(io, maskFlags); const quint16 padding = 0; // 2-byte padding SAFE_WRITE_EX(io, padding); } } else { const quint32 nullLayerMaskDataSize = 0; SAFE_WRITE_EX(io, nullLayerMaskDataSize); } { // blending ranges are not implemented yet const quint32 nullBlendingRangesSize = 0; SAFE_WRITE_EX(io, nullBlendingRangesSize); } // layer name: Pascal string, padded to a multiple of 4 bytes. psdwrite_pascalstring(io, layerName, 4); PsdAdditionalLayerInfoBlock additionalInfoBlock(m_header); // write 'luni' data block additionalInfoBlock.writeLuniBlockEx(io, layerName); // write 'lsct' data block if (sectionType != psd_other) { additionalInfoBlock.writeLsctBlockEx(io, sectionType, isPassThrough, blendModeKey); } // write 'lfx2' data block if (!stylesXmlDoc.isNull()) { additionalInfoBlock.writeLfx2BlockEx(io, stylesXmlDoc, useLfxsLayerStyleFormat); } } } catch (KisAslWriterUtils::ASLWriteException &e) { throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what())); } } KisPaintDeviceSP PSDLayerRecord::convertMaskDeviceIfNeeded(KisPaintDeviceSP dev) { KisPaintDeviceSP result = dev; if (m_header.channelDepth == 16) { result = new KisPaintDevice(*dev); delete result->convertTo(KoColorSpaceRegistry::instance()->alpha16()); } else if (m_header.channelDepth == 32) { result = new KisPaintDevice(*dev); delete result->convertTo(KoColorSpaceRegistry::instance()->alpha32f()); } return result; } void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice *io) { if (m_onlyTransparencyMask) { KisPaintDeviceSP device = convertMaskDeviceIfNeeded(m_onlyTransparencyMask->paintDevice()); QByteArray buffer(device->pixelSize() * m_onlyTransparencyMaskRect.width() * m_onlyTransparencyMaskRect.height(), 0); device->readBytes((quint8*)buffer.data(), m_onlyTransparencyMaskRect); PsdPixelUtils::writeChannelDataRLE(io, (quint8*)buffer.data(), device->pixelSize(), m_onlyTransparencyMaskRect, m_transparencyMaskSizeOffset, -1, true); } } void PSDLayerRecord::writePixelData(QIODevice *io) { try { writePixelDataImpl(io); } catch (KisAslWriterUtils::ASLWriteException &e) { throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what())); } } void PSDLayerRecord::writePixelDataImpl(QIODevice *io) { dbgFile << "writing pixel data for layer" << layerName << "at" << io->pos(); KisPaintDeviceSP dev = m_layerContentDevice; const QRect rc(left, top, right - left, bottom - top); if (rc.isEmpty()) { dbgFile << "Layer is empty! Writing placeholder information."; for (int i = 0; i < nChannels; i++) { const ChannelInfo *channelInfo = channelInfoRecords[i]; KisAslWriterUtils::OffsetStreamPusher channelBlockSizeExternalTag(io, 0, channelInfo->channelInfoPosition); SAFE_WRITE_EX(io, (quint16)Compression::Uncompressed); } writeTransparencyMaskPixelData(io); return; } // now write all the channels in display order dbgFile << "layer" << layerName; const int channelSize = m_header.channelDepth / 8; const psd_color_mode colorMode = m_header.colormode; QVector writingInfoList; Q_FOREACH (const ChannelInfo *channelInfo, channelInfoRecords) { writingInfoList << PsdPixelUtils::ChannelWritingInfo(channelInfo->channelId, channelInfo->channelInfoPosition); } PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, true, true, writingInfoList); writeTransparencyMaskPixelData(io); } bool PSDLayerRecord::valid() { // XXX: check validity! return true; } bool PSDLayerRecord::readPixelData(QIODevice *io, KisPaintDeviceSP device) { dbgFile << "Reading pixel data for layer" << layerName << "pos" << io->pos(); const int channelSize = m_header.channelDepth / 8; const QRect layerRect = QRect(left, top, right - left, bottom - top); try { PsdPixelUtils::readChannels(io, device, m_header.colormode, channelSize, layerRect, channelInfoRecords); } catch (KisAslReaderUtils::ASLParseException &e) { device->clear(); error = e.what(); return false; } return true; } QRect PSDLayerRecord::channelRect(ChannelInfo *channel) const { QRect result; if (channel->channelId < -1) { result = QRect(layerMask.left, layerMask.top, layerMask.right - layerMask.left, layerMask.bottom - layerMask.top); } else { result = QRect(left, top, right - left, bottom - top); } return result; } bool PSDLayerRecord::readMask(QIODevice *io, KisPaintDeviceSP dev, ChannelInfo *channelInfo) { KIS_ASSERT_RECOVER(channelInfo->channelId < -1) { return false; } dbgFile << "Going to read" << channelIdToChannelType(channelInfo->channelId, m_header.colormode) << "mask"; QRect maskRect = channelRect(channelInfo); if (maskRect.isEmpty()) { dbgFile << "Empty Channel"; return true; } // the device must be a pixel selection KIS_ASSERT_RECOVER(dev->pixelSize() == 1) { return false; } dev->setDefaultPixel(KoColor(&layerMask.defaultColor, dev->colorSpace())); const int pixelSize = m_header.channelDepth == 16 ? 2 : m_header.channelDepth == 32 ? 4 : 1; QVector infoRecords; infoRecords << channelInfo; PsdPixelUtils::readAlphaMaskChannels(io, dev, pixelSize, maskRect, infoRecords); return true; } QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer) { #ifndef NODEBUG dbg.nospace() << "valid: " << const_cast(&layer)->valid(); dbg.nospace() << ", name: " << layer.layerName; dbg.nospace() << ", top: " << layer.top; dbg.nospace() << ", left:" << layer.left; dbg.nospace() << ", bottom: " << layer.bottom; dbg.nospace() << ", right: " << layer.right; dbg.nospace() << ", number of channels: " << layer.nChannels; dbg.nospace() << ", blendModeKey: " << layer.blendModeKey; dbg.nospace() << ", opacity: " << layer.opacity; dbg.nospace() << ", clipping: " << layer.clipping; dbg.nospace() << ", transparency protected: " << layer.transparencyProtected; dbg.nospace() << ", visible: " << layer.visible; dbg.nospace() << ", irrelevant: " << layer.irrelevant << "\n"; Q_FOREACH (const ChannelInfo* channel, layer.channelInfoRecords) { dbg.space() << channel; } #endif return dbg.nospace(); } QDebug operator<<(QDebug dbg, const ChannelInfo &channel) { #ifndef NODEBUG dbg.nospace() << "\tChannel type" << channel.channelId << "size: " << channel.channelDataLength << "compression type" << channel.compressionType << "\n"; #endif return dbg.nospace(); } diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc index 5c95d1c10c..16e8749682 100644 --- a/plugins/tools/basictools/kis_tool_move.cc +++ b/plugins/tools/basictools/kis_tool_move.cc @@ -1,655 +1,672 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2002 Patrick Julien * 2004 Boudewijn Rempt * 2016 Michael Abrahams * * 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 "kis_tool_move.h" #include #include "kis_cursor.h" #include "kis_selection.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_tool_utils.h" #include "kis_paint_layer.h" #include "strokes/move_stroke_strategy.h" #include "kis_tool_movetooloptionswidget.h" #include "strokes/move_selection_stroke_strategy.h" #include "kis_resources_snapshot.h" #include "kis_action_registry.h" #include "krita_utils.h" #include #include #include "kis_node_manager.h" #include "kis_signals_blocker.h" #include struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable { KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {} KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); } bool operator ==(const KisToolMoveState &rhs) { return accumulatedOffset == rhs.accumulatedOffset; } QPoint accumulatedOffset; }; KisToolMove::KisToolMove(KoCanvasBase *canvas) : KisTool(canvas, KisCursor::moveCursor()) , m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE) { setObjectName("tool_move"); m_showCoordinatesAction = action("movetool-show-coordinates"); m_showCoordinatesAction = action("movetool-show-coordinates"); connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle())); m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId()); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates()); m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset()); connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()), Qt::UniqueConnection); connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)), Qt::UniqueConnection); connect(qobject_cast(canvas)->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList)), Qt::UniqueConnection); } KisToolMove::~KisToolMove() { endStroke(); } void KisToolMove::resetCursorStyle() { KisTool::resetCursorStyle(); if (!isActive()) return; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(moveToolMode(), &m_lastCursorPos, selection); if (nodes.isEmpty()) { canvas()->setCursor(Qt::ForbiddenCursor); } } KisNodeList KisToolMove::fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection) { KisNodeList nodes; KisImageSP image = this->image(); if (mode != MoveSelectedLayer && pixelPoint) { const bool wholeGroup = !selection && mode == MoveGroup; KisNodeSP node = KisToolUtils::findNode(image->root(), *pixelPoint, wholeGroup); if (node) { nodes = {node}; } } if (nodes.isEmpty()) { nodes = this->selectedNodes(); KritaUtils::filterContainer(nodes, [](KisNodeSP node) { return node->isEditable(); }); } return nodes; } bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos) { KisNodeSP node; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(mode, pos, selection); if (nodes.size() == 1) { node = nodes.first(); } if (nodes.isEmpty()) { return false; } /** * If the target node has changed, the stroke should be * restarted. Otherwise just continue processing current node. */ if (m_strokeId && !tryEndPreviousStroke(nodes)) { return true; } + /** + * FIXME: The move tool is not completely asynchronous, it + * needs the content of the layer and/or selection to calculate + * the bounding rectange. Technically, we can move its calculation + * into the stroke itself and pass it back to the tool via a signal. + * + * But currently, we will just disable starting a new stroke + * asynchronously. + */ + if (image->tryBarrierLock()) { + image->unlock(); + } else { + return false; + } + + initHandles(nodes); KisStrokeStrategy *strategy; KisPaintLayerSP paintLayer = node ? dynamic_cast(node.data()) : 0; if (paintLayer && selection && - !selection->isTotallyUnselected(image->bounds())) { + (!selection->selectedRect().isEmpty() && + !selection->selectedExactRect().isEmpty())) { strategy = new MoveSelectionStrokeStrategy(paintLayer, selection, image.data(), image.data()); } else { strategy = new MoveStrokeStrategy(nodes, image.data(), image.data()); } m_strokeId = image->startStroke(strategy); m_currentlyProcessingNodes = nodes; m_accumulatedOffset = QPoint(); KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) { m_changesTracker.reset(); } commitChanges(); return true; } QPoint KisToolMove::currentOffset() const { return m_accumulatedOffset + m_dragPos - m_dragStart; } void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage) { if (!m_optionsWidget) return; const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset(); KisSignalsBlocker b(m_optionsWidget); emit moveInNewPosition(currentTopLeft); // TODO: fetch this info not from options widget, but from config const bool showCoordinates = m_optionsWidget->showCoordinates(); if (showCoordinates && showFloatingMessage) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in move tool", "X: %1 px, Y: %2 px", currentTopLeft.x(), currentTopLeft.y()), QIcon(), 1000, KisFloatingMessage::High); } } bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes) { if (!m_strokeId) return false; bool strokeEnded = false; if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) { endStroke(); strokeEnded = true; } return strokeEnded; } void KisToolMove::commitChanges() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); QSharedPointer newState(new KisToolMoveState(m_accumulatedOffset)); KisToolMoveState *lastState = dynamic_cast(m_changesTracker.lastState().data()); if (lastState && *lastState == *newState) return; m_changesTracker.commitConfig(newState); } void KisToolMove::moveDiscrete(MoveDirection direction, bool big) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } // Larger movement if "shift" key is pressed. qreal scale = big ? m_optionsWidget->moveScale() : 1.0; qreal moveStep = m_optionsWidget->moveStep() * scale; const QPoint offset = direction == Up ? QPoint( 0, -moveStep) : direction == Down ? QPoint( 0, moveStep) : direction == Left ? QPoint(-moveStep, 0) : QPoint( moveStep, 0) ; m_accumulatedOffset += offset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); commitChanges(); setMode(KisTool::HOVER_MODE); } void KisToolMove::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); QAction *a = action("movetool-move-up"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);}); a = action("movetool-move-down"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);}); a = action("movetool-move-left"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);}); a = action("movetool-move-right"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);}); a = action("movetool-move-up-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);}); a = action("movetool-move-down-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);}); a = action("movetool-move-left-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);}); a = action("movetool-move-right-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);}); connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)), Qt::UniqueConnection); connect(&m_changesTracker, SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); slotNodeChanged(this->selectedNodes()); } void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (m_strokeId) { QPainterPath handles; handles.addRect(m_handlesRect.translated(currentOffset())); QPainterPath path = pixelToView(handles); paintToolOutline(&gc, path); } } void KisToolMove::initHandles(const KisNodeList &nodes) { /** * The handles should be initialized only once, **before** the start of * the stroke. If the nodes change, we should restart the stroke. */ KIS_SAFE_ASSERT_RECOVER_NOOP(!m_strokeId); m_handlesRect = QRect(); for (KisNodeSP node : nodes) { node->exactBounds(); m_handlesRect |= node->exactBounds(); } if (image()->globalSelection()) { m_handlesRect &= image()->globalSelection()->selectedExactRect(); } } void KisToolMove::deactivate() { QAction *a = action("movetool-move-up"); disconnect(a, 0, this, 0); a = action("movetool-move-down"); disconnect(a, 0, this, 0); a = action("movetool-move-left"); disconnect(a, 0, this, 0); a = action("movetool-move-right"); disconnect(a, 0, this, 0); a = action("movetool-move-up-more"); disconnect(a, 0, this, 0); a = action("movetool-move-down-more"); disconnect(a, 0, this, 0); a = action("movetool-move-left-more"); disconnect(a, 0, this, 0); a = action("movetool-move-right-more"); disconnect(a, 0, this, 0); disconnect(m_showCoordinatesAction, 0, this, 0); disconnect(m_optionsWidget, 0, this, 0); endStroke(); KisTool::deactivate(); } void KisToolMove::requestStrokeEnd() { endStroke(); } void KisToolMove::requestStrokeCancellation() { cancelStroke(); } void KisToolMove::requestUndoDuringStroke() { if (!m_strokeId) return; if (m_changesTracker.isEmpty()) { cancelStroke(); } else { m_changesTracker.requestUndo(); } } void KisToolMove::beginPrimaryAction(KoPointerEvent *event) { startAction(event, moveToolMode()); } void KisToolMove::continuePrimaryAction(KoPointerEvent *event) { continueAction(event); } void KisToolMove::endPrimaryAction(KoPointerEvent *event) { endAction(event); } void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { // Ctrl+Right click toggles between moving current layer and moving layer w/ content if (action == PickFgNode || action == PickBgImage) { MoveToolMode mode = moveToolMode(); if (mode == MoveSelectedLayer) { mode = MoveFirstLayer; } else if (mode == MoveFirstLayer) { mode = MoveSelectedLayer; } startAction(event, mode); } else { startAction(event, MoveGroup); } } void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) continueAction(event); } void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) endAction(event); } void KisToolMove::mouseMoveEvent(KoPointerEvent *event) { m_lastCursorPos = convertToPixelCoord(event).toPoint(); KisTool::mouseMoveEvent(event); if (moveToolMode() == MoveFirstLayer) { m_updateCursorCompressor.start(); } } void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode) { QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); m_dragStart = pos; m_dragPos = pos; if (startStrokeImpl(mode, &pos)) { setMode(KisTool::PAINT_MODE); } else { event->ignore(); m_dragPos = QPoint(); m_dragStart = QPoint(); } qobject_cast(canvas())->updateCanvas(); } void KisToolMove::continueAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); m_dragPos = pos; drag(pos); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::endAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); drag(pos); m_accumulatedOffset += pos - m_dragStart; m_dragStart = QPoint(); m_dragPos = QPoint(); commitChanges(); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::drag(const QPoint& newPos) { KisImageWSP image = currentImage(); QPoint offset = m_accumulatedOffset + newPos - m_dragStart; image->addJob(m_strokeId, new MoveStrokeStrategy::Data(offset)); } void KisToolMove::endStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->endStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); KisToolMoveState *newState = dynamic_cast(state.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(newState); if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging m_accumulatedOffset = newState->accumulatedOffset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); } void KisToolMove::cancelStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->cancelStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } QWidget* KisToolMove::createOptionWidget() { return m_optionsWidget; } KisToolMove::MoveToolMode KisToolMove::moveToolMode() const { if (m_optionsWidget) return m_optionsWidget->mode(); return MoveSelectedLayer; } QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos) { QPoint move = pos - m_dragStart; // Snap to axis if (modifiers & Qt::ShiftModifier) { move = snapToClosestAxis(move); } // "Precision mode" - scale down movement by 1/5 if (modifiers & Qt::AltModifier) { const qreal SCALE_FACTOR = .2; move = SCALE_FACTOR * move; } return m_dragStart + move; } void KisToolMove::moveBySpinX(int newX) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.rx() = newX - m_handlesRect.x(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::moveBySpinY(int newY) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.ry() = newY - m_handlesRect.y(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::slotNodeChanged(KisNodeList nodes) { if (m_strokeId && !tryEndPreviousStroke(nodes)) { return; } initHandles(nodes); notifyGuiAfterMove(false); } QList KisToolMoveFactory::createActionsImpl() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QList actions = KisToolPaintFactoryBase::createActionsImpl(); actions << actionRegistry->makeQAction("movetool-move-up"); actions << actionRegistry->makeQAction("movetool-move-down"); actions << actionRegistry->makeQAction("movetool-move-left"); actions << actionRegistry->makeQAction("movetool-move-right"); actions << actionRegistry->makeQAction("movetool-move-up-more"); actions << actionRegistry->makeQAction("movetool-move-down-more"); actions << actionRegistry->makeQAction("movetool-move-left-more"); actions << actionRegistry->makeQAction("movetool-move-right-more"); actions << actionRegistry->makeQAction("movetool-show-coordinates"); return actions; } diff --git a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp index cc953cdb98..02d8d4875a 100644 --- a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp +++ b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp @@ -1,173 +1,177 @@ /* * Copyright (c) 2012 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 "move_selection_stroke_strategy.h" #include #include #include #include "kis_image.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer, KisSelectionSP selection, KisUpdatesFacade *updatesFacade, KisStrokeUndoFacade *undoFacade) : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move Selection"), false, undoFacade), m_paintLayer(paintLayer), m_selection(selection), m_updatesFacade(updatesFacade) { - enableJob(KisSimpleStrokeStrategy::JOB_INIT); + /** + * Selection might have some update projection jobs pending, so we should ensure + * all of them are completed before we start our stroke. + */ + enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER); enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL); } MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs) : KisStrokeStrategyUndoCommandBased(rhs), m_paintLayer(rhs.m_paintLayer), m_selection(rhs.m_selection), m_updatesFacade(rhs.m_updatesFacade) { } void MoveSelectionStrokeStrategy::initStrokeCallback() { KisStrokeStrategyUndoCommandBased::initStrokeCallback(); KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); KisPaintDeviceSP movedDevice = new KisPaintDevice(m_paintLayer.data(), paintDevice->colorSpace()); QRect copyRect = m_selection->selectedRect(); KisPainter gc(movedDevice); gc.setSelection(m_selection); gc.bitBlt(copyRect.topLeft(), paintDevice, copyRect); gc.end(); KisTransaction cutTransaction(name(), paintDevice); paintDevice->clearSelection(m_selection); runAndSaveCommand(KUndo2CommandSP(cutTransaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); indirect->setTemporaryTarget(movedDevice); indirect->setTemporaryCompositeOp(COMPOSITE_OVER); indirect->setTemporaryOpacity(OPACITY_OPAQUE_U8); m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y()); m_selection->setVisible(false); } void MoveSelectionStrokeStrategy::finishStrokeCallback() { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); KisTransaction transaction(name(), m_paintLayer->paintDevice()); indirect->mergeToLayer(m_paintLayer, (KisPostExecutionUndoAdapter*)0, KUndo2MagicString()); runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); indirect->setTemporaryTarget(0); QPoint selectionOffset(m_selection->x(), m_selection->y()); m_updatesFacade->blockUpdates(); KUndo2CommandSP moveSelectionCommand( new KisSelectionMoveCommand2(m_selection, selectionOffset, selectionOffset + m_finalOffset)); runAndSaveCommand( moveSelectionCommand, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); m_updatesFacade->unblockUpdates(); m_selection->setVisible(true); KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); } void MoveSelectionStrokeStrategy::cancelStrokeCallback() { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); if (indirect) { KisPaintDeviceSP t = indirect->temporaryTarget(); if (t) { QRegion dirtyRegion = t->region(); indirect->setTemporaryTarget(0); m_selection->setVisible(true); m_paintLayer->setDirty(dirtyRegion); } } KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); } #include "tool/strokes/move_stroke_strategy.h" void MoveSelectionStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { MoveStrokeStrategy::Data *d = dynamic_cast(data); if (d) { KisIndirectPaintingSupport *indirect = static_cast(m_paintLayer.data()); KisPaintDeviceSP movedDevice = indirect->temporaryTarget(); QRegion dirtyRegion = movedDevice->region(); QPoint currentDeviceOffset(movedDevice->x(), movedDevice->y()); QPoint newDeviceOffset(m_initialDeviceOffset + d->offset); dirtyRegion |= dirtyRegion.translated(newDeviceOffset - currentDeviceOffset); movedDevice->setX(newDeviceOffset.x()); movedDevice->setY(newDeviceOffset.y()); m_finalOffset = d->offset; m_paintLayer->setDirty(dirtyRegion); } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } } KisStrokeStrategy* MoveSelectionStrokeStrategy::createLodClone(int levelOfDetail) { Q_UNUSED(levelOfDetail); MoveSelectionStrokeStrategy *clone = new MoveSelectionStrokeStrategy(*this); return clone; }