diff options
Diffstat (limited to 'src/quickshapes')
32 files changed, 6463 insertions, 0 deletions
diff --git a/src/quickshapes/qquicknvprfunctions.cpp b/src/quickshapes/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..409a59be7f --- /dev/null +++ b/src/quickshapes/qquicknvprfunctions.cpp @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicknvprfunctions_p.h" + +#if QT_CONFIG(opengl) + +#include <QOpenGLContext> +#include <QOffscreenSurface> +#include <QOpenGLExtraFunctions> +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +#define PROC(type, name) reinterpret_cast<type>(ctx->getProcAddress(#name)) + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer<QOpenGLContext> tempContext; + QScopedPointer<QOffscreenSurface> tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Check that GL_NV_Path_rendering extension is at least API revision 1.3 + if (!PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV)) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_CONFIG(opengl) diff --git a/src/quickshapes/qquicknvprfunctions_p.h b/src/quickshapes/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..92246cf4c8 --- /dev/null +++ b/src/quickshapes/qquicknvprfunctions_p.h @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <qopengl.h> +#include <QtGui/qsurfaceformat.h> + +#if QT_CONFIG(opengl) + +QT_BEGIN_NAMESPACE + +// note: fixed pipeline specific functions are removed - modern ES ext +// headers have all this, but not the fixed stuff + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +// this one originates from fixed pipeline so may not be in GLES ext headers, but we need it still +#ifndef GL_OBJECT_LINEAR_NV +#define GL_OBJECT_LINEAR_NV 0x2401 +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_CONFIG(opengl) + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/quickshapes/qquicknvprfunctions_p_p.h b/src/quickshapes/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..3d9ca0de9f --- /dev/null +++ b/src/quickshapes/qquicknvprfunctions_p_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuickShapes/private/qquicknvprfunctions_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp new file mode 100644 index 0000000000..edfa308c4d --- /dev/null +++ b/src/quickshapes/qquickshape.cpp @@ -0,0 +1,1620 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshape_p.h" +#include "qquickshape_p_p.h" +#include "qquickshapegenericrenderer_p.h" +#include "qquickshapenvprrenderer_p.h" +#include "qquickshapesoftwarerenderer_p.h" +#include <private/qsgtexture_p.h> +#include <private/qquicksvgparser_p.h> +#include <QtGui/private/qdrawhelper_p.h> +#include <QOpenGLFunctions> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync") + +/*! + \qmlmodule QtQuick.Shapes 1.11 + \title Qt Quick Shapes QML Types + \ingroup qmlmodules + \brief Provides QML types for drawing stroked and filled shapes. + + To use the types in this module, import the module with the following line: + + \badcode + import QtQuick.Shapes 1.11 + \endcode +*/ + +QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams() + : strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickShapePath::OddEvenFill), + joinStyle(QQuickShapePath::BevelJoin), + miterLimit(2), + capStyle(QQuickShapePath::SquareCap), + strokeStyle(QQuickShapePath::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +/*! + \qmltype ShapePath + \instantiates QQuickShapePath + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Path + \brief Describes a Path and associated properties for stroking and filling. + \since 5.10 + + A \l Shape contains one or more ShapePath elements. At least one ShapePath is + necessary in order to have a Shape output anything visible. A ShapePath + itself is a \l Path with additional properties describing the stroking and + filling parameters, such as the stroke width and color, the fill color or + gradient, join and cap styles, and so on. As with ordinary \l Path objects, + ShapePath also contains a list of path elements like \l PathMove, \l PathLine, + \l PathCubic, \l PathQuad, \l PathArc, together with a starting position. + + Any property changes in these data sets will be bubble up and change the + output of the Shape. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \qml + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + property int joinStyleIndex: 0 + + property variant styles: [ + ShapePath.BevelJoin, + ShapePath.MiterJoin, + ShapePath.RoundJoin + ] + + joinStyle: styles[joinStyleIndex] + + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + \endqml + + Once associated with a Shape, here is the output with a joinStyleIndex + of 2 (ShapePath.RoundJoin): + + \image visualpath-code-example.png + + \sa {Qt Quick Examples - Shapes}, Shape + */ + +QQuickShapePathPrivate::QQuickShapePathPrivate() + : dirty(DirtyAll) +{ + // Set this QQuickPath to be a ShapePath + isShapePath = true; +} + +QQuickShapePath::QQuickShapePath(QObject *parent) + : QQuickPath(*(new QQuickShapePathPrivate), parent) +{ + // The inherited changed() and the shapePathChanged() signals remain + // distinct, and this is intentional. Combining the two is not possible due + // to the difference in semantics and the need to act (see dirty flag + // below) differently on QQuickPath-related changes. + + connect(this, &QQuickPath::changed, [this]() { + Q_D(QQuickShapePath); + d->dirty |= QQuickShapePathPrivate::DirtyPath; + emit shapePathChanged(); + }); +} + +QQuickShapePath::~QQuickShapePath() +{ +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::strokeColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeColor; +} + +void QQuickShapePath::setStrokeColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + +qreal QQuickShapePath::strokeWidth() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeWidth; +} + +void QQuickShapePath::setStrokeWidth(qreal w) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::fillColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillColor; +} + +void QQuickShapePath::setFillColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyFillColor; + emit fillColorChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule + + This property holds the fill rule. The default value is + \c ShapePath.OddEvenFill. For an explanation on fill rules, see + QPainterPath::setFillRule(). + + \value ShapePath.OddEvenFill + Odd-even fill rule. + + \value ShapePath.WindingFill + Non-zero winding fill rule. + */ + +QQuickShapePath::FillRule QQuickShapePath::fillRule() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillRule; +} + +void QQuickShapePath::setFillRule(FillRule fillRule) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; + d->dirty |= QQuickShapePathPrivate::DirtyFillRule; + emit fillRuleChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is \c ShapePath.BevelJoin. + + \value ShapePath.MiterJoin + The outer edges of the lines are extended to meet at an angle, and + this area is filled. + + \value ShapePath.BevelJoin + The triangular notch between the two lines is filled. + + \value ShapePath.RoundJoin + A circular arc between the two lines is filled. + */ + +QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.joinStyle; +} + +void QQuickShapePath::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit joinStyleChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit + + When joinStyle is set to \c ShapePath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + +int QQuickShapePath::miterLimit() const +{ + Q_D(const QQuickShapePath); + return d->sfp.miterLimit; +} + +void QQuickShapePath::setMiterLimit(int limit) +{ + Q_D(QQuickShapePath); + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit miterLimitChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle + + This property defines how the end points of lines are drawn. The + default value is \c ShapePath.SquareCap. + + \value ShapePath.FlatCap + A square line end that does not cover the end point of the line. + + \value ShapePath.SquareCap + A square line end that covers the end point and extends beyond it + by half the line width. + + \value ShapePath.RoundCap + A rounded line end. + */ + +QQuickShapePath::CapStyle QQuickShapePath::capStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.capStyle; +} + +void QQuickShapePath::setCapStyle(CapStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit capStyleChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle + + This property defines the style of stroking. The default value is + ShapePath.SolidLine. + + \list + \li ShapePath.SolidLine - A plain line. + \li ShapePath.DashLine - Dashes separated by a few pixels. + \endlist + */ + +QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeStyle; +} + +void QQuickShapePath::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit strokeStyleChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + +qreal QQuickShapePath::dashOffset() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashOffset; +} + +void QQuickShapePath::setDashOffset(qreal offset) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashOffsetChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty list<real> QtQuick.Shapes::ShapePath::dashPattern + + This property defines the dash pattern when ShapePath.strokeStyle is set + to ShapePath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, + 6... are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth + pixels followed by a space of 2 * ShapePath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + +QVector<qreal> QQuickShapePath::dashPattern() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashPattern; +} + +void QQuickShapePath::setDashPattern(const QVector<qreal> &array) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashPatternChanged(); + emit shapePathChanged(); + } +} + +/*! + \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based + on the value of ShapePath.fillColor. + + When set, ShapePath.fillColor is ignored and filling is done using one of + the ShapeGradient subtypes. + + \note The Gradient type cannot be used here. Rather, prefer using one of + the advanced subtypes, like LinearGradient. + */ + +QQuickShapeGradient *QQuickShapePath::fillGradient() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillGradient; +} + +void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickShapePathPrivate::DirtyFillGradient; + emit shapePathChanged(); + } +} + +void QQuickShapePathPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickShapePath); + dirty |= DirtyFillGradient; + emit q->shapePathChanged(); +} + +void QQuickShapePath::resetFillGradient() +{ + setFillGradient(nullptr); +} + +/*! + \qmltype Shape + \instantiates QQuickShape + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path. + \since 5.10 + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using a GPU vendor extension like + \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. + Therefore Shape is suitable for creating shapes spreading over larger + areas of the screen, avoiding the performance penalty for texture uploads + or framebuffer blits. In addition, the declarative API allows manipulating, + binding to, and even animating the path element properties like starting + and ending position, the control points, and so on. + + The types for specifying path elements are shared between \l PathView and + Shape. However, not all Shape implementations support all path + element types, while some may not make sense for PathView. Shape's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc, and PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \qml + Shape { + width: 200 + height: 150 + anchors.centerIn: parent + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + \endqml + + \image pathitem-code-example.png + + Like \l Item, Shape also allows any visual or non-visual objects to be + declared as children. ShapePath objects are handled specially. This is + useful since it allows adding visual items, like \l Rectangle or \l Image, + and non-visual objects, like \l Timer directly as children of Shape. + + The following list summarizes the available Shape rendering approaches: + + \list + + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + Shape.vendorExtensionsEnabled property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist + + When using Shape, it is important to be aware of potential performance + implications: + + \list + + \li When the application is running with the generic, triangulation-based + Shape implementation, the geometry generation happens entirely on the + CPU. This is potentially expensive. Changing the set of path elements, + changing the properties of these elements, or changing certain properties + of the Shape itself all lead to retriangulation of the affected paths on + every change. Therefore, applying animation to such properties can affect + performance on less powerful systems. + + \li However, the data-driven, declarative nature of the Shape API often + means better cacheability for the underlying CPU and GPU resources. A + property change in one ShapePath will only lead to reprocessing the + affected ShapePath, leaving other parts of the Shape unchanged. Therefore, + a frequently changing property can still result in a lower overall system + load than with imperative painting approaches (for example, QPainter). + + \li If animating properties other than stroke and fill colors is a must, + it is recommended to target systems providing \c{GL_NV_path_rendering} + where the cost of property changes is smaller. + + \li At the same time, attention must be paid to the number of Shape + elements in the scene, in particular when using this special accelerated + approach for \c{GL_NV_path_rendering}. The way such a Shape item is + represented in the scene graph is different from an ordinary + geometry-based item, and incurs a certain cost when it comes to OpenGL + state changes. + + \li As a general rule, scenes should avoid using separate Shape items when + it is not absolutely necessary. Prefer using one Shape item with multiple + ShapePath elements over multiple Shape items. Scenes that cannot avoid + using a large number of individual Shape items should consider setting + Shape.vendorExtensionsEnabled to \c false. + \endlist + + \sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickShapePrivate::QQuickShapePrivate() + : effectRefCount(0) +{ +} + +QQuickShapePrivate::~QQuickShapePrivate() +{ + delete renderer; +} + +void QQuickShapePrivate::_q_shapePathChanged() +{ + Q_Q(QQuickShape); + spChanged = true; + q->polish(); +} + +void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus) +{ + Q_Q(QQuickShape); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + +struct QQuickShapeResourceInitializer +{ + QQuickShapeResourceInitializer() + { +#if defined(QT_STATIC) + Q_INIT_RESOURCE(qtquickshapes); +#endif + } +}; + +Q_GLOBAL_STATIC(QQuickShapeResourceInitializer, initQQuickShapeResources) + +QQuickShape::QQuickShape(QQuickItem *parent) + : QQuickItem(*(new QQuickShapePrivate), parent) +{ + initQQuickShapeResources(); + setFlag(ItemHasContents); +} + +QQuickShape::~QQuickShape() +{ +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType + + This property determines which path rendering backend is active. + + \value Shape.UnknownRenderer + The renderer is unknown. + + \value Shape.GeometryRenderer + The generic, driver independent solution for OpenGL. Uses the same + CPU-based triangulation approach as QPainter's OpenGL 2 paint + engine. This is the default on non-NVIDIA hardware when the default, + OpenGL Qt Quick scenegraph backend is in use. + + \value Shape.NvprRenderer + Path items are rendered by performing OpenGL calls using the + \c{GL_NV_path_rendering} extension. This is the default on NVIDIA + hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \value Shape.SoftwareRenderer + Pure QPainter drawing using the raster paint engine. This is the + default, and only, option when the Qt Quick scenegraph is running + with the \c software backend. +*/ + +QQuickShape::RendererType QQuickShape::rendererType() const +{ + Q_D(const QQuickShape); + return d->rendererType; +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::asynchronous + + When rendererType is \c Shape.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the Shape. This is + potentially expensive. To offload this work to separate worker threads, + set this property to \c true. + + When enabled, making a Shape visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous + work has been finished. + + The default value is \c false. + */ + +bool QQuickShape::asynchronous() const +{ + Q_D(const QQuickShape); + return d->async; +} + +void QQuickShape::setAsynchronous(bool async) +{ + Q_D(QQuickShape); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_shapePathChanged(); + } +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled + + This property controls the usage of non-standard OpenGL extensions like + \c GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickShape::vendorExtensionsEnabled() const +{ + Q_D(const QQuickShape); + return d->enableVendorExts; +} + +void QQuickShape::setVendorExtensionsEnabled(bool enable) +{ + Q_D(QQuickShape); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit vendorExtensionsEnabledChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::status + + This property determines the status of the Shape and is relevant when + Shape.asynchronous is set to \c true. + + \value Shape.Null + Not yet initialized. + + \value Shape.Ready + The Shape has finished processing. + + \value Shape.Processing + The path is being processed. + */ + +QQuickShape::Status QQuickShape::status() const +{ + Q_D(const QQuickShape); + return d->status; +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::containsMode + \since QtQuick.Shapes 1.11 + + This property determines the definition of \l {QQuickItem::contains()}{contains()} + for the Shape. It is useful in case you add + \l {Qt Quick Pointer Handlers QML Types}{Pointer Handlers} and you + want to react only when the mouse or touchpoint is fully inside the Shape. + + \value Shape.BoundingRectContains + The default implementation of \l QQuickItem::contains() checks only + whether the given point is inside the rectangular bounding box. This is + the most efficient implementation, which is why it's the default. + + \value Shape.FillContains + Check whether the interior (the part that would be filled if you are + rendering it with fill) of any \l ShapePath that makes up this Shape + contains the given point. The more complex and numerous ShapePaths you + add, the less efficient this is to check, which can potentially slow + down event delivery in your application. So it should be used with care. + + One way to speed up the \c FillContains check is to generate an approximate + outline with as few points as possible, place that in a transparent Shape + on top, and add your Pointer Handlers to that, so that the containment + check is cheaper during event delivery. +*/ +QQuickShape::ContainsMode QQuickShape::containsMode() const +{ + Q_D(const QQuickShape); + return d->containsMode; +} + +void QQuickShape::setContainsMode(QQuickShape::ContainsMode containsMode) +{ + Q_D(QQuickShape); + if (d->containsMode == containsMode) + return; + + d->containsMode = containsMode; + emit containsModeChanged(); +} + +bool QQuickShape::contains(const QPointF &point) const +{ + Q_D(const QQuickShape); + switch (d->containsMode) { + case BoundingRectContains: + return QQuickItem::contains(point); + case FillContains: + for (QQuickShapePath *path : d->sp) { + if (path->path().contains(point)) + return true; + } + } + return false; +} + +static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj) +{ + QQuickShape *item = static_cast<QQuickShape *>(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj); + if (path) + d->sp.append(path); + + QQuickItemPrivate::data_append(property, obj); + + if (path && d->componentComplete) { + QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); + d->_q_shapePathChanged(); + } +} + +static void vpe_clear(QQmlListProperty<QObject> *property) +{ + QQuickShape *item = static_cast<QQuickShape *>(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + + for (QQuickShapePath *p : d->sp) + QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); + + d->sp.clear(); + + QQuickItemPrivate::data_clear(property); + + if (d->componentComplete) + d->_q_shapePathChanged(); +} + +/*! + \qmlproperty list<Object> QtQuick.Shapes::Shape::data + + This property holds the ShapePath objects that define the contents of the + Shape. It can also contain any other type of objects, since Shape, like + Item, allows adding any visual or non-visual objects as children. + + \default + */ + +QQmlListProperty<QObject> QQuickShape::data() +{ + return QQmlListProperty<QObject>(this, + nullptr, + vpe_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + vpe_clear); +} + +void QQuickShape::classBegin() +{ + QQuickItem::classBegin(); +} + +void QQuickShape::componentComplete() +{ + Q_D(QQuickShape); + + QQuickItem::componentComplete(); + + for (QQuickShapePath *p : d->sp) + connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged())); + + d->_q_shapePathChanged(); +} + +void QQuickShape::updatePolish() +{ + Q_D(QQuickShape); + + const int currentEffectRefCount = d->extra.isAllocated() ? d->extra->recursiveEffectRefCount : 0; + if (!d->spChanged && currentEffectRefCount <= d->effectRefCount) + return; + + d->spChanged = false; + d->effectRefCount = currentEffectRefCount; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. + if (isVisible() || d->effectRefCount > 0) + d->sync(); + + update(); +} + +void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickShape); + + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + d->_q_shapePathChanged(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickShape); + if (d->renderer) { + if (!node) + node = d->createNode(); + d->renderer->updateNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickShapePrivate::createRenderer() +{ + Q_Q(QQuickShape); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#if QT_CONFIG(opengl) + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + rendererType = QQuickShape::NvprRenderer; + renderer = new QQuickShapeNvprRenderer; + } else { + rendererType = QQuickShape::GeometryRenderer; + renderer = new QQuickShapeGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickShape::SoftwareRenderer; + renderer = new QQuickShapeSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickShapePrivate::createNode() +{ + Q_Q(QQuickShape); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + switch (ri->graphicsApi()) { +#if QT_CONFIG(opengl) + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + node = new QQuickShapeNvprRenderNode; + static_cast<QQuickShapeNvprRenderer *>(renderer)->setNode( + static_cast<QQuickShapeNvprRenderNode *>(node)); + } else { + node = new QQuickShapeGenericNode; + static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode( + static_cast<QQuickShapeGenericNode *>(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickShapeSoftwareRenderNode(q); + static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode( + static_cast<QQuickShapeSoftwareRenderNode *>(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +void QQuickShapePrivate::asyncShapeReady(void *data) +{ + QQuickShapePrivate *self = static_cast<QQuickShapePrivate *>(data); + self->setStatus(QQuickShape::Ready); + if (self->syncTimingActive) + qDebug("[Shape %p] [%d] [dirty=0x%x] async update took %lld ms", + self->q_func(), self->syncTimeCounter, self->syncTimingTotalDirty, self->syncTimer.elapsed()); +} + +void QQuickShapePrivate::sync() +{ + syncTimingTotalDirty = 0; + syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled(); + if (syncTimingActive) + syncTimer.start(); + + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickShape::Processing); + renderer->setAsyncCallback(asyncShapeReady, this); + } + + const int count = sp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickShapePath *p = sp[i]; + int &dirty(QQuickShapePathPrivate::get(p)->dirty); + syncTimingTotalDirty |= dirty; + + if (dirty & QQuickShapePathPrivate::DirtyPath) + renderer->setPath(i, p); + if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickShapePathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickShapePathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickShapePathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickShapePathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickShapePathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; + } + + if (syncTimingTotalDirty) + ++syncTimeCounter; + else + syncTimingActive = false; + + renderer->endSync(useAsync); + + if (!useAsync) { + setStatus(QQuickShape::Ready); + if (syncTimingActive) + qDebug("[Shape %p] [%d] [dirty=0x%x] update took %lld ms", + q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed()); + } +} + +// ***** gradient support ***** + +/*! + \qmltype ShapeGradient + \instantiates QQuickShapeGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Gradient + \brief Base type of Shape fill gradients. + \since 5.10 + + This is an abstract base class for gradients like LinearGradient and + cannot be created directly. It extends \l Gradient with properties like the + spread mode. + */ + +QQuickShapeGradient::QQuickShapeGradient(QObject *parent) + : QQuickGradient(parent), + m_spread(PadSpread) +{ +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread + + Specifies how the area outside the gradient area should be filled. The + default value is \c ShapeGradient.PadSpread. + + \value ShapeGradient.PadSpread + The area is filled with the closest stop color. + + \value ShapeGradient.RepeatSpread + The gradient is repeated outside the gradient area. + + \value ShapeGradient.ReflectSpread + The gradient is reflected outside the gradient area. + */ + +QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const +{ + return m_spread; +} + +void QQuickShapeGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +/*! + \qmltype LinearGradient + \instantiates QQuickShapeLinearGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Linear gradient. + \since 5.10 + + Linear gradients interpolate colors between start and end points in Shape + items. Outside these points the gradient is either padded, reflected or + repeated depending on the spread type. + + \note LinearGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. + + \sa QLinearGradient + */ + +QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::LinearGradient::x1 + \qmlproperty real QtQuick.Shapes::LinearGradient::y1 + \qmlproperty real QtQuick.Shapes::LinearGradient::x2 + \qmlproperty real QtQuick.Shapes::LinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both points are set to (0, 0). + */ + +qreal QQuickShapeLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickShapeLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickShapeLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickShapeLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickShapeLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +/*! + \qmltype RadialGradient + \instantiates QQuickShapeRadialGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Radial gradient. + \since 5.10 + + Radial gradients interpolate colors between a focal circle and a center + circle in Shape items. Points outside the cone defined by the two circles + will be transparent. + + Outside the end points the gradient is either padded, reflected or repeated + depending on the spread type. + + Below is an example of a simple radial gradient. Here the colors are + interpolated between the specified point and the end points on a circle + specified by the radius: + + \code + fillGradient: RadialGradient { + centerX: 50; centerY: 50 + centerRadius: 100 + focalX: centerX; focalY: centerY + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + \endcode + + \image shape-radial-gradient.png + + Extended radial gradients, where a separate focal circle is specified, are + also supported. + + \note RadialGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. + + \sa QRadialGradient + */ + +QQuickShapeRadialGradient::QQuickShapeRadialGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::RadialGradient::centerX + \qmlproperty real QtQuick.Shapes::RadialGradient::centerY + \qmlproperty real QtQuick.Shapes::RadialGradient::focalX + \qmlproperty real QtQuick.Shapes::RadialGradient::focalY + + These properties define the center and focal points. To specify a simple + radial gradient, set focalX and focalY to the value of centerX and + centerY, respectively. + */ + +qreal QQuickShapeRadialGradient::centerX() const +{ + return m_centerPoint.x(); +} + +void QQuickShapeRadialGradient::setCenterX(qreal v) +{ + if (m_centerPoint.x() != v) { + m_centerPoint.setX(v); + emit centerXChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::centerY() const +{ + return m_centerPoint.y(); +} + +void QQuickShapeRadialGradient::setCenterY(qreal v) +{ + if (m_centerPoint.y() != v) { + m_centerPoint.setY(v); + emit centerYChanged(); + emit updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::RadialGradient::centerRadius + \qmlproperty real QtQuick.Shapes::RadialGradient::focalRadius + + These properties define the center and focal radius. For simple radial + gradients, focalRadius should be set to \c 0 (the default value). + */ + +qreal QQuickShapeRadialGradient::centerRadius() const +{ + return m_centerRadius; +} + +void QQuickShapeRadialGradient::setCenterRadius(qreal v) +{ + if (m_centerRadius != v) { + m_centerRadius = v; + emit centerRadiusChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalX() const +{ + return m_focalPoint.x(); +} + +void QQuickShapeRadialGradient::setFocalX(qreal v) +{ + if (m_focalPoint.x() != v) { + m_focalPoint.setX(v); + emit focalXChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalY() const +{ + return m_focalPoint.y(); +} + +void QQuickShapeRadialGradient::setFocalY(qreal v) +{ + if (m_focalPoint.y() != v) { + m_focalPoint.setY(v); + emit focalYChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalRadius() const +{ + return m_focalRadius; +} + +void QQuickShapeRadialGradient::setFocalRadius(qreal v) +{ + if (m_focalRadius != v) { + m_focalRadius = v; + emit focalRadiusChanged(); + emit updated(); + } +} + +/*! + \qmltype ConicalGradient + \instantiates QQuickShapeConicalGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Conical gradient. + \since 5.10 + + Conical gradients interpolate colors counter-clockwise around a center + point in Shape items. + + \note The \l{ShapeGradient::spread}{spread mode} setting has no effect for + conical gradients. + + \note ConicalGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. + + \sa QConicalGradient + */ + +QQuickShapeConicalGradient::QQuickShapeConicalGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ConicalGradient::centerX + \qmlproperty real QtQuick.Shapes::ConicalGradient::centerY + + These properties define the center point of the conical gradient. + */ + +qreal QQuickShapeConicalGradient::centerX() const +{ + return m_centerPoint.x(); +} + +void QQuickShapeConicalGradient::setCenterX(qreal v) +{ + if (m_centerPoint.x() != v) { + m_centerPoint.setX(v); + emit centerXChanged(); + emit updated(); + } +} + +qreal QQuickShapeConicalGradient::centerY() const +{ + return m_centerPoint.y(); +} + +void QQuickShapeConicalGradient::setCenterY(qreal v) +{ + if (m_centerPoint.y() != v) { + m_centerPoint.setY(v); + emit centerYChanged(); + emit updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ConicalGradient::angle + + This property defines the start angle for the conical gradient. The value + is in degrees (0-360). + */ + +qreal QQuickShapeConicalGradient::angle() const +{ + return m_angle; +} + +void QQuickShapeConicalGradient::setAngle(qreal v) +{ + if (m_angle != v) { + m_angle = v; + emit angleChanged(); + emit updated(); + } +} + +#if QT_CONFIG(opengl) + +// contexts sharing with each other get the same cache instance +class QQuickShapeGradientCacheWrapper +{ +public: + QQuickShapeGradientCache *get(QOpenGLContext *context) + { + return m_resource.value<QQuickShapeGradientCache>(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickShapeGradientCache *QQuickShapeGradientCache::currentCache() +{ + static QQuickShapeGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickShapeGradientCache::~QQuickShapeGradientCache() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickShapeGradientCache::Key &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickShapeGradientCache::get(const Key &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickShapeGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickShapeGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickShapeGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + tx->setFiltering(QSGTexture::Linear); + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_CONFIG(opengl) + +QT_END_NAMESPACE + +#include "moc_qquickshape_p.cpp" diff --git a/src/quickshapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h new file mode 100644 index 0000000000..cd242cafc3 --- /dev/null +++ b/src/quickshapes/qquickshape_p.h @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPE_P_H +#define QQUICKSHAPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuick/qquickitem.h> + +#include <private/qtquickglobal_p.h> +#include <private/qquickpath_p_p.h> +#include <private/qquickrectangle_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickShapePathPrivate; +class QQuickShapePrivate; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeGradient : public QQuickGradient +{ + Q_OBJECT + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickShapeGradient(QObject *parent = nullptr); + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void spreadChanged(); + +private: + SpreadMode m_spread; +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeLinearGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeRadialGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged) + Q_PROPERTY(qreal centerRadius READ centerRadius WRITE setCenterRadius NOTIFY centerRadiusChanged) + Q_PROPERTY(qreal focalX READ focalX WRITE setFocalX NOTIFY focalXChanged) + Q_PROPERTY(qreal focalY READ focalY WRITE setFocalY NOTIFY focalYChanged) + Q_PROPERTY(qreal focalRadius READ focalRadius WRITE setFocalRadius NOTIFY focalRadiusChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeRadialGradient(QObject *parent = nullptr); + + qreal centerX() const; + void setCenterX(qreal v); + + qreal centerY() const; + void setCenterY(qreal v); + + qreal centerRadius() const; + void setCenterRadius(qreal v); + + qreal focalX() const; + void setFocalX(qreal v); + + qreal focalY() const; + void setFocalY(qreal v); + + qreal focalRadius() const; + void setFocalRadius(qreal v); + +signals: + void centerXChanged(); + void centerYChanged(); + void focalXChanged(); + void focalYChanged(); + void centerRadiusChanged(); + void focalRadiusChanged(); + +private: + QPointF m_centerPoint; + QPointF m_focalPoint; + qreal m_centerRadius = 0; + qreal m_focalRadius = 0; +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeConicalGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeConicalGradient(QObject *parent = nullptr); + + qreal centerX() const; + void setCenterX(qreal v); + + qreal centerY() const; + void setCenterY(qreal v); + + qreal angle() const; + void setAngle(qreal v); + +signals: + void centerXChanged(); + void centerYChanged(); + void angleChanged(); + +private: + QPointF m_centerPoint; + qreal m_angle = 0; +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePath : public QQuickPath +{ + Q_OBJECT + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + QQuickShapePath(QObject *parent = nullptr); + ~QQuickShapePath(); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector<qreal> dashPattern() const; + void setDashPattern(const QVector<qreal> &array); + + QQuickShapeGradient *fillGradient() const; + void setFillGradient(QQuickShapeGradient *gradient); + void resetFillGradient(); + +Q_SIGNALS: + void shapePathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + +private: + Q_DISABLE_COPY(QQuickShapePath) + Q_DECLARE_PRIVATE(QQuickShapePath) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShape : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION 11) + Q_PROPERTY(QQmlListProperty<QObject> data READ data) + Q_CLASSINFO("DefaultProperty", "data") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + + enum ContainsMode { + BoundingRectContains, + FillContains + }; + Q_ENUM(ContainsMode) + + QQuickShape(QQuickItem *parent = nullptr); + ~QQuickShape(); + + RendererType rendererType() const; + + bool asynchronous() const; + void setAsynchronous(bool async); + + bool vendorExtensionsEnabled() const; + void setVendorExtensionsEnabled(bool enable); + + Status status() const; + + ContainsMode containsMode() const; + void setContainsMode(ContainsMode containsMode); + + bool contains(const QPointF &point) const override; + + QQmlListProperty<QObject> data(); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + void componentComplete() override; + void classBegin() override; + +Q_SIGNALS: + void rendererChanged(); + void asynchronousChanged(); + void vendorExtensionsEnabledChanged(); + void statusChanged(); + Q_REVISION(11) void containsModeChanged(); + +private: + Q_DISABLE_COPY(QQuickShape) + Q_DECLARE_PRIVATE(QQuickShape) + Q_PRIVATE_SLOT(d_func(), void _q_shapePathChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickShape) + +#endif // QQUICKSHAPE_P_H diff --git a/src/quickshapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h new file mode 100644 index 0000000000..bf4a47f62c --- /dev/null +++ b/src/quickshapes/qquickshape_p_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPE_P_P_H +#define QQUICKSHAPE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuickShapes/private/qquickshape_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QPainterPath> +#include <QColor> +#include <QBrush> +#include <QElapsedTimer> +#include <private/qopenglcontext_p.h> + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +class QQuickAbstractPathRenderer +{ +public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient }; + struct GradientDesc { // can fully describe a linear/radial/conical gradient + QGradientStops stops; + QQuickShapeGradient::SpreadMode spread; + QPointF a; // start (L) or center point (R/C) + QPointF b; // end (L) or focal point (R) + qreal v0; // center radius (R) or start angle (C) + qreal v1; // focal radius (R) + }; + + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + virtual void setPath(int index, const QQuickPath *path) = 0; + virtual void setStrokeColor(int index, const QColor &color) = 0; + virtual void setStrokeWidth(int index, qreal w) = 0; + virtual void setFillColor(int index, const QColor &color) = 0; + virtual void setFillRule(int index, QQuickShapePath::FillRule fillRule) = 0; + virtual void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) = 0; + virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) = 0; + virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0; + + // Render thread, with gui blocked + virtual void updateNode() = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + +struct QQuickShapeStrokeFillParams +{ + QQuickShapeStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickShapePath::FillRule fillRule; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector<qreal> dashPattern; + QQuickShapeGradient *fillGradient; +}; + +class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate +{ + Q_DECLARE_PUBLIC(QQuickShapePath) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickShapePathPrivate(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); } + + int dirty; + QQuickShapeStrokeFillParams sfp; +}; + +class QQuickShapePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickShape) + +public: + QQuickShapePrivate(); + ~QQuickShapePrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_shapePathChanged(); + void setStatus(QQuickShape::Status newStatus); + + static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); } + + static void asyncShapeReady(void *data); + + int effectRefCount; + QVector<QQuickShapePath *> sp; + QElapsedTimer syncTimer; + QQuickAbstractPathRenderer *renderer = nullptr; + int syncTimingTotalDirty = 0; + int syncTimeCounter = 0; + QQuickShape::Status status = QQuickShape::Null; + QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer; + QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains; + bool spChanged = false; + bool async = false; + bool enableVendorExts = true; + bool syncTimingActive = false; +}; + +#if QT_CONFIG(opengl) + +class QQuickShapeGradientCache : public QOpenGLSharedResource +{ +public: + struct Key { + Key(const QGradientStops &stops, QQuickShapeGradient::SpreadMode spread) + : stops(stops), spread(spread) + { } + QGradientStops stops; + QQuickShapeGradient::SpreadMode spread; + bool operator==(const Key &other) const + { + return spread == other.spread && stops == other.stops; + } + }; + + QQuickShapeGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickShapeGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const Key &grad); + + static QQuickShapeGradientCache *currentCache(); + +private: + QHash<Key, QSGPlainTexture *> m_cache; +}; + +inline uint qHash(const QQuickShapeGradientCache::Key &v, uint seed = 0) +{ + uint h = seed + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_CONFIG(opengl) + +QT_END_NAMESPACE + +#endif diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp new file mode 100644 index 0000000000..8a4785a83a --- /dev/null +++ b/src/quickshapes/qquickshapegenericrenderer.cpp @@ -0,0 +1,1007 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapegenericrenderer_p.h" +#include <QtGui/private/qtriangulator_p.h> +#include <QtGui/private/qtriangulatingstroker_p.h> +#include <QThreadPool> + +#if QT_CONFIG(opengl) +#include <QSGVertexColorMaterial> +#include <QOpenGLContext> +#include <QOffscreenSurface> +#include <QtGui/private/qopenglextensions_p.h> +#endif + +QT_BEGIN_NAMESPACE + +static const qreal TRI_SCALE = 1; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickShapeGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickShapeGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window) + : m_material(nullptr) +{ + setFlag(QSGNode::OwnsGeometry, true); + setGeometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)); + activateMaterial(window, MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif +} + +void QQuickShapeGenericStrokeFillNode::activateMaterial(QQuickWindow *window, Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + m_material.reset(QQuickShapeGenericMaterialFactory::createVertexColor(window)); + break; + case MatLinearGradient: + m_material.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(window, this)); + break; + case MatRadialGradient: + m_material.reset(QQuickShapeGenericMaterialFactory::createRadialGradient(window, this)); + break; + case MatConicalGradient: + m_material.reset(QQuickShapeGenericMaterialFactory::createConicalGradient(window, this)); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material.data()) + setMaterial(m_material.data()); +} + +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer<QOpenGLContext> dummyContext; + QScopedPointer<QOffscreenSurface> dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast<QOpenGLExtensions *>(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + +QQuickShapeGenericRenderer::~QQuickShapeGenericRenderer() +{ + for (ShapePathData &d : m_sp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + +// sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + +void QQuickShapeGenericRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (ShapePathData &d : m_sp) + d.syncDirty = 0; +} + +void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyFillGeom; +} + +void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) +{ + ShapePathData &d(m_sp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickShapePath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + } + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathData &d(m_sp[index]); + if (gradient) { + d.fillGradient.stops = gradient->gradientStops(); // sorted + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) { + d.fillGradientActive = LinearGradient; + d.fillGradient.a = QPointF(g->x1(), g->y1()); + d.fillGradient.b = QPointF(g->x2(), g->y2()); + } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) { + d.fillGradientActive = RadialGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.b = QPointF(g->focalX(), g->focalY()); + d.fillGradient.v0 = g->centerRadius(); + d.fillGradient.v1 = g->focalRadius(); + } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) { + d.fillGradientActive = ConicalGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.v0 = g->angle(); + } else { + Q_UNREACHABLE(); + } + } else { + d.fillGradientActive = NoGradient; + } + d.syncDirty |= DirtyFillGradient; +} + +void QQuickShapeFillRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); + emit done(this); +} + +void QQuickShapeStrokeRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + +void QQuickShapeGenericRenderer::endSync(bool async) +{ + bool didKickOffAsync = false; + + for (int i = 0; i < m_sp.count(); ++i) { + ShapePathData &d(m_sp[i]); + if (!d.syncDirty) + continue; + + m_accDirty |= d.syncDirty; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to updateNode() + // on the render thread (with the gui thread blocked). For our purposes + // here syncDirty is still required since geometry regeneration must only + // happen when there was an actual change in this particular sync round. + d.effectiveDirty |= d.syncDirty; + + if (d.path.isEmpty()) { + d.fillVertices.clear(); + d.fillIndices.clear(); + d.strokeVertices.clear(); + continue; + } + + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; + const int idealCount = QThread::idealThreadCount(); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); + if (async) { + QQuickShapeFillRunnable *r = new QQuickShapeFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); + // Unlikely in practice but in theory m_sp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.indexType = r->indexType; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); + } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickShapeStrokeRunnable *r = new QQuickShapeStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) { + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } + } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +void QQuickShapeGenericRenderer::maybeUpdateAsyncItem() +{ + for (const ShapePathData &d : qAsConst(m_sp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; + m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(fillVertices->data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); + + size_t indexByteSize; + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector<quint32>. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); + } else { + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); + } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); +} + +void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); + const qreal inverseScale = 1.0 / TRI_SCALE; + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, nullptr); + } else { + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, nullptr); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, nullptr); + } + + if (!stroker.vertexCount()) { + strokeVertices->clear(); + return; + } + + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(strokeVertices->data()); + const float *vsrc = stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); +} + +void QQuickShapeGenericRenderer::setRootNode(QQuickShapeGenericNode *node) +{ + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; + } +} + +// on the render thread with gui blocked +void QQuickShapeGenericRenderer::updateNode() +{ + if (!m_rootNode || !m_accDirty) + return; + +// [ m_rootNode ] +// / / / +// #0 [ fill ] [ stroke ] [ next ] +// / / | +// #1 [ fill ] [ stroke ] [ next ] +// / / | +// #2 [ fill ] [ stroke ] [ next ] +// ... +// ... + + QQuickShapeGenericNode **nodePtr = &m_rootNode; + QQuickShapeGenericNode *prevNode = nullptr; + + for (ShapePathData &d : m_sp) { + if (!*nodePtr) { + Q_ASSERT(prevNode); + *nodePtr = new QQuickShapeGenericNode; + prevNode->m_next = *nodePtr; + prevNode->appendChildNode(*nodePtr); + } + + QQuickShapeGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; + continue; + } + + if (d.fillColor.a == 0) { + delete node->m_fillNode; + node->m_fillNode = nullptr; + } else if (!node->m_fillNode) { + node->m_fillNode = new QQuickShapeGenericStrokeFillNode(m_item->window()); + if (node->m_strokeNode) + node->removeChildNode(node->m_strokeNode); + node->appendChildNode(node->m_fillNode); + if (node->m_strokeNode) + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyFillGeom; + } + + if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { + delete node->m_strokeNode; + node->m_strokeNode = nullptr; + } else if (!node->m_strokeNode) { + node->m_strokeNode = new QQuickShapeGenericStrokeFillNode(m_item->window()); + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyStrokeGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; + } + + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } + + m_accDirty = 0; +} + +void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + +void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_fillNode) + return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; + + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. + QQuickShapeGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + + QSGGeometry *g = n->geometry(); + if (d->fillVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + if (d->fillGradientActive) { + QQuickShapeGenericStrokeFillNode::Material gradMat; + switch (d->fillGradientActive) { + case LinearGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatLinearGradient; + break; + case RadialGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatRadialGradient; + break; + case ConicalGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatConicalGradient; + break; + default: + Q_UNREACHABLE(); + return; + } + n->activateMaterial(m_item->window(), gradMat); + if (d->effectiveDirty & DirtyFillGradient) { + // Gradients are implemented via a texture-based material. + n->markDirty(QSGNode::DirtyMaterial); + // stop here if only the gradient changed; no need to touch the geometry + if (!(d->effectiveDirty & DirtyFillGeom)) + return; + } + } else { + n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { + ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + } else { + g->allocate(d->fillVertices.count(), indexCount); + } + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + + n->markDirty(QSGNode::DirtyGeometry); +} + +void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_strokeNode) + return; + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) + return; + + QQuickShapeGenericStrokeFillNode *n = node->m_strokeNode; + QSGGeometry *g = n->geometry(); + if (d->strokeVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + n->markDirty(QSGNode::DirtyGeometry); + + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { + ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); + return; + } + + g->allocate(d->strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QQuickShapeLinearGradientMaterial(node); +#else + Q_UNUSED(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QQuickShapeRadialGradientMaterial(node); +#else + Q_UNUSED(node); +#endif + + qWarning("Radial gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QQuickShapeConicalGradientMaterial(node); +#else + Q_UNUSED(node); +#endif + + qWarning("Conical gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#if QT_CONFIG(opengl) + +QSGMaterialType QQuickShapeLinearGradientShader::type; + +QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/shapes/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/shapes/shaders/lineargradient.frag")); +} + +void QQuickShapeLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.a)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.b)); + + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); +} + +char const *const *QQuickShapeLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeLinearGradientMaterial *m = static_cast<const QQuickShapeLinearGradientMaterial *>(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) + return d; + if (int d = ga->b.x() - gb->b.x()) + return d; + if (int d = ga->b.y() - gb->b.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +QSGMaterialType QQuickShapeRadialGradientShader::type; + +QQuickShapeRadialGradientShader::QQuickShapeRadialGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/shapes/shaders/radialgradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/shapes/shaders/radialgradient.frag")); +} + +void QQuickShapeRadialGradientShader::initialize() +{ + QOpenGLShaderProgram *prog = program(); + m_opacityLoc = prog->uniformLocation("opacity"); + m_matrixLoc = prog->uniformLocation("matrix"); + m_translationPointLoc = prog->uniformLocation("translationPoint"); + m_focalToCenterLoc = prog->uniformLocation("focalToCenter"); + m_centerRadiusLoc = prog->uniformLocation("centerRadius"); + m_focalRadiusLoc = prog->uniformLocation("focalRadius"); +} + +void QQuickShapeRadialGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + + const QPointF centerPoint = node->m_fillGradient.a; + const QPointF focalPoint = node->m_fillGradient.b; + const QPointF focalToCenter = centerPoint - focalPoint; + const GLfloat centerRadius = node->m_fillGradient.v0; + const GLfloat focalRadius = node->m_fillGradient.v1; + + program()->setUniformValue(m_translationPointLoc, focalPoint); + program()->setUniformValue(m_centerRadiusLoc, centerRadius); + program()->setUniformValue(m_focalRadiusLoc, focalRadius); + program()->setUniformValue(m_focalToCenterLoc, focalToCenter); + + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); +} + +char const *const *QQuickShapeRadialGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeRadialGradientMaterial *m = static_cast<const QQuickShapeRadialGradientMaterial *>(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) + return d; + if (int d = ga->b.x() - gb->b.x()) + return d; + if (int d = ga->b.y() - gb->b.y()) + return d; + + if (int d = ga->v0 - gb->v0) + return d; + if (int d = ga->v1 - gb->v1) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +QSGMaterialType QQuickShapeConicalGradientShader::type; + +QQuickShapeConicalGradientShader::QQuickShapeConicalGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/shapes/shaders/conicalgradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/shapes/shaders/conicalgradient.frag")); +} + +void QQuickShapeConicalGradientShader::initialize() +{ + QOpenGLShaderProgram *prog = program(); + m_opacityLoc = prog->uniformLocation("opacity"); + m_matrixLoc = prog->uniformLocation("matrix"); + m_angleLoc = prog->uniformLocation("angle"); + m_translationPointLoc = prog->uniformLocation("translationPoint"); +} + +void QQuickShapeConicalGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + + const QPointF centerPoint = node->m_fillGradient.a; + const GLfloat angle = -qDegreesToRadians(node->m_fillGradient.v0); + + program()->setUniformValue(m_angleLoc, angle); + program()->setUniformValue(m_translationPointLoc, centerPoint); + + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, QQuickShapeGradient::RepeatSpread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); +} + +char const *const *QQuickShapeConicalGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeConicalGradientMaterial *m = static_cast<const QQuickShapeConicalGradientMaterial *>(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) + return d; + + if (int d = ga->v0 - gb->v0) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_CONFIG(opengl) + +QT_END_NAMESPACE diff --git a/src/quickshapes/qquickshapegenericrenderer_p.h b/src/quickshapes/qquickshapegenericrenderer_p.h new file mode 100644 index 0000000000..9928d7ab72 --- /dev/null +++ b/src/quickshapes/qquickshapegenericrenderer_p.h @@ -0,0 +1,393 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPEGENERICRENDERER_P_H +#define QQUICKSHAPEGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuickShapes/private/qquickshape_p_p.h> +#include <qsgnode.h> +#include <qsggeometry.h> +#include <qsgmaterial.h> +#include <qsgrendererinterface.h> +#include <QtCore/qrunnable.h> + +QT_BEGIN_NAMESPACE + +class QQuickShapeGenericNode; +class QQuickShapeGenericStrokeFillNode; +class QQuickShapeFillRunnable; +class QQuickShapeStrokeRunnable; + +class QQuickShapeGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty + }; + + QQuickShapeGenericRenderer(QQuickItem *item) + : m_item(item), + m_api(QSGRendererInterface::Unknown), + m_rootNode(nullptr), + m_accDirty(0), + m_asyncCallback(nullptr), + m_asyncCallbackData(nullptr) + { } + ~QQuickShapeGenericRenderer(); + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } + + void updateNode() override; + + void setRootNode(QQuickShapeGenericNode *node); + + struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector<QSGGeometry::ColoredPoint2D> VertexContainerType; + typedef QVector<quint32> IndexContainerType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize); + +private: + void maybeUpdateAsyncItem(); + + struct ShapePathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + FillGradientType fillGradientActive; + GradientDesc fillGradient; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; + int syncDirty; + int effectiveDirty = 0; + QQuickShapeFillRunnable *pendingFill = nullptr; + QQuickShapeStrokeRunnable *pendingStroke = nullptr; + }; + + void updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n); + void updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node); + void updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node); + + QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; + QQuickShapeGenericNode *m_rootNode; + QVector<ShapePathData> m_sp; + int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; +}; + +class QQuickShapeFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QQuickShapeGenericRenderer::Color4ub fillColor; + bool supportsElementIndexUint; + + // output + QQuickShapeGenericRenderer::VertexContainerType fillVertices; + QQuickShapeGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; + +Q_SIGNALS: + void done(QQuickShapeFillRunnable *self); +}; + +class QQuickShapeStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QPen pen; + QQuickShapeGenericRenderer::Color4ub strokeColor; + QSize clipSize; + + // output + QQuickShapeGenericRenderer::VertexContainerType strokeVertices; + +Q_SIGNALS: + void done(QQuickShapeStrokeRunnable *self); +}; + +class QQuickShapeGenericStrokeFillNode : public QSGGeometryNode +{ +public: + QQuickShapeGenericStrokeFillNode(QQuickWindow *window); + + enum Material { + MatSolidColor, + MatLinearGradient, + MatRadialGradient, + MatConicalGradient + }; + + void activateMaterial(QQuickWindow *window, Material m); + + // shadow data for custom materials + QQuickAbstractPathRenderer::GradientDesc m_fillGradient; + +private: + QScopedPointer<QSGMaterial> m_material; + + friend class QQuickShapeGenericRenderer; +}; + +class QQuickShapeGenericNode : public QSGNode +{ +public: + QQuickShapeGenericStrokeFillNode *m_fillNode = nullptr; + QQuickShapeGenericStrokeFillNode *m_strokeNode = nullptr; + QQuickShapeGenericNode *m_next = nullptr; +}; + +class QQuickShapeGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); + static QSGMaterial *createRadialGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); + static QSGMaterial *createConicalGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); +}; + +#if QT_CONFIG(opengl) + +class QQuickShapeLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_gradStartLoc = -1; + int m_gradEndLoc = -1; +}; + +class QQuickShapeLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeLinearGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + +class QQuickShapeRadialGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeRadialGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_translationPointLoc = -1; + int m_focalToCenterLoc = -1; + int m_centerRadiusLoc = -1; + int m_focalRadiusLoc = -1; +}; + +class QQuickShapeRadialGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeRadialGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeRadialGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + +class QQuickShapeConicalGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeConicalGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_angleLoc = -1; + int m_translationPointLoc = -1; +}; + +class QQuickShapeConicalGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeConicalGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeConicalGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + +#endif // QT_CONFIG(opengl) + +QT_END_NAMESPACE + +#endif // QQUICKSHAPEGENERICRENDERER_P_H diff --git a/src/quickshapes/qquickshapenvprrenderer.cpp b/src/quickshapes/qquickshapenvprrenderer.cpp new file mode 100644 index 0000000000..51af0d8961 --- /dev/null +++ b/src/quickshapes/qquickshapenvprrenderer.cpp @@ -0,0 +1,1001 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapenvprrenderer_p.h" +#include <QOpenGLExtraFunctions> +#include <QOpenGLFramebufferObject> +#include <QOpenGLShaderProgram> +#include <QOpenGLBuffer> +#include <qmath.h> +#include <private/qpainterpath_p.h> +#include <private/qquickpath_p_p.h> + +QT_BEGIN_NAMESPACE + +void QQuickShapeNvprRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeNvprRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeNvprRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeNvprRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + d.dashActive = strokeStyle == QQuickShapePath::DashLine; + d.dashOffset = dashOffset; + d.dashPattern = dashPattern; + d.dirty |= DirtyDash; + m_accDirty |= DirtyDash; +} + +void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + if (gradient) { + d.fillGradient.stops = gradient->gradientStops(); // sorted + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) { + d.fillGradientActive = LinearGradient; + d.fillGradient.a = QPointF(g->x1(), g->y1()); + d.fillGradient.b = QPointF(g->x2(), g->y2()); + } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) { + d.fillGradientActive = RadialGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.b = QPointF(g->focalX(), g->focalY()); + d.fillGradient.v0 = g->centerRadius(); + d.fillGradient.v1 = g->focalRadius(); + } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) { + d.fillGradientActive = ConicalGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.v0 = g->angle(); + } else { + Q_UNREACHABLE(); + } + } else { + d.fillGradientActive = NoGradient; + } + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; +} + +void QQuickShapeNvprRenderer::endSync(bool) +{ +} + +void QQuickShapeNvprRenderer::setNode(QQuickShapeNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path) +{ + QDebugStateSaver saver(debug); + debug.space().noquote(); + if (!path.str.isEmpty()) { + debug << "Path with SVG string" << path.str; + return debug; + } + debug << "Path with" << path.cmd.count() << "commands"; + int ci = 0; + for (GLubyte cmd : path.cmd) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector<GLfloat> *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector<GLfloat> *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiData *d) +{ + d->path = NvprPath(); + if (!path) + return; + + const QList<QQuickPathElement *> &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + QPointF startPos(path->startX(), path->startY()); + QPointF pos(startPos); + if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { + d->path.cmd.append(GL_MOVE_TO_NV); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + } + + for (QQuickPathElement *e : pp) { + if (QQuickPathMove *o = qobject_cast<QQuickPathMove *>(e)) { + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; + } else if (QQuickPathLine *o = qobject_cast<QQuickPathLine *>(e)) { + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast<QQuickPathQuad *>(e)) { + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathCubic *o = qobject_cast<QQuickPathCubic *>(e)) { + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&d->path.coord, o, pos); + appendControl2Coords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathArc *o = qobject_cast<QQuickPathArc *>(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(o->xAxisRotation()); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathSvg *o = qobject_cast<QQuickPathSvg *>(e)) { + // PathSvg cannot be combined with other elements. But take at + // least startX and startY into account. + if (d->path.str.isEmpty()) + d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); + d->path.str.append(o->path().toUtf8()); + } else if (QQuickPathAngleArc *o = qobject_cast<QQuickPathAngleArc *>(e)) { + QRectF rect(o->centerX() - o->radiusX(), o->centerY() - o->radiusY(), o->radiusX() * 2, o->radiusY() * 2); + QPointF startPoint; + QPointF endPoint; + qt_find_ellipse_coords(rect, o->startAngle(), -o->sweepAngle(), &startPoint, &endPoint); + + // get to our starting position + if (o->moveToStart()) + d->path.cmd.append(GL_MOVE_TO_NV); + else + d->path.cmd.append(GL_LINE_TO_NV); // ### should we check if startPoint == pos? + d->path.coord.append(startPoint.x()); + d->path.coord.append(startPoint.y()); + + const bool sweepFlag = o->sweepAngle() > 0; // maps to CCW, not a typo + d->path.cmd.append(qAbs(o->sweepAngle()) > 180.0 + ? (sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV) + : (sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV)); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(0); // xAxisRotation + d->path.coord.append(endPoint.x()); + d->path.coord.append(endPoint.y()); + pos = endPoint; + } else { + qWarning() << "Shape/NVPR: unsupported Path element" << e; + } + } + + // For compatibility with QTriangulatingStroker. SVG and others would not + // implicitly close the path when end_pos == start_pos (start_pos being the + // last moveTo pos); that would still need an explicit 'z' or similar. We + // don't have an explicit close command, so just fake a close when the + // positions match. + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickShapeNvprRenderer::updateNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeNvprRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]); + + int dirty = src.dirty; + src.dirty = 0; + if (listChanged) + dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; + + // updateNode() can be called several times with different dirty + // states before render() gets invoked. So accumulate. + dst.dirty |= dirty; + + if (dirty & DirtyPath) + dst.source = src.path; + + if (dirty & DirtyStyle) { + dst.strokeWidth = src.strokeWidth; + dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); + dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); + switch (src.joinStyle) { + case QQuickShapePath::MiterJoin: + dst.joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickShapePath::BevelJoin: + dst.joinStyle = GL_BEVEL_NV; + break; + case QQuickShapePath::RoundJoin: + dst.joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + dst.miterLimit = src.miterLimit; + switch (src.capStyle) { + case QQuickShapePath::FlatCap: + dst.capStyle = GL_FLAT; + break; + case QQuickShapePath::SquareCap: + dst.capStyle = GL_SQUARE_NV; + break; + case QQuickShapePath::RoundCap: + dst.capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyFillRule) { + switch (src.fillRule) { + case QQuickShapePath::OddEvenFill: + dst.fillRule = GL_INVERT; + break; + case QQuickShapePath::WindingFill: + dst.fillRule = GL_COUNT_UP_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyDash) { + // Multiply by strokeWidth because the Shape API follows QPen + // meaning the input dash pattern and dash offset here are in width units. + dst.dashOffset = src.dashOffset * src.strokeWidth; + if (src.dashActive) { + if (src.dashPattern.isEmpty()) { + // default values for DashLine as defined in qpen.cpp + dst.dashPattern.resize(2); + dst.dashPattern[0] = 4 * src.strokeWidth; // dash + dst.dashPattern[1] = 2 * src.strokeWidth; // space + } else { + dst.dashPattern.resize(src.dashPattern.count()); + for (int i = 0; i < src.dashPattern.count(); ++i) + dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + + // QPen expects a dash pattern of even length and so should we + if (src.dashPattern.count() % 2 != 0) { + qWarning("QQuickShapeNvprRenderNode: dash pattern not of even length"); + dst.dashPattern << src.strokeWidth; + } + } + } else { + dst.dashPattern.clear(); + } + } + + if (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +bool QQuickShapeNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickShapeNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickShapeNvprRenderNode::mtlmgr; + +QQuickShapeNvprRenderNode::~QQuickShapeNvprRenderNode() +{ + releaseResources(); +} + +void QQuickShapeNvprRenderNode::releaseResources() +{ + for (ShapePathRenderData &d : m_sp) { + if (d.path) { + nvpr.deletePaths(d.path, 1); + d.path = 0; + } + if (d.fallbackFbo) { + delete d.fallbackFbo; + d.fallbackFbo = nullptr; + } + } + + m_fallbackBlitter.destroy(); +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else if (m == MatRadialGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "uniform sampler2D gradTab;\n" + "uniform float opacity;\n" + "uniform vec2 focalToCenter;\n" + "uniform float centerRadius;\n" + "uniform float focalRadius;\n" + "uniform vec2 translationPoint;\n" + "layout(location = 0) in vec2 uv;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 coord = uv - translationPoint;\n" + " float rd = centerRadius - focalRadius;\n" + " float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter));\n" + " float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd;\n" + " float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);\n" + " float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord));\n" + " vec4 result = vec4(0.0);\n" + " if (det >= 0.0) {\n" + " float detSqrt = sqrt(det);\n" + " float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);\n" + " if (focalRadius + w * (centerRadius - focalRadius) >= 0.0)\n" + " result = texture(gradTab, vec2(w, 0.5)) * opacity;\n" + " }\n" + " fragColor = result;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for radial gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "focalToCenter"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "centerRadius"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + mtl.uniLoc[4] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "focalRadius"); + Q_ASSERT(mtl.uniLoc[4] >= 0); + mtl.uniLoc[5] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "translationPoint"); + Q_ASSERT(mtl.uniLoc[5] >= 0); + } else if (m == MatConicalGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "#define INVERSE_2PI 0.1591549430918953358\n" + "uniform sampler2D gradTab;\n" + "uniform float opacity;\n" + "uniform float angle;\n" + "uniform vec2 translationPoint;\n" + "layout(location = 0) in vec2 uv;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 coord = uv - translationPoint;\n" + " float t;\n" + " if (abs(coord.y) == abs(coord.x))\n" + " t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI;\n" + " else\n" + " t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI;\n" + " fragColor = texture(gradTab, vec2(t - floor(t), 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for conical gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "angle"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "translationPoint"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickShapeNvprRenderNode::updatePath(ShapePathRenderData *d) +{ + if (d->dirty & QQuickShapeNvprRenderer::DirtyPath) { + if (!d->path) { + d->path = nvpr.genPaths(1); + Q_ASSERT(d->path != 0); + } + if (d->source.str.isEmpty()) { + nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), + d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); + } else { + nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); + } + } + + if (d->dirty & QQuickShapeNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); + nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); + nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); + nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); + nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); + } + + if (d->dirty & QQuickShapeNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); + } + + if (d->dirty) + d->fallbackValid = false; +} + +void QQuickShapeNvprRenderNode::renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (d->fillGradientActive) { + QQuickShapeGradient::SpreadMode spread = d->fillGradient.spread; + if (d->fillGradientActive == QQuickAbstractPathRenderer::LinearGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.a.x(), d->fillGradient.a.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.b.x(), d->fillGradient.b.y()); + } else if (d->fillGradientActive == QQuickAbstractPathRenderer::RadialGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatRadialGradient); + // simply drive uv (location 0) with x and y, just like for the linear gradient + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + + const QPointF centerPoint = d->fillGradient.a; + const QPointF focalPoint = d->fillGradient.b; + const QPointF focalToCenter = centerPoint - focalPoint; + const GLfloat centerRadius = d->fillGradient.v0; + const GLfloat focalRadius = d->fillGradient.v1; + + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], focalToCenter.x(), focalToCenter.y()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[3], centerRadius); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[4], focalRadius); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[5], focalPoint.x(), focalPoint.y()); + } else if (d->fillGradientActive == QQuickAbstractPathRenderer::ConicalGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatConicalGradient); + // same old + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + + const QPointF centerPoint = d->fillGradient.a; + const GLfloat angle = -qDegreesToRadians(d->fillGradient.v0); + + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[2], angle); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], centerPoint.x(), centerPoint.y()); + + spread = QQuickShapeGradient::RepeatSpread; + } else { + Q_UNREACHABLE(); + } + const QQuickShapeGradientCache::Key cacheKey(d->fillGradient.stops, spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); + } else { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + const int writeMask = 0xFF; + nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickShapeNvprRenderNode::renderOffscreenFill(ShapePathRenderData *d) +{ + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); + + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; + } + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) + return; + + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); + f->glDisable(GL_DEPTH_TEST); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); + QMatrix4x4 proj; + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(d); + + d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; +} + +void QQuickShapeNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + +void QQuickShapeNvprRenderNode::render(const RenderState *state) +{ + f = QOpenGLContext::currentContext()->extraFunctions(); + + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + f->glUseProgram(0); + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + bool reloadMatrices = true; + + for (ShapePathRenderData &d : m_sp) { + updatePath(&d); + + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); + + if (hasFill && stencilClip) { + // Fall back to a texture when complex clipping is in use and we have + // to fill. Reconciling glStencilFillPath's and the scenegraph's clip + // stencil semantics has not succeeded so far... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); + renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); + } + + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + } + + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(&d); + } else { + if (!m_fallbackBlitter.isCreated()) + m_fallbackBlitter.create(); + f->glStencilFunc(GL_EQUAL, sv, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), mv, + inheritedOpacity()); + } + } + + // Stroke! + if (hasStroke) { + const int strokeStencilValue = 0x80; + const int writeMask = 0x80; + + setupStencilForCover(stencilClip, sv); + if (stencilClip) { + // for the stencil step (eff. read mask == 0xFF & ~writeMask) + nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); + // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. + // This assumes the clip stencil value is <= 127. + if (sv >= strokeStencilValue) + qWarning("Shape/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(&d, strokeStencilValue, writeMask); + } + + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + + d.dirty = 0; + } + + f->glBindProgramPipeline(0); +} + +QSGRenderNode::StateFlags QQuickShapeNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickShapeNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +bool QQuickShapeNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/shapes/shaders/blit_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/shapes/shaders/blit_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/shapes/shaders/blit.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/shapes/shaders/blit.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/quickshapes/qquickshapenvprrenderer_p.h b/src/quickshapes/qquickshapenvprrenderer_p.h new file mode 100644 index 0000000000..d40eb1bce9 --- /dev/null +++ b/src/quickshapes/qquickshapenvprrenderer_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPENVPRRENDERER_P_H +#define QQUICKSHAPENVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuickShapes/private/qquickshape_p_p.h> +#include <QtQuickShapes/private/qquicknvprfunctions_p.h> +#include <qsgrendernode.h> +#include <QColor> +#include <QVector4D> +#include <QDebug> + +#if QT_CONFIG(opengl) + +QT_BEGIN_NAMESPACE + +class QQuickShapeNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; + +class QQuickShapeNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + DirtyList = 0x20 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeNvprRenderNode *node); + + struct NvprPath { + QVector<GLubyte> cmd; + QVector<GLfloat> coord; + QByteArray str; + }; + +private: + struct ShapePathGuiData { + int dirty = 0; + NvprPath path; + qreal strokeWidth; + QColor strokeColor; + QColor fillColor; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::FillRule fillRule; + bool dashActive; + qreal dashOffset; + QVector<qreal> dashPattern; + FillGradientType fillGradientActive; + GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, ShapePathGuiData *d); + + QQuickShapeNvprRenderNode *m_node = nullptr; + int m_accDirty = 0; + + QVector<ShapePathGuiData> m_sp; +}; + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + MatRadialGradient, + MatConicalGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[8]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr = nullptr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc = -1; + int m_opacityLoc = -1; + QSize m_prevSize; +}; + +class QQuickShapeNvprRenderNode : public QSGRenderNode +{ +public: + ~QQuickShapeNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + + static bool isSupported(); + +private: + struct ShapePathRenderData { + GLuint path = 0; + int dirty = 0; + QQuickShapeNvprRenderer::NvprPath source; + GLfloat strokeWidth; + QVector4D strokeColor; + QVector4D fillColor; + GLenum joinStyle; + GLint miterLimit; + GLenum capStyle; + GLenum fillRule; + GLfloat dashOffset; + QVector<GLfloat> dashPattern; + QQuickAbstractPathRenderer::FillGradientType fillGradientActive; + QQuickAbstractPathRenderer::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } + }; + + void updatePath(ShapePathRenderData *d); + void renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask); + void renderFill(ShapePathRenderData *d); + void renderOffscreenFill(ShapePathRenderData *d); + void setupStencilForCover(bool stencilClip, int sv); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; + + QVector<ShapePathRenderData> m_sp; + + friend class QQuickShapeNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_CONFIG(opengl) + +#endif // QQUICKSHAPENVPRRENDERER_P_H diff --git a/src/quickshapes/qquickshapesglobal.h b/src/quickshapes/qquickshapesglobal.h new file mode 100644 index 0000000000..eb279c5d14 --- /dev/null +++ b/src/quickshapes/qquickshapesglobal.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQUICKSHAPESGLOBAL_H +#define QQUICKSHAPESGLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_QUICKSHAPES_LIB) +# define Q_QUICKSHAPES_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICKSHAPES_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_QUICKSHAPES_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QQUICKSHAPESGLOBAL_H + diff --git a/src/quickshapes/qquickshapesglobal_p.h b/src/quickshapes/qquickshapesglobal_p.h new file mode 100644 index 0000000000..2f559b45a0 --- /dev/null +++ b/src/quickshapes/qquickshapesglobal_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPESGLOBAL_P_H +#define QQUICKSHAPESGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshapesglobal.h" + +QT_BEGIN_NAMESPACE + +#define Q_QUICKSHAPES_PRIVATE_EXPORT Q_QUICKSHAPES_EXPORT + +QT_END_NAMESPACE + + +#endif // QQUICKSHAPESGLOBAL_P_H diff --git a/src/quickshapes/qquickshapesoftwarerenderer.cpp b/src/quickshapes/qquickshapesoftwarerenderer.cpp new file mode 100644 index 0000000000..0f5c3604b5 --- /dev/null +++ b/src/quickshapes/qquickshapesoftwarerenderer.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapesoftwarerenderer_p.h" +#include <private/qquickpath_p_p.h> + +QT_BEGIN_NAMESPACE + +void QQuickShapeSoftwareRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeSoftwareRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + switch (strokeStyle) { + case QQuickShapePath::SolidLine: + d.pen.setStyle(Qt::SolidLine); + break; + case QQuickShapePath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + break; + default: + break; + } + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +static inline void setupPainterGradient(QGradient *painterGradient, const QQuickShapeGradient &g) +{ + painterGradient->setStops(g.gradientStops()); // sorted + switch (g.spread()) { + case QQuickShapeGradient::PadSpread: + painterGradient->setSpread(QGradient::PadSpread); + break; + case QQuickShapeGradient::RepeatSpread: + painterGradient->setSpread(QGradient::RepeatSpread); + break; + case QQuickShapeGradient::ReflectSpread: + painterGradient->setSpread(QGradient::ReflectSpread); + break; + default: + break; + } +} + +void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) { + QLinearGradient painterGradient(g->x1(), g->y1(), g->x2(), g->y2()); + setupPainterGradient(&painterGradient, *g); + d.brush = QBrush(painterGradient); + } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) { + QRadialGradient painterGradient(g->centerX(), g->centerY(), g->centerRadius(), + g->focalX(), g->focalY(), g->focalRadius()); + setupPainterGradient(&painterGradient, *g); + d.brush = QBrush(painterGradient); + } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) { + QConicalGradient painterGradient(g->centerX(), g->centerY(), g->angle()); + setupPainterGradient(&painterGradient, *g); + d.brush = QBrush(painterGradient); + } else { + d.brush = QBrush(d.fillColor); + } + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::endSync(bool) +{ +} + +void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::updateNode() +{ + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + m_node->m_boundingRect = QRectF(); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeSoftwareRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]); + + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } + + if (listChanged || (src.dirty & DirtyFillRule)) + dst.path.setFillRule(src.fillRule); + + if (listChanged || (src.dirty & DirtyPen)) { + dst.pen = src.pen; + dst.strokeWidth = src.strokeWidth; + } + + if (listChanged || (src.dirty & DirtyBrush)) + dst.brush = src.brush; + + src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +QQuickShapeSoftwareRenderNode::QQuickShapeSoftwareRenderNode(QQuickShape *item) + : m_item(item) +{ +} + +QQuickShapeSoftwareRenderNode::~QQuickShapeSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickShapeSoftwareRenderNode::releaseResources() +{ +} + +void QQuickShapeSoftwareRenderNode::render(const RenderState *state) +{ + if (m_sp.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + for (const ShapePathRenderData &d : qAsConst(m_sp)) { + p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); + p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); + p->drawPath(d.path); + } +} + +QSGRenderNode::StateFlags QQuickShapeSoftwareRenderNode::changedStates() const +{ + return nullptr; +} + +QSGRenderNode::RenderingFlags QQuickShapeSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickShapeSoftwareRenderNode::rect() const +{ + return m_boundingRect; +} + +QT_END_NAMESPACE diff --git a/src/quickshapes/qquickshapesoftwarerenderer_p.h b/src/quickshapes/qquickshapesoftwarerenderer_p.h new file mode 100644 index 0000000000..11e658b4b7 --- /dev/null +++ b/src/quickshapes/qquickshapesoftwarerenderer_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPESOFTWARERENDERER_P_H +#define QQUICKSHAPESOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickShapes/private/qquickshapesglobal_p.h> +#include <QtQuickShapes/private/qquickshape_p_p.h> +#include <qsgrendernode.h> +#include <QPen> +#include <QBrush> + +QT_BEGIN_NAMESPACE + +class QQuickShapeSoftwareRenderNode; + +class QQuickShapeSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + DirtyList = 0x10 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector<qreal> &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeSoftwareRenderNode *node); + +private: + QQuickShapeSoftwareRenderNode *m_node = nullptr; + int m_accDirty = 0; + struct ShapePathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector<ShapePathGuiData> m_sp; +}; + +class QQuickShapeSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickShapeSoftwareRenderNode(QQuickShape *item); + ~QQuickShapeSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickShape *m_item; + + struct ShapePathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector<ShapePathRenderData> m_sp; + QRectF m_boundingRect; + + friend class QQuickShapeSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHAPESOFTWARERENDERER_P_H diff --git a/src/quickshapes/qtquickshapes.qrc b/src/quickshapes/qtquickshapes.qrc new file mode 100644 index 0000000000..f139861693 --- /dev/null +++ b/src/quickshapes/qtquickshapes.qrc @@ -0,0 +1,20 @@ +<RCC> + <qresource prefix="/qt-project.org/shapes"> + <file>shaders/blit.vert</file> + <file>shaders/blit.frag</file> + <file>shaders/blit_core.frag</file> + <file>shaders/blit_core.vert</file> + <file>shaders/lineargradient.vert</file> + <file>shaders/lineargradient.frag</file> + <file>shaders/lineargradient_core.vert</file> + <file>shaders/lineargradient_core.frag</file> + <file>shaders/radialgradient.vert</file> + <file>shaders/radialgradient.frag</file> + <file>shaders/radialgradient_core.vert</file> + <file>shaders/radialgradient_core.frag</file> + <file>shaders/conicalgradient.vert</file> + <file>shaders/conicalgradient.frag</file> + <file>shaders/conicalgradient_core.vert</file> + <file>shaders/conicalgradient_core.frag</file> + </qresource> +</RCC> diff --git a/src/quickshapes/quickshapes.pro b/src/quickshapes/quickshapes.pro new file mode 100644 index 0000000000..5a59dec18e --- /dev/null +++ b/src/quickshapes/quickshapes.pro @@ -0,0 +1,33 @@ +TARGET = QtQuickShapes + +QT = core gui-private qml quick-private + +CONFIG += simd optimize_full internal_module + +HEADERS += \ + qquickshapesglobal.h \ + qquickshapesglobal_p.h \ + qquickshape_p.h \ + qquickshape_p_p.h \ + qquickshapegenericrenderer_p.h \ + qquickshapesoftwarerenderer_p.h + +SOURCES += \ + qquickshape.cpp \ + qquickshapegenericrenderer.cpp \ + qquickshapesoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + qquicknvprfunctions_p.h \ + qquicknvprfunctions_p_p.h \ + qquickshapenvprrenderer_p.h + + SOURCES += \ + qquicknvprfunctions.cpp \ + qquickshapenvprrenderer.cpp +} + +RESOURCES += qtquickshapes.qrc + +load(qt_module) diff --git a/src/quickshapes/shaders/blit.frag b/src/quickshapes/shaders/blit.frag new file mode 100644 index 0000000000..505f0db179 --- /dev/null +++ b/src/quickshapes/shaders/blit.frag @@ -0,0 +1,9 @@ +varying highp vec2 qt_TexCoord0; + +uniform sampler2D source; +uniform lowp float qt_Opacity; + +void main() +{ + gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; +} diff --git a/src/quickshapes/shaders/blit.vert b/src/quickshapes/shaders/blit.vert new file mode 100644 index 0000000000..f8306bd945 --- /dev/null +++ b/src/quickshapes/shaders/blit.vert @@ -0,0 +1,12 @@ +uniform highp mat4 qt_Matrix; + +attribute highp vec4 qt_Vertex; +attribute highp vec2 qt_MultiTexCoord0; + +varying highp vec2 qt_TexCoord0; + +void main() +{ + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; +} diff --git a/src/quickshapes/shaders/blit_core.frag b/src/quickshapes/shaders/blit_core.frag new file mode 100644 index 0000000000..7073808fba --- /dev/null +++ b/src/quickshapes/shaders/blit_core.frag @@ -0,0 +1,13 @@ +#version 150 core + +in vec2 qt_TexCoord0; + +out vec4 fragColor; + +uniform sampler2D source; +uniform float qt_Opacity; + +void main() +{ + fragColor = texture(source, qt_TexCoord0) * qt_Opacity; +} diff --git a/src/quickshapes/shaders/blit_core.vert b/src/quickshapes/shaders/blit_core.vert new file mode 100644 index 0000000000..5246441da3 --- /dev/null +++ b/src/quickshapes/shaders/blit_core.vert @@ -0,0 +1,14 @@ +#version 150 core + +in vec4 qt_Vertex; +in vec2 qt_MultiTexCoord0; + +out vec2 qt_TexCoord0; + +uniform mat4 qt_Matrix; + +void main() +{ + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; +} diff --git a/src/quickshapes/shaders/conicalgradient.frag b/src/quickshapes/shaders/conicalgradient.frag new file mode 100644 index 0000000000..af5fdd5ee0 --- /dev/null +++ b/src/quickshapes/shaders/conicalgradient.frag @@ -0,0 +1,19 @@ +#define INVERSE_2PI 0.1591549430918953358 + +uniform sampler2D gradTabTexture; +uniform lowp float opacity; + +uniform highp float angle; + +varying highp vec2 coord; + +void main() +{ + highp float t; + if (abs(coord.y) == abs(coord.x)) + t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI; + else + t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI; + gl_FragColor = texture2D(gradTabTexture, vec2(t - floor(t), 0.5)) * opacity; + +} diff --git a/src/quickshapes/shaders/conicalgradient.vert b/src/quickshapes/shaders/conicalgradient.vert new file mode 100644 index 0000000000..3350b0675a --- /dev/null +++ b/src/quickshapes/shaders/conicalgradient.vert @@ -0,0 +1,13 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +varying vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders/conicalgradient_core.frag b/src/quickshapes/shaders/conicalgradient_core.frag new file mode 100644 index 0000000000..e18b80159a --- /dev/null +++ b/src/quickshapes/shaders/conicalgradient_core.frag @@ -0,0 +1,22 @@ +#version 150 core + +#define INVERSE_2PI 0.1591549430918953358 + +uniform sampler2D gradTabTexture; +uniform float opacity; + +uniform float angle; + +in vec2 coord; + +out vec4 fragColor; + +void main() +{ + float t; + if (abs(coord.y) == abs(coord.x)) + t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI; + else + t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI; + fragColor = texture(gradTabTexture, vec2(t - floor(t), 0.5)) * opacity; +} diff --git a/src/quickshapes/shaders/conicalgradient_core.vert b/src/quickshapes/shaders/conicalgradient_core.vert new file mode 100644 index 0000000000..f94a56401b --- /dev/null +++ b/src/quickshapes/shaders/conicalgradient_core.vert @@ -0,0 +1,15 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +out vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders/lineargradient.frag b/src/quickshapes/shaders/lineargradient.frag new file mode 100644 index 0000000000..7f4a739109 --- /dev/null +++ b/src/quickshapes/shaders/lineargradient.frag @@ -0,0 +1,9 @@ +uniform sampler2D gradTabTexture; +uniform highp float opacity; + +varying highp float gradTabIndex; + +void main() +{ + gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quickshapes/shaders/lineargradient.vert b/src/quickshapes/shaders/lineargradient.vert new file mode 100644 index 0000000000..eb21b8886b --- /dev/null +++ b/src/quickshapes/shaders/lineargradient.vert @@ -0,0 +1,15 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +varying float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders/lineargradient_core.frag b/src/quickshapes/shaders/lineargradient_core.frag new file mode 100644 index 0000000000..5908acfa67 --- /dev/null +++ b/src/quickshapes/shaders/lineargradient_core.frag @@ -0,0 +1,12 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +in float gradTabIndex; +out vec4 fragColor; + +void main() +{ + fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quickshapes/shaders/lineargradient_core.vert b/src/quickshapes/shaders/lineargradient_core.vert new file mode 100644 index 0000000000..60b56f38e3 --- /dev/null +++ b/src/quickshapes/shaders/lineargradient_core.vert @@ -0,0 +1,17 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +out float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders/radialgradient.frag b/src/quickshapes/shaders/radialgradient.frag new file mode 100644 index 0000000000..0f503bc0f7 --- /dev/null +++ b/src/quickshapes/shaders/radialgradient.frag @@ -0,0 +1,25 @@ +uniform sampler2D gradTabTexture; +uniform lowp float opacity; + +uniform highp vec2 focalToCenter; +uniform highp float centerRadius; +uniform highp float focalRadius; + +varying highp vec2 coord; + +void main() +{ + highp float rd = centerRadius - focalRadius; + highp float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter)); + highp float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd; + highp float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2); + highp float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord)); + lowp vec4 result = vec4(0.0); + if (det >= 0.0) { + highp float detSqrt = sqrt(det); + highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); + if (focalRadius + w * (centerRadius - focalRadius) >= 0.0) + result = texture2D(gradTabTexture, vec2(w, 0.5)) * opacity; + } + gl_FragColor = result; +} diff --git a/src/quickshapes/shaders/radialgradient.vert b/src/quickshapes/shaders/radialgradient.vert new file mode 100644 index 0000000000..3350b0675a --- /dev/null +++ b/src/quickshapes/shaders/radialgradient.vert @@ -0,0 +1,13 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +varying vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders/radialgradient_core.frag b/src/quickshapes/shaders/radialgradient_core.frag new file mode 100644 index 0000000000..706ce53e4d --- /dev/null +++ b/src/quickshapes/shaders/radialgradient_core.frag @@ -0,0 +1,29 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +uniform vec2 focalToCenter; +uniform float centerRadius; +uniform float focalRadius; + +in vec2 coord; + +out vec4 fragColor; + +void main() +{ + float rd = centerRadius - focalRadius; + float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter)); + float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd; + float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2); + float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord)); + vec4 result = vec4(0.0); + if (det >= 0.0) { + float detSqrt = sqrt(det); + float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); + if (focalRadius + w * (centerRadius - focalRadius) >= 0.0) + result = texture(gradTabTexture, vec2(w, 0.5)) * opacity; + } + fragColor = result; +} diff --git a/src/quickshapes/shaders/radialgradient_core.vert b/src/quickshapes/shaders/radialgradient_core.vert new file mode 100644 index 0000000000..f94a56401b --- /dev/null +++ b/src/quickshapes/shaders/radialgradient_core.vert @@ -0,0 +1,15 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +out vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} |