diff options
author | Andy Nichols <andy.nichols@qt.io> | 2016-11-29 15:29:39 +0100 |
---|---|---|
committer | Andy Nichols <andy.nichols@qt.io> | 2016-12-13 15:43:24 +0000 |
commit | 0119439c9d25dfd892bbee47068e9d726f4bff97 (patch) | |
tree | 71df5718b21cd1471eb6cb4c4eac4c19a691f869 | |
parent | 3f57f2b7cc3899af154257a3c858bd23d9f03a62 (diff) |
OpenVG: Support rendering paths with non-affine transforms
The current approach to rendering paths (used by Rectangles and Glyph
nodes) does not support rendering with non-affine transformations. That
is because OpenVG does not support passing a non-affine transformation
matrix when rendering paths. So instead when using a non-affine
transform we will fallback to rendering to an offscreen VGImage, then
rendering that as an image which can have a perspective transform.
Change-Id: I01508bcb67b339323cb6400c7ff6d885b62c5e02
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
19 files changed, 586 insertions, 197 deletions
diff --git a/src/plugins/scenegraph/openvg/openvg.pro b/src/plugins/scenegraph/openvg/openvg.pro index fb9b3b93f5..8a58796a05 100644 --- a/src/plugins/scenegraph/openvg/openvg.pro +++ b/src/plugins/scenegraph/openvg/openvg.pro @@ -31,7 +31,8 @@ HEADERS += \ qsgopenvgfontglyphcache.h \ qsgopenvgpainternode.h \ qsgopenvgspritenode.h \ - qsgopenvgrenderable.h + qsgopenvgrenderable.h \ + qopenvgoffscreensurface.h SOURCES += \ qsgopenvgadaptation.cpp \ @@ -51,4 +52,5 @@ SOURCES += \ qsgopenvgfontglyphcache.cpp \ qsgopenvgpainternode.cpp \ qsgopenvgspritenode.cpp \ - qsgopenvgrenderable.cpp + qsgopenvgrenderable.cpp \ + qopenvgoffscreensurface.cpp diff --git a/src/plugins/scenegraph/openvg/qopenvgmatrix.cpp b/src/plugins/scenegraph/openvg/qopenvgmatrix.cpp index e55de2ce0d..83ce96578e 100644 --- a/src/plugins/scenegraph/openvg/qopenvgmatrix.cpp +++ b/src/plugins/scenegraph/openvg/qopenvgmatrix.cpp @@ -97,6 +97,19 @@ void QOpenVGMatrix::setToIdentity() m[2][2] = 1.0f; } +bool QOpenVGMatrix::isAffine() const +{ + if (m[0][2] == 0.0f && m[1][2] == 0.0f && m[2][2] == 1.0f) + return true; + + return false; +} + +QPointF QOpenVGMatrix::map(const QPointF &point) const +{ + return *this * point; +} + void QOpenVGMatrix::fill(float value) { m[0][0] = value; @@ -287,6 +300,46 @@ QOpenVGMatrix operator*(const QOpenVGMatrix &m1, const QOpenVGMatrix &m2) return matrix; } +QPointF operator*(const QPointF& point, const QOpenVGMatrix& matrix) +{ + float xin = point.x(); + float yin = point.y(); + float x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][2]; + float y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][2]; + float w = xin * matrix.m[2][0] + + yin * matrix.m[2][1] + + matrix.m[2][2]; + if (w == 1.0f) { + return QPointF(float(x), float(y)); + } else { + return QPointF(float(x / w), float(y / w)); + } +} + +QPointF operator*(const QOpenVGMatrix& matrix, const QPointF& point) +{ + float xin = point.x(); + float yin = point.y(); + float x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[2][0]; + float y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[2][1]; + float w = xin * matrix.m[0][2] + + yin * matrix.m[1][2] + + matrix.m[2][2]; + if (w == 1.0f) { + return QPointF(float(x), float(y)); + } else { + return QPointF(float(x / w), float(y / w)); + } +} + QDebug operator<<(QDebug dbg, const QOpenVGMatrix &m) { QDebugStateSaver saver(dbg); diff --git a/src/plugins/scenegraph/openvg/qopenvgmatrix.h b/src/plugins/scenegraph/openvg/qopenvgmatrix.h index 644c9da5bc..f51bf8147d 100644 --- a/src/plugins/scenegraph/openvg/qopenvgmatrix.h +++ b/src/plugins/scenegraph/openvg/qopenvgmatrix.h @@ -42,6 +42,7 @@ #include <QtCore/qdebug.h> #include <QtCore/QDataStream> +#include <QtCore/QPointF> QT_BEGIN_NAMESPACE @@ -57,6 +58,10 @@ public: bool isIdentity() const; void setToIdentity(); + bool isAffine() const; + + QPointF map(const QPointF& point) const; + void fill(float value); QOpenVGMatrix transposed() const; @@ -67,6 +72,8 @@ public: QOpenVGMatrix& operator*=(float factor); QOpenVGMatrix& operator/=(float divisor); friend QOpenVGMatrix operator*(const QOpenVGMatrix& m1, const QOpenVGMatrix& m2); + friend QPointF operator*(const QPointF& point, const QOpenVGMatrix& matrix); + friend QPointF operator*(const QOpenVGMatrix& matrix, const QPointF& point); #ifndef QT_NO_DEBUG_STREAM friend QDebug operator<<(QDebug dbg, const QOpenVGMatrix &m); #endif @@ -84,6 +91,8 @@ private: }; QOpenVGMatrix operator*(const QOpenVGMatrix& m1, const QOpenVGMatrix& m2); +QPointF operator*(const QPointF& point, const QOpenVGMatrix& matrix); +QPointF operator*(const QOpenVGMatrix& matrix, const QPointF& point); #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QOpenVGMatrix &m); diff --git a/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.cpp b/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.cpp new file mode 100644 index 0000000000..3a4643ab5a --- /dev/null +++ b/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 "qopenvgoffscreensurface.h" + +QT_BEGIN_NAMESPACE + +QOpenVGOffscreenSurface::QOpenVGOffscreenSurface(const QSize &size) + : m_size(size) +{ + m_display = eglGetCurrentDisplay(); + m_image = vgCreateImage(VG_sARGB_8888_PRE, m_size.width(), m_size.height(), VG_IMAGE_QUALITY_BETTER); + + const EGLint configAttribs[] = { + EGL_CONFORMANT, EGL_OPENVG_BIT, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_ALPHA_MASK_SIZE, 8, + EGL_NONE + }; + + EGLConfig pbufferConfig; + EGLint numConfig; + eglChooseConfig(m_display, configAttribs, &pbufferConfig, 1, &numConfig); + + m_context = eglCreateContext(m_display, pbufferConfig, eglGetCurrentContext(), 0); + if (m_context == EGL_NO_CONTEXT) + qWarning("QOpenVGOffscreenSurface: failed to create EGLContext"); + + m_renderTarget = eglCreatePbufferFromClientBuffer(m_display, + EGL_OPENVG_IMAGE, + (EGLClientBuffer)m_image, + pbufferConfig, + 0); + if (m_renderTarget == EGL_NO_SURFACE) + qWarning("QOpenVGOffscreenSurface: failed to create EGLSurface from VGImage"); +} + +QOpenVGOffscreenSurface::~QOpenVGOffscreenSurface() +{ + vgDestroyImage(m_image); + eglDestroySurface(m_display, m_renderTarget); +} + +void QOpenVGOffscreenSurface::makeCurrent() +{ + EGLContext currentContext = eglGetCurrentContext(); + if (m_context != currentContext) { + m_previousContext = eglGetCurrentContext(); + m_previousReadSurface = eglGetCurrentSurface(EGL_READ); + m_previousDrawSurface = eglGetCurrentSurface(EGL_DRAW); + + eglMakeCurrent(m_display, m_renderTarget, m_renderTarget, m_context); + } +} + +void QOpenVGOffscreenSurface::doneCurrent() +{ + EGLContext currentContext = eglGetCurrentContext(); + if (m_context == currentContext) { + eglMakeCurrent(m_display, m_previousDrawSurface, m_previousReadSurface, m_previousContext); + m_context = EGL_NO_CONTEXT; + m_previousContext = EGL_NO_CONTEXT; + m_previousReadSurface = EGL_NO_SURFACE; + m_previousDrawSurface = EGL_NO_SURFACE; + } +} + +void QOpenVGOffscreenSurface::swapBuffers() +{ + eglSwapBuffers(m_display, m_renderTarget); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.h b/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.h new file mode 100644 index 0000000000..833c9669bf --- /dev/null +++ b/src/plugins/scenegraph/openvg/qopenvgoffscreensurface.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 QOPENVGOFFSCREENSURFACE_H +#define QOPENVGOFFSCREENSURFACE_H + +#include "qopenvgcontext_p.h" + +QT_BEGIN_NAMESPACE + +class QOpenVGOffscreenSurface +{ +public: + QOpenVGOffscreenSurface(const QSize &size); + ~QOpenVGOffscreenSurface(); + + void makeCurrent(); + void doneCurrent(); + void swapBuffers(); + + VGImage image() { return m_image; } + QSize size() const { return m_size; } + +private: + VGImage m_image; + QSize m_size; + EGLContext m_context; + EGLSurface m_renderTarget; + EGLContext m_previousContext = EGL_NO_CONTEXT; + EGLSurface m_previousReadSurface = EGL_NO_SURFACE; + EGLSurface m_previousDrawSurface = EGL_NO_SURFACE; + EGLDisplay m_display; +}; + +QT_END_NAMESPACE + +#endif // QOPENVGOFFSCREENSURFACE_H diff --git a/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp index c90f0f0bbe..8be2a97034 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp @@ -42,6 +42,7 @@ #include "qsgopenvgcontext_p.h" #include "qsgopenvghelpers.h" #include "qsgopenvgfontglyphcache.h" +#include "qopenvgoffscreensurface.h" QT_BEGIN_NAMESPACE @@ -82,6 +83,7 @@ void QSGOpenVGGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &gly m_position = position; m_glyphRun = glyphs; + m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent())); // Recreate ajustments m_xAdjustments.clear(); @@ -133,8 +135,22 @@ void QSGOpenVGGlyphNode::render() // Rendering Style qreal offset = 1.0; + QOpenVGOffscreenSurface *offscreenSurface = nullptr; + + // Set Transform + vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE); + if (transform().isAffine()) { + vgLoadMatrix(transform().constData()); + } else { + vgLoadIdentity(); + offscreenSurface = new QOpenVGOffscreenSurface(QSize(ceil(m_bounding_rect.width()), ceil(m_bounding_rect.height()))); + offscreenSurface->makeCurrent(); + } + + // Set Quality vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_BETTER); + switch (m_style) { case QQuickText::Normal: break; case QQuickText::Outline: @@ -159,6 +175,13 @@ void QSGOpenVGGlyphNode::render() vgSetPaint(m_fontColorPaint, VG_FILL_PATH); drawGlyphsAtOffset(QPointF(0.0, 0.0)); + if (!transform().isAffine()) { + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + offscreenSurface->doneCurrent(); + vgDrawImage(offscreenSurface->image()); + delete offscreenSurface; + } } void QSGOpenVGGlyphNode::setOpacity(float opacity) diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalimagenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvginternalimagenode.cpp index 2ab477f116..c9545d5e9a 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalimagenode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalimagenode.cpp @@ -72,6 +72,9 @@ void QSGOpenVGInternalImageNode::render() vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); } + // Set Transform + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); VGImage image = static_cast<VGImage>(m_texture->textureId()); QSize textureSize = m_texture->textureSize(); diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp index 372dffbbc2..be437303bc 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp @@ -193,6 +193,33 @@ void QSGOpenVGInternalRectangleNode::update() void QSGOpenVGInternalRectangleNode::render() { + // Set Transform + if (transform().isAffine()) { + // Use current transform matrix + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + if (m_offscreenSurface) { + delete m_offscreenSurface; + m_offscreenSurface = nullptr; + } + } else { + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadIdentity(); + if (m_radius > 0) { + // Fallback to rendering to an image for rounded rects with perspective transforms + if (m_offscreenSurface == nullptr || m_offscreenSurface->size() != QSize(ceil(m_rect.width()), ceil(m_rect.height()))) { + delete m_offscreenSurface; + m_offscreenSurface = new QOpenVGOffscreenSurface(QSize(ceil(m_rect.width()), ceil(m_rect.height()))); + } + + m_offscreenSurface->makeCurrent(); + } else if (m_offscreenSurface) { + delete m_offscreenSurface; + m_offscreenSurface = nullptr; + } + } + + // If path is dirty if (m_pathDirty) { vgClearPath(m_rectanglePath, VG_PATH_CAPABILITY_APPEND_TO); @@ -257,6 +284,14 @@ void QSGOpenVGInternalRectangleNode::render() vgSetPaint(m_rectanglePaint, VG_FILL_PATH); vgDrawPath(m_rectanglePath, VG_FILL_PATH); } + + if (!transform().isAffine() && m_radius > 0) { + m_offscreenSurface->doneCurrent(); + // Render offscreen surface + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + vgDrawImage(m_offscreenSurface->image()); + } } void QSGOpenVGInternalRectangleNode::setOpacity(float opacity) @@ -268,6 +303,15 @@ void QSGOpenVGInternalRectangleNode::setOpacity(float opacity) } } +void QSGOpenVGInternalRectangleNode::setTransform(const QOpenVGMatrix &transform) +{ + // if there transform matrix is not affine, regenerate the path + if (transform.isAffine()) + m_pathDirty = true; + + QSGOpenVGRenderable::setTransform(transform); +} + void QSGOpenVGInternalRectangleNode::createVGResources() { m_rectanglePaint = vgCreatePaint(); @@ -280,6 +324,9 @@ void QSGOpenVGInternalRectangleNode::createVGResources() void QSGOpenVGInternalRectangleNode::destroyVGResources() { + if (m_offscreenSurface) + delete m_offscreenSurface; + vgDestroyPaint(m_rectanglePaint); vgDestroyPaint(m_borderPaint); vgDestroyPath(m_rectanglePath); @@ -290,25 +337,50 @@ void QSGOpenVGInternalRectangleNode::generateRectanglePath(const QRectF &rect, f { if (radius == 0) { // Generate a rectangle + if (transform().isAffine()) { + // Create command list + static const VGubyte rectCommands[] = { + VG_MOVE_TO_ABS, + VG_HLINE_TO_REL, + VG_VLINE_TO_REL, + VG_HLINE_TO_REL, + VG_CLOSE_PATH + }; - // Create command list - static const VGubyte rectCommands[] = { - VG_MOVE_TO_ABS, - VG_HLINE_TO_REL, - VG_VLINE_TO_REL, - VG_HLINE_TO_REL, - VG_CLOSE_PATH - }; + // Create command data + QVector<VGfloat> coordinates(5); + coordinates[0] = rect.x(); + coordinates[1] = rect.y(); + coordinates[2] = rect.width(); + coordinates[3] = rect.height(); + coordinates[4] = -rect.width(); + vgAppendPathData(path, 5, rectCommands, coordinates.constData()); + } else { + // Pre-transform path + static const VGubyte rectCommands[] = { + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; - // Create command data - QVector<VGfloat> coordinates(5); - coordinates[0] = rect.x(); - coordinates[1] = rect.y(); - coordinates[2] = rect.width(); - coordinates[3] = rect.height(); - coordinates[4] = -rect.width(); - - vgAppendPathData(path, 5, rectCommands, coordinates.constData()); + QVector<VGfloat> coordinates(8); + const QPointF topLeft = transform().map(rect.topLeft()); + const QPointF topRight = transform().map(rect.topRight()); + const QPointF bottomLeft = transform().map(rect.bottomLeft()); + const QPointF bottomRight = transform().map(rect.bottomRight()); + coordinates[0] = bottomLeft.x(); + coordinates[1] = bottomLeft.y(); + coordinates[2] = bottomRight.x(); + coordinates[3] = bottomRight.y(); + coordinates[4] = topRight.x(); + coordinates[5] = topRight.y(); + coordinates[6] = topLeft.x(); + coordinates[7] = topLeft.y(); + + vgAppendPathData(path, 5, rectCommands, coordinates.constData()); + } } else { // Generate a rounded rectangle //Radius should never exceeds half of the width or half of the height @@ -377,35 +449,82 @@ void QSGOpenVGInternalRectangleNode::generateBorderPath(const QRectF &rect, floa { if (radius == 0) { // squared frame - // Create command list - static const VGubyte squaredBorderCommands[] = { - VG_MOVE_TO_ABS, - VG_HLINE_TO_REL, - VG_VLINE_TO_REL, - VG_HLINE_TO_REL, - VG_MOVE_TO_ABS, - VG_VLINE_TO_REL, - VG_HLINE_TO_REL, - VG_VLINE_TO_REL, - VG_CLOSE_PATH - }; + if (transform().isAffine()) { + // Create command list + static const VGubyte squaredBorderCommands[] = { + VG_MOVE_TO_ABS, + VG_HLINE_TO_REL, + VG_VLINE_TO_REL, + VG_HLINE_TO_REL, + VG_MOVE_TO_ABS, + VG_VLINE_TO_REL, + VG_HLINE_TO_REL, + VG_VLINE_TO_REL, + VG_CLOSE_PATH + }; - // Create command data - QVector<VGfloat> coordinates(10); - // Outside Square - coordinates[0] = rect.x(); - coordinates[1] = rect.y(); - coordinates[2] = rect.width(); - coordinates[3] = rect.height(); - coordinates[4] = -rect.width(); - // Inside Square (opposite direction) - coordinates[5] = rect.x() + borderWidth; - coordinates[6] = rect.y() + borderHeight; - coordinates[7] = rect.height() - (borderHeight * 2); - coordinates[8] = rect.width() - (borderWidth * 2); - coordinates[9] = -(rect.height() - (borderHeight * 2)); + // Create command data + QVector<VGfloat> coordinates(10); + // Outside Square + coordinates[0] = rect.x(); + coordinates[1] = rect.y(); + coordinates[2] = rect.width(); + coordinates[3] = rect.height(); + coordinates[4] = -rect.width(); + // Inside Square (opposite direction) + coordinates[5] = rect.x() + borderWidth; + coordinates[6] = rect.y() + borderHeight; + coordinates[7] = rect.height() - (borderHeight * 2); + coordinates[8] = rect.width() - (borderWidth * 2); + coordinates[9] = -(rect.height() - (borderHeight * 2)); + + vgAppendPathData(path, 9, squaredBorderCommands, coordinates.constData()); + } else { + // persepective transform + static const VGubyte squaredBorderCommands[] = { + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; - vgAppendPathData(path, 9, squaredBorderCommands, coordinates.constData()); + QVector<VGfloat> coordinates(16); + QRectF insideRect = rect.marginsRemoved(QMarginsF(borderWidth, borderHeight, borderWidth, borderHeight)); + QPointF outsideBottomLeft = transform().map(rect.bottomLeft()); + QPointF outsideBottomRight = transform().map(rect.bottomRight()); + QPointF outsideTopRight = transform().map(rect.topRight()); + QPointF outsideTopLeft = transform().map(rect.topLeft()); + QPointF insideBottomLeft = transform().map(insideRect.bottomLeft()); + QPointF insideTopLeft = transform().map(insideRect.topLeft()); + QPointF insideTopRight = transform().map(insideRect.topRight()); + QPointF insideBottomRight = transform().map(insideRect.bottomRight()); + + // Outside + coordinates[0] = outsideBottomLeft.x(); + coordinates[1] = outsideBottomLeft.y(); + coordinates[2] = outsideBottomRight.x(); + coordinates[3] = outsideBottomRight.y(); + coordinates[4] = outsideTopRight.x(); + coordinates[5] = outsideTopRight.y(); + coordinates[6] = outsideTopLeft.x(); + coordinates[7] = outsideTopLeft.y(); + // Inside + coordinates[8] = insideBottomLeft.x(); + coordinates[9] = insideBottomLeft.y(); + coordinates[10] = insideTopLeft.x(); + coordinates[11] = insideTopLeft.y(); + coordinates[12] = insideTopRight.x(); + coordinates[13] = insideTopRight.y(); + coordinates[14] = insideBottomRight.x(); + coordinates[15] = insideBottomRight.y(); + + vgAppendPathData(path, 9, squaredBorderCommands, coordinates.constData()); + } } else if (radius < qMax(borderWidth, borderHeight)){ // rounded outside, squared inside // Create command list diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h index 0d5283773f..e8d25c94f8 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h @@ -42,6 +42,7 @@ #include <private/qsgadaptationlayer_p.h> #include "qsgopenvgrenderable.h" +#include "qopenvgoffscreensurface.h" #include <VG/openvg.h> @@ -64,6 +65,7 @@ public: void render() override; void setOpacity(float opacity) override; + void setTransform(const QOpenVGMatrix &transform) override; private: void createVGResources(); @@ -89,6 +91,8 @@ private: VGPath m_borderPath; VGPaint m_rectanglePaint; VGPaint m_borderPaint; + + QOpenVGOffscreenSurface *m_offscreenSurface = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp index 03a82ca4ee..cafd0d4e1e 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp @@ -53,12 +53,9 @@ QSGOpenVGLayer::QSGOpenVGLayer(QSGRenderContext *renderContext) , m_grab(true) , m_recursive(false) , m_dirtyTexture(true) - , m_image(0) - , m_renderTarget(0) - , m_layerContext(0) + , m_offscreenSurface(nullptr) { m_context = static_cast<QSGOpenVGRenderContext*>(renderContext); - m_vgContext = m_context->vgContext(); } QSGOpenVGLayer::~QSGOpenVGLayer() @@ -68,15 +65,16 @@ QSGOpenVGLayer::~QSGOpenVGLayer() int QSGOpenVGLayer::textureId() const { - return static_cast<int>(m_image); + if (m_offscreenSurface) + return static_cast<int>(m_offscreenSurface->image()); + else + return 0; } QSize QSGOpenVGLayer::textureSize() const { - if (m_image != 0) { - VGint imageWidth = vgGetParameteri(m_image, VG_IMAGE_WIDTH); - VGint imageHeight = vgGetParameteri(m_image, VG_IMAGE_HEIGHT); - return QSize(imageWidth, imageHeight); + if (m_offscreenSurface) { + return m_offscreenSurface->size(); } return QSize(); @@ -84,42 +82,7 @@ QSize QSGOpenVGLayer::textureSize() const bool QSGOpenVGLayer::hasAlphaChannel() const { - VGImageFormat format = static_cast<VGImageFormat>(vgGetParameteri(m_image, VG_IMAGE_FORMAT)); - - switch (format) { - case VG_sRGBA_8888: - case VG_sRGBA_8888_PRE: - case VG_sRGBA_5551: - case VG_sRGBA_4444: - case VG_lRGBA_8888: - case VG_lRGBA_8888_PRE: - case VG_A_8: - case VG_A_1: - case VG_A_4: - case VG_sARGB_8888: - case VG_sARGB_8888_PRE: - case VG_sARGB_1555: - case VG_sARGB_4444: - case VG_lARGB_8888: - case VG_lARGB_8888_PRE: - case VG_sBGRA_8888: - case VG_sBGRA_8888_PRE: - case VG_sBGRA_5551: - case VG_sBGRA_4444: - case VG_lBGRA_8888: - case VG_lBGRA_8888_PRE: - case VG_sABGR_8888: - case VG_sABGR_8888_PRE: - case VG_sABGR_1555: - case VG_sABGR_4444: - case VG_lABGR_8888: - case VG_lABGR_8888_PRE: - return true; - break; - default: - break; - } - return false; + return true; } bool QSGOpenVGLayer::hasMipmaps() const @@ -149,8 +112,8 @@ void QSGOpenVGLayer::setItem(QSGNode *item) m_item = item; if (m_live && !m_item) { - vgDestroyImage(m_image); - m_image = 0; + delete m_offscreenSurface; + m_offscreenSurface = nullptr; } markDirtyTexture(); @@ -171,8 +134,8 @@ void QSGOpenVGLayer::setSize(const QSize &size) m_size = size; if (m_live && m_size.isNull()) { - vgDestroyImage(m_image); - m_image = 0; + delete m_offscreenSurface; + m_offscreenSurface = nullptr; } markDirtyTexture(); @@ -201,8 +164,8 @@ void QSGOpenVGLayer::setLive(bool live) m_live = live; if (m_live && (!m_item || m_size.isNull())) { - vgDestroyImage(m_image); - m_image = 0; + delete m_offscreenSurface; + m_offscreenSurface = nullptr; } markDirtyTexture(); @@ -254,15 +217,17 @@ void QSGOpenVGLayer::markDirtyTexture() void QSGOpenVGLayer::invalidated() { + delete m_offscreenSurface; delete m_renderer; - m_renderer = 0; + m_renderer = nullptr; + m_offscreenSurface = nullptr; } void QSGOpenVGLayer::grab() { if (!m_item || m_size.isNull()) { - vgDestroyImage(m_image); - m_image = 0; + delete m_offscreenSurface; + m_offscreenSurface = nullptr; m_dirtyTexture = false; return; } @@ -279,47 +244,12 @@ void QSGOpenVGLayer::grab() m_renderer->setDevicePixelRatio(m_device_pixel_ratio); m_renderer->setRootNode(static_cast<QSGRootNode *>(root)); - if (m_image == 0 || m_imageSize != m_size ) { - if (m_image != 0) - vgDestroyImage(m_image); - - m_image = vgCreateImage(VG_lARGB_8888_PRE, m_size.width(), m_size.height(), VG_IMAGE_QUALITY_BETTER); - m_imageSize = m_size; - - //Destroy old RenderTarget - if (m_renderTarget != 0) - eglDestroySurface(m_vgContext->eglDisplay(), m_renderTarget); - - const EGLint configAttribs[] = { - EGL_CONFORMANT, EGL_OPENVG_BIT, - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_ALPHA_MASK_SIZE, 8, - EGL_NONE - }; - - EGLConfig pbufferConfig; - EGLint numConfig; - eglChooseConfig(m_vgContext->eglDisplay(), configAttribs, &pbufferConfig, 1, &numConfig); - - if (m_layerContext == 0) { - // Create new context - m_layerContext = eglCreateContext(m_vgContext->eglDisplay(), pbufferConfig, m_vgContext->eglContext(), 0); - } - - m_renderTarget = eglCreatePbufferFromClientBuffer(m_vgContext->eglDisplay(), - EGL_OPENVG_IMAGE, - (EGLClientBuffer)m_image, - pbufferConfig, - 0); - } - if (m_renderTarget == EGL_NO_SURFACE) { - qDebug() << "invalid renderTarget!"; - return; + if (m_offscreenSurface == nullptr || m_offscreenSurface->size() != m_size ) { + if (m_offscreenSurface != nullptr) + delete m_offscreenSurface; + + m_offscreenSurface = new QOpenVGOffscreenSurface(m_size); } // Render texture. @@ -337,7 +267,7 @@ void QSGOpenVGLayer::grab() m_renderer->setProjectionMatrixToRect(mirrored); m_renderer->setClearColor(Qt::transparent); - eglMakeCurrent(m_vgContext->eglDisplay(), m_renderTarget, m_renderTarget, m_layerContext); + m_offscreenSurface->makeCurrent(); // Before Rendering setup context for adjusting to Qt Coordinates to PixelBuffer // Should already be inverted by default @@ -346,10 +276,8 @@ void QSGOpenVGLayer::grab() m_renderer->renderScene(); - eglSwapBuffers(m_vgContext->eglDisplay(), m_renderTarget); - - // make the default surface current again - m_vgContext->makeCurrent(); + // Make the previous surface and context active again + m_offscreenSurface->doneCurrent(); root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.h b/src/plugins/scenegraph/openvg/qsgopenvglayer.h index ee9984b9d9..760a22f0bc 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvglayer.h +++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.h @@ -44,6 +44,7 @@ #include <private/qsgcontext_p.h> #include "qopenvgcontext_p.h" +#include "qopenvgoffscreensurface.h" QT_BEGIN_NAMESPACE @@ -103,11 +104,7 @@ private: bool m_recursive; bool m_dirtyTexture; - QOpenVGContext *m_vgContext; - VGImage m_image; - QSize m_imageSize; - EGLSurface m_renderTarget; - EGLContext m_layerContext; + QOpenVGOffscreenSurface *m_offscreenSurface; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp index fc9f5eb57c..8aa179f705 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp @@ -93,12 +93,11 @@ bool QSGOpenVGNodeVisitor::visit(QSGClipNode *node) // Render clip node geometry to mask vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); + vgLoadIdentity(); VGPath clipPath = generateClipPath(node->clipRect()); vgRenderToMask(clipPath, VG_FILL_PATH, maskOperation); - auto clipState = new ClipState(clipPath, m_transformStack.top()); - m_clipStack.push(clipState); + m_clipStack.push(clipPath); return true; } @@ -107,29 +106,23 @@ void QSGOpenVGNodeVisitor::endVisit(QSGClipNode *) { // Remove clip node geometry from mask auto clipState = m_clipStack.pop(); - vgDestroyPath(clipState->path); + vgDestroyPath(clipState); if (m_clipStack.count() == 0) { vgSeti(VG_MASKING, VG_FALSE); } else { // Recreate the mask vgMask(0,VG_FILL_MASK, 0, 0, VG_MAXINT, VG_MAXINT); - for (auto state : m_clipStack) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); - vgLoadMatrix(state->transform.constData()); - vgRenderToMask(state->path, VG_FILL_PATH, VG_INTERSECT_MASK); + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadIdentity(); + for (auto path : qAsConst(m_clipStack)) { + vgRenderToMask(path, VG_FILL_PATH, VG_INTERSECT_MASK); } } - - delete clipState; } bool QSGOpenVGNodeVisitor::visit(QSGGeometryNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); - vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) { // TODO: Try and render the QSGSimpleRectNode Q_UNUSED(rectNode) @@ -169,8 +162,6 @@ void QSGOpenVGNodeVisitor::endVisit(QSGOpacityNode *) bool QSGOpenVGNodeVisitor::visit(QSGInternalImageNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); renderRenderableNode(static_cast<QSGOpenVGInternalImageNode*>(node)); return true; } @@ -181,8 +172,6 @@ void QSGOpenVGNodeVisitor::endVisit(QSGInternalImageNode *) bool QSGOpenVGNodeVisitor::visit(QSGPainterNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); renderRenderableNode(static_cast<QSGOpenVGPainterNode*>(node)); return true; } @@ -193,8 +182,6 @@ void QSGOpenVGNodeVisitor::endVisit(QSGPainterNode *) bool QSGOpenVGNodeVisitor::visit(QSGInternalRectangleNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); renderRenderableNode(static_cast<QSGOpenVGInternalRectangleNode*>(node)); return true; } @@ -205,8 +192,6 @@ void QSGOpenVGNodeVisitor::endVisit(QSGInternalRectangleNode *) bool QSGOpenVGNodeVisitor::visit(QSGGlyphNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); renderRenderableNode(static_cast<QSGOpenVGGlyphNode*>(node)); return true; } @@ -226,8 +211,6 @@ void QSGOpenVGNodeVisitor::endVisit(QSGRootNode *) bool QSGOpenVGNodeVisitor::visit(QSGSpriteNode *node) { - vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); - vgLoadMatrix(m_transformStack.top().constData()); renderRenderableNode(static_cast<QSGOpenVGSpriteNode*>(node)); return true; } @@ -253,19 +236,28 @@ VGPath QSGOpenVGNodeVisitor::generateClipPath(const QRectF &rect) const // Create command list static const VGubyte rectCommands[] = { VG_MOVE_TO_ABS, - VG_HLINE_TO_REL, - VG_VLINE_TO_REL, - VG_HLINE_TO_REL, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, VG_CLOSE_PATH }; + const QOpenVGMatrix &transform = m_transformStack.top(); + // Create command data - QVector<VGfloat> coordinates(5); - coordinates[0] = rect.x(); - coordinates[1] = rect.y(); - coordinates[2] = rect.width(); - coordinates[3] = rect.height(); - coordinates[4] = -rect.width(); + QVector<VGfloat> coordinates(8); + const QPointF topLeft = transform.map(rect.topLeft()); + const QPointF topRight = transform.map(rect.topRight()); + const QPointF bottomLeft = transform.map(rect.bottomLeft()); + const QPointF bottomRight = transform.map(rect.bottomRight()); + coordinates[0] = bottomLeft.x(); + coordinates[1] = bottomLeft.y(); + coordinates[2] = bottomRight.x(); + coordinates[3] = bottomRight.y(); + coordinates[4] = topRight.x(); + coordinates[5] = topRight.y(); + coordinates[6] = topLeft.x(); + coordinates[7] = topLeft.y(); vgAppendPathData(clipPath, 5, rectCommands, coordinates.constData()); return clipPath; @@ -275,7 +267,7 @@ void QSGOpenVGNodeVisitor::renderRenderableNode(QSGOpenVGRenderable *node) { if (!node) return; - + node->setTransform(m_transformStack.top()); node->setOpacity(m_opacityState.top()); node->render(); } diff --git a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h index 3105f2076d..4805d63024 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h @@ -79,24 +79,12 @@ public: void endVisit(QSGRenderNode *) override; private: - struct ClipState { - ClipState(VGPath p, QOpenVGMatrix t) - { - path = p; - transform = t; - } - - VGPath path; - QOpenVGMatrix transform; - }; - VGPath generateClipPath(const QRectF &rect) const; void renderRenderableNode(QSGOpenVGRenderable *node); - QStack<QOpenVGMatrix> m_transformStack; QStack<float> m_opacityState; - QStack<ClipState*> m_clipStack; + QStack<VGPath> m_clipStack; }; QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp index c09ca6a47e..fb68ebf2bc 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp @@ -193,6 +193,10 @@ void QSGOpenVGPainterNode::render() else vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_NONANTIALIASED); + // Set Transform + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + vgDrawImage(static_cast<VGImage>(m_texture->textureId())); } diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp index f5890210a9..1afc5ea7ca 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp @@ -73,10 +73,33 @@ void QSGOpenVGRectangleNode::setColor(const QColor &color) markDirty(DirtyMaterial); } +void QSGOpenVGRectangleNode::setTransform(const QOpenVGMatrix &transform) +{ + // if there transform matrix is not affine, regenerate the path + if (transform.isAffine()) + m_pathDirty = true; + markDirty(DirtyGeometry); + + QSGOpenVGRenderable::setTransform(transform); +} + void QSGOpenVGRectangleNode::render() { + // Set Transform + if (transform().isAffine()) { + // Use current transform matrix + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + } else { + // map the path's to handle the perspective matrix + vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + vgLoadIdentity(); + } + if (m_pathDirty) { vgClearPath(m_rectPath, VG_PATH_CAPABILITY_APPEND_TO); + + if (transform().isAffine()) { // Create command list static const VGubyte rectCommands[] = { VG_MOVE_TO_ABS, @@ -95,6 +118,34 @@ void QSGOpenVGRectangleNode::render() coordinates[4] = -m_rect.width(); vgAppendPathData(m_rectPath, 5, rectCommands, coordinates.constData()); + + } else { + // Pre-transform path + static const VGubyte rectCommands[] = { + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + + QVector<VGfloat> coordinates(8); + const QPointF topLeft = transform().map(m_rect.topLeft()); + const QPointF topRight = transform().map(m_rect.topRight()); + const QPointF bottomLeft = transform().map(m_rect.bottomLeft()); + const QPointF bottomRight = transform().map(m_rect.bottomRight()); + coordinates[0] = bottomLeft.x(); + coordinates[1] = bottomLeft.y(); + coordinates[2] = bottomRight.x(); + coordinates[3] = bottomRight.y(); + coordinates[4] = topRight.x(); + coordinates[5] = topRight.y(); + coordinates[6] = topLeft.x(); + coordinates[7] = topLeft.y(); + + vgAppendPathData(m_rectPath, 5, rectCommands, coordinates.constData()); + } + m_pathDirty = false; } @@ -155,6 +206,10 @@ void QSGOpenVGImageNode::render() vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); } + // Set Transform + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + VGImage image = static_cast<VGImage>(m_texture->textureId()); //Apply the TextureCoordinateTransform Flag @@ -251,6 +306,10 @@ void QSGOpenVGNinePatchNode::render() vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); } + // Set Transform + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + VGImage image = static_cast<VGImage>(m_texture->textureId()); //Draw borderImage diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h index 70b087e9fc..34c8e50615 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h @@ -64,6 +64,8 @@ public: void setColor(const QColor &color) override; QColor color() const override { return m_color; } + void setTransform(const QOpenVGMatrix &transform) override; + void render() override; private: diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderable.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderable.cpp index 0856acfc9a..97d0be99c8 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderable.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderable.cpp @@ -74,4 +74,14 @@ VGPaint QSGOpenVGRenderable::opacityPaint() const return m_opacityPaint; } +void QSGOpenVGRenderable::setTransform(const QOpenVGMatrix &transform) +{ + m_transform = transform; +} + +const QOpenVGMatrix &QSGOpenVGRenderable::transform() const +{ + return m_transform; +} + QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderable.h b/src/plugins/scenegraph/openvg/qsgopenvgrenderable.h index 7a09f2afbe..a544ae743e 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderable.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderable.h @@ -44,6 +44,8 @@ #include <VG/openvg.h> +#include "qopenvgmatrix.h" + QT_BEGIN_NAMESPACE class QSGOpenVGRenderable @@ -58,9 +60,13 @@ public: float opacity() const; VGPaint opacityPaint() const; + virtual void setTransform(const QOpenVGMatrix &transform); + const QOpenVGMatrix &transform() const; + private: float m_opacity; VGPaint m_opacityPaint; + QOpenVGMatrix m_transform; }; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgspritenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgspritenode.cpp index 9bce12c83f..fb24df7471 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgspritenode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgspritenode.cpp @@ -138,6 +138,10 @@ void QSGOpenVGSpriteNode::render() vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); } + // Set Image Matrix + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(transform().constData()); + if (sourceRect != targetRect) { // Scale float scaleX = targetRect.width() / sourceRect.width(); |