diff options
Diffstat (limited to 'src/gui/opengl')
-rw-r--r-- | src/gui/opengl/opengl.pri | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopengl.h | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopenglengineshadermanager.cpp | 63 | ||||
-rw-r--r-- | src/gui/opengl/qopenglengineshadermanager_p.h | 4 | ||||
-rw-r--r-- | src/gui/opengl/qopenglengineshadersource_p.h | 173 | ||||
-rw-r--r-- | src/gui/opengl/qopenglextensions_p.h | 3 | ||||
-rw-r--r-- | src/gui/opengl/qopenglframebufferobject.cpp | 86 | ||||
-rw-r--r-- | src/gui/opengl/qopenglfunctions.cpp | 43 | ||||
-rw-r--r-- | src/gui/opengl/qopenglfunctions.h | 3 | ||||
-rw-r--r-- | src/gui/opengl/qopenglpaintengine.cpp | 102 | ||||
-rw-r--r-- | src/gui/opengl/qopenglprogrambinarycache.cpp | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopenglshaderprogram.cpp | 11 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache.cpp | 199 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache_p.h | 24 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureuploader.cpp | 328 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureuploader_p.h | 84 |
16 files changed, 736 insertions, 393 deletions
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index 4c778b184e..24758afdeb 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -32,6 +32,7 @@ qtConfig(opengl) { opengl/qopengltexture.h \ opengl/qopengltexture_p.h \ opengl/qopengltexturehelper_p.h \ + opengl/qopengltextureuploader_p.h \ opengl/qopenglpixeltransferoptions.h \ opengl/qopenglextrafunctions.h \ opengl/qopenglprogrambinarycache_p.h @@ -56,6 +57,7 @@ qtConfig(opengl) { opengl/qopengltextureblitter.cpp \ opengl/qopengltexture.cpp \ opengl/qopengltexturehelper.cpp \ + opengl/qopengltextureuploader.cpp \ opengl/qopenglpixeltransferoptions.cpp \ opengl/qopenglprogrambinarycache.cpp diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h index b4657fa118..3a2393ea58 100644 --- a/src/gui/opengl/qopengl.h +++ b/src/gui/opengl/qopengl.h @@ -239,7 +239,7 @@ typedef unsigned long long int uint64_t; typedef long int int32_t; typedef long long int int64_t; typedef unsigned long long int uint64_t; -#elif defined(_WIN32) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1600)) +#elif defined(_WIN32) && (defined(__GNUC__) || defined(_MSC_VER)) #include <stdint.h> #elif defined(_WIN32) typedef __int32 int32_t; diff --git a/src/gui/opengl/qopenglengineshadermanager.cpp b/src/gui/opengl/qopenglengineshadermanager.cpp index 2f7afa4a66..b7bac2728a 100644 --- a/src/gui/opengl/qopenglengineshadermanager.cpp +++ b/src/gui/opengl/qopenglengineshadermanager.cpp @@ -153,12 +153,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core; code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core; - code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO_core; - code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM_core; code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core; code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core; - code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO_core; - code[MainFragmentShader_C] = qopenglslMainFragmentShader_C_core; code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core; code[MainFragmentShader] = qopenglslMainFragmentShader_core; code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core; @@ -171,7 +167,7 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core; - code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop_core; + code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_core; code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core; code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core; code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core; @@ -203,12 +199,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader; code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader; - code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO; - code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM; code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO; code[MainFragmentShader_M] = qopenglslMainFragmentShader_M; - code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO; - code[MainFragmentShader_C] = qopenglslMainFragmentShader_C; code[MainFragmentShader_O] = qopenglslMainFragmentShader_O; code[MainFragmentShader] = qopenglslMainFragmentShader; code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays; @@ -220,10 +212,7 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader; code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader; - if (context->isOpenGLES()) - code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_ES; - else - code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop; + code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader; code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader; code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader; code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader; @@ -238,21 +227,20 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) code[RgbMaskWithGammaFragmentShader] = ""; //### } - // These shaders are not implemented yet and therefore are the same - // for all profiles. Implementations should make a version for both - // profiles and put the appropriate lines in the if-statement above. + // The composition shaders are just layout qualifiers and the same + // for all profiles that support them. code[NoCompositionModeFragmentShader] = ""; - code[MultiplyCompositionModeFragmentShader] = ""; //### - code[ScreenCompositionModeFragmentShader] = ""; //### - code[OverlayCompositionModeFragmentShader] = ""; //### - code[DarkenCompositionModeFragmentShader] = ""; //### - code[LightenCompositionModeFragmentShader] = ""; //### - code[ColorDodgeCompositionModeFragmentShader] = ""; //### - code[ColorBurnCompositionModeFragmentShader] = ""; //### - code[HardLightCompositionModeFragmentShader] = ""; //### - code[SoftLightCompositionModeFragmentShader] = ""; //### - code[DifferenceCompositionModeFragmentShader] = ""; //### - code[ExclusionCompositionModeFragmentShader] = ""; //### + code[MultiplyCompositionModeFragmentShader] = qopenglslMultiplyCompositionModeFragmentShader; + code[ScreenCompositionModeFragmentShader] = qopenglslScreenCompositionModeFragmentShader; + code[OverlayCompositionModeFragmentShader] = qopenglslOverlayCompositionModeFragmentShader; + code[DarkenCompositionModeFragmentShader] = qopenglslDarkenCompositionModeFragmentShader; + code[LightenCompositionModeFragmentShader] = qopenglslLightenCompositionModeFragmentShader; + code[ColorDodgeCompositionModeFragmentShader] = qopenglslColorDodgeCompositionModeFragmentShader; + code[ColorBurnCompositionModeFragmentShader] = qopenglslColorBurnCompositionModeFragmentShader; + code[HardLightCompositionModeFragmentShader] = qopenglslHardLightCompositionModeFragmentShader; + code[SoftLightCompositionModeFragmentShader] = qopenglslSoftLightCompositionModeFragmentShader; + code[DifferenceCompositionModeFragmentShader] = qopenglslDifferenceCompositionModeFragmentShader; + code[ExclusionCompositionModeFragmentShader] = qopenglslExclusionCompositionModeFragmentShader; #if defined(QT_DEBUG) // Check that all the elements have been filled: @@ -612,8 +600,11 @@ void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mo if (compositionMode == mode) return; + bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus; + bool isAdvanced = mode > QPainter::CompositionMode_Plus; + compositionMode = mode; - shaderProgNeedsChanging = true; //### + shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced; } void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage) @@ -783,21 +774,13 @@ bool QOpenGLEngineShaderManager::useCorrectShaderProg() requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays; } else { bool useGlobalOpacity = (opacityMode == UniformOpacity); - if (hasCompose && hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CMO; - if (hasCompose && hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CM; - if (!hasCompose && hasMask && useGlobalOpacity) + if (hasMask && useGlobalOpacity) requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO; - if (!hasCompose && hasMask && !useGlobalOpacity) + if (hasMask && !useGlobalOpacity) requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M; - if (hasCompose && !hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CO; - if (hasCompose && !hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_C; - if (!hasCompose && !hasMask && useGlobalOpacity) + if (!hasMask && useGlobalOpacity) requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O; - if (!hasCompose && !hasMask && !useGlobalOpacity) + if (!hasMask && !useGlobalOpacity) requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader; } diff --git a/src/gui/opengl/qopenglengineshadermanager_p.h b/src/gui/opengl/qopenglengineshadermanager_p.h index 377501457d..d43788d777 100644 --- a/src/gui/opengl/qopenglengineshadermanager_p.h +++ b/src/gui/opengl/qopenglengineshadermanager_p.h @@ -281,12 +281,8 @@ public: AffinePositionWithTextureBrushVertexShader, // MainFragmentShader_CMO must be first in the list: - MainFragmentShader_CMO, - MainFragmentShader_CM, MainFragmentShader_MO, MainFragmentShader_M, - MainFragmentShader_CO, - MainFragmentShader_C, MainFragmentShader_O, MainFragmentShader, MainFragmentShader_ImageArrays, diff --git a/src/gui/opengl/qopenglengineshadersource_p.h b/src/gui/opengl/qopenglengineshadersource_p.h index a165643839..3ac599b6c2 100644 --- a/src/gui/opengl/qopenglengineshadersource_p.h +++ b/src/gui/opengl/qopenglengineshadersource_p.h @@ -303,17 +303,7 @@ static const char* const qopenglslPositionWithTextureBrushVertexShader = "\n\ static const char* const qopenglslAffinePositionWithTextureBrushVertexShader = qopenglslPositionWithTextureBrushVertexShader; -// OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead, -// we emulate GL_REPEAT by only taking the fractional part of the texture coords. -// TODO: Special case POT textures which don't need this emulation -static const char* const qopenglslTextureBrushSrcFragmentShader_ES = "\n\ - varying highp vec2 brushTextureCoords; \n\ - uniform sampler2D brushTexture; \n\ - lowp vec4 srcPixel() { \n\ - return texture2D(brushTexture, fract(brushTextureCoords)); \n\ - }\n"; - -static const char* const qopenglslTextureBrushSrcFragmentShader_desktop = "\n\ +static const char* const qopenglslTextureBrushSrcFragmentShader = "\n\ varying highp vec2 brushTextureCoords; \n\ uniform sampler2D brushTexture; \n\ lowp vec4 srcPixel() \n\ @@ -403,25 +393,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays = "\n\ gl_FragColor = srcPixel() * opacity; \n\ }\n"; -static const char* const qopenglslMainFragmentShader_CMO = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\ - }\n"; - -static const char* const qopenglslMainFragmentShader_CM = "\n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(compose(srcPixel())); \n\ - }\n"; - static const char* const qopenglslMainFragmentShader_MO = "\n\ uniform lowp float globalOpacity; \n\ lowp vec4 srcPixel(); \n\ @@ -439,23 +410,6 @@ static const char* const qopenglslMainFragmentShader_M = "\n\ gl_FragColor = applyMask(srcPixel()); \n\ }\n"; -static const char* const qopenglslMainFragmentShader_CO = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = compose(srcPixel()*globalOpacity); \n\ - }\n"; - -static const char* const qopenglslMainFragmentShader_C = "\n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = compose(srcPixel()); \n\ - }\n"; - static const char* const qopenglslMainFragmentShader_O = "\n\ uniform lowp float globalOpacity; \n\ lowp vec4 srcPixel(); \n\ @@ -513,22 +467,65 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\ return src * mask; \n\ }\n"; +static const char* const qopenglslMultiplyCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_multiply) out;\n\ + #endif\n"; + +static const char* const qopenglslScreenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_screen) out;\n\ + #endif\n"; + +static const char* const qopenglslOverlayCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_overlay) out;\n\ + #endif\n"; + +static const char* const qopenglslDarkenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_darken) out;\n\ + #endif\n"; + +static const char* const qopenglslLightenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_lighten) out;\n\ + #endif\n"; + +static const char* const qopenglslColorDodgeCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_colordodge) out;\n\ + #endif\n"; + +static const char* const qopenglslColorBurnCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_colorburn) out;\n\ + #endif\n"; + +static const char* const qopenglslHardLightCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_hardlight) out;\n\ + #endif\n"; + +static const char* const qopenglslSoftLightCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_softlight) out;\n\ + #endif\n"; + +static const char* const qopenglslDifferenceCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_difference) out;\n\ + #endif\n"; + +static const char* const qopenglslExclusionCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_exclusion) out;\n\ + #endif\n"; + /* Left to implement: RgbMaskFragmentShader, RgbMaskWithGammaFragmentShader, - - MultiplyCompositionModeFragmentShader, - ScreenCompositionModeFragmentShader, - OverlayCompositionModeFragmentShader, - DarkenCompositionModeFragmentShader, - LightenCompositionModeFragmentShader, - ColorDodgeCompositionModeFragmentShader, - ColorBurnCompositionModeFragmentShader, - HardLightCompositionModeFragmentShader, - SoftLightCompositionModeFragmentShader, - DifferenceCompositionModeFragmentShader, - ExclusionCompositionModeFragmentShader, */ /* @@ -788,7 +785,7 @@ static const char* const qopenglslPositionWithTextureBrushVertexShader_core = "\ static const char* const qopenglslAffinePositionWithTextureBrushVertexShader_core = qopenglslPositionWithTextureBrushVertexShader_core; -static const char* const qopenglslTextureBrushSrcFragmentShader_desktop_core = "\n\ +static const char* const qopenglslTextureBrushSrcFragmentShader_core = "\n\ in vec2 brushTextureCoords; \n\ uniform sampler2D brushTexture; \n\ vec4 srcPixel() \n\ @@ -880,29 +877,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays_core = fragColor = srcPixel() * opacity; \n\ }\n"; -static const char* const qopenglslMainFragmentShader_CMO_core = - "#version 150 core\n\ - out vec4 fragColor; \n\ - uniform float globalOpacity; \n\ - vec4 srcPixel(); \n\ - vec4 applyMask(vec4); \n\ - vec4 compose(vec4); \n\ - void main() \n\ - { \n\ - fragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\ - }\n"; - -static const char* const qopenglslMainFragmentShader_CM_core = - "#version 150 core\n\ - out vec4 fragColor; \n\ - vec4 srcPixel(); \n\ - vec4 applyMask(vec4); \n\ - vec4 compose(vec4); \n\ - void main() \n\ - { \n\ - fragColor = applyMask(compose(srcPixel())); \n\ - }\n"; - static const char* const qopenglslMainFragmentShader_MO_core = "#version 150 core\n\ out vec4 fragColor; \n\ @@ -924,27 +898,6 @@ static const char* const qopenglslMainFragmentShader_M_core = fragColor = applyMask(srcPixel()); \n\ }\n"; -static const char* const qopenglslMainFragmentShader_CO_core = - "#version 150 core\n\ - out vec4 fragColor; \n\ - uniform float globalOpacity; \n\ - vec4 srcPixel(); \n\ - vec4 compose(vec4); \n\ - void main() \n\ - { \n\ - fragColor = compose(srcPixel()*globalOpacity); \n\ - }\n"; - -static const char* const qopenglslMainFragmentShader_C_core = - "#version 150 core\n\ - out vec4 fragColor; \n\ - vec4 srcPixel(); \n\ - vec4 compose(vec4); \n\ - void main() \n\ - { \n\ - fragColor = compose(srcPixel()); \n\ - }\n"; - static const char* const qopenglslMainFragmentShader_O_core = "#version 150 core\n\ out vec4 fragColor; \n\ @@ -1010,18 +963,6 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\ Left to implement: RgbMaskFragmentShader_core, RgbMaskWithGammaFragmentShader_core, - - MultiplyCompositionModeFragmentShader_core, - ScreenCompositionModeFragmentShader_core, - OverlayCompositionModeFragmentShader_core, - DarkenCompositionModeFragmentShader_core, - LightenCompositionModeFragmentShader_core, - ColorDodgeCompositionModeFragmentShader_core, - ColorBurnCompositionModeFragmentShader_core, - HardLightCompositionModeFragmentShader_core, - SoftLightCompositionModeFragmentShader_core, - DifferenceCompositionModeFragmentShader_core, - ExclusionCompositionModeFragmentShader_core, */ QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h index a5f1a2cc88..af8ee8201d 100644 --- a/src/gui/opengl/qopenglextensions_p.h +++ b/src/gui/opengl/qopenglextensions_p.h @@ -90,7 +90,8 @@ public: MapBufferRange = 0x00100000, Sized8Formats = 0x00200000, DiscardFramebuffer = 0x00400000, - Sized16Formats = 0x00800000 + Sized16Formats = 0x00800000, + TextureSwizzle = 0x01000000, }; Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension) diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 83bc568ba7..cae3d516c4 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -110,6 +110,10 @@ QT_BEGIN_NAMESPACE #define GL_RGB10 0x8052 #endif +#ifndef GL_RGB16 +#define GL_RGB16 0x8054 +#endif + #ifndef GL_RGBA8 #define GL_RGBA8 0x8058 #endif @@ -118,6 +122,10 @@ QT_BEGIN_NAMESPACE #define GL_RGB10_A2 0x8059 #endif +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif @@ -134,6 +142,15 @@ QT_BEGIN_NAMESPACE #define GL_CONTEXT_LOST 0x0507 #endif +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif + +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif + + /*! \class QOpenGLFramebufferObjectFormat @@ -530,6 +547,8 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx) GLuint pixelType = GL_UNSIGNED_BYTE; if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) + pixelType = GL_UNSIGNED_SHORT; funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, GL_RGBA, pixelType, NULL); @@ -567,11 +586,16 @@ void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples) GLenum storageFormat = color.internalFormat; // ES requires a sized format. The older desktop extension does not. Correct the format on ES. - if (ctx->isOpenGLES() && color.internalFormat == GL_RGBA) { - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats)) - storageFormat = GL_RGBA8; - else - storageFormat = GL_RGBA4; + if (ctx->isOpenGLES()) { + if (color.internalFormat == GL_RGBA) { + if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats)) + storageFormat = GL_RGBA8; + else + storageFormat = GL_RGBA4; + } else if (color.internalFormat == GL_RGB10) { + // GL_RGB10 is not allowed in ES for glRenderbufferStorage. + storageFormat = GL_RGB10_A2; + } } funcs.glGenRenderbuffers(1, &color_buffer); @@ -603,7 +627,11 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext // free existing attachments if (depth_buffer_guard) { +#ifdef Q_OS_WASM + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); +#else funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); +#endif depth_buffer_guard->free(); } if (stencil_buffer_guard) { @@ -621,7 +649,35 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer // might not be supported while separate buffers are, according to QTBUG-12861. +#ifdef Q_OS_WASM + // WebGL doesn't allow separately attach buffers to + // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT + // QTBUG-69913 + if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) { + funcs.glGenRenderbuffers(1, &depth_buffer); + funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); + Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); + GLenum storageFormat = GL_DEPTH_STENCIL; + + if (samples != 0 ) { + funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, + storageFormat, dsSize.width(), dsSize.height()); + } else { + funcs.glRenderbufferStorage(GL_RENDERBUFFER, storageFormat, + dsSize.width(), dsSize.height()); + } + + funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depth_buffer); + + valid = checkFramebufferStatus(ctx); + if (!valid) { + funcs.glDeleteRenderbuffers(1, &depth_buffer); + depth_buffer = 0; + } + } +#else if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) { @@ -713,11 +769,16 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext stencil_buffer = 0; } } +#endif //Q_OS_WASM // The FBO might have become valid after removing the depth or stencil buffer. valid = checkFramebufferStatus(ctx); +#ifdef Q_OS_WASM + if (depth_buffer) { +#else if (depth_buffer && stencil_buffer) { +#endif fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil; } else if (depth_buffer) { fbo_attachment = QOpenGLFramebufferObject::Depth; @@ -1307,6 +1368,14 @@ static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool incl return img; } +static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context) +{ + // We assume OpenGL 1.2+ or ES 3.0+ here. + QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64); + context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits()); + return img; +} + static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -1324,6 +1393,10 @@ static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip); case GL_RGB10_A2: return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip); + case GL_RGB16: + return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip); + case GL_RGBA16: + return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip); case GL_RGBA: case GL_RGBA8: default: @@ -1352,7 +1425,8 @@ Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2 the function will fall back to premultiplied RGBA8888 or RGBx8888 when reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt - 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2. + 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since + Qt 5.12 a RGBA64 image is return if the internal format is RGBA16. If the rendering in the framebuffer was not done with premultiplied alpha in mind, create a wrapper QImage with a non-premultiplied format. This is necessary before diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index 977565516f..4f48604a88 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -45,6 +45,7 @@ #include <QtGui/private/qopengl_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> +#include <qpa/qplatformnativeinterface.h> #ifdef Q_OS_INTEGRITY #include <EGL/egl.h> @@ -294,9 +295,19 @@ QOpenGLExtensions::QOpenGLExtensions(QOpenGLContext *context) static int qt_gl_resolve_features() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLExtensionMatcher extensions; + int features = 0; + if ((extensions.match("GL_KHR_blend_equation_advanced") + || extensions.match("GL_NV_blend_equation_advanced")) && + (extensions.match("GL_KHR_blend_equation_advanced_coherent") + || extensions.match("GL_NV_blend_equation_advanced_coherent"))) { + // We need both the advanced equations and the coherency for us + // to be able to easily use the new blend equations + features |= QOpenGLFunctions::BlendEquationAdvanced; + } if (ctx->isOpenGLES()) { // OpenGL ES - int features = QOpenGLFunctions::Multitexture | + features |= QOpenGLFunctions::Multitexture | QOpenGLFunctions::Shaders | QOpenGLFunctions::Buffers | QOpenGLFunctions::Framebuffers | @@ -308,7 +319,6 @@ static int qt_gl_resolve_features() QOpenGLFunctions::CompressedTextures | QOpenGLFunctions::Multisample | QOpenGLFunctions::StencilSeparate; - QOpenGLExtensionMatcher extensions; if (extensions.match("GL_IMG_texture_npot")) features |= QOpenGLFunctions::NPOTTextures; if (extensions.match("GL_OES_texture_npot")) @@ -320,14 +330,18 @@ static int qt_gl_resolve_features() if (!(renderer && strstr(renderer, "Mesa"))) features |= QOpenGLFunctions::TextureRGFormats; } - if (ctx->format().majorVersion() >= 3) + if (ctx->format().majorVersion() >= 3) { features |= QOpenGLFunctions::MultipleRenderTargets; + if (ctx->format().minorVersion() >= 2 && extensions.match("GL_KHR_blend_equation_advanced_coherent")) { + // GL_KHR_blend_equation_advanced is included in OpenGL ES/3.2 + features |= QOpenGLFunctions::BlendEquationAdvanced; + } + } return features; } else { // OpenGL - int features = QOpenGLFunctions::TextureRGFormats; + features |= QOpenGLFunctions::TextureRGFormats; QSurfaceFormat format = QOpenGLContext::currentContext()->format(); - QOpenGLExtensionMatcher extensions; if (format.majorVersion() >= 3) features |= QOpenGLFunctions::Framebuffers | QOpenGLFunctions::MultipleRenderTargets; @@ -411,6 +425,8 @@ static int qt_gl_resolve_extensions() extensions |= QOpenGLExtensions::NVFloatBuffer; if (extensionMatcher.match("GL_ARB_pixel_buffer_object")) extensions |= QOpenGLExtensions::PixelBufferObject; + if (extensionMatcher.match("GL_ARB_texture_swizzle") || extensionMatcher.match("GL_EXT_texture_swizzle")) + extensions |= QOpenGLExtensions::TextureSwizzle; if (ctx->isOpenGLES()) { if (format.majorVersion() >= 2) @@ -423,7 +439,8 @@ static int qt_gl_resolve_extensions() | QOpenGLExtensions::MapBufferRange | QOpenGLExtensions::FramebufferBlit | QOpenGLExtensions::FramebufferMultisample - | QOpenGLExtensions::Sized8Formats; + | QOpenGLExtensions::Sized8Formats + | QOpenGLExtensions::TextureSwizzle; } else { // Recognize features by extension name. if (extensionMatcher.match("GL_OES_packed_depth_stencil")) @@ -449,6 +466,17 @@ static int qt_gl_resolve_extensions() // We don't match GL_APPLE_texture_format_BGRA8888 here because it has different semantics. if (extensionMatcher.match("GL_IMG_texture_format_BGRA8888") || extensionMatcher.match("GL_EXT_texture_format_BGRA8888")) extensions |= QOpenGLExtensions::BGRATextureFormat; +#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) + QString *deviceName = + static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); + static bool wrongfullyReportsBgra8888Support = deviceName != 0 + && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0); + if (wrongfullyReportsBgra8888Support) + extensions &= ~QOpenGLExtensions::BGRATextureFormat; +#endif + if (extensionMatcher.match("GL_EXT_discard_framebuffer")) extensions |= QOpenGLExtensions::DiscardFramebuffer; if (extensionMatcher.match("GL_EXT_texture_norm16")) @@ -482,6 +510,9 @@ static int qt_gl_resolve_extensions() if (format.version() >= qMakePair(3, 2) || extensionMatcher.match("GL_ARB_geometry_shader4")) extensions |= QOpenGLExtensions::GeometryShaders; + if (format.version() >= qMakePair(3, 3)) + extensions |= QOpenGLExtensions::TextureSwizzle; + if (extensionMatcher.match("GL_ARB_map_buffer_range")) extensions |= QOpenGLExtensions::MapBufferRange; diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h index 1a43f13d9b..00287b0665 100644 --- a/src/gui/opengl/qopenglfunctions.h +++ b/src/gui/opengl/qopenglfunctions.h @@ -277,7 +277,8 @@ public: NPOTTextureRepeat = 0x2000, FixedFunctionPipeline = 0x4000, TextureRGFormats = 0x8000, - MultipleRenderTargets = 0x10000 + MultipleRenderTargets = 0x10000, + BlendEquationAdvanced = 0x20000, }; Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature) diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index 6a89089f18..001cb839fa 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -87,8 +87,27 @@ #include <QDebug> -QT_BEGIN_NAMESPACE +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ +QT_BEGIN_NAMESPACE Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); @@ -244,7 +263,7 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QGradient &gradient) struct ImageWithBindOptions { const QImage ℑ - QOpenGLTextureCache::BindOptions options; + QOpenGLTextureUploader::BindOptions options; }; template<> @@ -253,6 +272,12 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const ImageWithBindOptions &ima return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, imageWithOptions.image, imageWithOptions.options); } +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + void QOpenGL2PaintEngineExPrivate::updateBrushTexture() { Q_Q(QOpenGL2PaintEngineEx); @@ -285,16 +310,18 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture() currentBrushImage = currentBrush.textureImage(); int max_texture_size = ctx->d_func()->maxTextureSize(); - if (currentBrushImage.width() > max_texture_size || currentBrushImage.height() > max_texture_size) - currentBrushImage = currentBrushImage.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + QSize newSize = currentBrushImage.size(); + newSize = newSize.boundedTo(QSize(max_texture_size, max_texture_size)); + if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) { + if (!isPowerOfTwo(newSize.width()) || !isPowerOfTwo(newSize.height())) { + newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1)); + newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1)); + } + } + if (currentBrushImage.size() != newSize) + currentBrushImage = currentBrushImage.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); GLuint wrapMode = GL_REPEAT; - if (QOpenGLContext::currentContext()->isOpenGLES()) { - // OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead, - // we emulate GL_REPEAT by only taking the fractional part of the texture coords - // in the qopenglslTextureBrushSrcFragmentShader program. - wrapMode = GL_CLAMP_TO_EDGE; - } updateTexture(QT_BRUSH_TEXTURE_UNIT, currentBrushImage, wrapMode, filterMode, ForceUpdate); } @@ -498,6 +525,21 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode() // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these // composition modes look odd. // qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode; + if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::BlendEquationAdvanced)) { + if (q->state()->composition_mode <= QPainter::CompositionMode_Plus) { + funcs.glDisable(GL_BLEND_ADVANCED_COHERENT_KHR); + funcs.glBlendEquation(GL_FUNC_ADD); + } else { + funcs.glEnable(GL_BLEND_ADVANCED_COHERENT_KHR); + } + shaderManager->setCompositionMode(q->state()->composition_mode); + } else { + if (q->state()->composition_mode > QPainter::CompositionMode_Plus) { + qWarning("Unsupported composition mode"); + compositionModeDirty = false; + return; + } + } switch(q->state()->composition_mode) { case QPainter::CompositionMode_SourceOver: funcs.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -538,6 +580,39 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode() case QPainter::CompositionMode_Plus: funcs.glBlendFunc(GL_ONE, GL_ONE); break; + case QPainter::CompositionMode_Multiply: + funcs.glBlendEquation(GL_MULTIPLY_KHR); + break; + case QPainter::CompositionMode_Screen: + funcs.glBlendEquation(GL_SCREEN_KHR); + break; + case QPainter::CompositionMode_Overlay: + funcs.glBlendEquation(GL_OVERLAY_KHR); + break; + case QPainter::CompositionMode_Darken: + funcs.glBlendEquation(GL_DARKEN_KHR); + break; + case QPainter::CompositionMode_Lighten: + funcs.glBlendEquation(GL_LIGHTEN_KHR); + break; + case QPainter::CompositionMode_ColorDodge: + funcs.glBlendEquation(GL_COLORDODGE_KHR); + break; + case QPainter::CompositionMode_ColorBurn: + funcs.glBlendEquation(GL_COLORBURN_KHR); + break; + case QPainter::CompositionMode_HardLight: + funcs.glBlendEquation(GL_HARDLIGHT_KHR); + break; + case QPainter::CompositionMode_SoftLight: + funcs.glBlendEquation(GL_SOFTLIGHT_KHR); + break; + case QPainter::CompositionMode_Difference: + funcs.glBlendEquation(GL_DIFFERENCE_KHR); + break; + case QPainter::CompositionMode_Exclusion: + funcs.glBlendEquation(GL_EXCLUSION_KHR); + break; default: qWarning("Unsupported composition mode"); break; @@ -1487,25 +1562,26 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c ensureActive(); d->transferMode(ImageDrawingMode); - QOpenGLTextureCache::BindOptions bindOption = QOpenGLTextureCache::PremultipliedAlphaBindOption; + QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption; // Use specialized bind for formats we have specialized shaders for. switch (image.format()) { case QImage::Format_RGBA8888: case QImage::Format_ARGB32: + case QImage::Format_RGBA64: d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc); bindOption = 0; break; case QImage::Format_Alpha8: if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) { d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc); - bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption; + bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption; } else d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); break; case QImage::Format_Grayscale8: if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) { d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc); - bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption; + bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption; } else d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); break; diff --git a/src/gui/opengl/qopenglprogrambinarycache.cpp b/src/gui/opengl/qopenglprogrambinarycache.cpp index d16173df83..f2d093ebed 100644 --- a/src/gui/opengl/qopenglprogrambinarycache.cpp +++ b/src/gui/opengl/qopenglprogrambinarycache.cpp @@ -62,6 +62,7 @@ const quint32 BINSHADER_MAGIC = 0x5174; const quint32 BINSHADER_VERSION = 0x2; const quint32 BINSHADER_QTVERSION = QT_VERSION; +namespace { struct GLEnvInfo { GLEnvInfo(); @@ -70,6 +71,7 @@ struct GLEnvInfo QByteArray glrenderer; QByteArray glversion; }; +} GLEnvInfo::GLEnvInfo() { diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp index 2ae9c6d327..6d7aecac6f 100644 --- a/src/gui/opengl/qopenglshaderprogram.cpp +++ b/src/gui/opengl/qopenglshaderprogram.cpp @@ -496,6 +496,13 @@ static const char redefineHighp[] = "#endif\n"; #endif +// Boiler-plate header to have the layout attributes available we need later +static const char blendEquationAdvancedHeader[] = + "#ifdef GL_KHR_blend_equation_advanced\n" + "#extension GL_ARB_fragment_coord_conventions : enable\n" + "#extension GL_KHR_blend_equation_advanced : enable\n" + "#endif\n"; + struct QVersionDirectivePosition { Q_DECL_CONSTEXPR QVersionDirectivePosition(int position = 0, int line = -1) @@ -636,6 +643,10 @@ bool QOpenGLShader::compileSourceCode(const char *source) } } } + if (d->shaderType == Fragment) { + sourceChunks.append(blendEquationAdvancedHeader); + sourceChunkLengths.append(GLint(sizeof(blendEquationAdvancedHeader) - 1)); + } // The precision qualifiers are useful on OpenGL/ES systems, // but usually not present on desktop systems. diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp index 27aa8db33a..8de0b25fee 100644 --- a/src/gui/opengl/qopengltexturecache.cpp +++ b/src/gui/opengl/qopengltexturecache.cpp @@ -38,39 +38,14 @@ ****************************************************************************/ #include "qopengltexturecache_p.h" +#include "qopengltextureuploader_p.h" #include <qmath.h> #include <qopenglfunctions.h> -#include <private/qopenglcontext_p.h> -#include <private/qopenglextensions_p.h> #include <private/qimagepixmapcleanuphooks_p.h> #include <qpa/qplatformpixmap.h> QT_BEGIN_NAMESPACE -#ifndef GL_RED -#define GL_RED 0x1903 -#endif - -#ifndef GL_RGB10_A2 -#define GL_RGB10_A2 0x8059 -#endif - -#ifndef GL_BGR -#define GL_BGR 0x80E0 -#endif - -#ifndef GL_BGRA -#define GL_BGRA 0x80E1 -#endif - -#ifndef GL_UNSIGNED_INT_8_8_8_8_REV -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#endif - -#ifndef GL_UNSIGNED_INT_2_10_10_10_REV -#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 -#endif - class QOpenGLTextureCacheWrapper { public: @@ -130,7 +105,7 @@ QOpenGLTextureCache::~QOpenGLTextureCache() { } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, BindOptions options) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureUploader::BindOptions options) { if (pixmap.isNull()) return 0; @@ -153,7 +128,7 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap & return id; } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureUploader::BindOptions options) { if (image.isNull()) return 0; @@ -170,16 +145,8 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i } QImage img = image; - if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) { - // Scale the pixmap if needed. GL textures needs to have the - // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL - // 2.0 or use the GL_TEXTURE_RECTANGLE texture target - int tx_w = qNextPowerOfTwo(image.width() - 1); - int tx_h = qNextPowerOfTwo(image.height() - 1); - if (tx_w != image.width() || tx_h != image.height()) { - img = img.scaled(tx_w, tx_h); - } - } + if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) + options |= QOpenGLTextureUploader::PowerOfTwoBindOption; GLuint id = bindTexture(context, key, img, options); if (id > 0) @@ -188,164 +155,16 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i return id; } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, BindOptions options) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options) { GLuint id; QOpenGLFunctions *funcs = context->functions(); funcs->glGenTextures(1, &id); funcs->glBindTexture(GL_TEXTURE_2D, id); - QImage tx; - GLenum externalFormat; - GLenum internalFormat; - GLuint pixelType; - QImage::Format targetFormat = QImage::Format_Invalid; - const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); - - switch (image.format()) { - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - if (isOpenGL12orBetter) { - externalFormat = GL_BGRA; - internalFormat = GL_RGBA; - pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; - } else { -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian. - break; -#endif - if (static_cast<QOpenGLExtensions*>(context->functions())->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat)) { - // GL_EXT_bgra or GL_EXT_texture_format_BGRA8888 extensions. - if (context->isOpenGLES()) { - // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. - externalFormat = internalFormat = GL_BGRA; - } else { - // OpenGL BGRA/BGR format is not allowed as an internal format - externalFormat = GL_BGRA; - internalFormat = GL_RGBA; - } - pixelType = GL_UNSIGNED_BYTE; - } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { - // Is only allowed as an external format like OpenGL. - externalFormat = GL_BGRA; - internalFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - } else { - // No support for direct ARGB32 upload. - break; - } - } - targetFormat = image.format(); - break; - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) { - pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - externalFormat = GL_RGBA; - internalFormat = GL_RGB10_A2; - targetFormat = image.format(); - } - break; - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_Premultiplied: - if (isOpenGL12orBetter) { - pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - externalFormat = GL_BGRA; - internalFormat = GL_RGB10_A2; - targetFormat = image.format(); - } else if (context->isOpenGLES() && context->format().majorVersion() >= 3) { - pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; - externalFormat = GL_RGBA; - internalFormat = GL_RGB10_A2; - targetFormat = QImage::Format_A2BGR30_Premultiplied; - } - break; - case QImage::Format_RGB444: - case QImage::Format_RGB555: - case QImage::Format_RGB16: - if (isOpenGL12orBetter || context->isOpenGLES()) { - externalFormat = internalFormat = GL_RGB; - pixelType = GL_UNSIGNED_SHORT_5_6_5; - targetFormat = QImage::Format_RGB16; - } - break; - case QImage::Format_RGB666: - case QImage::Format_RGB888: - externalFormat = internalFormat = GL_RGB; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = QImage::Format_RGB888; - break; - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: - externalFormat = internalFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - break; - case QImage::Format_Indexed8: - if (options & UseRedFor8BitBindOption) { - externalFormat = internalFormat = GL_RED; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - } - break; - case QImage::Format_Alpha8: - if (options & UseRedFor8BitBindOption) { - externalFormat = internalFormat = GL_RED; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { - externalFormat = internalFormat = GL_ALPHA; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - } - break; - case QImage::Format_Grayscale8: - if (options & UseRedFor8BitBindOption) { - externalFormat = internalFormat = GL_RED; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { - externalFormat = internalFormat = GL_LUMINANCE; - pixelType = GL_UNSIGNED_BYTE; - targetFormat = image.format(); - } - break; - default: - break; - } - - if (targetFormat == QImage::Format_Invalid) { - externalFormat = internalFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - if (!image.hasAlphaChannel()) - targetFormat = QImage::Format_RGBX8888; - else - targetFormat = QImage::Format_RGBA8888; - } - - if (options & PremultipliedAlphaBindOption) { - if (targetFormat == QImage::Format_ARGB32) - targetFormat = QImage::Format_ARGB32_Premultiplied; - else if (targetFormat == QImage::Format_RGBA8888) - targetFormat = QImage::Format_RGBA8888_Premultiplied; - } else { - if (targetFormat == QImage::Format_ARGB32_Premultiplied) - targetFormat = QImage::Format_ARGB32; - else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) - targetFormat = QImage::Format_RGBA8888; - } - - if (image.format() != targetFormat) - tx = image.convertToFormat(targetFormat); - else - tx = image; - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, const_cast<const QImage &>(tx).bits()); + int cost = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options); - int cost = tx.width() * tx.height() * tx.depth() / (1024 * 8); - m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost); + m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost / 1024); return id; } @@ -371,7 +190,7 @@ static void freeTexture(QOpenGLFunctions *funcs, GLuint id) funcs->glDeleteTextures(1, &id); } -QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context) : m_options(options) +QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context) : m_options(options) { m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture); } diff --git a/src/gui/opengl/qopengltexturecache_p.h b/src/gui/opengl/qopengltexturecache_p.h index b9d7df91e3..88ef06e744 100644 --- a/src/gui/opengl/qopengltexturecache_p.h +++ b/src/gui/opengl/qopengltexturecache_p.h @@ -56,6 +56,7 @@ #include <QObject> #include <QCache> #include <private/qopenglcontext_p.h> +#include <private/qopengltextureuploader_p.h> #include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE @@ -70,15 +71,10 @@ public: QOpenGLTextureCache(QOpenGLContext *); ~QOpenGLTextureCache(); - enum BindOption { - NoBindOption = 0x0000, - PremultipliedAlphaBindOption = 0x0001, - UseRedFor8BitBindOption = 0x0002, - }; - Q_DECLARE_FLAGS(BindOptions, BindOption) - - GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption); - GLuint bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption); + GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, + QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption); + GLuint bindTexture(QOpenGLContext *context, const QImage &image, + QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption); void invalidate(qint64 key); @@ -86,26 +82,24 @@ public: void freeResource(QOpenGLContext *ctx) override; private: - GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureCache::BindOptions options); + GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options); QMutex m_mutex; QCache<quint64, QOpenGLCachedTexture> m_cache; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureCache::BindOptions) - class QOpenGLCachedTexture { public: - QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context); + QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context); ~QOpenGLCachedTexture() { m_resource->free(); } GLuint id() const { return m_resource->id(); } - QOpenGLTextureCache::BindOptions options() const { return m_options; } + QOpenGLTextureUploader::BindOptions options() const { return m_options; } private: QOpenGLSharedResourceGuard *m_resource; - QOpenGLTextureCache::BindOptions m_options; + QOpenGLTextureUploader::BindOptions m_options; }; QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp new file mode 100644 index 0000000000..2428baed93 --- /dev/null +++ b/src/gui/opengl/qopengltextureuploader.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltextureuploader_p.h" + +#include <qimage.h> +#include <qmath.h> +#include <qopenglfunctions.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> + +#ifndef GL_RED +#define GL_RED 0x1903 +#endif + +#ifndef GL_GREEN +#define GL_GREEN 0x1904 +#endif + +#ifndef GL_BLUE +#define GL_BLUE 0x1905 +#endif + +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif + +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif + +#ifndef GL_TEXTURE_SWIZZLE_RGBA +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + +#ifndef GL_SRGB +#define GL_SRGB 0x8C40 +#endif +#ifndef GL_SRGB_ALPHA +#define GL_SRGB_ALPHA 0x8C42 +#endif + +QT_BEGIN_NAMESPACE + +qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &image, QOpenGLTextureUploader::BindOptions options, QSize maxSize) +{ + QOpenGLContext *context = QOpenGLContext::currentContext(); + QOpenGLExtensions *funcs = static_cast<QOpenGLExtensions*>(context->functions()); + + QImage tx; + GLenum externalFormat; + GLenum internalFormat; + GLuint pixelType; + QImage::Format targetFormat = QImage::Format_Invalid; + const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); + const bool isOpenGLES3orBetter = context->isOpenGLES() && context->format().majorVersion() >= 3; + const bool sRgbBinding = (options & SRgbBindOption); + Q_ASSERT(isOpenGL12orBetter || context->isOpenGLES()); + Q_ASSERT((options & (SRgbBindOption | UseRedFor8BitBindOption)) != (SRgbBindOption | UseRedFor8BitBindOption)); + + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + if (isOpenGL12orBetter) { + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle) && false) { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA }; + funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); +#else + GLint swizzle[4] = { GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED }; + funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); +#endif + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + } else { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian. + if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) { + // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. + externalFormat = internalFormat = GL_BGRA; + pixelType = GL_UNSIGNED_BYTE; + } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { + // Is only allowed as an external format like OpenGL. + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + } else { + // No support for direct ARGB32 upload. + break; + } +#else + // Big endian requires GL_UNSIGNED_INT_8_8_8_8_REV for ARGB to match BGRA + break; +#endif + } + targetFormat = image.format(); + break; + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (isOpenGL12orBetter || isOpenGLES3orBetter) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } + break; + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (isOpenGL12orBetter) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_BGRA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle) && (isOpenGL12orBetter || isOpenGLES3orBetter)) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA }; + funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + targetFormat = image.format(); + } + break; + case QImage::Format_RGB444: + case QImage::Format_RGB555: + case QImage::Format_RGB16: + if (isOpenGL12orBetter || context->isOpenGLES()) { + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_SHORT_5_6_5; + targetFormat = QImage::Format_RGB16; + } + break; + case QImage::Format_RGB666: + case QImage::Format_RGB888: + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_RGB888; + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBA8888_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) + internalFormat = GL_RGBA16; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + break; + case QImage::Format_Indexed8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedFor8BitBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + case QImage::Format_Alpha8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedFor8BitBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { + externalFormat = internalFormat = GL_ALPHA; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + GLint swizzle[4] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; + funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + case QImage::Format_Grayscale8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedFor8BitBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { + externalFormat = internalFormat = GL_LUMINANCE; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + GLint swizzle[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + default: + break; + } + + // If no direct upload was detected above, convert to RGBA8888 and upload that + if (targetFormat == QImage::Format_Invalid) { + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + if (!image.hasAlphaChannel()) + targetFormat = QImage::Format_RGBX8888; + else + targetFormat = QImage::Format_RGBA8888; + } + + if (options & PremultipliedAlphaBindOption) { + if (targetFormat == QImage::Format_ARGB32) + targetFormat = QImage::Format_ARGB32_Premultiplied; + else if (targetFormat == QImage::Format_RGBA8888) + targetFormat = QImage::Format_RGBA8888_Premultiplied; + else if (targetFormat == QImage::Format_RGBA64) + targetFormat = QImage::Format_RGBA64_Premultiplied; + } else { + if (targetFormat == QImage::Format_ARGB32_Premultiplied) + targetFormat = QImage::Format_ARGB32; + else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) + targetFormat = QImage::Format_RGBA8888; + else if (targetFormat == QImage::Format_RGBA64_Premultiplied) + targetFormat = QImage::Format_RGBA64; + } + + if (sRgbBinding) { + Q_ASSERT(internalFormat == GL_RGBA || internalFormat == GL_RGB); + if (image.hasAlphaChannel()) + internalFormat = GL_SRGB_ALPHA; + else + internalFormat = GL_SRGB; + } + + if (image.format() != targetFormat) + tx = image.convertToFormat(targetFormat); + else + tx = image; + + QSize newSize = tx.size(); + if (!maxSize.isEmpty()) + newSize = newSize.boundedTo(maxSize); + if (options & PowerOfTwoBindOption) { + newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1)); + newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1)); + } + + if (newSize != tx.size()) + tx = tx.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + // Handle cases where the QImage is actually a sub image of its image data: + qsizetype naturalBpl = ((qsizetype(tx.width()) * tx.depth() + 31) >> 5) << 2; + if (tx.bytesPerLine() != naturalBpl) + tx = tx.copy(tx.rect()); + + funcs->glTexImage2D(target, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, tx.constBits()); + + qsizetype cost = qint64(tx.width()) * tx.height() * tx.depth() / 8; + + return cost; +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengltextureuploader_p.h b/src/gui/opengl/qopengltextureuploader_p.h new file mode 100644 index 0000000000..d758b3787b --- /dev/null +++ b/src/gui/opengl/qopengltextureuploader_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QOPENGLTEXTUREUPLOADER_P_H +#define QOPENGLTEXTUREUPLOADER_P_H + +#include <QtCore/qsize.h> +#include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/private/qopenglcontext_p.h> + +QT_BEGIN_NAMESPACE + +class QImage; + +class Q_GUI_EXPORT QOpenGLTextureUploader +{ +public: + enum BindOption { + NoBindOption = 0x0000, + PremultipliedAlphaBindOption = 0x0001, + UseRedFor8BitBindOption = 0x0002, + SRgbBindOption = 0x0004, + PowerOfTwoBindOption = 0x0008 + }; + Q_DECLARE_FLAGS(BindOptions, BindOption) + Q_FLAGS(BindOptions) + + static qsizetype textureImage(GLenum target, const QImage &image, BindOptions options, QSize maxSize = QSize()); + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureUploader::BindOptions) + +QT_END_NAMESPACE + +#endif + |