diff options
Diffstat (limited to 'src')
44 files changed, 7522 insertions, 528 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 3e6771c..0721c39 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -1143,6 +1143,11 @@ class QDataStream; # else # define Q_OPENGL_EXPORT Q_DECL_IMPORT # endif +# if defined(QT_BUILD_OPENVG_LIB) +# define Q_OPENVG_EXPORT Q_DECL_EXPORT +# else +# define Q_OPENVG_EXPORT Q_DECL_IMPORT +# endif # if defined(QT_BUILD_XML_LIB) # define Q_XML_EXPORT Q_DECL_EXPORT # else @@ -1182,6 +1187,7 @@ class QDataStream; # define Q_SVG_EXPORT Q_DECL_IMPORT # define Q_CANVAS_EXPORT Q_DECL_IMPORT # define Q_OPENGL_EXPORT Q_DECL_IMPORT +# define Q_OPENVG_EXPORT Q_DECL_IMPORT # define Q_XML_EXPORT Q_DECL_IMPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_IMPORT # define Q_SCRIPT_EXPORT Q_DECL_IMPORT @@ -1207,6 +1213,7 @@ class QDataStream; # define Q_NETWORK_EXPORT Q_DECL_EXPORT # define Q_SVG_EXPORT Q_DECL_EXPORT # define Q_OPENGL_EXPORT Q_DECL_EXPORT +# define Q_OPENVG_EXPORT Q_DECL_EXPORT # define Q_XML_EXPORT Q_DECL_EXPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_EXPORT # define Q_SCRIPT_EXPORT Q_DECL_EXPORT @@ -2252,6 +2259,7 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #define QT_MODULE_TEST 0x04000 #define QT_MODULE_DBUS 0x08000 #define QT_MODULE_SCRIPTTOOLS 0x10000 +#define QT_MODULE_OPENVG 0x20000 /* Qt editions */ #define QT_EDITION_CONSOLE (QT_MODULE_CORE \ @@ -2271,6 +2279,7 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); | QT_MODULE_GUI \ | QT_MODULE_NETWORK \ | QT_MODULE_OPENGL \ + | QT_MODULE_OPENVG \ | QT_MODULE_SQL \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ @@ -2314,6 +2323,9 @@ QT_LICENSED_MODULE(Network) #if (QT_EDITION & QT_MODULE_OPENGL) QT_LICENSED_MODULE(OpenGL) #endif +#if (QT_EDITION & QT_MODULE_OPENVG) +QT_LICENSED_MODULE(OpenVG) +#endif #if (QT_EDITION & QT_MODULE_SQL) QT_LICENSED_MODULE(Sql) #endif diff --git a/src/gui/egl/egl.pri b/src/gui/egl/egl.pri new file mode 100644 index 0000000..651507f --- /dev/null +++ b/src/gui/egl/egl.pri @@ -0,0 +1,28 @@ +HEADERS += \ + egl/qegl_p.h \ + egl/qeglproperties_p.h + +SOURCES += \ + egl/qegl.cpp \ + egl/qeglproperties.cpp + +contains(QT_CONFIG, wince*): SOURCES += egl/qegl_wince.cpp + +unix { + embedded { + SOURCES += egl/qegl_qws.cpp + } else { + symbian { + SOURCES += egl/qegl_symbian.cpp + } else { + SOURCES += egl/qegl_x11.cpp + } + } +} + +for(p, QMAKE_LIBDIR_EGL) { + exists($$p):LIBS += -L$$p +} + +!isEmpty(QMAKE_INCDIR_EGL): INCLUDEPATH += $$QMAKE_INCDIR_EGL +!isEmpty(QMAKE_LIBS_EGL): LIBS += $$QMAKE_LIBS_EGL diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp new file mode 100644 index 0000000..89d9d1b --- /dev/null +++ b/src/gui/egl/qegl.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qpaintdevice.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qwidget.h> +#include <QtCore/qdebug.h> +#include "qegl_p.h" + +QT_BEGIN_NAMESPACE + +QEglContext::QEglContext() + : apiType(QEgl::OpenGL) + , dpy(EGL_NO_DISPLAY) + , ctx(EGL_NO_CONTEXT) + , surf(EGL_NO_SURFACE) + , cfg(0) + , share(false) + , current(false) + , reserved(0) +{ +} + +QEglContext::~QEglContext() +{ + destroy(); +} + +bool QEglContext::isValid() const +{ + return (ctx != EGL_NO_CONTEXT); +} + +bool QEglContext::isSharing() const +{ + return share; +} + +bool QEglContext::isCurrent() const +{ + return current; +} + +// Open the EGL display associated with "device". +bool QEglContext::openDisplay(QPaintDevice *device) +{ + if (dpy == EGL_NO_DISPLAY) + dpy = defaultDisplay(device); + return (dpy != EGL_NO_DISPLAY); +} + +// Choose a configuration that matches "properties". +bool QEglContext::chooseConfig + (const QEglProperties& properties, QEgl::PixelFormatMatch match) +{ + QEglProperties props(properties); + do { + // Get the number of matching configurations for this set of properties. + EGLint matching = 0; + if (!eglChooseConfig(dpy, props.properties(), 0, 256, &matching) || !matching) + continue; + + // If we want the best pixel format, then return the first + // matching configuration. + if (match == QEgl::BestPixelFormat) { + eglChooseConfig(dpy, props.properties(), &cfg, 1, &matching); + if (matching < 1) + continue; + return true; + } + + // Fetch all of the matching configurations and find the + // first that matches the pixel format we wanted. + EGLint size = matching; + EGLConfig *configs = new EGLConfig [size]; + eglChooseConfig(dpy, props.properties(), configs, size, &matching); + for (EGLint index = 0; index < size; ++index) { + EGLint red, green, blue, alpha; + eglGetConfigAttrib(dpy, configs[index], EGL_RED_SIZE, &red); + eglGetConfigAttrib(dpy, configs[index], EGL_GREEN_SIZE, &green); + eglGetConfigAttrib(dpy, configs[index], EGL_BLUE_SIZE, &blue); + eglGetConfigAttrib(dpy, configs[index], EGL_ALPHA_SIZE, &alpha); + if (red == props.value(EGL_RED_SIZE) && + green == props.value(EGL_GREEN_SIZE) && + blue == props.value(EGL_BLUE_SIZE) && + (props.value(EGL_ALPHA_SIZE) == 0 || + alpha == props.value(EGL_ALPHA_SIZE))) { + cfg = configs[index]; + delete [] configs; + return true; + } + } + delete [] configs; + } while (props.reduceConfiguration()); + +#ifdef EGL_BIND_TO_TEXTURE_RGBA + // Don't report an error just yet if we failed to get a pbuffer + // configuration with texture rendering. Only report failure if + // we cannot get any pbuffer configurations at all. + if (props.value(EGL_BIND_TO_TEXTURE_RGBA) == EGL_DONT_CARE && + props.value(EGL_BIND_TO_TEXTURE_RGB) == EGL_DONT_CARE) +#endif + { + qWarning() << "QEglContext::chooseConfig(): Could not find a suitable EGL configuration"; + qWarning() << "Requested:" << props.toString(); + qWarning() << "Available:"; + dumpAllConfigs(); + } + return false; +} + +// Create the EGLContext. +bool QEglContext::createContext(QEglContext *shareContext) +{ + // We need to select the correct API before calling eglCreateContext(). +#ifdef EGL_OPENGL_ES_API + if (apiType == QEgl::OpenGL) + eglBindAPI(EGL_OPENGL_ES_API); +#endif +#ifdef EGL_OPENVG_API + if (apiType == QEgl::OpenVG) + eglBindAPI(EGL_OPENVG_API); +#endif + + // Create a new context for the configuration. + QEglProperties contextProps; +#if defined(QT_OPENGL_ES_2) + if (apiType == QEgl::OpenGL) + contextProps.setValue(EGL_CONTEXT_CLIENT_VERSION, 2); +#endif + if (shareContext && shareContext->ctx == EGL_NO_CONTEXT) + shareContext = 0; + if (shareContext) { + ctx = eglCreateContext(dpy, cfg, shareContext->ctx, contextProps.properties()); + if (ctx == EGL_NO_CONTEXT) { + qWarning() << "QEglContext::createContext(): Could not share context:" << errorString(eglGetError()); + shareContext = 0; + } + } + if (ctx == EGL_NO_CONTEXT) { + ctx = eglCreateContext(dpy, cfg, 0, contextProps.properties()); + if (ctx == EGL_NO_CONTEXT) { + qWarning() << "QEglContext::createContext(): Unable to create EGL context:" << errorString(eglGetError()); + return false; + } + } + share = (shareContext != 0); + return true; +} + +// Recreate the surface for a paint device because the native id has changed. +bool QEglContext::recreateSurface(QPaintDevice *device) +{ + // Bail out if the surface has not been created for the first time yet. + if (surf == EGL_NO_SURFACE) + return true; + + // Destroy the old surface. + eglDestroySurface(dpy, surf); + surf = EGL_NO_SURFACE; + + // Create a new one. + return createSurface(device); +} + +// Destroy the EGL surface object. +void QEglContext::destroySurface() +{ + if (surf != EGL_NO_SURFACE) { + eglDestroySurface(dpy, surf); + surf = EGL_NO_SURFACE; + } +} + +// Destroy the context. Note: this does not destroy the surface. +void QEglContext::destroy() +{ + if (ctx != EGL_NO_CONTEXT) + eglDestroyContext(dpy, ctx); + dpy = EGL_NO_DISPLAY; + ctx = EGL_NO_CONTEXT; + surf = EGL_NO_SURFACE; + cfg = 0; + share = false; +} + +bool QEglContext::makeCurrent() +{ + if (ctx == EGL_NO_CONTEXT) { + qWarning() << "QEglContext::makeCurrent(): Cannot make invalid context current"; + return false; + } + + current = true; + + bool ok = eglMakeCurrent(dpy, surf, surf, ctx); + if (!ok) + qWarning() << "QEglContext::makeCurrent():" << errorString(eglGetError()); + return ok; +} + +bool QEglContext::doneCurrent() +{ + // If the context is invalid, we assume that an error was reported + // when makeCurrent() was called. + if (ctx == EGL_NO_CONTEXT) + return false; + + current = false; + + // We need to select the correct API before calling eglMakeCurrent() + // with EGL_NO_CONTEXT because threads can have both OpenGL and OpenVG + // contexts active at the same time. +#ifdef EGL_OPENGL_ES_API + if (apiType == QEgl::OpenGL) + eglBindAPI(EGL_OPENGL_ES_API); +#endif +#ifdef EGL_OPENVG_API + if (apiType == QEgl::OpenVG) + eglBindAPI(EGL_OPENVG_API); +#endif + + bool ok = eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!ok) + qWarning() << "QEglContext::doneCurrent():" << errorString(eglGetError()); + return ok; +} + +bool QEglContext::swapBuffers() +{ + if(ctx == EGL_NO_CONTEXT) + return false; + + bool ok = eglSwapBuffers(dpy, surf); + if (!ok) + qWarning() << "QEglContext::swapBuffers():" << errorString(eglGetError()); + return ok; +} + +// Wait for native rendering operations to complete before starting +// to use OpenGL/OpenVG operations. +void QEglContext::waitNative() +{ +#ifdef EGL_CORE_NATIVE_ENGINE + eglWaitNative(EGL_CORE_NATIVE_ENGINE); +#endif +} + +// Wait for client OpenGL/OpenVG operations to complete before +// using native rendering operations. +void QEglContext::waitClient() +{ +#ifdef EGL_OPENGL_ES_API + if (apiType == QEgl::OpenGL) { + eglBindAPI(EGL_OPENGL_ES_API); + eglWaitClient(); + } +#else + if (apiType == QEgl::OpenGL) + eglWaitGL(); +#endif +#ifdef EGL_OPENVG_API + if (apiType == QEgl::OpenVG) { + eglBindAPI(EGL_OPENVG_API); + eglWaitClient(); + } +#endif +} + +// Query the actual size of the EGL surface. +QSize QEglContext::surfaceSize() const +{ + int w, h; + eglQuerySurface(dpy, surf, EGL_WIDTH, &w); + eglQuerySurface(dpy, surf, EGL_HEIGHT, &h); + return QSize(w, h); +} + +// Query the value of a configuration attribute. +bool QEglContext::configAttrib(int name, EGLint *value) const +{ + return eglGetConfigAttrib(dpy, cfg, name, value); +} + +// Retrieve all of the properties on "cfg". If zero, return +// the context's configuration. +QEglProperties QEglContext::configProperties(EGLConfig cfg) const +{ + if (!cfg) + cfg = config(); + QEglProperties props; + for (int name = 0x3020; name <= 0x304F; ++name) { + EGLint value; + if (name != EGL_NONE && eglGetConfigAttrib(dpy, cfg, name, &value)) + props.setValue(name, value); + } + eglGetError(); // Clear the error state. + return props; +} + +// Initialize and return the default display. +EGLDisplay QEglContext::defaultDisplay(QPaintDevice *device) +{ + static EGLDisplay dpy = EGL_NO_DISPLAY; + if (dpy == EGL_NO_DISPLAY) { + dpy = getDisplay(device); + if (dpy == EGL_NO_DISPLAY) { + qWarning() << "QEglContext::defaultDisplay(): Cannot open EGL display"; + return EGL_NO_DISPLAY; + } + if (!eglInitialize(dpy, NULL, NULL)) { + qWarning() << "QEglContext::defaultDisplay(): Cannot initialize EGL display:" << errorString(eglGetError()); + return EGL_NO_DISPLAY; + } +#ifdef EGL_OPENGL_ES_API + eglBindAPI(EGL_OPENGL_ES_API); +#endif + } + return dpy; +} + +// Return the error string associated with a specific code. +QString QEglContext::errorString(EGLint code) +{ + static const char * const errors[] = { + "Success (0x3000)", // No tr + "Not initialized (0x3001)", // No tr + "Bad access (0x3002)", // No tr + "Bad alloc (0x3003)", // No tr + "Bad attribute (0x3004)", // No tr + "Bad config (0x3005)", // No tr + "Bad context (0x3006)", // No tr + "Bad current surface (0x3007)", // No tr + "Bad display (0x3008)", // No tr + "Bad match (0x3009)", // No tr + "Bad native pixmap (0x300A)", // No tr + "Bad native window (0x300B)", // No tr + "Bad parameter (0x300C)", // No tr + "Bad surface (0x300D)", // No tr + "Context lost (0x300E)" // No tr + }; + if (code >= 0x3000 && code <= 0x300E) { + return QString::fromLatin1(errors[code - 0x3000]); + } else { + return QLatin1String("0x") + QString::number(int(code), 16); + } +} + +// Dump all of the EGL configurations supported by the system. +void QEglContext::dumpAllConfigs() +{ + QEglProperties props; + EGLint count = 0; + if (!eglGetConfigs(dpy, 0, 0, &count) || count < 1) + return; + EGLConfig *configs = new EGLConfig [count]; + eglGetConfigs(dpy, configs, count, &count); + for (EGLint index = 0; index < count; ++index) { + props = configProperties(configs[index]); + qWarning() << props.toString(); + } + delete [] configs; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qegl_p.h b/src/gui/egl/qegl_p.h index 4e46965..89949e2 100644 --- a/src/opengl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtOpenGL module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -46,25 +46,20 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of the QGLWidget class. This header file may change from +// This file is not part of the Qt API. It exists for the convenience of +// the QGLWidget and QVGWidget classes. This header file may change from // version to version without notice, or even be removed. // // We mean it. // -#include "QtCore/qvarlengtharray.h" -#include "QtCore/qsize.h" -#include "QtGui/qimage.h" +#include <QtCore/qsize.h> +#include <QtGui/qimage.h> -#if defined(QT_OPENGL_ES) || defined(QT_OPENVG) +#include "qeglproperties_p.h" QT_BEGIN_INCLUDE_NAMESPACE -#if defined(QT_OPENGL_ES_2) || defined(QT_OPENVG) -#include <EGL/egl.h> -#else -#include <GLES/egl.h> -#endif + #if !defined(EGL_VERSION_1_3) && !defined(QEGL_NATIVE_TYPES_DEFINED) #undef EGLNativeWindowType #undef EGLNativePixmapType @@ -76,69 +71,27 @@ typedef NativeDisplayType EGLNativeDisplayType; #endif QT_END_INCLUDE_NAMESPACE -class QX11Info; -class QPaintDevice; -class QImage; -class QPixmap; -class QWidget; - QT_BEGIN_NAMESPACE -class Q_OPENGL_EXPORT QEglProperties -{ -public: - QEglProperties(); - QEglProperties(const QEglProperties& other) : props(other.props) {} - ~QEglProperties() {} - - int value(int name) const; - void setValue(int name, int value); - bool removeValue(int name); - - const int *properties() const { return props.constData(); } - - void setPixelFormat(QImage::Format pixelFormat); -#ifdef Q_WS_X11 - void setVisualFormat(const QX11Info *xinfo); -#endif - void setRenderableType(int api); - - bool reduceConfiguration(); - - QString toString() const; - -private: - QVarLengthArray<int> props; -}; - -class Q_OPENGL_EXPORT QEglContext +class Q_GUI_EXPORT QEglContext { public: QEglContext(); ~QEglContext(); - enum API - { - OpenGL, - OpenVG - }; - - enum PixelFormatMatch - { - ExactPixelFormat, - BestPixelFormat - }; - bool isValid() const; bool isSharing() const; + bool isCurrent() const; + + QEgl::API api() const { return apiType; } + void setApi(QEgl::API api) { apiType = api; } - void setApi(QEglContext::API api) { apiType = api; } bool openDisplay(QPaintDevice *device); - bool chooseConfig(const QEglProperties& properties, PixelFormatMatch match = ExactPixelFormat); + bool chooseConfig(const QEglProperties& properties, QEgl::PixelFormatMatch match = QEgl::ExactPixelFormat); bool createContext(QEglContext *shareContext = 0); - bool createSurface(QPaintDevice *device); + bool createSurface(QPaintDevice *device, const QEglProperties *properties = 0); bool recreateSurface(QPaintDevice *device); - void setSurface(EGLSurface surface) { surf = surface; } + void destroySurface(); void destroy(); @@ -153,29 +106,30 @@ public: bool configAttrib(int name, EGLint *value) const; - void clearError() const { eglGetError(); } - - QEglContext::API api() const { return apiType; } + static void clearError() { eglGetError(); } + static EGLint error() { return eglGetError(); } + static QString errorString(EGLint code); EGLDisplay display() const { return dpy; } EGLContext context() const { return ctx; } EGLSurface surface() const { return surf; } + void setSurface(EGLSurface surface) { surf = surface; } EGLConfig config() const { return cfg; } QEglProperties configProperties(EGLConfig cfg = 0) const; static EGLDisplay defaultDisplay(QPaintDevice *device); - static QString errorString(int code); void dumpAllConfigs(); private: - QEglContext::API apiType; + QEgl::API apiType; EGLDisplay dpy; EGLContext ctx; EGLSurface surf; EGLConfig cfg; bool share; + bool current; void *reserved; // For extension data in future versions. static EGLDisplay getDisplay(QPaintDevice *device); @@ -183,6 +137,4 @@ private: QT_END_NAMESPACE -#endif // QT_OPENGL_ES || QT_OPENVG - #endif // QEGL_P_H diff --git a/src/gui/egl/qegl_qws.cpp b/src/gui/egl/qegl_qws.cpp new file mode 100644 index 0000000..69eaf1b --- /dev/null +++ b/src/gui/egl/qegl_qws.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qpaintdevice.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qwidget.h> +#include "qegl_p.h" + +#if !defined(QT_NO_EGL) + +#include <qscreen_qws.h> +#include <qscreenproxy_qws.h> +#include <qapplication.h> +#include <qdesktopwidget.h> + +QT_BEGIN_NAMESPACE + +// Create the surface for a QPixmap, QImage, or QWidget. +// We don't have QGLScreen to create EGL surfaces for us, +// so surface creation needs to be done in QtOpenGL or +// QtOpenVG for Qt/Embedded. +bool QEglContext::createSurface(QPaintDevice *device, const QEglProperties *properties) +{ + Q_UNUSED(device); + Q_UNUSED(properties); + return false; +} + +EGLDisplay QEglContext::getDisplay(QPaintDevice *device) +{ + Q_UNUSED(device); + return eglGetDisplay(EGLNativeDisplayType(EGL_DEFAULT_DISPLAY)); +} + +static QScreen *screenForDevice(QPaintDevice *device) +{ + QScreen *screen = qt_screen; + if (!screen) + return 0; + if (screen->classId() == QScreen::MultiClass) { + int screenNumber; + if (device && device->devType() == QInternal::Widget) + screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device)); + else + screenNumber = 0; + screen = screen->subScreens()[screenNumber]; + } + while (screen->classId() == QScreen::ProxyClass) { + screen = static_cast<QProxyScreen *>(screen)->screen(); + } + return screen; +} + +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + if (!dev) + return; + + // Find the QGLScreen for this paint device. + QScreen *screen = screenForDevice(dev); + if (!screen) + return; + int devType = dev->devType(); + if (devType == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setPixelFormat(screen->pixelFormat()); +} + +QT_END_NAMESPACE + +#endif // !QT_NO_EGL diff --git a/src/opengl/qegl_qws.cpp b/src/gui/egl/qegl_symbian.cpp index 89bee09..16a72a1 100644 --- a/src/opengl/qegl_qws.cpp +++ b/src/gui/egl/qegl_symbian.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtOpenGL module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -44,58 +44,26 @@ #include <QtGui/qwidget.h> #include "qegl_p.h" -#if defined(QT_OPENGL_ES) || defined(QT_OPENVG) - -#include <qglscreen_qws.h> -#include <qscreenproxy_qws.h> -#include <private/qglwindowsurface_qws_p.h> -#include <qapplication.h> -#include <qdesktopwidget.h> +#include <coecntrl.h> QT_BEGIN_NAMESPACE -static QGLScreen *glScreenForDevice(QPaintDevice *device) +bool QEglContext::createSurface(QPaintDevice *device, const QEglProperties *properties) { - QScreen *screen = qt_screen; - if (screen->classId() == QScreen::MultiClass) { - int screenNumber; - if (device && device->devType() == QInternal::Widget) - screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device)); - else - screenNumber = 0; - screen = screen->subScreens()[screenNumber]; - } - while (screen->classId() == QScreen::ProxyClass) { - screen = static_cast<QProxyScreen *>(screen)->screen(); - } - if (screen->classId() == QScreen::GLClass) - return static_cast<QGLScreen *>(screen); - else - return 0; -} - -// Create the surface for a QPixmap, QImage, or QWidget. -bool QEglContext::createSurface(QPaintDevice *device) -{ - // Get the screen surface functions, which are used to create native ids. - QGLScreen *glScreen = glScreenForDevice(device); - if (!glScreen) - return false; - QGLScreenSurfaceFunctions *funcs = glScreen->surfaceFunctions(); - if (!funcs) - return false; - // Create the native drawable for the paint device. int devType = device->devType(); EGLNativePixmapType pixmapDrawable = 0; EGLNativeWindowType windowDrawable = 0; bool ok; if (devType == QInternal::Pixmap) { - ok = funcs->createNativePixmap(static_cast<QPixmap *>(device), &pixmapDrawable); - } else if (devType == QInternal::Image) { - ok = funcs->createNativeImage(static_cast<QImage *>(device), &pixmapDrawable); + pixmapDrawable = 0; + ok = (pixmapDrawable != 0); + } else if (devType == QInternal::Widget) { + QWidget *w = static_cast<QWidget *>(device); + windowDrawable = (EGLNativeWindowType)(w->winId()->DrawableWindow()); + ok = (windowDrawable != 0); } else { - ok = funcs->createNativeWindow(static_cast<QWidget *>(device), &windowDrawable); + ok = false; } if (!ok) { qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable"); @@ -103,6 +71,11 @@ bool QEglContext::createSurface(QPaintDevice *device) } // Create the EGL surface to draw into, based on the native drawable. + const int *props; + if (properties) + props = properties->properties(); + else + props = 0; if (devType == QInternal::Widget) surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0); else @@ -116,10 +89,21 @@ bool QEglContext::createSurface(QPaintDevice *device) EGLDisplay QEglContext::getDisplay(QPaintDevice *device) { - Q_UNUSED(device); - return eglGetDisplay(EGLNativeDisplayType(EGL_DEFAULT_DISPLAY)); + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (dpy == EGL_NO_DISPLAY) + qWarning("QEglContext::defaultDisplay(): Falling back to EGL_DEFAULT_DISPLAY"); + return dpy; } -QT_END_NAMESPACE +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + int devType = dev->devType(); + if (devType == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setPixelFormat(QImage::Format_RGB32); +} -#endif // QT_OPENGL_ES || QT_OPENVG + +QT_END_NAMESPACE diff --git a/src/opengl/qegl_wince.cpp b/src/gui/egl/qegl_wince.cpp index b978bc6..7bcbcf8 100644 --- a/src/opengl/qegl_wince.cpp +++ b/src/gui/egl/qegl_wince.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtOpenGL module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -44,14 +44,12 @@ #include <QtGui/qwidget.h> #include "qegl_p.h" -#if defined(QT_OPENGL_ES) || defined(QT_OPENVG) - #include <windows.h> QT_BEGIN_NAMESPACE -bool QEglContext::createSurface(QPaintDevice *device) +bool QEglContext::createSurface(QPaintDevice *device, const QEglProperties *properties) { // Create the native drawable for the paint device. int devType = device->devType(); @@ -73,10 +71,15 @@ bool QEglContext::createSurface(QPaintDevice *device) } // Create the EGL surface to draw into, based on the native drawable. + const int *props; + if (properties) + props = properties->properties(); + else + props = 0; if (devType == QInternal::Widget) - surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0); + surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, props); else - surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0); + surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, props); if (surf == EGL_NO_SURFACE) { qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError()); return false; @@ -87,19 +90,27 @@ bool QEglContext::createSurface(QPaintDevice *device) EGLDisplay QEglContext::getDisplay(QPaintDevice *device) { EGLDisplay dpy = 0; - HWND win = ((QWidget*)device)->winId(); - HDC myDc = GetDC(win); - if (!myDc) { - qWarning("QEglContext::defaultDisplay(): WinCE display is not open"); - } - dpy = eglGetDisplay(EGLNativeDisplayType(myDc)); - if (dpy == EGL_NO_DISPLAY) { - qWarning("QEglContext::defaultDisplay(): Falling back to EGL_DEFAULT_DISPLAY"); - dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + HWND win = (static_cast<QWidget*>(device))->winId(); + HDC myDc = GetDC(win); + if (!myDc) { + qWarning("QEglContext::defaultDisplay(): WinCE display is not open"); + } + dpy = eglGetDisplay(EGLNativeDisplayType(myDc)); + if (dpy == EGL_NO_DISPLAY) { + qWarning("QEglContext::defaultDisplay(): Falling back to EGL_DEFAULT_DISPLAY"); + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); } return dpy; } -QT_END_NAMESPACE +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + int devType = dev->devType(); + if (devType == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setPixelFormat(QImage::Format_RGB16); // XXX +} -#endif // QT_OPENGL_ES || QT_OPENVG +QT_END_NAMESPACE diff --git a/src/opengl/qegl_x11egl.cpp b/src/gui/egl/qegl_x11.cpp index 23d1ac6..be89efe 100644 --- a/src/opengl/qegl_x11egl.cpp +++ b/src/gui/egl/qegl_x11.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtOpenGL module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -45,17 +45,13 @@ #include <QtCore/qdebug.h> #include "qegl_p.h" -#if defined(QT_OPENGL_ES) || defined(QT_OPENVG) - -#if defined(Q_WS_X11) #include <QtGui/qx11info_x11.h> #include <X11/Xlib.h> #include <X11/Xutil.h> -#endif QT_BEGIN_NAMESPACE -bool QEglContext::createSurface(QPaintDevice *device) +bool QEglContext::createSurface(QPaintDevice *device, const QEglProperties *properties) { // Create the native drawable for the paint device. int devType = device->devType(); @@ -77,11 +73,15 @@ bool QEglContext::createSurface(QPaintDevice *device) } // Create the EGL surface to draw into, based on the native drawable. + const int *props; + if (properties) + props = properties->properties(); + else + props = 0; if (devType == QInternal::Widget) - surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0); + surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, props); else - surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0); - + surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, props); if (surf == EGL_NO_SURFACE) { qWarning() << "QEglContext::createSurface(): Unable to create EGL surface:" << errorString(eglGetError()); @@ -128,6 +128,17 @@ void QEglProperties::setVisualFormat(const QX11Info *xinfo) setValue(EGL_ALPHA_SIZE, 0); // XXX } -QT_END_NAMESPACE +extern const QX11Info *qt_x11Info(const QPaintDevice *pd); + +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + if (!dev) + return; + if (dev->devType() == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setVisualFormat(qt_x11Info(dev)); +} -#endif // QT_OPENGL_ES || QT_OPENVG +QT_END_NAMESPACE diff --git a/src/opengl/qegl.cpp b/src/gui/egl/qeglproperties.cpp index 61c7427..e0ae8a6 100644 --- a/src/opengl/qegl.cpp +++ b/src/gui/egl/qeglproperties.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtOpenGL module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,358 +39,12 @@ ** ****************************************************************************/ -#include <QtGui/qpaintdevice.h> -#include <QtGui/qpixmap.h> -#include <QtGui/qwidget.h> -#include <QtCore/qdebug.h> -#include "qegl_p.h" - -#if defined(QT_OPENGL_ES) || defined(QT_OPENVG) +#include "qeglproperties_p.h" QT_BEGIN_NAMESPACE -QEglContext::QEglContext() -{ - apiType = OpenGL; - dpy = EGL_NO_DISPLAY; - ctx = EGL_NO_CONTEXT; - surf = EGL_NO_SURFACE; - cfg = 0; - share = false; - reserved = 0; -} - -QEglContext::~QEglContext() -{ - destroy(); -} - -bool QEglContext::isValid() const -{ - return (ctx != EGL_NO_CONTEXT); -} - -bool QEglContext::isSharing() const -{ - return share; -} - -// Open the EGL display associated with "device". -bool QEglContext::openDisplay(QPaintDevice *device) -{ - if (dpy == EGL_NO_DISPLAY) - dpy = defaultDisplay(device); - return (dpy != EGL_NO_DISPLAY); -} - -// Choose a configuration that matches "properties". -bool QEglContext::chooseConfig - (const QEglProperties& properties, PixelFormatMatch match) -{ - QEglProperties props(properties); - EGLConfig *configs; - EGLint matching, size; - do { - // Get the number of matching configurations for this set of properties. - matching = 0; - if (!eglChooseConfig(dpy, props.properties(), 0, 256, &matching) || !matching) - continue; - - // If we want the best pixel format, then return the first - // matching configuration. - if (match == BestPixelFormat) { - eglChooseConfig(dpy, props.properties(), &cfg, 1, &matching); - if (matching < 1) - continue; - return true; - } - - // Fetch all of the matching configurations and find the - // first that matches the pixel format we wanted. - size = matching; - configs = new EGLConfig [size]; - eglChooseConfig(dpy, props.properties(), configs, size, &matching); - for (EGLint index = 0; index < size; ++index) { - EGLint red, green, blue, alpha; - eglGetConfigAttrib(dpy, configs[index], EGL_RED_SIZE, &red); - eglGetConfigAttrib(dpy, configs[index], EGL_GREEN_SIZE, &green); - eglGetConfigAttrib(dpy, configs[index], EGL_BLUE_SIZE, &blue); - eglGetConfigAttrib(dpy, configs[index], EGL_ALPHA_SIZE, &alpha); - if (red == props.value(EGL_RED_SIZE) && - green == props.value(EGL_GREEN_SIZE) && - blue == props.value(EGL_BLUE_SIZE) && - (props.value(EGL_ALPHA_SIZE) == 0 || - alpha == props.value(EGL_ALPHA_SIZE))) { - cfg = configs[index]; - delete [] configs; - return true; - } - } - delete [] configs; - } while (props.reduceConfiguration()); - -#ifdef EGL_BIND_TO_TEXTURE_RGBA - // Don't report an error just yet if we failed to get a pbuffer - // configuration with texture rendering. Only report failure if - // we cannot get any pbuffer configurations at all. - if (props.value(EGL_BIND_TO_TEXTURE_RGBA) == EGL_DONT_CARE && - props.value(EGL_BIND_TO_TEXTURE_RGB) == EGL_DONT_CARE) -#endif - { - qWarning() << "QEglContext::chooseConfig(): Could not find a suitable EGL configuration"; - qWarning() << "Requested:" << props.toString(); - qWarning() << "Available:"; - dumpAllConfigs(); - } - return false; -} - -// Create the EGLContext. -bool QEglContext::createContext(QEglContext *shareContext) -{ - // We need to select the correct API before calling eglCreateContext(). -#ifdef EGL_OPENGL_ES_API - if (apiType == OpenGL) - eglBindAPI(EGL_OPENGL_ES_API); -#endif -#ifdef EGL_OPENVG_API - if (apiType == OpenVG) - eglBindAPI(EGL_OPENVG_API); -#endif - - // Create a new context for the configuration. - QEglProperties contextProps; -#if defined(QT_OPENGL_ES_2) - if (apiType == OpenGL) - contextProps.setValue(EGL_CONTEXT_CLIENT_VERSION, 2); -#endif - if (shareContext && shareContext->ctx == EGL_NO_CONTEXT) - shareContext = 0; - if (shareContext) { - ctx = eglCreateContext(dpy, cfg, shareContext->ctx, contextProps.properties()); - if (ctx == EGL_NO_CONTEXT) { - qWarning() << "QEglContext::createContext(): Could not share context:" << errorString(eglGetError()); - shareContext = 0; - } - } - if (ctx == EGL_NO_CONTEXT) { - ctx = eglCreateContext(dpy, cfg, 0, contextProps.properties()); - if (ctx == EGL_NO_CONTEXT) { - qWarning() << "QEglContext::createContext(): Unable to create EGL context:" << errorString(eglGetError()); - return false; - } - } - share = (shareContext != 0); - return true; -} - -// Recreate the surface for a paint device because the native id has changed. -bool QEglContext::recreateSurface(QPaintDevice *device) -{ - // Bail out if the surface has not been created for the first time yet. - if (surf == EGL_NO_SURFACE) - return true; - - // Destroy the old surface. - eglDestroySurface(dpy, surf); - - // Create a new one. - return createSurface(device); -} - -void QEglContext::destroy() -{ - if (ctx != EGL_NO_CONTEXT) - eglDestroyContext(dpy, ctx); - dpy = EGL_NO_DISPLAY; - ctx = EGL_NO_CONTEXT; - surf = EGL_NO_SURFACE; - cfg = 0; - share = false; -} - -bool QEglContext::makeCurrent() -{ - if(ctx == EGL_NO_CONTEXT) { - qWarning() << "QEglContext::makeCurrent(): Cannot make invalid context current"; - return false; - } - - bool ok = eglMakeCurrent(dpy, surf, surf, ctx); - if (!ok) { - EGLint err = eglGetError(); - qWarning() << "QEglContext::makeCurrent():" << errorString(err); - } - return ok; -} - -bool QEglContext::doneCurrent() -{ - // If the context is invalid, we assume that an error was reported - // when makeCurrent() was called. - if (ctx == EGL_NO_CONTEXT) - return false; - - // We need to select the correct API before calling eglMakeCurrent() - // with EGL_NO_CONTEXT because threads can have both OpenGL and OpenVG - // contexts active at the same time. -#ifdef EGL_OPENGL_ES_API - if (apiType == OpenGL) - eglBindAPI(EGL_OPENGL_ES_API); -#endif -#ifdef EGL_OPENVG_API - if (apiType == OpenVG) - eglBindAPI(EGL_OPENVG_API); -#endif - - bool ok = eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (!ok) { - EGLint err = eglGetError(); - qWarning() << "QEglContext::doneCurrent():" << errorString(err); - } - return ok; -} - -bool QEglContext::swapBuffers() -{ - if(ctx == EGL_NO_CONTEXT) - return false; - - bool ok = eglSwapBuffers(dpy, surf); - if (!ok) { - EGLint err = eglGetError(); - qWarning() << "QEglContext::swapBuffers():" << errorString(err); - } - return ok; -} - -// Wait for native rendering operations to complete before starting -// to use OpenGL/OpenVG operations. -void QEglContext::waitNative() -{ -#ifdef EGL_CORE_NATIVE_ENGINE - eglWaitNative(EGL_CORE_NATIVE_ENGINE); -#endif -} - -// Wait for client OpenGL/OpenVG operations to complete before -// using native rendering operations. -void QEglContext::waitClient() -{ -#ifdef EGL_OPENGL_ES_API - if (apiType == OpenGL) { - eglBindAPI(EGL_OPENGL_ES_API); - eglWaitClient(); - } -#else - if (apiType == OpenGL) - eglWaitGL(); -#endif -#ifdef EGL_OPENVG_API - if (apiType == OpenVG) { - eglBindAPI(EGL_OPENVG_API); - eglWaitClient(); - } -#endif -} - -// Query the actual size of the EGL surface. -QSize QEglContext::surfaceSize() const -{ - int w, h; - eglQuerySurface(dpy, surf, EGL_WIDTH, &w); - eglQuerySurface(dpy, surf, EGL_HEIGHT, &h); - return QSize(w, h); -} - -// Query the value of a configuration attribute. -bool QEglContext::configAttrib(int name, EGLint *value) const -{ - return eglGetConfigAttrib(dpy, cfg, name, value); -} - -// Retrieve all of the properties on "cfg". If zero, return -// the context's configuration. -QEglProperties QEglContext::configProperties(EGLConfig cfg) const -{ - if (!cfg) - cfg = config(); - QEglProperties props; - for (int name = 0x3020; name <= 0x304F; ++name) { - EGLint value; - if (name != EGL_NONE && eglGetConfigAttrib(dpy, cfg, name, &value)) - props.setValue(name, value); - } - eglGetError(); // Clear the error state. - return props; -} - -// Initialize and return the default display. -EGLDisplay QEglContext::defaultDisplay(QPaintDevice *device) -{ - static EGLDisplay dpy = EGL_NO_DISPLAY; - if (dpy == EGL_NO_DISPLAY) { - dpy = getDisplay(device); - if (dpy == EGL_NO_DISPLAY) { - qWarning() << "QEglContext::defaultDisplay(): Cannot open EGL display"; - return EGL_NO_DISPLAY; - } - if (!eglInitialize(dpy, NULL, NULL)) { - EGLint err = eglGetError(); - qWarning() << "QEglContext::defaultDisplay(): Cannot initialize EGL display:" << errorString(err); - return EGL_NO_DISPLAY; - } -#ifdef EGL_OPENGL_ES_API - eglBindAPI(EGL_OPENGL_ES_API); -#endif - } - return dpy; -} - -// Return the error string associated with a specific code. -QString QEglContext::errorString(int code) -{ - static const char * const errors[] = { - "Success (0x3000)", // No tr - "Not initialized (0x3001)", // No tr - "Bad access (0x3002)", // No tr - "Bad alloc (0x3003)", // No tr - "Bad attribute (0x3004)", // No tr - "Bad config (0x3005)", // No tr - "Bad context (0x3006)", // No tr - "Bad current surface (0x3007)", // No tr - "Bad display (0x3008)", // No tr - "Bad match (0x3009)", // No tr - "Bad native pixmap (0x300A)", // No tr - "Bad native window (0x300B)", // No tr - "Bad parameter (0x300C)", // No tr - "Bad surface (0x300D)", // No tr - "Context lost (0x300E)" // No tr - }; - if (code >= 0x3000 && code <= 0x300E) { - return QString::fromLatin1(errors[code - 0x3000]); - } else { - return QLatin1String("0x") + QString::number(code, 16); - } -} - -// Dump all of the EGL configurations supported by the system. -void QEglContext::dumpAllConfigs() -{ - QEglProperties props; - EGLint count = 0; - if (!eglGetConfigs(dpy, 0, 0, &count)) - return; - if (count < 1) - return; - EGLConfig *configs = new EGLConfig [count]; - eglGetConfigs(dpy, configs, count, &count); - for (EGLint index = 0; index < count; ++index) { - props = configProperties(configs[index]); - qWarning() << props.toString(); - } - delete [] configs; -} +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> // Initialize a property block. QEglProperties::QEglProperties() @@ -455,10 +109,10 @@ int QEglProperties::value(int name) const case EGL_MAX_PBUFFER_PIXELS: case EGL_NATIVE_VISUAL_ID: case EGL_NONE: - qWarning("QEglProperties::value() - Attibute %d does not affect config selection", name); + // Attribute does not affect config selection. return EGL_DONT_CARE; default: - qWarning("QEglProperties::value() - Attibute %d is unknown in EGL <=1.4", name); + // Attribute is unknown in EGL <= 1.4. return EGL_DONT_CARE; } } @@ -532,18 +186,18 @@ void QEglProperties::setPixelFormat(QImage::Format pixelFormat) setValue(EGL_ALPHA_SIZE, alpha); } -void QEglProperties::setRenderableType(int api) +void QEglProperties::setRenderableType(QEgl::API api) { #if defined(EGL_RENDERABLE_TYPE) #if defined(QT_OPENGL_ES_2) - if (api == QEglContext::OpenGL) + if (api == QEgl::OpenGL) setValue(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT); #elif defined(QT_OPENGL_ES) - if (api == QEglContext::OpenGL) + if (api == QEgl::OpenGL) setValue(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT); #endif #if defined(EGL_OPENVG_BIT) - if (api == QEglContext::OpenVG) + if (api == QEgl::OpenVG) setValue(EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT); #endif #else @@ -839,4 +493,4 @@ QString QEglProperties::toString() const QT_END_NAMESPACE -#endif // QT_OPENGL_ES || QT_OPENVG + diff --git a/src/gui/egl/qeglproperties_p.h b/src/gui/egl/qeglproperties_p.h new file mode 100644 index 0000000..3570f80 --- /dev/null +++ b/src/gui/egl/qeglproperties_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLPROPERTIES_P_H +#define QEGLPROPERTIES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QGLWidget class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qimage.h> + +QT_BEGIN_INCLUDE_NAMESPACE + +#if defined(QT_GLES_EGL) +#include <GLES/egl.h> +#else +#include <EGL/egl.h> +#endif + +#if defined(Q_WS_X11) +// If <EGL/egl.h> included <X11/Xlib.h>, then the global namespace +// may have been polluted with X #define's. The following makes sure +// the X11 headers were included properly and then cleans things up. +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#undef Bool +#undef Status +#undef None +#undef KeyPress +#undef KeyRelease +#undef FocusIn +#undef FocusOut +#undef Type +#undef FontChange +#undef CursorShape +#endif + +QT_END_INCLUDE_NAMESPACE + +QT_BEGIN_NAMESPACE + +namespace QEgl { + enum API + { + OpenGL, + OpenVG + }; + + enum PixelFormatMatch + { + ExactPixelFormat, + BestPixelFormat + }; +}; + +class QX11Info; +class QPaintDevice; + +class Q_GUI_EXPORT QEglProperties +{ +public: + QEglProperties(); + QEglProperties(const QEglProperties& other) : props(other.props) {} + ~QEglProperties() {} + + int value(int name) const; + void setValue(int name, int value); + bool removeValue(int name); + bool isEmpty() const { return props[0] == EGL_NONE; } + + const int *properties() const { return props.constData(); } + + void setPixelFormat(QImage::Format pixelFormat); +#ifdef Q_WS_X11 + void setVisualFormat(const QX11Info *xinfo); +#endif + void setRenderableType(QEgl::API api); + + void setPaintDeviceFormat(QPaintDevice *dev); + + bool reduceConfiguration(); + + QString toString() const; + +private: + QVarLengthArray<int> props; +}; + +QT_END_NAMESPACE + +#endif // QEGLPROPERTIES_P_H diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 329fb01..b77bfdc 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -35,6 +35,8 @@ include(util/util.pri) include(statemachine/statemachine.pri) include(math3d/math3d.pri) +contains(QT_CONFIG, egl): include(egl/egl.pri) + embedded: QT += network QMAKE_LIBS += $$QMAKE_LIBS_GUI diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 73af174..c92b8cf 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -62,12 +62,9 @@ x11 { contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles1cl)|contains(QT_CONFIG, opengles2) { SOURCES += qgl_x11egl.cpp \ qglpixelbuffer_egl.cpp \ - qgl_egl.cpp \ - qegl.cpp \ - qegl_x11egl.cpp + qgl_egl.cpp - HEADERS += qegl_p.h \ - qgl_egl_p.h + HEADERS += qgl_egl_p.h } else { SOURCES += qgl_x11.cpp \ @@ -93,13 +90,10 @@ win32:!wince*: { wince*: { SOURCES += qgl_wince.cpp \ qglpixelbuffer_egl.cpp \ - qgl_egl.cpp \ - qegl.cpp \ - qegl_wince.cpp + qgl_egl.cpp HEADERS += qgl_cl_p.h \ qgl_egl_p.h \ - qegl_p.h } embedded { @@ -108,15 +102,12 @@ embedded { qglpixelbuffer_egl.cpp \ qglscreen_qws.cpp \ qglwindowsurface_qws.cpp \ - qegl.cpp \ - qegl_qws.cpp \ qgl_egl.cpp HEADERS += qglpaintdevice_qws_p.h \ qglscreen_qws.h \ qglwindowsurface_qws_p.h \ - qgl_egl_p.h \ - qegl_p.h + qgl_egl_p.h contains(QT_CONFIG, fontconfig) { include($$QT_SOURCE_TREE/config.tests/unix/freetype/freetype.pri) diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h index 2fb2e8f..d54036d 100644 --- a/src/opengl/qgl_egl_p.h +++ b/src/opengl/qgl_egl_p.h @@ -53,7 +53,7 @@ // We mean it. // -#include "qegl_p.h" +#include <QtGui/private/qegl_p.h> QT_BEGIN_NAMESPACE diff --git a/src/opengl/qgl_qws.cpp b/src/opengl/qgl_qws.cpp index 3b6ad63..a71a734 100644 --- a/src/opengl/qgl_qws.cpp +++ b/src/opengl/qgl_qws.cpp @@ -116,6 +116,57 @@ void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device) props.setPixelFormat(glScreen->pixelFormat()); } +static bool qt_egl_create_surface + (QEglContext *context, QPaintDevice *device, + const QEglProperties *properties = 0) +{ + // Get the screen surface functions, which are used to create native ids. + QGLScreen *glScreen = glScreenForDevice(device); + if (!glScreen) + return false; + QGLScreenSurfaceFunctions *funcs = glScreen->surfaceFunctions(); + if (!funcs) + return false; + + // Create the native drawable for the paint device. + int devType = device->devType(); + EGLNativePixmapType pixmapDrawable = 0; + EGLNativeWindowType windowDrawable = 0; + bool ok; + if (devType == QInternal::Pixmap) { + ok = funcs->createNativePixmap(static_cast<QPixmap *>(device), &pixmapDrawable); + } else if (devType == QInternal::Image) { + ok = funcs->createNativeImage(static_cast<QImage *>(device), &pixmapDrawable); + } else { + ok = funcs->createNativeWindow(static_cast<QWidget *>(device), &windowDrawable); + } + if (!ok) { + qWarning("QEglContext::createSurface(): Cannot create the native EGL drawable"); + return false; + } + + // Create the EGL surface to draw into, based on the native drawable. + const int *props; + if (properties) + props = properties->properties(); + else + props = 0; + EGLSurface surf; + if (devType == QInternal::Widget) { + surf = eglCreateWindowSurface + (context->display(), context->config(), windowDrawable, props); + } else { + surf = eglCreatePixmapSurface + (context->display(), context->config(), pixmapDrawable, props); + } + if (surf == EGL_NO_SURFACE) { + qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError()); + return false; + } + context->setSurface(surf); + return true; +} + bool QGLContext::chooseContext(const QGLContext* shareContext) { Q_D(QGLContext); @@ -131,7 +182,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) // Get the display and initialize it. d->eglContext = new QEglContext(); - d->eglContext->setApi(QEglContext::OpenGL); + d->eglContext->setApi(QEgl::OpenGL); if (!d->eglContext->openDisplay(device())) { delete d->eglContext; d->eglContext = 0; @@ -142,7 +193,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) QEglProperties configProps; qt_egl_add_platform_config(configProps, device()); qt_egl_set_format(configProps, devType, d->glFormat); - configProps.setRenderableType(QEglContext::OpenGL); + configProps.setRenderableType(QEgl::OpenGL); // Search for a matching configuration, reducing the complexity // each time until we get something that matches. @@ -168,8 +219,10 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval()); #endif - // Create the EGL surface to draw into. - if (!d->eglContext->createSurface(device())) { + // Create the EGL surface to draw into. We cannot use + // QEglContext::createSurface() because it does not have + // access to the QGLScreen. + if (!qt_egl_create_surface(d->eglContext, device())) { delete d->eglContext; d->eglContext = 0; return false; diff --git a/src/opengl/qgl_wince.cpp b/src/opengl/qgl_wince.cpp index 47dc2d4..afe26ab 100644 --- a/src/opengl/qgl_wince.cpp +++ b/src/opengl/qgl_wince.cpp @@ -140,7 +140,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) // Get the display and initialize it. d->eglContext = new QEglContext(); - d->eglContext->setApi(QEglContext::OpenGL); + d->eglContext->setApi(QEgl::OpenGL); if (!d->eglContext->openDisplay(device())) { delete d->eglContext; d->eglContext = 0; @@ -151,7 +151,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) QEglProperties configProps; qt_egl_add_platform_config(configProps, device()); qt_egl_set_format(configProps, devType, d->glFormat); - configProps.setRenderableType(QEglContext::OpenGL); + configProps.setRenderableType(QEgl::OpenGL); // Search for a matching configuration, reducing the complexity // each time until we get something that matches. diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 7fdbfbd..9db3a30 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -77,7 +77,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) // Get the display and initialize it. d->eglContext = new QEglContext(); - d->eglContext->setApi(QEglContext::OpenGL); + d->eglContext->setApi(QEgl::OpenGL); if (!d->eglContext->openDisplay(device())) { delete d->eglContext; d->eglContext = 0; @@ -88,11 +88,11 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) QEglProperties configProps; qt_egl_set_format(configProps, devType, d->glFormat); qt_egl_add_platform_config(configProps, device()); - configProps.setRenderableType(QEglContext::OpenGL); + configProps.setRenderableType(QEgl::OpenGL); // Search for a matching configuration, reducing the complexity // each time until we get something that matches. - if (!d->eglContext->chooseConfig(configProps, QEglContext::BestPixelFormat)) { + if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) { delete d->eglContext; d->eglContext = 0; return false; diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp index fca1a31..38e4f74 100644 --- a/src/opengl/qglpixelbuffer_egl.cpp +++ b/src/opengl/qglpixelbuffer_egl.cpp @@ -63,7 +63,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge { // Create the EGL context. ctx = new QEglContext(); - ctx->setApi(QEglContext::OpenGL); + ctx->setApi(QEgl::OpenGL); // Open the EGL display. if (!ctx->openDisplay(0)) { @@ -82,13 +82,13 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge #if QGL_RENDER_TEXTURE textureFormat = EGL_TEXTURE_RGBA; configProps.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE); - ok = ctx->chooseConfig(configProps, QEglContext::BestPixelFormat); + ok = ctx->chooseConfig(configProps, QEgl::BestPixelFormat); if (!ok) { // Try again with RGB texture rendering. textureFormat = EGL_TEXTURE_RGB; configProps.removeValue(EGL_BIND_TO_TEXTURE_RGBA); configProps.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE); - ok = ctx->chooseConfig(configProps, QEglContext::BestPixelFormat); + ok = ctx->chooseConfig(configProps, QEgl::BestPixelFormat); if (!ok) { // One last try for a pbuffer with no texture rendering. configProps.removeValue(EGL_BIND_TO_TEXTURE_RGB); @@ -99,7 +99,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge textureFormat = EGL_NONE; #endif if (!ok) { - if (!ctx->chooseConfig(configProps, QEglContext::BestPixelFormat)) { + if (!ctx->chooseConfig(configProps, QEgl::BestPixelFormat)) { delete ctx; ctx = 0; return false; @@ -208,7 +208,7 @@ bool QGLPixelBuffer::hasOpenGLPbuffers() return false; QEglProperties configProps; qt_egl_set_format(configProps, QInternal::Pbuffer, QGLFormat::defaultFormat()); - configProps.setRenderableType(QEglContext::OpenGL); + configProps.setRenderableType(QEgl::OpenGL); return ctx.chooseConfig(configProps); } diff --git a/src/openvg/openvg.pro b/src/openvg/openvg.pro new file mode 100644 index 0000000..4d499ef --- /dev/null +++ b/src/openvg/openvg.pro @@ -0,0 +1,48 @@ +TARGET = QtOpenVG +QT += core \ + gui + +DEFINES+=QT_BUILD_OPENVG_LIB + +contains(QT_CONFIG, shivavg) { + DEFINES += QVG_NO_DRAW_GLYPHS + DEFINES += QVG_NO_RENDER_TO_MASK + DEFINES += QVG_SCISSOR_CLIP +} + +HEADERS += \ + qvg.h \ + qvg_p.h \ + qpaintengine_vg_p.h \ + qpixmapdata_vg_p.h \ + qpixmapfilter_vg_p.h \ + qvgcompositionhelper_p.h +SOURCES += \ + qpaintengine_vg.cpp \ + qpixmapdata_vg.cpp \ + qpixmapfilter_vg.cpp + +contains(QT_CONFIG, egl) { + HEADERS += \ + qwindowsurface_vgegl_p.h \ + qwindowsurface_vg_p.h + SOURCES += \ + qwindowsurface_vg.cpp \ + qwindowsurface_vgegl.cpp +} + +include(../qbase.pri) + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui + +!isEmpty(QMAKE_INCDIR_OPENVG): INCLUDEPATH += $$QMAKE_INCDIR_OPENVG +!isEmpty(QMAKE_LIBDIR_OPENVG): LIBS += -L$$QMAKE_LIBDIR_OPENVG +!isEmpty(QMAKE_LIBS_OPENVG): LIBS += $$QMAKE_LIBS_OPENVG + +contains(QT_CONFIG, egl) { + !isEmpty(QMAKE_INCDIR_EGL): INCLUDEPATH += $$QMAKE_INCDIR_EGL + !isEmpty(QMAKE_LIBDIR_EGL): LIBS += -L$$QMAKE_LIBDIR_EGL + !isEmpty(QMAKE_LIBS_EGL): LIBS += $$QMAKE_LIBS_EGL +} + +INCLUDEPATH += ../3rdparty/harfbuzz/src diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp new file mode 100644 index 0000000..37945bf --- /dev/null +++ b/src/openvg/qpaintengine_vg.cpp @@ -0,0 +1,3231 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpaintengine_vg_p.h" +#include "qpixmapdata_vg_p.h" +#include "qpixmapfilter_vg_p.h" +#include "qvgcompositionhelper_p.h" +#if !defined(QT_NO_EGL) +#include <QtGui/private/qegl_p.h> +#include "qwindowsurface_vgegl_p.h" +#endif +#include <QtCore/qvarlengtharray.h> +#include <QtGui/private/qdrawhelper_p.h> +#include <QtGui/private/qtextureglyphcache_p.h> +#include <QtGui/private/qtextengine_p.h> +#include <QtGui/private/qfontengine_p.h> +#include <QDebug> +#include <QSet> + +QT_BEGIN_NAMESPACE + +// vgDrawGlyphs() only exists in OpenVG 1.1 and higher. +#if !defined(OPENVG_VERSION_1_1) && !defined(QVG_NO_DRAW_GLYPHS) +#define QVG_NO_DRAW_GLYPHS 1 +#endif + +// vgRenderToMask() only exists in OpenVG 1.1 and higher. +// Also, disable masking completely if we are using the scissor to clip. +#if !defined(OPENVG_VERSION_1_1) && !defined(QVG_NO_RENDER_TO_MASK) +#define QVG_NO_RENDER_TO_MASK 1 +#endif +#if defined(QVG_SCISSOR_CLIP) && !defined(QVG_NO_RENDER_TO_MASK) +#define QVG_NO_RENDER_TO_MASK 1 +#endif + +#if !defined(QVG_NO_DRAW_GLYPHS) + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +class QVGPaintEnginePrivate; + +class QVGFontGlyphCache +{ +public: + QVGFontGlyphCache(); + ~QVGFontGlyphCache(); + + void cacheGlyphs(QVGPaintEnginePrivate *d, + const QTextItemInt &ti, + const QVarLengthArray<glyph_t> &glyphs); + void setScaleFromText(const QTextItemInt &ti); + + VGFont font; + VGfloat scaleX; + VGfloat scaleY; + + uint cachedGlyphsMask[256 / 32]; + QSet<glyph_t> cachedGlyphs; +}; + +typedef QHash<QFontEngine*, QVGFontGlyphCache*> QVGFontCache; + +#endif + +class QVGFontEngineCleaner : public QObject +{ + Q_OBJECT +public: + QVGFontEngineCleaner(QVGPaintEnginePrivate *d); + ~QVGFontEngineCleaner(); + +public slots: + void fontEngineDestroyed(); + +private: + QVGPaintEnginePrivate *d_ptr; +}; + +class QVGPaintEnginePrivate : public QPaintEngineExPrivate +{ +public: + QVGPaintEnginePrivate(); + ~QVGPaintEnginePrivate(); + + void init(); + void initObjects(); + void destroy(); + void setTransform(VGMatrixMode mode, const QTransform& transform); + void updateTransform(QPaintDevice *pdev); + void draw(VGPath path, const QPen& pen, const QBrush& brush, VGint rule = VG_EVEN_ODD); + void stroke(VGPath path, const QPen& pen); + void fill(VGPath path, const QBrush& brush, VGint rule = VG_EVEN_ODD); + VGPath vectorPathToVGPath(const QVectorPath& path); + VGPath painterPathToVGPath(const QPainterPath& path); + VGPaintType setBrush + (VGPaint paint, const QBrush& brush, VGMatrixMode mode, + VGPaintType prevPaintType); + void setPenParams(const QPen& pen); + void setBrushTransform(const QBrush& brush, VGMatrixMode mode); + void setupColorRamp(const QGradient *grad, VGPaint paint); + void setImageOptions(); +#if !defined(QVG_SCISSOR_CLIP) + void ensureMask(QVGPaintEngine *engine, int width, int height); + void modifyMask + (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region); +#endif + + VGint maxScissorRects; // Maximum scissor rectangles for clipping. + + VGPaint penPaint; // Paint for currently active pen. + VGPaint brushPaint; // Paint for currently active brush. + VGPaint opacityPaint; // Paint for drawing images with opacity. + VGPaint fillPaint; // Current fill paint that is active. + + QPen currentPen; // Current pen set in "penPaint". + QBrush currentBrush; // Current brush set in "brushPaint". + + bool forcePenChange; // Force a pen change, even if the same. + bool forceBrushChange; // Force a brush change, even if the same. + + VGPaintType penType; // Type of the last pen that was set. + VGPaintType brushType; // Type of the last brush that was set. + + QPointF brushOrigin; // Current brush origin. + + VGint fillRule; // Last fill rule that was set. + + qreal opacity; // Current drawing opacity. + qreal paintOpacity; // Opacity in opacityPaint. + +#if !defined(QVG_NO_MODIFY_PATH) + VGPath rectPath; // Cached path for quick drawing of rectangles. + VGPath linePath; // Cached path for quick drawing of lines. +#endif + + QTransform transform; // Currently active transform. + bool simpleTransform; // True if the transform is simple (non-projective). + qreal penScale; // Pen scaling factor from "transform". + + QTransform pathTransform; // Calculated VG path transformation. + QTransform imageTransform; // Calculated VG image transformation. + bool pathTransformSet; // True if path transform set in the VG context. + + bool maskValid; // True if vgMask() contains valid data. + bool maskIsSet; // True if mask would be fully set if it was valid. + bool rawVG; // True if processing a raw VG escape. + + QRect maskRect; // Rectangle version of mask if it is simple. + + QTransform penTransform; // Transform for the pen. + QTransform brushTransform; // Transform for the brush. + + VGMatrixMode matrixMode; // Last matrix mode that was set. + VGImageMode imageMode; // Last image mode that was set. + + QRegion scissorRegion; // Currently active scissor region. + bool scissorActive; // True if scissor region is active. + + QPaintEngine::DirtyFlags dirty; + + QColor clearColor; // Last clear color that was set. + VGfloat clearOpacity; // Opacity during the last clear. + + VGBlendMode blendMode; // Active blend mode. + VGRenderingQuality renderingQuality; // Active rendering quality. + VGImageQuality imageQuality; // Active image quality. + +#if !defined(QVG_NO_DRAW_GLYPHS) + QVGFontCache fontCache; + QVGFontEngineCleaner *fontEngineCleaner; +#endif + + // Ensure that the path transform is properly set in the VG context + // before we perform a vgDrawPath() operation. + inline void ensurePathTransform() + { + if (!pathTransformSet) { + setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, pathTransform); + pathTransformSet = true; + } + } + + // Ensure that a specific pen has been set into penPaint. + inline void ensurePen(const QPen& pen) { + if (forcePenChange || pen != currentPen) { + currentPen = pen; + forcePenChange = false; + penType = setBrush + (penPaint, pen.brush(), + VG_MATRIX_STROKE_PAINT_TO_USER, penType); + setPenParams(pen); + } + } + + // Ensure that a specific brush has been set into brushPaint. + inline void ensureBrush(const QBrush& brush) { + if (forceBrushChange || brush != currentBrush) { + currentBrush = brush; + forceBrushChange = false; + brushType = setBrush + (brushPaint, brush, VG_MATRIX_FILL_PAINT_TO_USER, brushType); + } + if (fillPaint != brushPaint) { + vgSetPaint(brushPaint, VG_FILL_PATH); + fillPaint = brushPaint; + } + } + + // Set various modes, but only if different. + inline void setImageMode(VGImageMode mode); + inline void setRenderingQuality(VGRenderingQuality mode); + inline void setImageQuality(VGImageQuality mode); + inline void setBlendMode(VGBlendMode mode); + inline void setFillRule(VGint mode); + + // Clear all lazily-set modes. + void clearModes(); +}; + +inline void QVGPaintEnginePrivate::setImageMode(VGImageMode mode) +{ + if (imageMode != mode) { + imageMode = mode; + vgSeti(VG_IMAGE_MODE, mode); + } +} + +inline void QVGPaintEnginePrivate::setRenderingQuality(VGRenderingQuality mode) +{ + if (renderingQuality != mode) { + vgSeti(VG_RENDERING_QUALITY, mode); + renderingQuality = mode; + } +} + +inline void QVGPaintEnginePrivate::setImageQuality(VGImageQuality mode) +{ + if (imageQuality != mode) { + vgSeti(VG_IMAGE_QUALITY, mode); + imageQuality = mode; + } +} + +inline void QVGPaintEnginePrivate::setBlendMode(VGBlendMode mode) +{ + if (blendMode != mode) { + vgSeti(VG_BLEND_MODE, mode); + blendMode = mode; + } +} + +inline void QVGPaintEnginePrivate::setFillRule(VGint mode) +{ + if (fillRule != mode) { + fillRule = mode; + vgSeti(VG_FILL_RULE, mode); + } +} + +void QVGPaintEnginePrivate::clearModes() +{ + matrixMode = (VGMatrixMode)0; + imageMode = (VGImageMode)0; + blendMode = (VGBlendMode)0; + renderingQuality = (VGRenderingQuality)0; + imageQuality = (VGImageQuality)0; +} + +QVGPaintEnginePrivate::QVGPaintEnginePrivate() +{ + init(); +} + +void QVGPaintEnginePrivate::init() +{ + maxScissorRects = 0; + + penPaint = 0; + brushPaint = 0; + opacityPaint = 0; + fillPaint = 0; + + forcePenChange = true; + forceBrushChange = true; + penType = (VGPaintType)0; + brushType = (VGPaintType)0; + + brushOrigin = QPointF(0.0f, 0.0f); + + fillRule = 0; + + opacity = 1.0; + paintOpacity = 1.0f; + +#if !defined(QVG_NO_MODIFY_PATH) + rectPath = 0; + linePath = 0; +#endif + + simpleTransform = true; + pathTransformSet = false; + penScale = 1.0; + + maskValid = false; + maskIsSet = false; + rawVG = false; + + scissorActive = false; + + dirty = 0; + + clearOpacity = 1.0f; + +#if !defined(QVG_NO_DRAW_GLYPHS) + fontEngineCleaner = 0; +#endif + + clearModes(); +} + +QVGPaintEnginePrivate::~QVGPaintEnginePrivate() +{ + destroy(); +} + +void QVGPaintEnginePrivate::initObjects() +{ + maxScissorRects = vgGeti(VG_MAX_SCISSOR_RECTS); + + penPaint = vgCreatePaint(); + vgSetParameteri(penPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetPaint(penPaint, VG_STROKE_PATH); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); + vgLoadIdentity(); + + brushPaint = vgCreatePaint(); + vgSetParameteri(brushPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetPaint(brushPaint, VG_FILL_PATH); + fillPaint = brushPaint; + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); + vgLoadIdentity(); + matrixMode = VG_MATRIX_FILL_PAINT_TO_USER; + + opacityPaint = vgCreatePaint(); + vgSetParameteri(opacityPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + VGfloat values[4]; + values[0] = 1.0f; + values[1] = 1.0f; + values[2] = 1.0f; + values[3] = paintOpacity; + vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values); + +#if !defined(QVG_NO_MODIFY_PATH) + // Create a dummy path for rectangle drawing, which we can + // modify later with vgModifyPathCoords(). This should be + // faster than constantly creating and destroying paths. + rectPath = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + 5, // segmentCapacityHint + 8, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + static VGubyte const segments[5] = { + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + VGfloat coords[8]; + coords[0] = 0.0f; + coords[1] = 0.0f; + coords[2] = 100.0f; + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = 100.0f; + coords[6] = coords[0]; + coords[7] = coords[5]; + vgAppendPathData(rectPath, 5, segments, coords); + + // Create a dummy line drawing path as well. + linePath = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + 2, // segmentCapacityHint + 4, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + vgAppendPathData(linePath, 2, segments, coords); +#endif +} + +void QVGPaintEnginePrivate::destroy() +{ + if (penPaint) + vgDestroyPaint(penPaint); + if (brushPaint) + vgDestroyPaint(brushPaint); + if (opacityPaint) + vgDestroyPaint(opacityPaint); + +#if !defined(QVG_NO_MODIFY_PATH) + if (rectPath) + vgDestroyPath(rectPath); + if (linePath) + vgDestroyPath(linePath); +#endif + +#if !defined(QVG_NO_DRAW_GLYPHS) + QVGFontCache::Iterator it; + for (it = fontCache.begin(); it != fontCache.end(); ++it) + delete it.value(); + fontCache.clear(); + delete fontEngineCleaner; +#endif +} + +// Set a specific VG transformation matrix in the current VG context. +void QVGPaintEnginePrivate::setTransform + (VGMatrixMode mode, const QTransform& transform) +{ + VGfloat mat[9]; + if (mode != matrixMode) { + vgSeti(VG_MATRIX_MODE, mode); + matrixMode = mode; + } + mat[0] = transform.m11(); + mat[1] = transform.m12(); + mat[2] = transform.m13(); + mat[3] = transform.m21(); + mat[4] = transform.m22(); + mat[5] = transform.m23(); + mat[6] = transform.m31(); + mat[7] = transform.m32(); + mat[8] = transform.m33(); + vgLoadMatrix(mat); +} + +extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); + +void QVGPaintEnginePrivate::updateTransform(QPaintDevice *pdev) +{ + VGfloat devh = pdev->height() - 1; + + // Construct the VG transform by combining the Qt transform with + // the following viewport transformation: + // | 1 0 0 | | 1 0 0.5 | | 1 0 0.5 | + // | 0 -1 devh | * | 0 1 -0.5 | = | 0 -1 (0.5 + devh) | + // | 0 0 1 | | 0 0 1 | | 0 0 1 | + // The full VG transform is effectively: + // 1. Apply the user's transformation matrix. + // 2. Translate by (0.5, -0.5) to correct for Qt and VG putting + // the centre of the pixel at different positions. + // 3. Flip the co-ordinate system upside down. + QTransform viewport(1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.5f, devh + 0.5f, 1.0f); + + // The image transform is always the full transformation, + // because it can be projective. + imageTransform = transform * viewport; + + // Determine if the transformation is projective. + bool projective = (imageTransform.m13() != 0.0f || + imageTransform.m23() != 0.0f || + imageTransform.m33() != 1.0f); + if (projective) { + // The engine cannot do projective path transforms for us, + // so we will have to convert the co-ordinates ourselves. + // Change the matrix to just the viewport transformation. + pathTransform = viewport; + simpleTransform = false; + } else { + pathTransform = imageTransform; + simpleTransform = true; + } + pathTransformSet = false; + + // Calculate the scaling factor to use for turning cosmetic pens + // into ordinary non-cosmetic pens. + qt_scaleForTransform(transform, &penScale); +} + +VGPath QVGPaintEnginePrivate::vectorPathToVGPath(const QVectorPath& path) +{ + int count = path.elementCount(); + const qreal *points = path.points(); + const QPainterPath::ElementType *elements = path.elements(); + + VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + count + 1, // segmentCapacityHint + count * 2, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + + // Size is sufficient segments for drawRoundedRect() paths. + QVarLengthArray<VGubyte, 20> segments; + + if (sizeof(qreal) == sizeof(VGfloat) && elements && simpleTransform) { + // If Qt was compiled with qreal the same size as VGfloat, + // then convert the segment types and use the incoming + // points array directly. + for (int i = 0; i < count; ++i) { + switch (elements[i]) { + + case QPainterPath::MoveToElement: + segments.append(VG_MOVE_TO_ABS); break; + + case QPainterPath::LineToElement: + segments.append(VG_LINE_TO_ABS); break; + + case QPainterPath::CurveToElement: break; + + case QPainterPath::CurveToDataElement: + segments.append(VG_CUBIC_TO_ABS); break; + + } + } + if (path.hasImplicitClose()) + segments.append(VG_CLOSE_PATH); + + vgAppendPathData(vgpath, segments.count(), segments.constData(), + reinterpret_cast<const VGfloat *>(points)); + + return vgpath; + } + + // Sizes chosen so that drawRoundedRect() paths fit in these arrays. + QVarLengthArray<VGfloat, 48> coords; + + int curvePos = 0; + QPointF temp; + + if (elements && simpleTransform) { + // Convert the members of the element array. + for (int i = 0; i < count; ++i) { + switch (elements[i]) { + + case QPainterPath::MoveToElement: + { + coords.append(points[0]); + coords.append(points[1]); + segments.append(VG_MOVE_TO_ABS); + } + break; + + case QPainterPath::LineToElement: + { + coords.append(points[0]); + coords.append(points[1]); + segments.append(VG_LINE_TO_ABS); + } + break; + + case QPainterPath::CurveToElement: + { + coords.append(points[0]); + coords.append(points[1]); + curvePos = 2; + } + break; + + case QPainterPath::CurveToDataElement: + { + coords.append(points[0]); + coords.append(points[1]); + curvePos += 2; + if (curvePos == 6) { + curvePos = 0; + segments.append(VG_CUBIC_TO_ABS); + } + } + break; + + } + points += 2; + } + } else if (elements && !simpleTransform) { + // Convert the members of the element array after applying the + // current transform to the path locally. + for (int i = 0; i < count; ++i) { + switch (elements[i]) { + + case QPainterPath::MoveToElement: + { + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + segments.append(VG_MOVE_TO_ABS); + } + break; + + case QPainterPath::LineToElement: + { + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + segments.append(VG_LINE_TO_ABS); + } + break; + + case QPainterPath::CurveToElement: + { + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + curvePos = 2; + } + break; + + case QPainterPath::CurveToDataElement: + { + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + curvePos += 2; + if (curvePos == 6) { + curvePos = 0; + segments.append(VG_CUBIC_TO_ABS); + } + } + break; + + } + points += 2; + } + } else if (count > 0 && simpleTransform) { + // If there is no element array, then the path is assumed + // to be a MoveTo followed by several LineTo's. + coords.append(points[0]); + coords.append(points[1]); + segments.append(VG_MOVE_TO_ABS); + while (count > 1) { + points += 2; + coords.append(points[0]); + coords.append(points[1]); + segments.append(VG_LINE_TO_ABS); + --count; + } + } else if (count > 0 && !simpleTransform) { + // Convert a simple path, and apply the transform locally. + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + segments.append(VG_MOVE_TO_ABS); + while (count > 1) { + points += 2; + temp = transform.map(QPointF(points[0], points[1])); + coords.append(temp.x()); + coords.append(temp.y()); + segments.append(VG_LINE_TO_ABS); + --count; + } + } + + // Close the path if specified. + if (path.hasImplicitClose()) + segments.append(VG_CLOSE_PATH); + + vgAppendPathData(vgpath, segments.count(), + segments.constData(), coords.constData()); + + return vgpath; +} + +VGPath QVGPaintEnginePrivate::painterPathToVGPath(const QPainterPath& path) +{ + int count = path.elementCount(); + + VGPath vgpath = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + count + 1, // segmentCapacityHint + count * 2, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + + if (count == 0) + return vgpath; + + const QPainterPath::Element *elements = &(path.elementAt(0)); + + // Sizes chosen so that drawRoundedRect() paths fit in these arrays. + QVarLengthArray<VGfloat, 48> coords; + QVarLengthArray<VGubyte, 20> segments; + + int curvePos = 0; + QPointF temp; + + // Keep track of the start and end of each sub-path. QPainterPath + // does not have an "implicit close" flag like QVectorPath does. + // We therefore have to detect closed paths by looking for a LineTo + // element that connects back to the initial MoveTo element. + qreal startx = 0.0; + qreal starty = 0.0; + qreal endx = 0.0; + qreal endy = 0.0; + bool haveStart = false; + bool haveEnd = false; + + if (simpleTransform) { + // Convert the members of the element array. + for (int i = 0; i < count; ++i) { + switch (elements[i].type) { + + case QPainterPath::MoveToElement: + { + if (haveStart && haveEnd && startx == endx && starty == endy) { + // Implicitly close the previous sub-path. + segments.append(VG_CLOSE_PATH); + } + startx = elements[i].x; + starty = elements[i].y; + coords.append(startx); + coords.append(starty); + haveStart = true; + haveEnd = false; + segments.append(VG_MOVE_TO_ABS); + } + break; + + case QPainterPath::LineToElement: + { + endx = elements[i].x; + endy = elements[i].y; + coords.append(endx); + coords.append(endy); + haveEnd = true; + segments.append(VG_LINE_TO_ABS); + } + break; + + case QPainterPath::CurveToElement: + { + coords.append(elements[i].x); + coords.append(elements[i].y); + haveEnd = false; + curvePos = 2; + } + break; + + case QPainterPath::CurveToDataElement: + { + coords.append(elements[i].x); + coords.append(elements[i].y); + haveEnd = false; + curvePos += 2; + if (curvePos == 6) { + curvePos = 0; + segments.append(VG_CUBIC_TO_ABS); + } + } + break; + + } + } + } else { + // Convert the members of the element array after applying the + // current transform to the path locally. + for (int i = 0; i < count; ++i) { + switch (elements[i].type) { + + case QPainterPath::MoveToElement: + { + if (haveStart && haveEnd && startx == endx && starty == endy) { + // Implicitly close the previous sub-path. + segments.append(VG_CLOSE_PATH); + } + temp = transform.map(QPointF(elements[i].x, elements[i].y)); + startx = temp.x(); + starty = temp.y(); + coords.append(startx); + coords.append(starty); + haveStart = true; + haveEnd = false; + segments.append(VG_MOVE_TO_ABS); + } + break; + + case QPainterPath::LineToElement: + { + temp = transform.map(QPointF(elements[i].x, elements[i].y)); + endx = temp.x(); + endy = temp.y(); + coords.append(endx); + coords.append(endy); + haveEnd = true; + segments.append(VG_LINE_TO_ABS); + } + break; + + case QPainterPath::CurveToElement: + { + temp = transform.map(QPointF(elements[i].x, elements[i].y)); + coords.append(temp.x()); + coords.append(temp.y()); + haveEnd = false; + curvePos = 2; + } + break; + + case QPainterPath::CurveToDataElement: + { + temp = transform.map(QPointF(elements[i].x, elements[i].y)); + coords.append(temp.x()); + coords.append(temp.y()); + haveEnd = false; + curvePos += 2; + if (curvePos == 6) { + curvePos = 0; + segments.append(VG_CUBIC_TO_ABS); + } + } + break; + + } + } + } + + if (haveStart && haveEnd && startx == endx && starty == endy) { + // Implicitly close the last sub-path. + segments.append(VG_CLOSE_PATH); + } + + vgAppendPathData(vgpath, segments.count(), + segments.constData(), coords.constData()); + + return vgpath; +} + +extern QImage qt_imageForBrush(int style, bool invert); + +static QImage colorizeBitmap(const QImage &image, const QColor &color) +{ + QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB); + QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied); + + QRgb fg = PREMUL(color.rgba()); + QRgb bg = 0; + + int height = sourceImage.height(); + int width = sourceImage.width(); + for (int y=0; y<height; ++y) { + uchar *source = sourceImage.scanLine(y); + QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y)); + for (int x=0; x < width; ++x) + target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg; + } + return dest; +} + +static VGImage toVGImage + (const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor) +{ + QImage img(image); + + VGImageFormat format; + switch (img.format()) { + case QImage::Format_Mono: + img = image.convertToFormat(QImage::Format_MonoLSB, flags); + format = VG_BW_1; + break; + case QImage::Format_MonoLSB: + format = VG_BW_1; + break; + case QImage::Format_RGB32: + format = VG_sXRGB_8888; + break; + case QImage::Format_ARGB32: + format = VG_sARGB_8888; + break; + case QImage::Format_ARGB32_Premultiplied: + format = VG_sARGB_8888_PRE; + break; + case QImage::Format_RGB16: + format = VG_sRGB_565; + break; + default: + // Convert everything else into ARGB32_Premultiplied. + img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags); + format = VG_sARGB_8888_PRE; + break; + } + + const uchar *pixels = img.bits(); + + VGImage vgImg = vgCreateImage + (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData + (vgImg, pixels, img.bytesPerLine(), format, 0, 0, + img.width(), img.height()); + + return vgImg; +} + +static VGImage toVGImageSubRect + (const QImage & image, const QRect& sr, + Qt::ImageConversionFlags flags = Qt::AutoColor) +{ + QImage img(image); + + VGImageFormat format; + int bpp = 4; + + switch (img.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + return VG_INVALID_HANDLE; + case QImage::Format_RGB32: + format = VG_sXRGB_8888; + break; + case QImage::Format_ARGB32: + format = VG_sARGB_8888; + break; + case QImage::Format_ARGB32_Premultiplied: + format = VG_sARGB_8888_PRE; + break; + case QImage::Format_RGB16: + format = VG_sRGB_565; + bpp = 2; + break; + default: + // Convert everything else into ARGB32_Premultiplied. + img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied, flags); + format = VG_sARGB_8888_PRE; + break; + } + + const uchar *pixels = img.bits() + bpp * sr.x() + + img.bytesPerLine() * sr.y(); + + VGImage vgImg = vgCreateImage + (format, sr.width(), sr.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData + (vgImg, pixels, img.bytesPerLine(), format, 0, 0, + sr.width(), sr.height()); + + return vgImg; +} + +static VGImage toVGImageWithOpacity(const QImage & image, qreal opacity) +{ + QImage img(image.size(), QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter painter; + painter.begin(&img); + painter.setOpacity(opacity); + painter.drawImage(0, 0, image); + painter.end(); + + const uchar *pixels = img.bits(); + + VGImage vgImg = vgCreateImage + (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData + (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, + img.width(), img.height()); + + return vgImg; +} + +static VGImage toVGImageWithOpacitySubRect + (const QImage & image, qreal opacity, const QRect& sr) +{ + QImage img(sr.size(), QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter painter; + painter.begin(&img); + painter.setOpacity(opacity); + painter.drawImage(QPoint(0, 0), image, sr); + painter.end(); + + const uchar *pixels = img.bits(); + + VGImage vgImg = vgCreateImage + (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData + (vgImg, pixels, img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, + img.width(), img.height()); + + return vgImg; +} + +VGPaintType QVGPaintEnginePrivate::setBrush + (VGPaint paint, const QBrush& brush, VGMatrixMode mode, + VGPaintType prevType) +{ + VGfloat values[5]; + setBrushTransform(brush, mode); + + // Reset the paint pattern on the brush, which will discard + // the previous VGImage if one was set. + if (prevType == VG_PAINT_TYPE_PATTERN || prevType == (VGPaintType)0) + vgPaintPattern(paint, VG_INVALID_HANDLE); + + switch (brush.style()) { + + case Qt::SolidPattern: { + // The brush is a solid color. + QColor color(brush.color()); + values[0] = color.redF(); + values[1] = color.greenF(); + values[2] = color.blueF(); + values[3] = color.alphaF() * opacity; + if (prevType != VG_PAINT_TYPE_COLOR) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values); + return VG_PAINT_TYPE_COLOR; + } + + case Qt::LinearGradientPattern: { + // The brush is a linear gradient. + Q_ASSERT(brush.gradient()->type() == QGradient::LinearGradient); + const QLinearGradient *grad = + static_cast<const QLinearGradient*>(brush.gradient()); + values[0] = grad->start().x(); + values[1] = grad->start().y(); + values[2] = grad->finalStop().x(); + values[3] = grad->finalStop().y(); + if (prevType != VG_PAINT_TYPE_LINEAR_GRADIENT) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, values); + setupColorRamp(grad, paint); + return VG_PAINT_TYPE_LINEAR_GRADIENT; + } + + case Qt::RadialGradientPattern: { + // The brush is a radial gradient. + Q_ASSERT(brush.gradient()->type() == QGradient::RadialGradient); + const QRadialGradient *grad = + static_cast<const QRadialGradient*>(brush.gradient()); + values[0] = grad->center().x(); + values[1] = grad->center().y(); + values[2] = grad->focalPoint().x(); + values[3] = grad->focalPoint().y(); + values[4] = grad->radius(); + if (prevType != VG_PAINT_TYPE_RADIAL_GRADIENT) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); + vgSetParameterfv(paint, VG_PAINT_RADIAL_GRADIENT, 5, values); + setupColorRamp(grad, paint); + return VG_PAINT_TYPE_RADIAL_GRADIENT; + } + + case Qt::TexturePattern: { + // The brush is a texture specified by a QPixmap/QImage. + QPixmapData *pd = brush.texture().pixmapData(); + VGImage vgImg; + if (pd->pixelType() == QPixmapData::BitmapType) { + // Colorize bitmaps using the brush color and opacity. + QColor color = brush.color(); + if (opacity != 1.0) + color.setAlphaF(color.alphaF() * opacity); + QImage image = colorizeBitmap(*(pd->buffer()), color); + vgImg = toVGImage(image); + } else if (opacity == 1.0) { + if (pd->classId() == QPixmapData::OpenVGClass) { + QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); + vgImg = vgpd->toVGImage(); + } else { + vgImg = toVGImage(*(pd->buffer())); + } + } else { + vgImg = toVGImageWithOpacity(*(pd->buffer()), opacity); + } + if (vgImg == VG_INVALID_HANDLE) + break; + if (prevType != VG_PAINT_TYPE_PATTERN) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); + vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT); + vgPaintPattern(paint, vgImg); + vgDestroyImage(vgImg); // Will stay valid until pattern is destroyed. + return VG_PAINT_TYPE_PATTERN; + } + + case Qt::ConicalGradientPattern: { + // Convert conical gradients into the first stop color. + qWarning() << "QVGPaintEnginePrivate::setBrush: conical gradients are not supported by OpenVG"; + Q_ASSERT(brush.gradient()->type() == QGradient::ConicalGradient); + const QConicalGradient *grad = + static_cast<const QConicalGradient*>(brush.gradient()); + const QGradientStops stops = grad->stops(); + QColor color; + if (stops.size() > 0) + color = stops[0].second; + values[0] = color.redF(); + values[1] = color.greenF(); + values[2] = color.blueF(); + values[3] = color.alphaF() * opacity; + if (prevType != VG_PAINT_TYPE_COLOR) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(paint, VG_PAINT_COLOR, 4, values); + return VG_PAINT_TYPE_COLOR; + } + + case Qt::Dense1Pattern: + case Qt::Dense2Pattern: + case Qt::Dense3Pattern: + case Qt::Dense4Pattern: + case Qt::Dense5Pattern: + case Qt::Dense6Pattern: + case Qt::Dense7Pattern: + case Qt::HorPattern: + case Qt::VerPattern: + case Qt::CrossPattern: + case Qt::BDiagPattern: + case Qt::FDiagPattern: + case Qt::DiagCrossPattern: { + // The brush is a traditional dotted or cross-hatched pattern brush. + QColor color = brush.color(); + if (opacity != 1.0) + color.setAlphaF(color.alphaF() * opacity); + QImage image = colorizeBitmap + (qt_imageForBrush(brush.style(), true), color); + VGImage vgImg = toVGImage(image); + if (prevType != VG_PAINT_TYPE_PATTERN) + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); + vgSetParameteri(paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT); + vgPaintPattern(paint, vgImg); + vgDestroyImage(vgImg); // Will stay valid until pattern is destroyed. + return VG_PAINT_TYPE_PATTERN; + } + + default: break; + } + return (VGPaintType)0; +} + +void QVGPaintEnginePrivate::setPenParams(const QPen& pen) +{ + // Note: OpenVG does not support zero-width or cosmetic pens, + // so we have to simulate cosmetic pens by reversing the scale. + VGfloat width = pen.widthF(); + if (width <= 0.0f) + width = 1.0f; + if (pen.isCosmetic()) { + if (penScale != 1.0 && penScale != 0.0) + width /= penScale; + } + vgSetf(VG_STROKE_LINE_WIDTH, width); + + if (pen.capStyle() == Qt::FlatCap) + vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_BUTT); + else if (pen.capStyle() == Qt::SquareCap) + vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_SQUARE); + else + vgSetf(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); + + if (pen.joinStyle() == Qt::MiterJoin) { + vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER); + vgSetf(VG_STROKE_MITER_LIMIT, pen.miterLimit()); + } else if (pen.joinStyle() == Qt::BevelJoin) { + vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL); + } else { + vgSetf(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); + } + + if (pen.style() == Qt::SolidLine) { + vgSetfv(VG_STROKE_DASH_PATTERN, 0, NULL); + } else { + const QVector<qreal> dashPattern = pen.dashPattern(); + QVector<VGfloat> currentDashPattern(dashPattern.count()); + for (int i = 0; i < dashPattern.count(); ++i) + currentDashPattern[i] = dashPattern[i] * width; + vgSetfv(VG_STROKE_DASH_PATTERN, currentDashPattern.count(), currentDashPattern.data()); + vgSetf(VG_STROKE_DASH_PHASE, pen.dashOffset()); + vgSetf(VG_STROKE_DASH_PHASE_RESET, VG_FALSE); + } +} + +void QVGPaintEnginePrivate::setBrushTransform + (const QBrush& brush, VGMatrixMode mode) +{ + // Compute the new brush transformation matrix. + QTransform transform(brush.transform()); + if (brushOrigin.x() != 0.0f || brushOrigin.y() != 0.0f) + transform.translate(brushOrigin.x(), brushOrigin.y()); + + // Bail out if the matrix is the same as last time, to avoid + // updating the VG context state unless absolutely necessary. + // Most applications won't have a brush transformation set, + // which will leave the VG setting at its default of identity. + // Always change the transform if coming out of raw VG mode. + if (mode == VG_MATRIX_FILL_PAINT_TO_USER) { + if (!rawVG && transform == brushTransform) + return; + brushTransform = transform; + } else { + if (!rawVG && transform == penTransform) + return; + penTransform = transform; + } + + // Set the brush transformation matrix. + if (mode != matrixMode) { + vgSeti(VG_MATRIX_MODE, mode); + matrixMode = mode; + } + if (transform.isIdentity()) { + vgLoadIdentity(); + } else { + VGfloat mat[9]; + mat[0] = transform.m11(); + mat[1] = transform.m12(); + mat[2] = transform.m13(); + mat[3] = transform.m21(); + mat[4] = transform.m22(); + mat[5] = transform.m23(); + mat[6] = transform.m31(); + mat[7] = transform.m32(); + mat[8] = transform.m33(); + vgLoadMatrix(mat); + } +} + +void QVGPaintEnginePrivate::setupColorRamp(const QGradient *grad, VGPaint paint) +{ + QGradient::Spread spread = grad->spread(); + VGColorRampSpreadMode spreadMode; + if (spread == QGradient::ReflectSpread) + spreadMode = VG_COLOR_RAMP_SPREAD_REFLECT; + else if (spread == QGradient::RepeatSpread) + spreadMode = VG_COLOR_RAMP_SPREAD_REPEAT; + else + spreadMode = VG_COLOR_RAMP_SPREAD_PAD; + + const QGradientStops stops = grad->stops(); + int n = 5*stops.size(); + QVector<VGfloat> fill_stops(n); + + for (int i = 0; i < stops.size(); ++i ) { + QColor col = stops[i].second; + fill_stops[i*5] = stops[i].first; + fill_stops[i*5 + 1] = col.redF(); + fill_stops[i*5 + 2] = col.greenF(); + fill_stops[i*5 + 3] = col.blueF(); + fill_stops[i*5 + 4] = col.alphaF() * opacity; + } + + vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, spreadMode); + vgSetParameteri(paint, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, VG_FALSE); + vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, n, fill_stops.data()); +} + +QVGPainterState::QVGPainterState(QVGPainterState& other) + : QPainterState(other), + isNew(true), clipRegion(other.clipRegion), + savedDirty(0) +{ +} + +QVGPainterState::QVGPainterState() + : isNew(true), savedDirty(0) +{ +} + +QVGPainterState::~QVGPainterState() +{ +} + +QVGPaintEngine::QVGPaintEngine() + : QPaintEngineEx(*new QVGPaintEnginePrivate) +{ +} + +QVGPaintEngine::QVGPaintEngine(QVGPaintEnginePrivate &data) + : QPaintEngineEx(data) +{ +} + +QVGPaintEngine::~QVGPaintEngine() +{ +} + +QPainterState *QVGPaintEngine::createState(QPainterState *orig) const +{ + if (!orig) { + return new QVGPainterState(); + } else { + QVGPaintEnginePrivate *d = + static_cast<QVGPaintEnginePrivate *>(d_ptr); + QVGPainterState *origState = static_cast<QVGPainterState *>(orig); + origState->savedDirty = d->dirty; + d->dirty = 0; + return new QVGPainterState(*origState); + } +} + +void QVGPaintEnginePrivate::draw + (VGPath path, const QPen& pen, const QBrush& brush, VGint rule) +{ + VGbitfield mode = 0; + if (pen.style() != Qt::NoPen) { + ensurePen(pen); + mode |= VG_STROKE_PATH; + } + if (brush.style() != Qt::NoBrush) { + ensureBrush(brush); + setFillRule(rule); + mode |= VG_FILL_PATH; + } + if (mode != 0) { + ensurePathTransform(); + vgDrawPath(path, mode); + } +} + +void QVGPaintEnginePrivate::stroke(VGPath path, const QPen& pen) +{ + if (pen.style() == Qt::NoPen) + return; + ensurePen(pen); + ensurePathTransform(); + vgDrawPath(path, VG_STROKE_PATH); +} + +void QVGPaintEnginePrivate::fill(VGPath path, const QBrush& brush, VGint rule) +{ + if (brush.style() == Qt::NoBrush) + return; + ensureBrush(brush); + setFillRule(rule); + ensurePathTransform(); + vgDrawPath(path, VG_FILL_PATH); +} + +bool QVGPaintEngine::begin(QPaintDevice *pdev) +{ + Q_UNUSED(pdev); + Q_D(QVGPaintEngine); + + // Initialize the VG painting objects if we haven't done it yet. + if (!d->penPaint) + d->initObjects(); + + // The initial clip region is the entire device area. + QVGPainterState *s = state(); + s->clipRegion = defaultClipRegion(); + + // Initialize the VG state for this paint operation. + restoreState(QPaintEngine::AllDirty); + d->dirty = 0; + d->rawVG = false; + return true; +} + +bool QVGPaintEngine::end() +{ + return true; +} + +void QVGPaintEngine::draw(const QVectorPath &path) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + VGPath vgpath = d->vectorPathToVGPath(path); + if (!path.hasWindingFill()) + d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD); + else + d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO); + vgDestroyPath(vgpath); +} + +void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QVGPaintEngine); + VGPath vgpath = d->vectorPathToVGPath(path); + if (!path.hasWindingFill()) + d->fill(vgpath, brush, VG_EVEN_ODD); + else + d->fill(vgpath, brush, VG_NON_ZERO); + vgDestroyPath(vgpath); +} + +void QVGPaintEngine::stroke(const QVectorPath &path, const QPen &pen) +{ + Q_D(QVGPaintEngine); + VGPath vgpath = d->vectorPathToVGPath(path); + d->stroke(vgpath, pen); + vgDestroyPath(vgpath); +} + +// Determine if a co-ordinate transform is simple enough to allow +// rectangle-based clipping with vgMask(). Simple transforms most +// often result from origin translations. +static inline bool clipTransformIsSimple(const QTransform& transform) +{ + QTransform::TransformationType type = transform.type(); + return (type == QTransform::TxNone || type == QTransform::TxTranslate); +} + +#if defined(QVG_SCISSOR_CLIP) + +void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + if (op == Qt::NoClip) { + s->clipRegion = defaultClipRegion(); + updateScissor(); + return; + } + + // We aren't using masking, so handle simple QRectF's only. + if (path.shape() == QVectorPath::RectangleHint && + path.elementCount() == 4 && clipTransformIsSimple(d->transform)) { + // Clipping region that resulted from QPainter::setClipRect(QRectF). + // Convert it into a QRect and apply. + const qreal *points = path.points(); + QRectF rect(points[0], points[1], points[2] - points[0], + points[5] - points[1]); + clip(rect.toRect(), op); + } +} + +void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + // If we have a non-simple transform, then use path-based clipping. + if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) { + QPaintEngineEx::clip(rect, op); + return; + } + + switch (op) { + case Qt::NoClip: + { + s->clipRegion = defaultClipRegion(); + } + break; + + case Qt::ReplaceClip: + { + s->clipRegion = d->transform.map(QRegion(rect)); + } + break; + + case Qt::IntersectClip: + { + s->clipRegion = s->clipRegion.intersect(d->transform.map(QRegion(rect))); + } + break; + + case Qt::UniteClip: + { + s->clipRegion = s->clipRegion.unite(d->transform.map(QRegion(rect))); + } + break; + } + + updateScissor(); +} + +void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + // If we have a non-simple transform, then use path-based clipping. + if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) { + QPaintEngineEx::clip(region, op); + return; + } + + switch (op) { + case Qt::NoClip: + { + s->clipRegion = defaultClipRegion(); + } + break; + + case Qt::ReplaceClip: + { + s->clipRegion = d->transform.map(region); + } + break; + + case Qt::IntersectClip: + { + s->clipRegion = s->clipRegion.intersect(d->transform.map(region)); + } + break; + + case Qt::UniteClip: + { + s->clipRegion = s->clipRegion.unite(d->transform.map(region)); + } + break; + } + + updateScissor(); +} + +void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op) +{ + QPaintEngineEx::clip(path, op); +} + +#else // !QVG_SCISSOR_CLIP + +void QVGPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QVGPaintEngine); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + if (op == Qt::NoClip) { + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + vgSeti(VG_MASKING, VG_FALSE); + return; + } + +#if defined(QVG_NO_RENDER_TO_MASK) + // We don't have vgRenderToMask(), so handle simple QRectF's only. + if (path.shape() == QVectorPath::RectangleHint && + path.elementCount() == 4 && clipTransformIsSimple(d->transform)) { + // Clipping region that resulted from QPainter::setClipRect(QRectF). + // Convert it into a QRect and apply. + const qreal *points = path.points(); + QRectF rect(points[0], points[1], points[2] - points[0], + points[5] - points[1]); + clip(rect.toRect(), op); + } +#else + QPaintDevice *pdev = paintDevice(); + int width = pdev->width(); + int height = pdev->height(); + + if (op == Qt::ReplaceClip) { + vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height); + d->maskRect = QRect(); + } else if (!d->maskValid) { + d->ensureMask(this, width, height); + } + + d->ensurePathTransform(); + VGPath vgpath = d->vectorPathToVGPath(path); + switch (op) { + case Qt::ReplaceClip: + case Qt::UniteClip: + vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK); + break; + + case Qt::IntersectClip: + vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK); + break; + + default: break; + } + vgDestroyPath(vgpath); + + vgSeti(VG_MASKING, VG_TRUE); + d->maskValid = true; + d->maskIsSet = false; +#endif +} + +void QVGPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) +{ + clip(QRegion(rect), op); +} + +void QVGPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ + Q_D(QVGPaintEngine); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + // If we have a non-simple transform, then use path-based clipping. + if (op != Qt::NoClip && !clipTransformIsSimple(d->transform)) { + QPaintEngineEx::clip(region, op); + return; + } + + switch (op) { + case Qt::NoClip: + { + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + vgSeti(VG_MASKING, VG_FALSE); + } + break; + + case Qt::ReplaceClip: + { + QRegion r = d->transform.map(region); + if (isDefaultClipRegion(r)) { + // Replacing the clip with a full-window region is the + // same as turning off clipping. + if (d->maskValid) + vgSeti(VG_MASKING, VG_FALSE); + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + } else { + // Special case: if the intersection of the system + // clip and the region is a single rectangle, then + // use the scissor for clipping. + QRegion clip = d->systemClip; + if (clip.isEmpty()) + clip = r; + else + clip = clip.intersect(r); + if (r.numRects() == 1) { + d->maskValid = false; + d->maskIsSet = false; + d->maskRect = r.boundingRect(); + vgSeti(VG_MASKING, VG_FALSE); + updateScissor(); + } else { + d->maskValid = false; + d->maskIsSet = false; + d->maskRect = QRect(); + d->modifyMask(this, VG_FILL_MASK, r); + } + } + } + break; + + case Qt::IntersectClip: + { + if (region.numRects() != 1) { + // If there is more than one rectangle, then intersecting + // the rectangles one by one in modifyMask() will not give + // the desired result. So fall back to path-based clipping. + QPaintEngineEx::clip(region, op); + return; + } + QRegion r = d->transform.map(region); + if (d->maskIsSet && isDefaultClipRegion(r)) { + // Intersecting a full-window clip with a full-window + // region is the same as turning off clipping. + if (d->maskValid) + vgSeti(VG_MASKING, VG_FALSE); + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + } else { + d->modifyMask(this, VG_INTERSECT_MASK, r); + } + } + break; + + case Qt::UniteClip: + { + // If we already have a full-window clip, then uniting a + // region with it will do nothing. Otherwise union. + if (!(d->maskIsSet)) + d->modifyMask(this, VG_UNION_MASK, d->transform.map(region)); + } + break; + } +} + +void QVGPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op) +{ +#if !defined(QVG_NO_RENDER_TO_MASK) + Q_D(QVGPaintEngine); + + d->dirty |= QPaintEngine::DirtyClipRegion; + + if (op == Qt::NoClip) { + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + vgSeti(VG_MASKING, VG_FALSE); + return; + } + + QPaintDevice *pdev = paintDevice(); + int width = pdev->width(); + int height = pdev->height(); + + if (op == Qt::ReplaceClip) { + vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height); + d->maskRect = QRect(); + } else if (!d->maskValid) { + d->ensureMask(this, width, height); + } + + d->ensurePathTransform(); + VGPath vgpath = d->painterPathToVGPath(path); + switch (op) { + case Qt::ReplaceClip: + case Qt::UniteClip: + vgRenderToMask(vgpath, VG_FILL_PATH, VG_UNION_MASK); + break; + + case Qt::IntersectClip: + vgRenderToMask(vgpath, VG_FILL_PATH, VG_INTERSECT_MASK); + break; + + default: break; + } + vgDestroyPath(vgpath); + + vgSeti(VG_MASKING, VG_TRUE); + d->maskValid = true; + d->maskIsSet = false; +#else + QPaintEngineEx::clip(path, op); +#endif +} + +void QVGPaintEnginePrivate::ensureMask + (QVGPaintEngine *engine, int width, int height) +{ + if (maskIsSet) { + vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, width, height); + maskRect = QRect(); + } else { + vgMask(VG_INVALID_HANDLE, VG_CLEAR_MASK, 0, 0, width, height); + if (!maskRect.isNull()) { + vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, + maskRect.x(), height - maskRect.y() - maskRect.height(), + maskRect.width(), maskRect.height()); + maskRect = QRect(); + engine->updateScissor(); + } + } +} + +void QVGPaintEnginePrivate::modifyMask + (QVGPaintEngine *engine, VGMaskOperation op, const QRegion& region) +{ + QPaintDevice *pdev = engine->paintDevice(); + int width = pdev->width(); + int height = pdev->height(); + + if (!maskValid) + ensureMask(engine, width, height); + + QVector<QRect> rects = region.rects(); + for (int i = 0; i < rects.size(); ++i) { + vgMask(VG_INVALID_HANDLE, op, + rects[i].x(), height - rects[i].y() - rects[i].height(), + rects[i].width(), rects[i].height()); + } + + vgSeti(VG_MASKING, VG_TRUE); + maskValid = true; + maskIsSet = false; +} + +#endif // !QVG_SCISSOR_CLIP + +void QVGPaintEngine::updateScissor() +{ + Q_D(QVGPaintEngine); + + QRegion region = d->systemClip; + +#if defined(QVG_SCISSOR_CLIP) + // Using the scissor to do clipping, so combine the systemClip + // with the current painting clipRegion. + QVGPainterState *s = state(); + if (s->clipEnabled) { + if (region.isEmpty()) + region = s->clipRegion; + else + region = region.intersect(s->clipRegion); + if (isDefaultClipRegion(region)) { + // The scissor region is the entire drawing surface, + // so there is no point doing any scissoring. + vgSeti(VG_SCISSORING, VG_FALSE); + d->scissorActive = false; + return; + } + } else +#endif + { +#if !defined(QVG_SCISSOR_CLIP) + // Combine the system clip with the simple mask rectangle. + if (!d->maskRect.isNull()) { + if (region.isEmpty()) + region = d->maskRect; + else + region.intersect(d->maskRect); + if (isDefaultClipRegion(region)) { + // The scissor region is the entire drawing surface, + // so there is no point doing any scissoring. + vgSeti(VG_SCISSORING, VG_FALSE); + d->scissorActive = false; + return; + } + } else +#endif + + // Disable the scissor completely if the system clip is empty. + if (region.isEmpty()) { + vgSeti(VG_SCISSORING, VG_FALSE); + d->scissorActive = false; + return; + } + } + + if (d->scissorActive && region == d->scissorRegion) + return; + + QVector<QRect> rects = region.rects(); + int count = rects.count(); + if (count > d->maxScissorRects) + count = d->maxScissorRects; + QVarLengthArray<VGint> params(count * 4); + int height = paintDevice()->height(); + for (int i = 0; i < count; ++i) { + params[i * 4 + 0] = rects[i].x(); + params[i * 4 + 1] = height - rects[i].y() - rects[i].height(); + params[i * 4 + 2] = rects[i].width(); + params[i * 4 + 3] = rects[i].height(); + } + + vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data()); + vgSeti(VG_SCISSORING, VG_TRUE); + d->scissorActive = true; + d->scissorRegion = region; +} + +QRegion QVGPaintEngine::defaultClipRegion() +{ + // The default clip region for a paint device is the whole drawing area. + QPaintDevice *pdev = paintDevice(); + return QRegion(0, 0, pdev->width(), pdev->height()); +} + +bool QVGPaintEngine::isDefaultClipRegion(const QRegion& region) +{ + if (region.numRects() != 1) + return false; + + QPaintDevice *pdev = paintDevice(); + int width = pdev->width(); + int height = pdev->height(); + + QRect rect = region.boundingRect(); + return (rect.x() == 0 && rect.y() == 0 && + rect.width() == width && rect.height() == height); +} + +void QVGPaintEngine::clipEnabledChanged() +{ +#if defined(QVG_SCISSOR_CLIP) + updateScissor(); +#else + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + d->dirty |= QPaintEngine::DirtyClipEnabled; + if (s->clipEnabled && s->clipOperation != Qt::NoClip) { + // Replay the entire clip stack to put the mask into the right state. + d->maskValid = false; + d->maskIsSet = true; + d->maskRect = QRect(); + s->clipRegion = defaultClipRegion(); + d->replayClipOperations(); + d->transform = s->transform(); + d->updateTransform(paintDevice()); + } else { + vgSeti(VG_MASKING, VG_FALSE); + d->maskValid = false; + d->maskIsSet = false; + d->maskRect = QRect(); + } +#endif +} + +void QVGPaintEngine::penChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyPen; +} + +void QVGPaintEngine::brushChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyBrush; +} + +void QVGPaintEngine::brushOriginChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyBrushOrigin; + d->brushOrigin = state()->brushOrigin; + d->forcePenChange = true; + d->forceBrushChange = true; +} + +void QVGPaintEngine::opacityChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyOpacity; + d->opacity = state()->opacity; + d->forcePenChange = true; + d->forceBrushChange = true; +} + +void QVGPaintEngine::compositionModeChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyCompositionMode; + + VGBlendMode vgMode = VG_BLEND_SRC_OVER; + + switch (state()->composition_mode) { + case QPainter::CompositionMode_SourceOver: + vgMode = VG_BLEND_SRC_OVER; + break; + case QPainter::CompositionMode_DestinationOver: + vgMode = VG_BLEND_DST_OVER; + break; + case QPainter::CompositionMode_Source: + vgMode = VG_BLEND_SRC; + break; + case QPainter::CompositionMode_SourceIn: + vgMode = VG_BLEND_SRC_IN; + break; + case QPainter::CompositionMode_DestinationIn: + vgMode = VG_BLEND_DST_IN; + break; + case QPainter::CompositionMode_Plus: + vgMode = VG_BLEND_ADDITIVE; + break; + case QPainter::CompositionMode_Multiply: + vgMode = VG_BLEND_MULTIPLY; + break; + case QPainter::CompositionMode_Screen: + vgMode = VG_BLEND_SCREEN; + break; + case QPainter::CompositionMode_Darken: + vgMode = VG_BLEND_DARKEN; + break; + case QPainter::CompositionMode_Lighten: + vgMode = VG_BLEND_LIGHTEN; + break; + default: + qWarning() << "QVGPaintEngine::compositionModeChanged unsupported mode" << state()->composition_mode; + break; // Fall back to VG_BLEND_SRC_OVER. + } + + d->setBlendMode(vgMode); +} + +void QVGPaintEngine::renderHintsChanged() +{ + Q_D(QVGPaintEngine); + d->dirty |= QPaintEngine::DirtyHints; + + QPainter::RenderHints hints = state()->renderHints; + + VGRenderingQuality rq = + (hints & QPainter::Antialiasing) + ? VG_RENDERING_QUALITY_BETTER + : VG_RENDERING_QUALITY_NONANTIALIASED; + VGImageQuality iq = + (hints & QPainter::SmoothPixmapTransform) + ? VG_IMAGE_QUALITY_BETTER + : VG_IMAGE_QUALITY_NONANTIALIASED; + + d->setRenderingQuality(rq); + d->setImageQuality(iq); +} + +void QVGPaintEngine::transformChanged() +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + d->dirty |= QPaintEngine::DirtyTransform; + d->transform = s->transform(); + qreal oldPenScale = d->penScale; + d->updateTransform(paintDevice()); + if (d->penScale != oldPenScale) + d->forcePenChange = true; +} + +bool QVGPaintEngine::clearRect(const QRectF &rect, const QColor &color) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + if (!s->clipEnabled || s->clipOperation == Qt::NoClip) { + // The transform will either be identity or a simple translation, + // so do a simpler version of "r = d->transform.map(rect).toRect()". + QRect r = QRect(qRound(rect.x() + d->transform.dx()), + qRound(rect.y() + d->transform.dy()), + qRound(rect.width()), + qRound(rect.height())); + int height = paintDevice()->height(); + if (d->clearColor != color || d->clearOpacity != s->opacity) { + VGfloat values[4]; + values[0] = color.redF(); + values[1] = color.greenF(); + values[2] = color.blueF(); + values[3] = color.alphaF() * s->opacity; + vgSetfv(VG_CLEAR_COLOR, 4, values); + d->clearColor = color; + d->clearOpacity = s->opacity; + } + vgClear(r.x(), height - r.y() - r.height(), + r.width(), r.height()); + return true; + } + return false; +} + +void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) +{ + Q_D(QVGPaintEngine); + + if (brush.style() == Qt::NoBrush) + return; + + // Check to see if we can use vgClear() for faster filling. + if (brush.style() == Qt::SolidPattern && + clipTransformIsSimple(d->transform) && d->opacity == 1.0f && + clearRect(rect, brush.color())) { + return; + } + +#if !defined(QVG_NO_MODIFY_PATH) + VGfloat coords[8]; + if (d->simpleTransform) { + coords[0] = rect.x(); + coords[1] = rect.y(); + coords[2] = rect.x() + rect.width(); + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = rect.y() + rect.height(); + coords[6] = coords[0]; + coords[7] = coords[5]; + } else { + QPointF tl = d->transform.map(rect.topLeft()); + QPointF tr = d->transform.map(rect.topRight()); + QPointF bl = d->transform.map(rect.bottomLeft()); + QPointF br = d->transform.map(rect.bottomRight()); + coords[0] = tl.x(); + coords[1] = tl.y(); + coords[2] = tr.x(); + coords[3] = tr.y(); + coords[4] = br.x(); + coords[5] = br.y(); + coords[6] = bl.x(); + coords[7] = bl.y(); + } + vgModifyPathCoords(d->rectPath, 0, 4, coords); + d->fill(d->rectPath, brush); +#else + QPaintEngineEx::fillRect(rect, brush); +#endif +} + +void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color) +{ + Q_D(QVGPaintEngine); + + // Check to see if we can use vgClear() for faster filling. + if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f && + clearRect(rect, color)) { + return; + } + +#if !defined(QVG_NO_MODIFY_PATH) + VGfloat coords[8]; + if (d->simpleTransform) { + coords[0] = rect.x(); + coords[1] = rect.y(); + coords[2] = rect.x() + rect.width(); + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = rect.y() + rect.height(); + coords[6] = coords[0]; + coords[7] = coords[5]; + } else { + QPointF tl = d->transform.map(rect.topLeft()); + QPointF tr = d->transform.map(rect.topRight()); + QPointF bl = d->transform.map(rect.bottomLeft()); + QPointF br = d->transform.map(rect.bottomRight()); + coords[0] = tl.x(); + coords[1] = tl.y(); + coords[2] = tr.x(); + coords[3] = tr.y(); + coords[4] = br.x(); + coords[5] = br.y(); + coords[6] = bl.x(); + coords[7] = bl.y(); + } + vgModifyPathCoords(d->rectPath, 0, 4, coords); + d->fill(d->rectPath, QBrush(color)); +#else + QPaintEngineEx::fillRect(rect, QBrush(color)); +#endif +} + +void QVGPaintEngine::drawRects(const QRect *rects, int rectCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + for (int i = 0; i < rectCount; ++i, ++rects) { + VGfloat coords[8]; + if (d->simpleTransform) { + coords[0] = rects->x(); + coords[1] = rects->y(); + coords[2] = rects->x() + rects->width(); + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = rects->y() + rects->height(); + coords[6] = coords[0]; + coords[7] = coords[5]; + } else { + QPointF tl = d->transform.map(QPointF(rects->x(), rects->y())); + QPointF tr = d->transform.map(QPointF(rects->x() + rects->width(), + rects->y())); + QPointF bl = d->transform.map(QPointF(rects->x(), + rects->y() + rects->height())); + QPointF br = d->transform.map(QPointF(rects->x() + rects->width(), + rects->y() + rects->height())); + coords[0] = tl.x(); + coords[1] = tl.y(); + coords[2] = tr.x(); + coords[3] = tr.y(); + coords[4] = br.x(); + coords[5] = br.y(); + coords[6] = bl.x(); + coords[7] = bl.y(); + } + vgModifyPathCoords(d->rectPath, 0, 4, coords); + d->draw(d->rectPath, s->pen, s->brush); + } +#else + QPaintEngineEx::drawRects(rects, rectCount); +#endif +} + +void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + for (int i = 0; i < rectCount; ++i, ++rects) { + VGfloat coords[8]; + if (d->simpleTransform) { + coords[0] = rects->x(); + coords[1] = rects->y(); + coords[2] = rects->x() + rects->width(); + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = rects->y() + rects->height(); + coords[6] = coords[0]; + coords[7] = coords[5]; + } else { + QPointF tl = d->transform.map(rects->topLeft()); + QPointF tr = d->transform.map(rects->topRight()); + QPointF bl = d->transform.map(rects->bottomLeft()); + QPointF br = d->transform.map(rects->bottomRight()); + coords[0] = tl.x(); + coords[1] = tl.y(); + coords[2] = tr.x(); + coords[3] = tr.y(); + coords[4] = br.x(); + coords[5] = br.y(); + coords[6] = bl.x(); + coords[7] = bl.y(); + } + vgModifyPathCoords(d->rectPath, 0, 4, coords); + d->draw(d->rectPath, s->pen, s->brush); + } +#else + QPaintEngineEx::drawRects(rects, rectCount); +#endif +} + +void QVGPaintEngine::drawLines(const QLine *lines, int lineCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + for (int i = 0; i < lineCount; ++i, ++lines) { + VGfloat coords[4]; + if (d->simpleTransform) { + coords[0] = lines->x1(); + coords[1] = lines->y1(); + coords[2] = lines->x2(); + coords[3] = lines->y2(); + } else { + QPointF p1 = d->transform.map(QPointF(lines->x1(), lines->y1())); + QPointF p2 = d->transform.map(QPointF(lines->x2(), lines->y2())); + coords[0] = p1.x(); + coords[1] = p1.y(); + coords[2] = p2.x(); + coords[3] = p2.y(); + } + vgModifyPathCoords(d->linePath, 0, 2, coords); + d->stroke(d->linePath, s->pen); + } +#else + QPaintEngineEx::drawLines(lines, lineCount); +#endif +} + +void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + for (int i = 0; i < lineCount; ++i, ++lines) { + VGfloat coords[4]; + if (d->simpleTransform) { + coords[0] = lines->x1(); + coords[1] = lines->y1(); + coords[2] = lines->x2(); + coords[3] = lines->y2(); + } else { + QPointF p1 = d->transform.map(lines->p1()); + QPointF p2 = d->transform.map(lines->p2()); + coords[0] = p1.x(); + coords[1] = p1.y(); + coords[2] = p2.x(); + coords[3] = p2.y(); + } + vgModifyPathCoords(d->linePath, 0, 2, coords); + d->stroke(d->linePath, s->pen); + } +#else + QPaintEngineEx::drawLines(lines, lineCount); +#endif +} + +void QVGPaintEngine::drawEllipse(const QRectF &r) +{ + // Based on the description of vguEllipse() in the OpenVG specification. + // We don't use vguEllipse(), to avoid unnecessary library dependencies. + Q_D(QVGPaintEngine); + if (d->simpleTransform) { + QVGPainterState *s = state(); + VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + 4, // segmentCapacityHint + 12, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + static VGubyte segments[4] = { + VG_MOVE_TO_ABS, + VG_SCCWARC_TO_REL, + VG_SCCWARC_TO_REL, + VG_CLOSE_PATH + }; + VGfloat coords[12]; + VGfloat halfwid = r.width() / 2; + VGfloat halfht = r.height() / 2; + coords[0] = r.x() + r.width(); + coords[1] = r.y() + halfht; + coords[2] = halfwid; + coords[3] = halfht; + coords[4] = 0.0f; + coords[5] = -r.width(); + coords[6] = 0.0f; + coords[7] = halfwid; + coords[8] = halfht; + coords[9] = 0.0f; + coords[10] = r.width(); + coords[11] = 0.0f; + vgAppendPathData(path, 4, segments, coords); + d->draw(path, s->pen, s->brush); + vgDestroyPath(path); + } else { + // The projective transform version of an ellipse is difficult. + // Generate a QVectorPath containing cubic curves and transform that. + QPaintEngineEx::drawEllipse(r); + } +} + +void QVGPaintEngine::drawEllipse(const QRect &r) +{ + drawEllipse(QRectF(r)); +} + +void QVGPaintEngine::drawPath(const QPainterPath &path) +{ + // Shortcut past the QPainterPath -> QVectorPath conversion, + // converting the QPainterPath directly into a VGPath. + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + VGPath vgpath = d->painterPathToVGPath(path); + if (path.fillRule() == Qt::OddEvenFill) + d->draw(vgpath, s->pen, s->brush, VG_EVEN_ODD); + else + d->draw(vgpath, s->pen, s->brush, VG_NON_ZERO); + vgDestroyPath(vgpath); +} + +void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + + // Set up a new pen if necessary. + QPen pen = state()->pen; + if (pen.style() == Qt::NoPen) + return; + if (pen.capStyle() == Qt::FlatCap) + pen.setCapStyle(Qt::SquareCap); + + for (int i = 0; i < pointCount; ++i, ++points) { + VGfloat coords[4]; + if (d->simpleTransform) { + coords[0] = points->x(); + coords[1] = points->y(); + coords[2] = coords[0]; + coords[3] = coords[1]; + } else { + QPointF p = d->transform.map(*points); + coords[0] = p.x(); + coords[1] = p.y(); + coords[2] = coords[0]; + coords[3] = coords[1]; + } + vgModifyPathCoords(d->linePath, 0, 2, coords); + d->stroke(d->linePath, pen); + } +#else + QPaintEngineEx::drawPoints(points, pointCount); +#endif +} + +void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount) +{ +#if !defined(QVG_NO_MODIFY_PATH) + Q_D(QVGPaintEngine); + + // Set up a new pen if necessary. + QPen pen = state()->pen; + if (pen.style() == Qt::NoPen) + return; + if (pen.capStyle() == Qt::FlatCap) + pen.setCapStyle(Qt::SquareCap); + + for (int i = 0; i < pointCount; ++i, ++points) { + VGfloat coords[4]; + if (d->simpleTransform) { + coords[0] = points->x(); + coords[1] = points->y(); + coords[2] = coords[0]; + coords[3] = coords[1]; + } else { + QPointF p = d->transform.map(QPointF(*points)); + coords[0] = p.x(); + coords[1] = p.y(); + coords[2] = coords[0]; + coords[3] = coords[1]; + } + vgModifyPathCoords(d->linePath, 0, 2, coords); + d->stroke(d->linePath, pen); + } +#else + QPaintEngineEx::drawPoints(points, pointCount); +#endif +} + +void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + pointCount + 1, // segmentCapacityHint + pointCount * 2, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + QVarLengthArray<VGfloat, 16> coords; + QVarLengthArray<VGubyte, 10> segments; + for (int i = 0; i < pointCount; ++i, ++points) { + if (d->simpleTransform) { + coords.append(points->x()); + coords.append(points->y()); + } else { + QPointF temp = d->transform.map(*points); + coords.append(temp.x()); + coords.append(temp.y()); + } + if (i == 0) + segments.append(VG_MOVE_TO_ABS); + else + segments.append(VG_LINE_TO_ABS); + } + if (mode != QPaintEngine::PolylineMode) + segments.append(VG_CLOSE_PATH); + vgAppendPathData(path, segments.count(), + segments.constData(), coords.constData()); + switch (mode) { + case QPaintEngine::WindingMode: + d->draw(path, s->pen, s->brush, VG_NON_ZERO); + break; + + case QPaintEngine::PolylineMode: + d->stroke(path, s->pen); + break; + + default: + d->draw(path, s->pen, s->brush, VG_EVEN_ODD); + break; + } + vgDestroyPath(path); +} + +void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) +{ + Q_D(QVGPaintEngine); + QVGPainterState *s = state(); + VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + pointCount + 1, // segmentCapacityHint + pointCount * 2, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + QVarLengthArray<VGfloat, 16> coords; + QVarLengthArray<VGubyte, 10> segments; + for (int i = 0; i < pointCount; ++i, ++points) { + if (d->simpleTransform) { + coords.append(points->x()); + coords.append(points->y()); + } else { + QPointF temp = d->transform.map(QPointF(*points)); + coords.append(temp.x()); + coords.append(temp.y()); + } + if (i == 0) + segments.append(VG_MOVE_TO_ABS); + else + segments.append(VG_LINE_TO_ABS); + } + if (mode != QPaintEngine::PolylineMode) + segments.append(VG_CLOSE_PATH); + vgAppendPathData(path, segments.count(), + segments.constData(), coords.constData()); + switch (mode) { + case QPaintEngine::WindingMode: + d->draw(path, s->pen, s->brush, VG_NON_ZERO); + break; + + case QPaintEngine::PolylineMode: + d->stroke(path, s->pen); + break; + + default: + d->draw(path, s->pen, s->brush, VG_EVEN_ODD); + break; + } + vgDestroyPath(path); +} + +void QVGPaintEnginePrivate::setImageOptions() +{ + if (opacity != 1.0f && simpleTransform) { + if (opacity != paintOpacity) { + VGfloat values[4]; + values[0] = 1.0f; + values[1] = 1.0f; + values[2] = 1.0f; + values[3] = opacity; + vgSetParameterfv(opacityPaint, VG_PAINT_COLOR, 4, values); + paintOpacity = opacity; + } + if (fillPaint != opacityPaint) { + vgSetPaint(opacityPaint, VG_FILL_PATH); + fillPaint = opacityPaint; + } + setImageMode(VG_DRAW_IMAGE_MULTIPLY); + } else { + setImageMode(VG_DRAW_IMAGE_NORMAL); + } +} + +static void drawVGImage(QVGPaintEnginePrivate *d, + const QRectF& r, VGImage vgImg, + const QSize& imageSize, const QRectF& sr) +{ + if (vgImg == VG_INVALID_HANDLE) + return; + VGImage child = VG_INVALID_HANDLE; + + if (sr.topLeft().isNull() && sr.size() == imageSize) { + child = vgImg; + } else { + QRect src = sr.toRect(); +#if !defined(QT_SHIVAVG) + child = vgChildImage(vgImg, src.x(), src.y(), src.width(), src.height()); +#else + child = vgImg; // XXX: ShivaVG doesn't have vgChildImage(). +#endif + } + + QTransform transform(d->imageTransform); + VGfloat scaleX = sr.width() == 0.0f ? 0.0f : r.width() / sr.width(); + VGfloat scaleY = sr.height() == 0.0f ? 0.0f : r.height() / sr.height(); + transform.translate(r.x(), r.y()); + transform.scale(scaleX, scaleY); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform); + + d->setImageOptions(); + vgDrawImage(child); + + if(child != vgImg) + vgDestroyImage(child); +} + +static void drawVGImage(QVGPaintEnginePrivate *d, + const QPointF& pos, VGImage vgImg) +{ + if (vgImg == VG_INVALID_HANDLE) + return; + + QTransform transform(d->imageTransform); + transform.translate(pos.x(), pos.y()); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform); + + d->setImageOptions(); + vgDrawImage(vgImg); +} + +// Used by qpixmapfilter_vg.cpp to draw filtered VGImage's. +void qt_vg_drawVGImage(QPainter *painter, const QPointF& pos, VGImage vgImg) +{ + QVGPaintEngine *engine = + static_cast<QVGPaintEngine *>(painter->paintEngine()); + drawVGImage(engine->vgPrivate(), pos, vgImg); +} + +void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + QPixmapData *pd = pm.pixmapData(); + if (pd->classId() == QPixmapData::OpenVGClass) { + Q_D(QVGPaintEngine); + QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); + if (!vgpd->isValid()) + return; + if (d->simpleTransform) + drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr); + else + drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr); + } else { + drawImage(r, *(pd->buffer()), sr, Qt::AutoColor); + } +} + +void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) +{ + QPixmapData *pd = pm.pixmapData(); + if (pd->classId() == QPixmapData::OpenVGClass) { + Q_D(QVGPaintEngine); + QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); + if (!vgpd->isValid()) + return; + if (d->simpleTransform) + drawVGImage(d, pos, vgpd->toVGImage()); + else + drawVGImage(d, pos, vgpd->toVGImage(d->opacity)); + } else { + drawImage(pos, *(pd->buffer())); + } +} + +void QVGPaintEngine::drawImage + (const QRectF &r, const QImage &image, const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QVGPaintEngine); + VGImage vgImg; + if (d->simpleTransform || d->opacity == 1.0f) + vgImg = toVGImageSubRect(image, sr.toRect(), flags); + else + vgImg = toVGImageWithOpacitySubRect(image, d->opacity, sr.toRect()); + if (vgImg != VG_INVALID_HANDLE) { + if (r.size() == sr.size()) { + drawVGImage(d, r.topLeft(), vgImg); + } else { + drawVGImage(d, r, vgImg, sr.size().toSize(), + QRectF(QPointF(0, 0), sr.size())); + } + } else { + // Monochrome images need to use the vgChildImage() path. + vgImg = toVGImage(image, flags); + drawVGImage(d, r, vgImg, image.size(), sr); + } + vgDestroyImage(vgImg); +} + +void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image) +{ + Q_D(QVGPaintEngine); + VGImage vgImg; + if (d->simpleTransform || d->opacity == 1.0f) + vgImg = toVGImage(image); + else + vgImg = toVGImageWithOpacity(image, d->opacity); + drawVGImage(d, pos, vgImg); + vgDestroyImage(vgImg); +} + +void QVGPaintEngine::drawTiledPixmap + (const QRectF &r, const QPixmap &pixmap, const QPointF &s) +{ + QBrush brush(state()->pen.color(), pixmap); + QTransform xform; + xform.translate(-s.x(), -s.y()); + brush.setTransform(xform); + fillRect(r, brush); +} + +QVGFontEngineCleaner::QVGFontEngineCleaner(QVGPaintEnginePrivate *d) + : QObject(), d_ptr(d) +{ +} + +QVGFontEngineCleaner::~QVGFontEngineCleaner() +{ +} + +void QVGFontEngineCleaner::fontEngineDestroyed() +{ +#if !defined(QVG_NO_DRAW_GLYPHS) + QFontEngine *engine = static_cast<QFontEngine *>(sender()); + QVGFontCache::Iterator it = d_ptr->fontCache.find(engine); + if (it != d_ptr->fontCache.end()) { + delete it.value(); + d_ptr->fontCache.erase(it); + } +#endif +} + +#if !defined(QVG_NO_DRAW_GLYPHS) + +QVGFontGlyphCache::QVGFontGlyphCache() +{ + font = vgCreateFont(0); + scaleX = scaleY = 0.0; + memset(cachedGlyphsMask, 0, sizeof(cachedGlyphsMask)); +} + +QVGFontGlyphCache::~QVGFontGlyphCache() +{ + if (font != VG_INVALID_HANDLE) + vgDestroyFont(font); +} + +void QVGFontGlyphCache::setScaleFromText(const QTextItemInt &ti) +{ + QFontInfo fi(ti.font()); + qreal pixelSize = fi.pixelSize(); + qreal emSquare = ti.fontEngine->properties().emSquare.toReal(); + scaleX = scaleY = static_cast<VGfloat>(pixelSize / emSquare); +} + +void QVGFontGlyphCache::cacheGlyphs + (QVGPaintEnginePrivate *d, const QTextItemInt &ti, + const QVarLengthArray<glyph_t> &glyphs) +{ + VGfloat origin[2]; + VGfloat escapement[2]; + const glyph_t *g = glyphs.constData(); + int count = glyphs.size(); + glyph_metrics_t metrics; + // Some Qt font engines don't set yoff in getUnscaledGlyph(). + // Zero the metric structure so that everything has a default value. + memset(&metrics, 0, sizeof(metrics)); + while (count-- > 0) { + // Skip this glyph if we have already cached it before. + glyph_t glyph = *g++; + if (glyph < 256) { + if ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0) + continue; + cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32)); + } else if (cachedGlyphs.contains(glyph)) { + continue; + } else { + cachedGlyphs.insert(glyph); + } +#if !defined(QVG_NO_IMAGE_GLYPHS) + Q_UNUSED(d); + QImage scaledImage = ti.fontEngine->alphaMapForGlyph(glyph); + VGImage vgImage = VG_INVALID_HANDLE; + metrics = ti.fontEngine->boundingBox(glyph); + if (!scaledImage.isNull()) { // Not a space character + // The QPF implementation of alphaMapForGlyph() uses the color + // RGBA = (value, value, value, 255) instead of the color + // RGBA = (0, 0, 0, value) that the other font engines use. + // We modify the image colors to rectify this situation. + QFontEngine::Type type = ti.fontEngine->type(); + if (type == QFontEngine::QPF1 || type == QFontEngine::QPF2) { + if (scaledImage.format() == QImage::Format_Indexed8) { + for (int i = 0; i < 256; ++i) + scaledImage.setColor(i, qRgba(0, 0, 0, i)); + } else if (scaledImage.format() == QImage::Format_Mono) { + scaledImage.setColor(0, qRgba(0, 0, 0, 0)); + scaledImage.setColor(1, qRgba(0, 0, 0, 255)); + } + } + if (scaledImage.format() == QImage::Format_Indexed8) { + vgImage = vgCreateImage(VG_A_8, scaledImage.width(), scaledImage.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData(vgImage, scaledImage.bits(), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height()); + } else if (scaledImage.format() == QImage::Format_Mono) { + QImage img = scaledImage.convertToFormat(QImage::Format_Indexed8); + vgImage = vgCreateImage(VG_A_8, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData(vgImage, img.bits(), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height()); + } else { + QImage img = scaledImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); + vgImage = vgCreateImage(VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER); + vgImageSubData(vgImage, img.bits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height()); + } + } + origin[0] = -metrics.x.toReal() + 0.5f; + origin[1] = -metrics.y.toReal() + 0.5f; + escapement[0] = metrics.xoff.toReal(); + escapement[1] = metrics.yoff.toReal(); + vgSetGlyphToImage(font, glyph, vgImage, origin, escapement); + vgDestroyImage(vgImage); // Reduce reference count. +#else + // Calculate the path for the glyph and cache it. + QPainterPath path; + ti.fontEngine->getUnscaledGlyph(glyph, &path, &metrics); + VGPath vgPath; + if (!path.isEmpty()) { + vgPath = d->painterPathToVGPath(path); + } else { + // Probably a "space" character with no visible outline. + vgPath = VG_INVALID_HANDLE; + } + origin[0] = 0; + origin[1] = 0; + escapement[0] = metrics.xoff.toReal(); + escapement[1] = metrics.yoff.toReal(); + vgSetGlyphToPath(font, glyph, vgPath, VG_FALSE, origin, escapement); + vgDestroyPath(vgPath); // Reduce reference count. +#endif // !defined(QVG_NO_IMAGE_GLYPHS) + } +} + +#endif // !defined(QVG_NO_DRAW_GLYPHS) + +void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) +{ +#if !defined(QVG_NO_DRAW_GLYPHS) + Q_D(QVGPaintEngine); + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + + // If we are not using a simple transform, then fall back + // to the default Qt path stroking algorithm. + if (!d->simpleTransform) { + QPaintEngineEx::drawTextItem(p, textItem); + return; + } + + // Get the glyphs and positions associated with the text item. + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> glyphs; + QTransform matrix = d->transform; + matrix.translate(p.x(), p.y()); + ti.fontEngine->getGlyphPositions + (ti.glyphs, matrix, ti.flags, glyphs, positions); + + // Find the glyph cache for this font. + QVGFontCache::ConstIterator it = d->fontCache.constFind(ti.fontEngine); + QVGFontGlyphCache *glyphCache; + if (it != d->fontCache.constEnd()) { + glyphCache = it.value(); + } else { + glyphCache = new QVGFontGlyphCache(); + if (glyphCache->font == VG_INVALID_HANDLE) { + qWarning("QVGPaintEngine::drawTextItem: OpenVG fonts are not supported by the OpenVG engine"); + delete glyphCache; + QPaintEngineEx::drawTextItem(p, textItem); + return; + } + glyphCache->setScaleFromText(ti); + d->fontCache.insert(ti.fontEngine, glyphCache); + if (!d->fontEngineCleaner) + d->fontEngineCleaner = new QVGFontEngineCleaner(d); + QObject::connect(ti.fontEngine, SIGNAL(destroyed()), + d->fontEngineCleaner, SLOT(fontEngineDestroyed())); + } + + // Set the transformation to use for drawing the current glyphs. + QTransform glyphTransform(d->pathTransform); + glyphTransform.translate(p.x(), p.y()); +#if defined(QVG_NO_IMAGE_GLYPHS) + glyphTransform.scale(glyphCache->scaleX, glyphCache->scaleY); +#endif + d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, glyphTransform); + + // Add the glyphs from the text item into the glyph cache. + glyphCache->cacheGlyphs(d, ti, glyphs); + + // Set the glyph drawing origin. + VGfloat origin[2]; + origin[0] = 0; + origin[1] = 0; + vgSetfv(VG_GLYPH_ORIGIN, 2, origin); + + // Fast anti-aliasing for paths, better for images. +#if !defined(QVG_NO_IMAGE_GLYPHS) + d->setImageQuality(VG_IMAGE_QUALITY_BETTER); + d->setImageMode(VG_DRAW_IMAGE_STENCIL); +#else + d->setRenderingQuality(VG_RENDERING_QUALITY_FASTER); +#endif + + // Draw the glyphs. We need to fill with the brush associated with + // the Qt pen, not the Qt brush. + d->ensureBrush(state()->pen.brush()); + vgDrawGlyphs(glyphCache->font, glyphs.size(), (VGuint*)glyphs.data(), + NULL, NULL, VG_FILL_PATH, VG_TRUE); +#else + // OpenGL 1.0 does not have support for VGFont and glyphs, + // so fall back to the default Qt path stroking algorithm. + QPaintEngineEx::drawTextItem(p, textItem); +#endif +} + +void QVGPaintEngine::setState(QPainterState *s) +{ + Q_D(QVGPaintEngine); + QPaintEngineEx::setState(s); + QVGPainterState *ps = static_cast<QVGPainterState *>(s); + if (ps->isNew) { + // Newly created state object. The call to setState() + // will either be followed by a call to begin(), or we are + // setting the state as part of a save(). + ps->isNew = false; + } else { + // This state object was set as part of a restore(). + restoreState(d->dirty); + d->dirty = ps->savedDirty; + } +} + +// Called from QPaintEngine::syncState() to force a state flush. +// This should be called before and after raw VG operations. +void QVGPaintEngine::updateState(const QPaintEngineState &state) +{ + Q_UNUSED(state); + Q_D(QVGPaintEngine); + + if (!(d->rawVG)) { + // About to enter raw VG mode: flush pending changes and make + // sure that all matrices are set to the current transformation. + QVGPainterState *s = this->state(); + d->ensurePen(s->pen); + d->ensureBrush(s->brush); + d->ensurePathTransform(); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform); +#if !defined(QVG_NO_DRAW_GLYPHS) + d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform); +#endif + d->rawVG = true; + } else { + // Exiting raw VG mode: force all state values to be + // explicitly set on the VG engine to undo any changes + // that were made by the raw VG function calls. + QPaintEngine::DirtyFlags dirty = d->dirty; + d->clearModes(); + d->forcePenChange = true; + d->forceBrushChange = true; + d->penType = (VGPaintType)0; + d->brushType = (VGPaintType)0; + d->clearColor = QColor(); + d->fillPaint = d->brushPaint; + restoreState(QPaintEngine::AllDirty); + d->dirty = dirty; + d->rawVG = false; + vgSetPaint(d->penPaint, VG_STROKE_PATH); + vgSetPaint(d->brushPaint, VG_FILL_PATH); + } +} + +QPixmapFilter *QVGPaintEngine::createPixmapFilter(int type) const +{ +#if !defined(QT_SHIVAVG) + if (type == QPixmapFilter::ConvolutionFilter) + return new QVGPixmapConvolutionFilter; + else if (type == QPixmapFilter::ColorizeFilter) + return new QVGPixmapColorizeFilter; + else if (type == QPixmapFilter::DropShadowFilter) + return new QVGPixmapDropShadowFilter; + else +#endif + return QPaintEngineEx::createPixmapFilter(type); +} + +void QVGPaintEngine::restoreState(QPaintEngine::DirtyFlags dirty) +{ + Q_D(QVGPaintEngine); + + // Restore the pen, brush, and other settings. + if ((dirty & QPaintEngine::DirtyBrushOrigin) != 0) + brushOriginChanged(); + d->fillRule = 0; + if ((dirty & QPaintEngine::DirtyOpacity) != 0) + opacityChanged(); + if ((dirty & QPaintEngine::DirtyTransform) != 0) + transformChanged(); + if ((dirty & QPaintEngine::DirtyCompositionMode) != 0) + compositionModeChanged(); + if ((dirty & QPaintEngine::DirtyHints) != 0) + renderHintsChanged(); + if ((dirty & (QPaintEngine::DirtyClipRegion | + QPaintEngine::DirtyClipPath | + QPaintEngine::DirtyClipEnabled)) != 0) { + d->maskValid = false; + d->maskIsSet = false; + d->maskRect = QRect(); + clipEnabledChanged(); + } + +#if defined(QVG_SCISSOR_CLIP) + if ((dirty & (QPaintEngine::DirtyClipRegion | + QPaintEngine::DirtyClipPath | + QPaintEngine::DirtyClipEnabled)) == 0) { + updateScissor(); + } +#else + updateScissor(); +#endif +} + +#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL) + +QVGCompositionHelper::QVGCompositionHelper() +{ + d = qt_vg_create_paint_engine()->vgPrivate(); +} + +QVGCompositionHelper::~QVGCompositionHelper() +{ +} + +void QVGCompositionHelper::startCompositing(const QSize& screenSize) +{ + this->screenSize = screenSize; + clearScissor(); + d->setBlendMode(VG_BLEND_SRC_OVER); +} + +void QVGCompositionHelper::endCompositing() +{ + clearScissor(); +} + +void QVGCompositionHelper::blitWindow + (QVGEGLWindowSurfacePrivate *surface, const QRect& rect, + const QPoint& topLeft, int opacity) +{ + // Get the VGImage that is acting as a back buffer for the window. + VGImage image = surface->surfaceImage(); + if (image == VG_INVALID_HANDLE) + return; + QSize imageSize = surface->surfaceSize(); + + // Determine which sub rectangle of the window to draw. + QRect sr = rect.translated(-topLeft); + + if (opacity >= 255) { + // Fully opaque: use vgSetPixels() to directly copy the sub-region. + int y = screenSize.height() - (rect.bottom() + 1); + vgSetPixels(rect.x(), y, image, sr.x(), + imageSize.height() - (sr.y() + sr.height()), + sr.width(), sr.height()); + } else { + // Extract the child image that we want to draw. + VGImage child; + if (sr.topLeft().isNull() && sr.size() == imageSize) + child = image; + else { + child = vgChildImage + (image, sr.x(), imageSize.height() - (sr.y() + sr.height()), + sr.width(), sr.height()); + } + + // Set the image transform. + QTransform transform; + int y = screenSize.height() - (rect.bottom() + 1); + transform.translate(rect.x() + 0.5f, y + 0.5f); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform); + + // Enable opacity for image drawing if necessary. + if (opacity < 255) { + if (opacity != d->paintOpacity) { + VGfloat values[4]; + values[0] = 1.0f; + values[1] = 1.0f; + values[2] = 1.0f; + values[3] = ((VGfloat)opacity) / 255.0f; + vgSetParameterfv(d->opacityPaint, VG_PAINT_COLOR, 4, values); + d->paintOpacity = values[3]; + } + if (d->fillPaint != d->opacityPaint) { + vgSetPaint(d->opacityPaint, VG_FILL_PATH); + d->fillPaint = d->opacityPaint; + } + d->setImageMode(VG_DRAW_IMAGE_MULTIPLY); + } else { + d->setImageMode(VG_DRAW_IMAGE_NORMAL); + } + + // Draw the child image. + vgDrawImage(child); + + // Destroy the child image. + if(child != image) + vgDestroyImage(child); + } +} + +static void fillBackgroundRect(const QRect& rect, QVGPaintEnginePrivate *d) +{ + VGfloat coords[8]; + coords[0] = rect.x(); + coords[1] = rect.y(); + coords[2] = rect.x() + rect.width(); + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = rect.y() + rect.height(); + coords[6] = coords[0]; + coords[7] = coords[5]; +#if !defined(QVG_NO_MODIFY_PATH) + vgModifyPathCoords(d->rectPath, 0, 4, coords); + vgDrawPath(d->rectPath, VG_FILL_PATH); +#else + Q_UNUSED(d); + VGPath rectPath = vgCreatePath + (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.0f, // scale + 0.0f, // bias + 5, // segmentCapacityHint + 8, // coordCapacityHint + VG_PATH_CAPABILITY_ALL); + static VGubyte const segments[5] = { + VG_MOVE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + vgAppendPathData(rectPath, 5, segments, coords); + vgDrawPath(rectPath, VG_FILL_PATH); + vgDestroyPath(rectPath); +#endif +} + +void QVGCompositionHelper::fillBackground + (const QRegion& region, const QBrush& brush) +{ + // Set the path transform to the default viewport transformation. + VGfloat devh = screenSize.height() - 1; + QTransform viewport(1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.5f, devh + 0.5f, 1.0f); + d->setTransform(VG_MATRIX_PATH_USER_TO_SURFACE, viewport); + + // Set the brush to use to fill the background. + d->ensureBrush(brush); + d->setFillRule(VG_EVEN_ODD); + + if (region.numRects() == 1) { + fillBackgroundRect(region.boundingRect(), d); + } else { + const QVector<QRect> rects = region.rects(); + for (int i = 0; i < rects.size(); ++i) + fillBackgroundRect(rects.at(i), d); + } + + // We will need to reset the path transform during the next paint. + d->pathTransformSet = false; +} + +void QVGCompositionHelper::drawCursorImage + (const QImage& image, const QPoint& offset) +{ + QImage img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + VGImage vgImg = vgCreateImage + (VG_sARGB_8888_PRE, img.width(), img.height(), + VG_IMAGE_QUALITY_FASTER); + vgImageSubData + (vgImg, img.bits() + img.bytesPerLine() * (img.height() - 1), + -(img.bytesPerLine()), VG_sARGB_8888_PRE, 0, 0, + img.width(), img.height()); + + QTransform transform; + int y = screenSize.height() - (offset.y() + img.height()); + transform.translate(offset.x() + 0.5f, y + 0.5f); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform); + + d->setImageMode(VG_DRAW_IMAGE_NORMAL); + vgDrawImage(vgImg); + + vgDestroyImage(vgImg); +} + +void QVGCompositionHelper::drawCursorPixmap + (const QPixmap& pixmap, const QPoint& offset) +{ + QPixmapData *pd = pixmap.pixmapData(); + if (pd->classId() == QPixmapData::OpenVGClass) { + QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); + if (vgpd->isValid()) { + VGfloat devh = screenSize.height() - 1; + QTransform transform(1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.5f, devh + 0.5f, 1.0f); + transform.translate(offset.x(), offset.y()); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, transform); + + d->setImageMode(VG_DRAW_IMAGE_NORMAL); + vgDrawImage(vgpd->toVGImage()); + return; + } + } + + drawCursorImage(pixmap.toImage(), offset); +} + +void QVGCompositionHelper::setScissor(const QRegion& region) +{ + QVector<QRect> rects = region.rects(); + int count = rects.count(); + if (count > d->maxScissorRects) + count = d->maxScissorRects; + QVarLengthArray<VGint> params(count * 4); + int height = screenSize.height(); + for (int i = 0; i < count; ++i) { + params[i * 4 + 0] = rects[i].x(); + params[i * 4 + 1] = height - rects[i].y() - rects[i].height(); + params[i * 4 + 2] = rects[i].width(); + params[i * 4 + 3] = rects[i].height(); + } + + vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data()); + vgSeti(VG_SCISSORING, VG_TRUE); + d->scissorActive = true; + d->scissorRegion = region; +} + +void QVGCompositionHelper::clearScissor() +{ + if (d->scissorActive) { + vgSeti(VG_SCISSORING, VG_FALSE); + d->scissorActive = false; + } +} + +#endif // !QVG_NO_SINGLE_CONTEXT && !QT_NO_EGL + +VGImageFormat qt_vg_image_to_vg_format(QImage::Format format) +{ + switch (format) { + case QImage::Format_MonoLSB: + return VG_BW_1; + case QImage::Format_ARGB32_Premultiplied: + return VG_sARGB_8888_PRE; + case QImage::Format_RGB32: + return VG_sXRGB_8888; + case QImage::Format_ARGB32: + return VG_sARGB_8888; + case QImage::Format_RGB16: + return VG_sRGB_565; + case QImage::Format_ARGB4444_Premultiplied: + return VG_sARGB_4444; + default: break; + } + return VG_sARGB_8888; // XXX +} + +QT_END_NAMESPACE + +#include "qpaintengine_vg.moc" diff --git a/src/openvg/qpaintengine_vg_p.h b/src/openvg/qpaintengine_vg_p.h new file mode 100644 index 0000000..a390c80 --- /dev/null +++ b/src/openvg/qpaintengine_vg_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAINTENGINE_VG_P_H +#define QPAINTENGINE_VG_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 <QtGui/private/qpaintengineex_p.h> + +QT_BEGIN_NAMESPACE + +class QVGPaintEnginePrivate; +class QPixmapData; +class QVGEGLWindowSurfacePrivate; + +class Q_OPENVG_EXPORT QVGPainterState : public QPainterState +{ +public: + QVGPainterState(QVGPainterState& other); + QVGPainterState(); + ~QVGPainterState(); + + bool isNew; + QRegion clipRegion; + QPaintEngine::DirtyFlags savedDirty; +}; + +class Q_OPENVG_EXPORT QVGPaintEngine : public QPaintEngineEx +{ + Q_DECLARE_PRIVATE(QVGPaintEngine) +public: + QVGPaintEngine(); + ~QVGPaintEngine(); + + Type type() const { return OpenVG; } + + QPainterState *createState(QPainterState *orig) const; + + bool begin(QPaintDevice *pdev); + bool end(); + + void draw(const QVectorPath &path); + void fill(const QVectorPath &path, const QBrush &brush); + void stroke(const QVectorPath &path, const QPen &pen); + + void clip(const QVectorPath &path, Qt::ClipOperation op); + void clip(const QRect &rect, Qt::ClipOperation op); + void clip(const QRegion ®ion, Qt::ClipOperation op); + void clip(const QPainterPath &path, Qt::ClipOperation op); + + void clipEnabledChanged(); + void penChanged(); + void brushChanged(); + void brushOriginChanged(); + void opacityChanged(); + void compositionModeChanged(); + void renderHintsChanged(); + void transformChanged(); + + void fillRect(const QRectF &rect, const QBrush &brush); + void fillRect(const QRectF &rect, const QColor &color); + + void drawRects(const QRect *rects, int rectCount); + void drawRects(const QRectF *rects, int rectCount); + + void drawLines(const QLine *lines, int lineCount); + void drawLines(const QLineF *lines, int lineCount); + + void drawEllipse(const QRectF &r); + void drawEllipse(const QRect &r); + + void drawPath(const QPainterPath &path); + + void drawPoints(const QPointF *points, int pointCount); + void drawPoints(const QPoint *points, int pointCount); + + void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); + + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + void drawPixmap(const QPointF &pos, const QPixmap &pm); + + void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor); + void drawImage(const QPointF &pos, const QImage &image); + + void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + + void drawTextItem(const QPointF &p, const QTextItem &textItem); + + void setState(QPainterState *s); + QVGPainterState *state() { return static_cast<QVGPainterState *>(QPaintEngineEx::state()); } + const QVGPainterState *state() const { return static_cast<const QVGPainterState *>(QPaintEngineEx::state()); } + + void updateState(const QPaintEngineState &state); + + QPixmapFilter *createPixmapFilter(int type) const; + + QVGPaintEnginePrivate *vgPrivate() { Q_D(QVGPaintEngine); return d; } + +protected: + QVGPaintEngine(QVGPaintEnginePrivate &data); + +private: + void restoreState(QPaintEngine::DirtyFlags dirty); + void updateScissor(); + QRegion defaultClipRegion(); + bool isDefaultClipRegion(const QRegion& region); + bool clearRect(const QRectF &rect, const QColor &color); +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp new file mode 100644 index 0000000..90fd9a5 --- /dev/null +++ b/src/openvg/qpixmapdata_vg.cpp @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmapdata_vg_p.h" +#include "qpaintengine_vg_p.h" +#include <QtGui/private/qdrawhelper_p.h> +#include "qvg_p.h" + +QT_BEGIN_NAMESPACE + +static int qt_vg_pixmap_serial = 0; + +QVGPixmapData::QVGPixmapData(PixelType type) + : QPixmapData(type, OpenVGClass) +{ + Q_ASSERT(type == QPixmapData::PixmapType); + vgImage = VG_INVALID_HANDLE; + vgImageOpacity = VG_INVALID_HANDLE; + cachedOpacity = 1.0f; + recreate = true; +#if !defined(QT_NO_EGL) + context = 0; +#endif + setSerialNumber(++qt_vg_pixmap_serial); +} + +QVGPixmapData::~QVGPixmapData() +{ + if (vgImage != VG_INVALID_HANDLE) { + // We need to have a context current to destroy the image. +#if !defined(QT_NO_EGL) + if (context->isCurrent()) { + vgDestroyImage(vgImage); + if (vgImageOpacity != VG_INVALID_HANDLE) + vgDestroyImage(vgImageOpacity); + } else { + // We don't currently have a widget surface active, but we + // need a surface to make the context current. So use the + // shared pbuffer surface instead. + qt_vg_make_current(context, qt_vg_shared_surface()); + vgDestroyImage(vgImage); + if (vgImageOpacity != VG_INVALID_HANDLE) + vgDestroyImage(vgImageOpacity); + qt_vg_done_current(context); + context->setSurface(EGL_NO_SURFACE); + } +#else + vgDestroyImage(vgImage); + if (vgImageOpacity != VG_INVALID_HANDLE) + vgDestroyImage(vgImageOpacity); + } else { +#endif + } +#if !defined(QT_NO_EGL) + if (context) + qt_vg_destroy_context(context); +#endif +} + +bool QVGPixmapData::isValid() const +{ + return (w > 0 && h > 0); +} + +void QVGPixmapData::resize(int wid, int ht) +{ + if (w == wid && h == ht) + return; + + w = wid; + h = ht; + d = 32; // We always use ARGB_Premultiplied for VG pixmaps. + is_null = (w <= 0 || h <= 0); + source = QImage(); + recreate = true; + + setSerialNumber(++qt_vg_pixmap_serial); +} + +void QVGPixmapData::fromImage + (const QImage &image, Qt::ImageConversionFlags flags) +{ + if (image.size() == QSize(w, h)) + setSerialNumber(++qt_vg_pixmap_serial); + else + resize(image.width(), image.height()); + source = image.convertToFormat(sourceFormat(), flags); + recreate = true; +} + +void QVGPixmapData::fill(const QColor &color) +{ + if (!isValid()) + return; + + if (source.isNull()) + source = QImage(w, h, sourceFormat()); + + if (source.depth() == 1) { + // Pick the best approximate color in the image's colortable. + int gray = qGray(color.rgba()); + if (qAbs(qGray(source.color(0)) - gray) < qAbs(qGray(source.color(1)) - gray)) + source.fill(0); + else + source.fill(1); + } else { + source.fill(PREMUL(color.rgba())); + } + + // Re-upload the image to VG the next time toVGImage() is called. + recreate = true; +} + +bool QVGPixmapData::hasAlphaChannel() const +{ + if (!source.isNull()) + return source.hasAlphaChannel(); + else + return isValid(); +} + +void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel) +{ + forceToImage(); + source.setAlphaChannel(alphaChannel.toImage()); +} + +QImage QVGPixmapData::toImage() const +{ + if (!isValid()) + return QImage(); + + if (source.isNull()) { + source = QImage(w, h, sourceFormat()); + recreate = true; + } + + return source; +} + +QImage *QVGPixmapData::buffer() +{ + forceToImage(); + return &source; +} + +QPaintEngine* QVGPixmapData::paintEngine() const +{ + // If the application wants to paint into the QPixmap, we first + // force it to QImage format and then paint into that. + // This is simpler than juggling multiple VG contexts. + const_cast<QVGPixmapData *>(this)->forceToImage(); + return source.paintEngine(); +} + +VGImage QVGPixmapData::toVGImage() +{ + if (!isValid()) + return VG_INVALID_HANDLE; + +#if !defined(QT_NO_EGL) + // Increase the reference count on the shared context. + if (!context) + context = qt_vg_create_context(0); +#endif + + if (recreate) { + if (vgImage != VG_INVALID_HANDLE) { + vgDestroyImage(vgImage); + vgImage = VG_INVALID_HANDLE; + } + if (vgImageOpacity != VG_INVALID_HANDLE) { + vgDestroyImage(vgImageOpacity); + vgImageOpacity = VG_INVALID_HANDLE; + } + } + + if (vgImage == VG_INVALID_HANDLE) { + vgImage = vgCreateImage + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + } + + if (!source.isNull() && recreate) { + vgImageSubData + (vgImage, + source.bits(), source.bytesPerLine(), + VG_sARGB_8888_PRE, 0, 0, w, h); + } + + recreate = false; + + return vgImage; +} + +VGImage QVGPixmapData::toVGImage(qreal opacity) +{ +#if !defined(QT_SHIVAVG) + if (!isValid()) + return VG_INVALID_HANDLE; + +#if !defined(QT_NO_EGL) + // Increase the reference count on the shared context. + if (!context) + context = qt_vg_create_context(0); +#endif + + if (recreate) { + if (vgImage != VG_INVALID_HANDLE) { + vgDestroyImage(vgImage); + vgImage = VG_INVALID_HANDLE; + } + if (vgImageOpacity != VG_INVALID_HANDLE) { + vgDestroyImage(vgImageOpacity); + vgImageOpacity = VG_INVALID_HANDLE; + } + } + + if (vgImage == VG_INVALID_HANDLE) { + vgImage = vgCreateImage + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + } + + if (!source.isNull() && recreate) { + vgImageSubData + (vgImage, + source.bits(), source.bytesPerLine(), + VG_sARGB_8888_PRE, 0, 0, w, h); + } + + recreate = false; + + if (opacity == 1.0f) + return vgImage; + + if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) { + if (vgImageOpacity == VG_INVALID_HANDLE) { + vgImageOpacity = vgCreateImage + (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + } + VGfloat matrix[20] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, opacity, + 0.0f, 0.0f, 0.0f, 0.0f + }; + vgColorMatrix(vgImageOpacity, vgImage, matrix); + cachedOpacity = opacity; + } + + return vgImageOpacity; +#else + // vgColorMatrix() doesn't work with ShivaVG, so ignore the opacity. + Q_UNUSED(opacity); + return toVGImage(); +#endif +} + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); + +int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + switch (metric) { + case QPaintDevice::PdmWidth: + return w; + case QPaintDevice::PdmHeight: + return h; + case QPaintDevice::PdmNumColors: + return 0; + case QPaintDevice::PdmDepth: + return d; + case QPaintDevice::PdmWidthMM: + return qRound(w * 25.4 / qt_defaultDpiX()); + case QPaintDevice::PdmHeightMM: + return qRound(h * 25.4 / qt_defaultDpiY()); + case QPaintDevice::PdmDpiX: + case QPaintDevice::PdmPhysicalDpiX: + return qt_defaultDpiX(); + case QPaintDevice::PdmDpiY: + case QPaintDevice::PdmPhysicalDpiY: + return qt_defaultDpiY(); + default: + qWarning("QVGPixmapData::metric(): Invalid metric"); + return 0; + } +} + +// Force the pixmap data to be in QImage format. +void QVGPixmapData::forceToImage() +{ + if (!isValid()) + return; + + if (source.isNull()) + source = QImage(w, h, sourceFormat()); + + recreate = true; +} + +QImage::Format QVGPixmapData::sourceFormat() const +{ + return QImage::Format_ARGB32_Premultiplied; +} + +QT_END_NAMESPACE diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h new file mode 100644 index 0000000..bb019be --- /dev/null +++ b/src/openvg/qpixmapdata_vg_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_VG_P_H +#define QPIXMAPDATA_VG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qpixmap_raster_p.h> +#include "qvg_p.h" +#if !defined(QT_NO_EGL) +#include <QtGui/private/qegl_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QEglContext; + +class Q_OPENVG_EXPORT QVGPixmapData : public QPixmapData +{ +public: + QVGPixmapData(PixelType type); + ~QVGPixmapData(); + + // Is this pixmap valid (i.e. non-zero in size)? + bool isValid() const; + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + + void fill(const QColor &color); + bool hasAlphaChannel() const; + void setAlphaChannel(const QPixmap &alphaChannel); + QImage toImage() const; + QImage *buffer(); + QPaintEngine* paintEngine() const; + + // Return the VGImage form of this pixmap, creating it if necessary. + // This assumes that there is a VG context current. + VGImage toVGImage(); + + // Return the VGImage form for a specific opacity setting. + VGImage toVGImage(qreal opacity); + + QSize size() const { return QSize(w, h); } + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +private: + VGImage vgImage; + VGImage vgImageOpacity; + qreal cachedOpacity; + mutable QImage source; + mutable bool recreate; +#if !defined(QT_NO_EGL) + mutable QEglContext *context; +#endif + + void forceToImage(); + QImage::Format sourceFormat() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qpixmapfilter_vg.cpp b/src/openvg/qpixmapfilter_vg.cpp new file mode 100644 index 0000000..ff8604b --- /dev/null +++ b/src/openvg/qpixmapfilter_vg.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmapfilter_vg_p.h" +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qpainter.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_SHIVAVG) + +QVGPixmapConvolutionFilter::QVGPixmapConvolutionFilter() + : QPixmapConvolutionFilter() +{ +} + +QVGPixmapConvolutionFilter::~QVGPixmapConvolutionFilter() +{ +} + +extern void qt_vg_drawVGImage + (QPainter *painter, const QPointF& pos, VGImage vgImg); + +void QVGPixmapConvolutionFilter::draw + (QPainter *painter, const QPointF &dest, + const QPixmap &src, const QRectF &srcRect) const +{ + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { + // The pixmap data is not an instance of QVGPixmapData, so fall + // back to the default convolution filter implementation. + QPixmapConvolutionFilter::draw(painter, dest, src, srcRect); + return; + } + + QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData()); + + VGImage srcImage = pd->toVGImage(); + if (srcImage == VG_INVALID_HANDLE) + return; + + QSize size = pd->size(); + VGImage dstImage = vgCreateImage + (VG_sARGB_8888_PRE, size.width(), size.height(), + VG_IMAGE_QUALITY_FASTER); + if (dstImage == VG_INVALID_HANDLE) + return; + + int kernelWidth = rows(); + int kernelHeight = columns(); + const qreal *kern = convolutionKernel(); + QVarLengthArray<VGshort> kernel; + for (int i = 0; i < kernelWidth; ++i) { + for (int j = 0; j < kernelHeight; ++j) { + kernel.append((VGshort)(kern[j * kernelWidth + i] * 1024.0f)); + } + } + + VGfloat values[4]; + values[0] = 0.0f; + values[1] = 0.0f; + values[2] = 0.0f; + values[3] = 0.0f; + vgSetfv(VG_TILE_FILL_COLOR, 4, values); + + vgConvolve(dstImage, srcImage, + kernelWidth, kernelHeight, 0, 0, + kernel.constData(), 1.0f / 1024.0f, 0.0f, + VG_TILE_FILL); + + VGImage child = VG_INVALID_HANDLE; + + if (srcRect.isNull() || + (srcRect.topLeft().isNull() && srcRect.size() == size)) { + child = dstImage; + } else { + QRect src = srcRect.toRect(); + child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height()); + } + + qt_vg_drawVGImage(painter, dest, child); + + if(child != dstImage) + vgDestroyImage(child); + vgDestroyImage(dstImage); +} + +QVGPixmapColorizeFilter::QVGPixmapColorizeFilter() + : QPixmapColorizeFilter(), + firstTime(true) +{ +} + +QVGPixmapColorizeFilter::~QVGPixmapColorizeFilter() +{ +} + +void QVGPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const +{ + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { + // The pixmap data is not an instance of QVGPixmapData, so fall + // back to the default convolution filter implementation. + QPixmapColorizeFilter::draw(painter, dest, src, srcRect); + return; + } + + QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData()); + + VGImage srcImage = pd->toVGImage(); + if (srcImage == VG_INVALID_HANDLE) + return; + + QSize size = pd->size(); + VGImage dstImage = vgCreateImage + (VG_sARGB_8888_PRE, size.width(), size.height(), + VG_IMAGE_QUALITY_FASTER); + if (dstImage == VG_INVALID_HANDLE) + return; + + // Recompute the color matrix if the color has changed. + QColor c = color(); + if (c != prevColor || firstTime) { + prevColor = c; + + // Determine the weights for the matrix from the color. + VGfloat weights[3]; + VGfloat invweights[3]; + VGfloat alpha = c.alphaF(); + weights[0] = c.redF() * alpha; + weights[1] = c.greenF() * alpha; + weights[2] = c.blueF() * alpha; + invweights[0] = 1.0f - weights[0]; + invweights[1] = 1.0f - weights[1]; + invweights[2] = 1.0f - weights[2]; + + // Grayscale weights. + static const VGfloat redGray = 11.0f / 32.0f; + static const VGfloat greenGray = 16.0f / 32.0f; + static const VGfloat blueGray = 1.0f - (redGray + greenGray); + + matrix[0][0] = redGray * invweights[0]; + matrix[0][1] = redGray * invweights[1]; + matrix[0][2] = redGray * invweights[2]; + matrix[0][3] = 0.0f; + matrix[1][0] = greenGray * invweights[0]; + matrix[1][1] = greenGray * invweights[1]; + matrix[1][2] = greenGray * invweights[2]; + matrix[1][3] = 0.0f; + matrix[2][0] = blueGray * invweights[0]; + matrix[2][1] = blueGray * invweights[1]; + matrix[2][2] = blueGray * invweights[2]; + matrix[2][3] = 0.0f; + matrix[3][0] = 0.0f; + matrix[3][1] = 0.0f; + matrix[3][2] = 0.0f; + matrix[3][3] = 1.0f; + matrix[4][0] = weights[0]; + matrix[4][1] = weights[1]; + matrix[4][2] = weights[2]; + matrix[4][3] = 0.0f; + } + + firstTime = false; + + vgColorMatrix(dstImage, srcImage, matrix[0]); + + VGImage child = VG_INVALID_HANDLE; + + if (srcRect.isNull() || + (srcRect.topLeft().isNull() && srcRect.size() == size)) { + child = dstImage; + } else { + QRect src = srcRect.toRect(); + child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height()); + } + + qt_vg_drawVGImage(painter, dest, child); + + if(child != dstImage) + vgDestroyImage(child); + vgDestroyImage(dstImage); +} + +QVGPixmapDropShadowFilter::QVGPixmapDropShadowFilter() + : QPixmapDropShadowFilter(), + prevRadius(0.0f), + kernelSize(0), + firstTime(true) +{ +} + +QVGPixmapDropShadowFilter::~QVGPixmapDropShadowFilter() +{ +} + +void QVGPixmapDropShadowFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const +{ + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { + // The pixmap data is not an instance of QVGPixmapData, so fall + // back to the default drop shadow filter implementation. + QPixmapDropShadowFilter::draw(painter, dest, src, srcRect); + return; + } + + QVGPixmapData *pd = static_cast<QVGPixmapData *>(src.pixmapData()); + + VGImage srcImage = pd->toVGImage(); + if (srcImage == VG_INVALID_HANDLE) + return; + + QSize size = pd->size(); + VGImage tmpImage = vgCreateImage + (VG_sARGB_8888_PRE, size.width(), size.height(), + VG_IMAGE_QUALITY_FASTER); + if (tmpImage == VG_INVALID_HANDLE) + return; + + VGImage dstImage = vgCreateImage + (VG_sARGB_8888_PRE, size.width(), size.height(), + VG_IMAGE_QUALITY_FASTER); + if (dstImage == VG_INVALID_HANDLE) { + vgDestroyImage(tmpImage); + return; + } + + // Recompute the color matrix if the color has changed. + QColor c = color(); + if (c != prevColor || firstTime) { + prevColor = c; + + matrix[0][0] = 0.0f; + matrix[0][1] = 0.0f; + matrix[0][2] = 0.0f; + matrix[0][3] = 0.0f; + matrix[1][0] = 0.0f; + matrix[1][1] = 0.0f; + matrix[1][2] = 0.0f; + matrix[1][3] = 0.0f; + matrix[2][0] = 0.0f; + matrix[2][1] = 0.0f; + matrix[2][2] = 0.0f; + matrix[2][3] = 0.0f; + matrix[3][0] = c.redF(); + matrix[3][1] = c.greenF(); + matrix[3][2] = c.blueF(); + matrix[3][3] = c.alphaF(); + matrix[4][0] = 0.0f; + matrix[4][1] = 0.0f; + matrix[4][2] = 0.0f; + matrix[4][3] = 0.0f; + } + + // Blacken the source image. + vgColorMatrix(tmpImage, srcImage, matrix[0]); + + // Recompute the convolution kernel if the blur radius has changed. + qreal radius = blurRadius(); + if (radius != prevRadius || firstTime) { + prevRadius = radius; + int dim = 2 * qRound(radius) + 1; + int size = dim * dim; + VGshort f = VGshort(1024.0f / size); + kernel.resize(size); + for (int i = 0; i < size; ++i) + kernel[i] = f; + kernelSize = dim; + } + + // Apply the convolution filter using the kernel. + VGfloat values[4]; + values[0] = 0.0f; + values[1] = 0.0f; + values[2] = 0.0f; + values[3] = 0.0f; + vgSetfv(VG_TILE_FILL_COLOR, 4, values); + vgConvolve(dstImage, tmpImage, + kernelSize, kernelSize, 0, 0, + kernel.constData(), 1.0f / 1024.0f, 0.0f, + VG_TILE_FILL); + + firstTime = false; + + VGImage child = VG_INVALID_HANDLE; + + if (srcRect.isNull() || + (srcRect.topLeft().isNull() && srcRect.size() == size)) { + child = dstImage; + } else { + QRect src = srcRect.toRect(); + child = vgChildImage(dstImage, src.x(), src.y(), src.width(), src.height()); + } + + qt_vg_drawVGImage(painter, dest + offset(), child); + + if(child != dstImage) + vgDestroyImage(child); + vgDestroyImage(tmpImage); + vgDestroyImage(dstImage); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/openvg/qpixmapfilter_vg_p.h b/src/openvg/qpixmapfilter_vg_p.h new file mode 100644 index 0000000..412fb55 --- /dev/null +++ b/src/openvg/qpixmapfilter_vg_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPFILTER_VG_P_H +#define QPIXMAPFILTER_VG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpixmapdata_vg_p.h" +#include <QtGui/private/qpixmapfilter_p.h> +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_SHIVAVG) + +class Q_OPENVG_EXPORT QVGPixmapConvolutionFilter : public QPixmapConvolutionFilter +{ +public: + QVGPixmapConvolutionFilter(); + ~QVGPixmapConvolutionFilter(); + + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const; +}; + +class Q_OPENVG_EXPORT QVGPixmapColorizeFilter : public QPixmapColorizeFilter +{ +public: + QVGPixmapColorizeFilter(); + ~QVGPixmapColorizeFilter(); + + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const; + +private: + mutable VGfloat matrix[5][4]; + mutable QColor prevColor; + mutable bool firstTime; +}; + +class Q_OPENVG_EXPORT QVGPixmapDropShadowFilter : public QPixmapDropShadowFilter +{ +public: + QVGPixmapDropShadowFilter(); + ~QVGPixmapDropShadowFilter(); + + void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src) const; + +private: + mutable VGfloat matrix[5][4]; + mutable QColor prevColor; + mutable qreal prevRadius; + mutable int kernelSize; + mutable bool firstTime; + mutable QVarLengthArray<VGshort, 16> kernel; +}; + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qvg.h b/src/openvg/qvg.h new file mode 100644 index 0000000..8da6159 --- /dev/null +++ b/src/openvg/qvg.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVG_H +#define QVG_H + +#include <QtCore/qglobal.h> + +// Include the OpenVG headers for use in applications that +// issue raw OpenVG function calls. +#if defined(QT_LOWER_CASE_VG_INCLUDES) +#include <vg/openvg.h> +#else +#include <VG/openvg.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenVG) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/openvg/qvg_p.h b/src/openvg/qvg_p.h new file mode 100644 index 0000000..7ac2430 --- /dev/null +++ b/src/openvg/qvg_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVG_P_H +#define QVG_P_H + +#include "qvg.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qimage.h> + +#if !defined(QT_NO_EGL) +#include <QtGui/private/qegl_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QVGPaintEngine; + +#if !defined(QT_NO_EGL) + +class QEglContext; + +// Create an EGL context, but don't bind it to a surface. If single-context +// mode is enabled, this will return the previously-created context. +Q_OPENVG_EXPORT QEglContext *qt_vg_create_context(QPaintDevice *device); + +// Destroy an EGL context that was created by qt_vg_create_context(). +// If single-context mode is enabled, this will decrease the reference count. +Q_OPENVG_EXPORT void qt_vg_destroy_context(QEglContext *context); + +// Return the shared pbuffer surface that can be made current to +// destroy VGImage objects when there is no other surface available. +Q_OPENVG_EXPORT EGLSurface qt_vg_shared_surface(void); + +// Make a context current with a specific surface. +Q_OPENVG_EXPORT void qt_vg_make_current(QEglContext *context, EGLSurface surface); + +// Make a context uncurrent. +Q_OPENVG_EXPORT void qt_vg_done_current(QEglContext *context, bool force = false); + +// Destroy a surface that was previously associated with a context. +Q_OPENVG_EXPORT void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface); + +// Convert the configuration format in a context to a VG or QImage format. +Q_OPENVG_EXPORT VGImageFormat qt_vg_config_to_vg_format(QEglContext *context); +Q_OPENVG_EXPORT QImage::Format qt_vg_config_to_image_format(QEglContext *context); + +#endif + +// Create a paint engine. Returns the common engine in single-context mode. +Q_OPENVG_EXPORT QVGPaintEngine *qt_vg_create_paint_engine(void); + +// Destroy a paint engine. Does nothing in single-context mode. +Q_OPENVG_EXPORT void qt_vg_destroy_paint_engine(QVGPaintEngine *engine); + +// Convert between QImage and VGImage format values. +Q_OPENVG_EXPORT VGImageFormat qt_vg_image_to_vg_format(QImage::Format format); + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qvgcompositionhelper_p.h b/src/openvg/qvgcompositionhelper_p.h new file mode 100644 index 0000000..cdf1be9 --- /dev/null +++ b/src/openvg/qvgcompositionhelper_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVGCOMPOSITIONHELPER_H +#define QVGCOMPOSITIONHELPER_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 "qwindowsurface_vgegl_p.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QT_NO_EGL) + +class QVGPaintEnginePrivate; +class QVGEGLWindowSurfacePrivate; + +class Q_OPENVG_EXPORT QVGCompositionHelper +{ +public: + QVGCompositionHelper(); + virtual ~QVGCompositionHelper(); + + void startCompositing(const QSize& screenSize); + void endCompositing(); + + void blitWindow(QVGEGLWindowSurfacePrivate *surface, const QRect& rect, + const QPoint& topLeft, int opacity); + void fillBackground(const QRegion& region, const QBrush& brush); + void drawCursorImage(const QImage& image, const QPoint& offset); + void drawCursorPixmap(const QPixmap& pixmap, const QPoint& offset); + void setScissor(const QRegion& region); + void clearScissor(); + +private: + QVGPaintEnginePrivate *d; + QSize screenSize; +}; + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qwindowsurface_vg.cpp b/src/openvg/qwindowsurface_vg.cpp new file mode 100644 index 0000000..c77aa93 --- /dev/null +++ b/src/openvg/qwindowsurface_vg.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsurface_vg_p.h" +#include "qwindowsurface_vgegl_p.h" +#include "qpaintengine_vg_p.h" +#include "qpixmapdata_vg_p.h" +#include "qvg_p.h" + +#if !defined(QT_NO_EGL) + +#include <QtGui/private/qegl_p.h> + +QT_BEGIN_NAMESPACE + +QVGWindowSurface::QVGWindowSurface(QWidget *window) + : QWindowSurface(window) +{ + d_ptr = QVGEGLWindowSurfacePrivate::create + (QVGEGLWindowSurfacePrivate::WindowSurface, this); +} + +QVGWindowSurface::QVGWindowSurface + (QWidget *window, QVGEGLWindowSurfacePrivate *d) + : QWindowSurface(window), d_ptr(d) +{ +} + +QVGWindowSurface::~QVGWindowSurface() +{ + delete d_ptr; +} + +QPaintDevice *QVGWindowSurface::paintDevice() +{ + d_ptr->beginPaint(window()); + return this; +} + +void QVGWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(offset); + QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget(); + d_ptr->endPaint(parent, region); +} + +void QVGWindowSurface::setGeometry(const QRect &rect) +{ + QWindowSurface::setGeometry(rect); + d_ptr->ensureContext(window()); +} + +bool QVGWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ + return QWindowSurface::scroll(area, dx, dy); +} + +void QVGWindowSurface::beginPaint(const QRegion ®ion) +{ + // Nothing to do here. + Q_UNUSED(region); +} + +void QVGWindowSurface::endPaint(const QRegion ®ion) +{ + // Nothing to do here. + Q_UNUSED(region); +} + +QPaintEngine *QVGWindowSurface::paintEngine() const +{ + return d_ptr->paintEngine(); +} + +int QVGWindowSurface::metric(PaintDeviceMetric met) const +{ + return window()->metric(met); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qwindowsurface_vg_p.h b/src/openvg/qwindowsurface_vg_p.h new file mode 100644 index 0000000..e2ee583 --- /dev/null +++ b/src/openvg/qwindowsurface_vg_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_VG_P_H +#define QWINDOWSURFACE_VG_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 <QtGui/private/qwindowsurface_p.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_EGL) + +class QVGEGLWindowSurfacePrivate; + +class Q_OPENVG_EXPORT QVGWindowSurface : public QWindowSurface, public QPaintDevice +{ +public: + QVGWindowSurface(QWidget *window); + QVGWindowSurface(QWidget *window, QVGEGLWindowSurfacePrivate *d); + ~QVGWindowSurface(); + + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + bool scroll(const QRegion &area, int dx, int dy); + + void beginPaint(const QRegion ®ion); + void endPaint(const QRegion ®ion); + + QPaintEngine *paintEngine() const; + +protected: + int metric(PaintDeviceMetric metric) const; + +private: + QVGEGLWindowSurfacePrivate *d_ptr; +}; + +#endif + +QT_END_NAMESPACE + +#endif // QWINDOWSURFACE_VG_P_H diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp new file mode 100644 index 0000000..11c7021 --- /dev/null +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -0,0 +1,751 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsurface_vgegl_p.h" +#include "qpaintengine_vg_p.h" +#include "qpixmapdata_vg_p.h" +#include "qvg_p.h" + +#if !defined(QT_NO_EGL) + +QT_BEGIN_NAMESPACE + +// Turn off "direct to window" rendering if EGL cannot support it. +#if !defined(EGL_RENDER_BUFFER) || !defined(EGL_SINGLE_BUFFER) +#if defined(QVG_DIRECT_TO_WINDOW) +#undef QVG_DIRECT_TO_WINDOW +#endif +#endif + +// Determine if preserved window contents should be used. +#if !defined(EGL_SWAP_BEHAVIOR) || !defined(EGL_BUFFER_PRESERVED) +#if !defined(QVG_NO_PRESERVED_SWAP) +#define QVG_NO_PRESERVED_SWAP 1 +#endif +#endif + +VGImageFormat qt_vg_config_to_vg_format(QEglContext *context) +{ + return qt_vg_image_to_vg_format + (qt_vg_config_to_image_format(context)); +} + +QImage::Format qt_vg_config_to_image_format(QEglContext *context) +{ + EGLint red = 0; + EGLint green = 0; + EGLint blue = 0; + EGLint alpha = 0; + context->configAttrib(EGL_RED_SIZE, &red); + context->configAttrib(EGL_GREEN_SIZE, &green); + context->configAttrib(EGL_BLUE_SIZE, &blue); + context->configAttrib(EGL_ALPHA_SIZE, &alpha); + QImage::Format argbFormat; +#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT + EGLint type = 0; + context->configAttrib(EGL_SURFACE_TYPE, &type); + if ((type & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0) + argbFormat = QImage::Format_ARGB32_Premultiplied; + else + argbFormat = QImage::Format_ARGB32; +#else + argbFormat = QImage::Format_ARGB32; +#endif + if (red == 8 && green == 8 && blue == 8 && alpha == 8) + return argbFormat; + else if (red == 8 && green == 8 && blue == 8 && alpha == 0) + return QImage::Format_RGB32; + else if (red == 5 && green == 6 && blue == 5 && alpha == 0) + return QImage::Format_RGB16; + else if (red == 4 && green == 4 && blue == 4 && alpha == 4) + return QImage::Format_ARGB4444_Premultiplied; + else + return argbFormat; // XXX +} + +static void copySubImage(QImage *image, VGImage vgImage, const QRect& rect) +{ + vgGetImageSubData + (vgImage, + image->bits() + rect.bottom() * image->bytesPerLine() + + rect.x() * (image->depth() / 8), + -(image->bytesPerLine()), + qt_vg_image_to_vg_format(image->format()), + rect.x(), (image->height() - 1) - rect.bottom(), + rect.width(), rect.height()); +} + +#if !defined(QVG_NO_SINGLE_CONTEXT) + +class QVGSharedContext +{ +public: + QVGSharedContext(); + ~QVGSharedContext(); + + QEglContext *context; + int refCount; + QVGPaintEngine *engine; + EGLSurface surface; + EGLSurface lastSurface; +}; + +QVGSharedContext::QVGSharedContext() + : context(0) + , refCount(0) + , engine(0) + , surface(EGL_NO_SURFACE) + , lastSurface(EGL_NO_SURFACE) +{ +} + +QVGSharedContext::~QVGSharedContext() +{ + if (context) + qt_vg_make_current(context, qt_vg_shared_surface()); + delete engine; + if (context) + qt_vg_done_current(context, true); + if (surface != EGL_NO_SURFACE) + qt_vg_destroy_surface(context, surface); + delete context; +} + +Q_GLOBAL_STATIC(QVGSharedContext, sharedContext); + +QVGPaintEngine *qt_vg_create_paint_engine(void) +{ + QVGSharedContext *shared = sharedContext(); + if (!shared->engine) + shared->engine = new QVGPaintEngine(); + return shared->engine; +} + +void qt_vg_destroy_paint_engine(QVGPaintEngine *engine) +{ + Q_UNUSED(engine); +} + +#else + +QVGPaintEngine *qt_vg_create_paint_engine(void) +{ + return new QVGPaintEngine(); +} + +void qt_vg_destroy_paint_engine(QVGPaintEngine *engine) +{ + delete engine; +} + +#endif + +#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT + +static bool isPremultipliedContext(const QEglContext *context) +{ + EGLint value = 0; + if (context->configAttrib(EGL_SURFACE_TYPE, &value)) + return (value & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0; + else + return false; +} + +#endif + +static QEglContext *createContext(QPaintDevice *device) +{ + QEglContext *context; + + // Create the context object and open the display. + context = new QEglContext(); + context->setApi(QEgl::OpenVG); + if (!context->openDisplay(device)) { + delete context; + return 0; + } + + // Choose an appropriate configuration for rendering into the device. + QEglProperties configProps; + configProps.setPaintDeviceFormat(device); + int redSize = configProps.value(EGL_RED_SIZE); + if (redSize == EGL_DONT_CARE || redSize == 0) + configProps.setPixelFormat(QImage::Format_ARGB32); // XXX +#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT + configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT | + EGL_VG_ALPHA_FORMAT_PRE_BIT); + configProps.setRenderableType(QEgl::OpenVG); + if (!context->chooseConfig(configProps)) { + // Try again without the "pre" bit. + configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT); + if (!context->chooseConfig(configProps)) { + delete context; + return 0; + } + } +#else + configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT); + configProps.setRenderableType(QEgl::OpenVG); + if (!context->chooseConfig(configProps)) { + delete context; + return 0; + } +#endif + + // Construct a new EGL context for the selected configuration. + if (!context->createContext()) { + delete context; + return 0; + } + + return context; +} + +#if !defined(QVG_NO_SINGLE_CONTEXT) + +QEglContext *qt_vg_create_context(QPaintDevice *device) +{ + QVGSharedContext *shared = sharedContext(); + if (shared->context) { + ++(shared->refCount); + return shared->context; + } else { + shared->context = createContext(device); + shared->refCount = 1; + return shared->context; + } +} + +void qt_vg_destroy_context(QEglContext *context) +{ + QVGSharedContext *shared = sharedContext(); + if (shared->context != context) { + // This is not the shared context. Shouldn't happen! + delete context; + } else if (--(shared->refCount) <= 0) { + qt_vg_make_current(shared->context, qt_vg_shared_surface()); + delete shared->engine; + shared->engine = 0; + qt_vg_done_current(shared->context, true); + if (shared->surface != EGL_NO_SURFACE) { + qt_vg_destroy_surface(shared->context, shared->surface); + shared->surface = EGL_NO_SURFACE; + } + delete shared->context; + shared->context = 0; + } +} + +EGLSurface qt_vg_shared_surface(void) +{ + QVGSharedContext *shared = sharedContext(); + if (shared->surface == EGL_NO_SURFACE) { + EGLint attribs[7]; + attribs[0] = EGL_WIDTH; + attribs[1] = 16; + attribs[2] = EGL_HEIGHT; + attribs[3] = 16; +#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT + if (isPremultipliedContext(shared->context)) { + attribs[4] = EGL_VG_ALPHA_FORMAT; + attribs[5] = EGL_VG_ALPHA_FORMAT_PRE; + attribs[6] = EGL_NONE; + } else +#endif + { + attribs[4] = EGL_NONE; + } + shared->surface = eglCreatePbufferSurface + (shared->context->display(), shared->context->config(), attribs); + } + return shared->surface; +} + +void qt_vg_make_current(QEglContext *context, EGLSurface surface) +{ + // Bail out if the context and surface are already current. + if (context->isCurrent() && context->surface() == surface) + return; + + // Are we setting the surface to the same as the last elided doneCurrent()? + QVGSharedContext *shared = sharedContext(); + if (context->isCurrent() && shared->lastSurface == surface) { + shared->lastSurface = EGL_NO_SURFACE; + context->setSurface(surface); + return; + } + + // Switch to the new context and surface. + shared->lastSurface = EGL_NO_SURFACE; + context->setSurface(surface); + context->makeCurrent(); +} + +void qt_vg_done_current(QEglContext *context, bool force) +{ + QVGSharedContext *shared = sharedContext(); + if (force) { + context->doneCurrent(); + shared->lastSurface = EGL_NO_SURFACE; + } else { + // Keep the context current for now just in case we immediately + // reuse the same surface for the next frame. + shared->lastSurface = context->surface(); + } +} + +void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface) +{ + QVGSharedContext *shared = sharedContext(); + if (shared->lastSurface == surface) { + shared->lastSurface = EGL_NO_SURFACE; + context->doneCurrent(); + } + context->setSurface(surface); + context->destroySurface(); +} + +#else + +QEglContext *qt_vg_create_context(QPaintDevice *device) +{ + return createContext(device); +} + +void qt_vg_destroy_context(QEglContext *context) +{ + delete context; +} + +EGLSurface qt_vg_shared_surface(void) +{ + return EGL_NO_SURFACE; +} + +void qt_vg_make_current(QEglContext *context, EGLSurface surface) +{ + context->setSurface(surface); + context->makeCurrent(); +} + +void qt_vg_done_current(QEglContext *context, bool force) +{ + Q_UNUSED(force); + context->doneCurrent(); +} + +void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface) +{ + context->setSurface(surface); + context->destroySurface(); +} + +#endif + +QVGEGLWindowSurfacePrivate::QVGEGLWindowSurfacePrivate(QWindowSurface *win) +{ + winSurface = win; + engine = 0; +} + +QVGEGLWindowSurfacePrivate::~QVGEGLWindowSurfacePrivate() +{ + // Destroy the paint engine if it hasn't been destroyed already. + destroyPaintEngine(); +} + +QVGEGLWindowSurfacePrivate *QVGEGLWindowSurfacePrivate::create + (SurfaceType type, QWindowSurface *win) +{ +#if defined(QVG_VGIMAGE_BACKBUFFERS) + if (type == VGImageSurface) + return new QVGEGLWindowSurfaceVGImage(win); + else if (type == QImageSurface) + return new QVGEGLWindowSurfaceQImage(win); +#endif + if (type == WindowSurface) + return new QVGEGLWindowSurfaceDirect(win); + return 0; +} + +QVGPaintEngine *QVGEGLWindowSurfacePrivate::paintEngine() +{ + if (!engine) + engine = qt_vg_create_paint_engine(); + return engine; +} + +VGImage QVGEGLWindowSurfacePrivate::surfaceImage() const +{ + return VG_INVALID_HANDLE; +} + +void QVGEGLWindowSurfacePrivate::destroyPaintEngine() +{ + if (engine) { + qt_vg_destroy_paint_engine(engine); + engine = 0; + } +} + +QSize QVGEGLWindowSurfacePrivate::windowSurfaceSize(QWidget *widget) const +{ + Q_UNUSED(widget); + + QRect rect = winSurface->geometry(); + QSize newSize = rect.size(); + +#if defined(Q_WS_QWS) + // Account for the widget mask, if any. + if (widget && !widget->mask().isEmpty()) { + const QRegion region = widget->mask() + & rect.translated(-widget->geometry().topLeft()); + newSize = region.boundingRect().size(); + } +#endif + + return newSize; +} + +#if defined(QVG_VGIMAGE_BACKBUFFERS) + +QVGEGLWindowSurfaceVGImage::QVGEGLWindowSurfaceVGImage(QWindowSurface *win) + : QVGEGLWindowSurfacePrivate(win) + , context(0) + , backBuffer(VG_INVALID_HANDLE) + , backBufferSurface(EGL_NO_SURFACE) + , recreateBackBuffer(false) + , isPaintingActive(false) + , windowSurface(EGL_NO_SURFACE) +{ +} + +QVGEGLWindowSurfaceVGImage::~QVGEGLWindowSurfaceVGImage() +{ + destroyPaintEngine(); + if (context) { + if (backBufferSurface != EGL_NO_SURFACE) { + // We need a current context to be able to destroy the image. + // We use the shared surface because the native window handle + // associated with "windowSurface" may have been destroyed already. + qt_vg_make_current(context, qt_vg_shared_surface()); + qt_vg_destroy_surface(context, backBufferSurface); + vgDestroyImage(backBuffer); + qt_vg_done_current(context, true); + } + if (windowSurface != EGL_NO_SURFACE) + qt_vg_destroy_surface(context, windowSurface); + qt_vg_destroy_context(context); + } +} + +QEglContext *QVGEGLWindowSurfaceVGImage::ensureContext(QWidget *widget) +{ + QSize newSize = windowSurfaceSize(widget); + if (context && size != newSize) { + // The surface size has changed, so we need to recreate + // the back buffer. Keep the same context and paint engine. + size = newSize; + if (isPaintingActive) + qt_vg_done_current(context, true); + isPaintingActive = false; + recreateBackBuffer = true; + } + if (!context) { + // Create a new EGL context. We create the surface in beginPaint(). + size = newSize; + context = qt_vg_create_context(widget); + if (!context) + return 0; + isPaintingActive = false; + } + return context; +} + +void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget) +{ + QEglContext *context = ensureContext(widget); + if (context) { + if (recreateBackBuffer || backBufferSurface == EGL_NO_SURFACE) { + // Create a VGImage object to act as the back buffer + // for this window. We have to create the VGImage with a + // current context, so activate the main surface for the window. + qt_vg_make_current(context, mainSurface()); + recreateBackBuffer = false; + if (backBufferSurface != EGL_NO_SURFACE) { + eglDestroySurface(context->display(), backBufferSurface); + backBufferSurface = EGL_NO_SURFACE; + } + if (backBuffer != VG_INVALID_HANDLE) { + vgDestroyImage(backBuffer); + } + VGImageFormat format = qt_vg_config_to_vg_format(context); + backBuffer = vgCreateImage + (format, size.width(), size.height(), + VG_IMAGE_QUALITY_FASTER); + if (backBuffer != VG_INVALID_HANDLE) { + // Create an EGL surface for rendering into the VGImage. + backBufferSurface = eglCreatePbufferFromClientBuffer + (context->display(), EGL_OPENVG_IMAGE, + (EGLClientBuffer)(backBuffer), + context->config(), NULL); + if (backBufferSurface == EGL_NO_SURFACE) { + vgDestroyImage(backBuffer); + backBuffer = VG_INVALID_HANDLE; + } + } + } + if (backBufferSurface != EGL_NO_SURFACE) + qt_vg_make_current(context, backBufferSurface); + else + qt_vg_make_current(context, mainSurface()); + isPaintingActive = true; + } +} + +void QVGEGLWindowSurfaceVGImage::endPaint + (QWidget *widget, const QRegion& region, QImage *image) +{ + Q_UNUSED(region); + Q_UNUSED(image); + QEglContext *context = ensureContext(widget); + if (context) { + if (backBufferSurface != EGL_NO_SURFACE) { + if (isPaintingActive) + vgFlush(); + qt_vg_done_current(context); + } + context->setSurface(EGL_NO_SURFACE); + isPaintingActive = false; + } +} + +VGImage QVGEGLWindowSurfaceVGImage::surfaceImage() const +{ + return backBuffer; +} + +EGLSurface QVGEGLWindowSurfaceVGImage::mainSurface() const +{ + if (windowSurface != EGL_NO_SURFACE) + return windowSurface; + else + return qt_vg_shared_surface(); +} + +QVGEGLWindowSurfaceQImage::QVGEGLWindowSurfaceQImage(QWindowSurface *win) + : QVGEGLWindowSurfaceVGImage(win) +{ +} + +QVGEGLWindowSurfaceQImage::~QVGEGLWindowSurfaceQImage() +{ +} + +void QVGEGLWindowSurfaceQImage::endPaint + (QWidget *widget, const QRegion& region, QImage *image) +{ + QEglContext *context = ensureContext(widget); + if (context) { + if (backBufferSurface != EGL_NO_SURFACE) { + if (isPaintingActive) + vgFlush(); + qt_vg_make_current(context, mainSurface()); + QRegion rgn = region.intersected + (QRect(0, 0, image->width(), image->height())); + if (rgn.numRects() == 1) { + copySubImage(image, backBuffer, rgn.boundingRect()); + } else { + QVector<QRect> rects = rgn.rects(); + for (int index = 0; index < rects.size(); ++index) + copySubImage(image, backBuffer, rects[index]); + } + qt_vg_done_current(context); + } + context->setSurface(EGL_NO_SURFACE); + isPaintingActive = false; + } +} + +#endif // QVG_VGIMAGE_BACKBUFFERS + +QVGEGLWindowSurfaceDirect::QVGEGLWindowSurfaceDirect(QWindowSurface *win) + : QVGEGLWindowSurfacePrivate(win) + , context(0) + , isPaintingActive(false) + , needToSwap(false) + , windowSurface(EGL_NO_SURFACE) +{ +} + +QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect() +{ +} + +QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) +{ + QSize newSize = windowSurfaceSize(widget); + QEglProperties surfaceProps; + +#if defined(QVG_RECREATE_ON_SIZE_CHANGE) +#if !defined(QVG_NO_SINGLE_CONTEXT) + if (context && size != newSize) { + // The surface size has changed, so we need to recreate it. + // We can keep the same context and paint engine. + size = newSize; + if (isPaintingActive) + qt_vg_done_current(context, true); + context->setSurface(windowSurface); + context->destroySurface(); +#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT) + if (isPremultipliedContext(context)) { + surfaceProps.setValue + (EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE); + } else { + surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT); + } +#endif + context->createSurface(widget, &surfaceProps); + windowSurface = context->surface(); + context->setSurface(EGL_NO_SURFACE); + isPaintingActive = false; + } +#else + if (context && size != newSize) { + // The surface size has changed, so we need to recreate + // the EGL context for the widget. We also need to recreate + // the surface's paint engine if context sharing is not + // enabled because we cannot reuse the existing paint objects + // in the new context. + qt_vg_destroy_paint_engine(engine); + engine = 0; + context->setSurface(windowSurface); + context->destroySurface(); + qt_vg_destroy_context(context); + context = 0; + windowSurface = EGL_NO_SURFACE; + } +#endif +#endif + if (!context) { + // Create a new EGL context and bind it to the widget surface. + size = newSize; + context = qt_vg_create_context(widget); + if (!context) + return 0; + // We want a direct to window rendering surface if possible. +#if defined(QVG_DIRECT_TO_WINDOW) + surfaceProps.setValue(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); +#endif +#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT) + if (isPremultipliedContext(context)) { + surfaceProps.setValue + (EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE); + } else { + surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT); + } +#endif + if (!context->createSurface(widget, &surfaceProps)) { + qt_vg_destroy_context(context); + context = 0; + return 0; + } + needToSwap = true; +#if defined(QVG_DIRECT_TO_WINDOW) + // Did we get a direct to window rendering surface? + EGLint buffer = 0; + if (eglQueryContext(context->display(), context->context(), + EGL_RENDER_BUFFER, &buffer) && + buffer == EGL_SINGLE_BUFFER) { + needToSwap = false; + } +#endif +#if !defined(QVG_NO_PRESERVED_SWAP) + // Try to force the surface back buffer to preserve its contents. + if (needToSwap) { + eglGetError(); // Clear error state first. + eglSurfaceAttrib(context->display(), context->surface(), + EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + if (eglGetError() != EGL_SUCCESS) { + qWarning("QVG: could not enable preserved swap"); + } + } +#endif + windowSurface = context->surface(); + context->setSurface(EGL_NO_SURFACE); + isPaintingActive = false; + } + return context; +} + +void QVGEGLWindowSurfaceDirect::beginPaint(QWidget *widget) +{ + QEglContext *context = ensureContext(widget); + if (context) { + qt_vg_make_current(context, windowSurface); + isPaintingActive = true; + } +} + +void QVGEGLWindowSurfaceDirect::endPaint + (QWidget *widget, const QRegion& region, QImage *image) +{ + Q_UNUSED(region); + Q_UNUSED(image); + QEglContext *context = ensureContext(widget); + if (context) { + if (needToSwap) { + if (!isPaintingActive) + qt_vg_make_current(context, windowSurface); + context->swapBuffers(); + qt_vg_done_current(context); + } else if (isPaintingActive) { + vgFlush(); + qt_vg_done_current(context); + } + context->setSurface(EGL_NO_SURFACE); + isPaintingActive = false; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/openvg/qwindowsurface_vgegl_p.h b/src/openvg/qwindowsurface_vgegl_p.h new file mode 100644 index 0000000..aa23772 --- /dev/null +++ b/src/openvg/qwindowsurface_vgegl_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_VGEGL_P_H +#define QWINDOWSURFACE_VGEGL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qwindowsurface_p.h> +#include "qvg_p.h" + +#if !defined(QT_NO_EGL) + +#include <QtGui/private/qegl_p.h> + +QT_BEGIN_NAMESPACE + +class QWindowSurface; + +class Q_OPENVG_EXPORT QVGEGLWindowSurfacePrivate +{ +public: + QVGEGLWindowSurfacePrivate(QWindowSurface *win); + virtual ~QVGEGLWindowSurfacePrivate(); + + enum SurfaceType + { + WindowSurface, + VGImageSurface, + QImageSurface + }; + + static QVGEGLWindowSurfacePrivate *create + (SurfaceType type, QWindowSurface *win); + + QVGPaintEngine *paintEngine(); + virtual QEglContext *ensureContext(QWidget *widget) = 0; + virtual void beginPaint(QWidget *widget) = 0; + virtual void endPaint + (QWidget *widget, const QRegion& region, QImage *image = 0) = 0; + virtual VGImage surfaceImage() const; + virtual QSize surfaceSize() const = 0; + +private: + QVGPaintEngine *engine; + +protected: + QWindowSurface *winSurface; + + void destroyPaintEngine(); + QSize windowSurfaceSize(QWidget *widget) const; +}; + +#if defined(EGL_OPENVG_IMAGE) && !defined(QVG_NO_SINGLE_CONTEXT) + +#define QVG_VGIMAGE_BACKBUFFERS 1 + +class Q_OPENVG_EXPORT QVGEGLWindowSurfaceVGImage : public QVGEGLWindowSurfacePrivate +{ +public: + QVGEGLWindowSurfaceVGImage(QWindowSurface *win); + virtual ~QVGEGLWindowSurfaceVGImage(); + + QEglContext *ensureContext(QWidget *widget); + void beginPaint(QWidget *widget); + void endPaint(QWidget *widget, const QRegion& region, QImage *image); + VGImage surfaceImage() const; + QSize surfaceSize() const { return size; } + +protected: + QEglContext *context; + VGImage backBuffer; + EGLSurface backBufferSurface; + bool recreateBackBuffer; + bool isPaintingActive; + QSize size; + EGLSurface windowSurface; + + EGLSurface mainSurface() const; +}; + +class Q_OPENVG_EXPORT QVGEGLWindowSurfaceQImage : public QVGEGLWindowSurfaceVGImage +{ +public: + QVGEGLWindowSurfaceQImage(QWindowSurface *win); + virtual ~QVGEGLWindowSurfaceQImage(); + + void endPaint(QWidget *widget, const QRegion& region, QImage *image); +}; + +#endif // EGL_OPENVG_IMAGE + +class Q_OPENVG_EXPORT QVGEGLWindowSurfaceDirect : public QVGEGLWindowSurfacePrivate +{ +public: + QVGEGLWindowSurfaceDirect(QWindowSurface *win); + virtual ~QVGEGLWindowSurfaceDirect(); + + QEglContext *ensureContext(QWidget *widget); + void beginPaint(QWidget *widget); + void endPaint(QWidget *widget, const QRegion& region, QImage *image); + QSize surfaceSize() const { return size; } + +protected: + QEglContext *context; + QSize size; + bool isPaintingActive; + bool needToSwap; + EGLSurface windowSurface; +}; + +QT_END_NAMESPACE + +#endif // !QT_NO_EGL + +#endif // QWINDOWSURFACE_VGEGL_P_H diff --git a/src/plugins/graphicssystems/graphicssystems.pro b/src/plugins/graphicssystems/graphicssystems.pro index b8216ff..bfdec6a 100644 --- a/src/plugins/graphicssystems/graphicssystems.pro +++ b/src/plugins/graphicssystems/graphicssystems.pro @@ -1,2 +1,8 @@ TEMPLATE = subdirs contains(QT_CONFIG, opengl):SUBDIRS += opengl +contains(QT_CONFIG, openvg):contains(QT_CONFIG, egl):SUBDIRS += openvg + +contains(QT_CONFIG, shivavg) { + # Only works under X11 at present + !win32:!embedded:!mac:SUBDIRS += shivavg +} diff --git a/src/plugins/graphicssystems/openvg/main.cpp b/src/plugins/graphicssystems/openvg/main.cpp new file mode 100644 index 0000000..265ccd3 --- /dev/null +++ b/src/plugins/graphicssystems/openvg/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qgraphicssystemplugin_p.h> +#include "qgraphicssystem_vg_p.h" + +QT_BEGIN_NAMESPACE + +class QVGGraphicsSystemPlugin : public QGraphicsSystemPlugin +{ +public: + QStringList keys() const; + QGraphicsSystem *create(const QString&); +}; + +QStringList QVGGraphicsSystemPlugin::keys() const +{ + QStringList list; + list << "OpenVG"; + return list; +} + +QGraphicsSystem* QVGGraphicsSystemPlugin::create(const QString& system) +{ + if (system.toLower() == "openvg") + return new QVGGraphicsSystem; + + return 0; +} + +Q_EXPORT_PLUGIN2(openvg, QVGGraphicsSystemPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/openvg/openvg.pro b/src/plugins/graphicssystems/openvg/openvg.pro new file mode 100644 index 0000000..0abbfbd --- /dev/null +++ b/src/plugins/graphicssystems/openvg/openvg.pro @@ -0,0 +1,20 @@ +TARGET = qvggraphicssystem +include(../../qpluginbase.pri) + +QT += openvg + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/graphicssystems + +SOURCES = main.cpp qgraphicssystem_vg.cpp +HEADERS = qgraphicssystem_vg_p.h + +target.path += $$[QT_INSTALL_PLUGINS]/graphicssystems +INSTALLS += target + +!isEmpty(QMAKE_INCDIR_OPENVG): INCLUDEPATH += $$QMAKE_INCDIR_OPENVG +!isEmpty(QMAKE_LIBDIR_OPENVG): LIBS += -L$$QMAKE_LIBDIR_OPENVG +!isEmpty(QMAKE_LIBS_OPENVG): LIBS += $$QMAKE_LIBS_OPENVG + +!isEmpty(QMAKE_INCDIR_EGL): INCLUDEPATH += $$QMAKE_INCDIR_EGL +!isEmpty(QMAKE_LIBDIR_EGL): LIBS += -L$$QMAKE_LIBDIR_EGL +!isEmpty(QMAKE_LIBS_EGL): LIBS += $$QMAKE_LIBS_EGL diff --git a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp new file mode 100644 index 0000000..d975ad7 --- /dev/null +++ b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicssystem_vg_p.h" +#include <QtOpenVG/private/qpixmapdata_vg_p.h> +#include <QtOpenVG/private/qwindowsurface_vg_p.h> + +QT_BEGIN_NAMESPACE + +QVGGraphicsSystem::QVGGraphicsSystem() +{ +} + +QPixmapData *QVGGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const +{ +#if !defined(QVG_NO_SINGLE_CONTEXT) && !defined(QVG_NO_PIXMAP_DATA) + // Pixmaps can use QVGPixmapData; bitmaps must use raster. + if (type == QPixmapData::PixmapType) + return new QVGPixmapData(type); + else + return new QRasterPixmapData(type); +#else + return new QRasterPixmapData(type); +#endif +} + +QWindowSurface *QVGGraphicsSystem::createWindowSurface(QWidget *widget) const +{ + return new QVGWindowSurface(widget); +} + +QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h new file mode 100644 index 0000000..368f48d --- /dev/null +++ b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSYSTEM_VG_P_H +#define QGRAPHICSSYSTEM_VG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qgraphicssystem_p.h> + +QT_BEGIN_NAMESPACE + +class QVGGraphicsSystem : public QGraphicsSystem +{ +public: + QVGGraphicsSystem(); + + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + QWindowSurface *createWindowSurface(QWidget *widget) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/graphicssystems/shivavg/README b/src/plugins/graphicssystems/shivavg/README new file mode 100644 index 0000000..15ee710 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/README @@ -0,0 +1,8 @@ + +This graphics system uses ShivaVG (http://sourceforge.net/projects/shivavg) +to perform OpenVG rendering on X11 systems. The graphics system name for +the "-graphicssystem" command-line option is "ShivaVG". + +ShivaVG support is experimental, mostly to demonstrate how to integrate +non-EGL OpenVG engines into the system. It will probably not produce +good output. diff --git a/src/plugins/graphicssystems/shivavg/main.cpp b/src/plugins/graphicssystems/shivavg/main.cpp new file mode 100644 index 0000000..037bfb2 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qgraphicssystemplugin_p.h> +#include "shivavggraphicssystem.h" + +QT_BEGIN_NAMESPACE + +class ShivaVGGraphicsSystemPlugin : public QGraphicsSystemPlugin +{ +public: + QStringList keys() const; + QGraphicsSystem *create(const QString&); +}; + +QStringList ShivaVGGraphicsSystemPlugin::keys() const +{ + QStringList list; + list << "ShivaVG"; + return list; +} + +QGraphicsSystem* ShivaVGGraphicsSystemPlugin::create(const QString& system) +{ + if (system.toLower() == "shivavg") + return new ShivaVGGraphicsSystem; + + return 0; +} + +Q_EXPORT_PLUGIN2(shivavg, ShivaVGGraphicsSystemPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/shivavg/shivavg.pro b/src/plugins/graphicssystems/shivavg/shivavg.pro new file mode 100644 index 0000000..68345af --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/shivavg.pro @@ -0,0 +1,16 @@ +TARGET = qshivavggraphicssystem +include(../../qpluginbase.pri) + +QT += openvg + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/graphicssystems + +SOURCES = main.cpp shivavggraphicssystem.cpp shivavgwindowsurface.cpp +HEADERS = shivavggraphicssystem.h shivavgwindowsurface.h + +target.path += $$[QT_INSTALL_PLUGINS]/graphicssystems +INSTALLS += target + +!isEmpty(QMAKE_INCDIR_OPENVG): INCLUDEPATH += $$QMAKE_INCDIR_OPENVG +!isEmpty(QMAKE_LIBDIR_OPENVG): LIBS += -L$$QMAKE_LIBDIR_OPENVG +!isEmpty(QMAKE_LIBS_OPENVG): LIBS += $$QMAKE_LIBS_OPENVG diff --git a/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.cpp b/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.cpp new file mode 100644 index 0000000..fb60a42 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shivavggraphicssystem.h" +#include "shivavgwindowsurface.h" +#include <QtGui/private/qpixmap_raster_p.h> + +QT_BEGIN_NAMESPACE + +ShivaVGGraphicsSystem::ShivaVGGraphicsSystem() +{ +} + +QPixmapData *ShivaVGGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const +{ + return new QRasterPixmapData(type); +} + +QWindowSurface *ShivaVGGraphicsSystem::createWindowSurface(QWidget *widget) const +{ + return new ShivaVGWindowSurface(widget); +} + +QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.h b/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.h new file mode 100644 index 0000000..1c9ec70 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/shivavggraphicssystem.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHIVAVGGRAPHICSSYSTEM_H +#define SHIVAVGGRAPHICSSYSTEM_H + +#include <QtGui/private/qgraphicssystem_p.h> + +QT_BEGIN_NAMESPACE + +class ShivaVGGraphicsSystem : public QGraphicsSystem +{ +public: + ShivaVGGraphicsSystem(); + + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + QWindowSurface *createWindowSurface(QWidget *widget) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.cpp b/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.cpp new file mode 100644 index 0000000..bf1f942 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.cpp @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define GL_GLEXT_PROTOTYPES +#include "shivavgwindowsurface.h" +#include <QtOpenVG/private/qpaintengine_vg_p.h> +#if defined(Q_WS_X11) +#include "private/qt_x11_p.h" +#include "qx11info_x11.h" +#include <GL/glx.h> + +extern QX11Info *qt_x11Info(const QPaintDevice *pd); +#endif + +// Define this to use framebuffer objects. +//#define QVG_USE_FBO 1 + +#include <vg/openvg.h> + +QT_BEGIN_NAMESPACE + +class QShivaContext +{ +public: + QShivaContext(); + ~QShivaContext(); + + bool makeCurrent(ShivaVGWindowSurfacePrivate *surface); + void doneCurrent(); + + bool initialized; + QSize currentSize; + ShivaVGWindowSurfacePrivate *currentSurface; +}; + +Q_GLOBAL_STATIC(QShivaContext, shivaContext); + +class ShivaVGWindowSurfacePrivate +{ +public: + ShivaVGWindowSurfacePrivate() + : isCurrent(false) + , needsResize(true) + , engine(0) +#if defined(QVG_USE_FBO) + , fbo(0) + , texture(0) +#endif +#if defined(Q_WS_X11) + , drawable(0) + , context(0) +#endif + { + } + ~ShivaVGWindowSurfacePrivate(); + + void ensureContext(QWidget *widget); + + QSize size; + bool isCurrent; + bool needsResize; + QVGPaintEngine *engine; +#if defined(QVG_USE_FBO) + GLuint fbo; + GLuint texture; +#endif +#if defined(Q_WS_X11) + GLXDrawable drawable; + GLXContext context; +#endif +}; + +QShivaContext::QShivaContext() + : initialized(false) + , currentSurface(0) +{ +} + +QShivaContext::~QShivaContext() +{ + if (initialized) + vgDestroyContextSH(); +} + +bool QShivaContext::makeCurrent(ShivaVGWindowSurfacePrivate *surface) +{ + if (currentSurface) + currentSurface->isCurrent = false; + surface->isCurrent = true; + currentSurface = surface; + currentSize = surface->size; +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, surface->drawable, surface->context); +#endif + if (!initialized) { + if (!vgCreateContextSH(currentSize.width(), currentSize.height())) { + qWarning("vgCreateContextSH(%d, %d): could not create context", currentSize.width(), currentSize.height()); + return false; + } + initialized = true; + } else { + vgResizeSurfaceSH(currentSize.width(), currentSize.height()); + } +#if defined(QVG_USE_FBO) + if (surface->fbo) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, surface->fbo); + else + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +#endif + return true; +} + +void QShivaContext::doneCurrent() +{ + if (currentSurface) { + currentSurface->isCurrent = false; + currentSurface = 0; + } +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, 0, 0); +#endif +} + +ShivaVGWindowSurfacePrivate::~ShivaVGWindowSurfacePrivate() +{ +#if defined(QVG_USE_FBO) + if (fbo) { + glDeleteTextures(1, &texture); + glDeleteFramebuffersEXT(1, &fbo); + } +#endif +} + +void ShivaVGWindowSurfacePrivate::ensureContext(QWidget *widget) +{ +#if defined(Q_WS_X11) + Window win = widget->winId(); + if (win != drawable) { + if (context) + glXDestroyContext(X11->display, context); + drawable = win; + } + if (context == 0) { + const QX11Info *xinfo = qt_x11Info(widget); + int spec[64]; + int i = 0; + spec[i++] = GLX_DOUBLEBUFFER; + spec[i++] = GLX_DEPTH_SIZE; + spec[i++] = 1; + spec[i++] = GLX_STENCIL_SIZE; + spec[i++] = 1; + spec[i++] = GLX_RGBA; + spec[i++] = GLX_RED_SIZE; + spec[i++] = 1; + spec[i++] = GLX_GREEN_SIZE; + spec[i++] = 1; + spec[i++] = GLX_BLUE_SIZE; + spec[i++] = 1; + spec[i++] = GLX_SAMPLE_BUFFERS_ARB; + spec[i++] = 1; + spec[i++] = GLX_SAMPLES_ARB; + spec[i++] = 4; + spec[i] = XNone; + XVisualInfo *visual = glXChooseVisual + (xinfo->display(), xinfo->screen(), spec); + context = glXCreateContext(X11->display, visual, 0, True); + if (!context) + qWarning("glXCreateContext: could not create GL context for VG rendering"); + } +#else + Q_UNUSED(widget); +#endif +#if defined(QVG_USE_FBO) + if (needsResize && fbo) { +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, drawable, context); +#endif + glDeleteTextures(1, &texture); + glDeleteFramebuffersEXT(1, &fbo); +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, 0, 0); +#endif + fbo = 0; + texture = 0; + } + if (!fbo) { +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, drawable, context); +#endif + glGenFramebuffersEXT(1, &fbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2DEXT + (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, + texture, 0); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +#if defined(Q_WS_X11) + glXMakeCurrent(X11->display, 0, 0); +#endif + } +#endif + needsResize = false; +} + +ShivaVGWindowSurface::ShivaVGWindowSurface(QWidget *window) + : QWindowSurface(window), d_ptr(new ShivaVGWindowSurfacePrivate) +{ +} + +ShivaVGWindowSurface::~ShivaVGWindowSurface() +{ + if (d_ptr->isCurrent) { + shivaContext()->doneCurrent(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } +#if defined(Q_WS_X11) + if (d_ptr->context) + glXDestroyContext(X11->display, d_ptr->context); +#endif + delete d_ptr; +} + +QPaintDevice *ShivaVGWindowSurface::paintDevice() +{ + d_ptr->ensureContext(window()); + shivaContext()->makeCurrent(d_ptr); + glClearDepth(0.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + return this; +} + +void ShivaVGWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget(); + d_ptr->ensureContext(parent); + QShivaContext *context = shivaContext(); + if (!d_ptr->isCurrent) + context->makeCurrent(d_ptr); +#if defined(QVG_USE_FBO) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + if (d_ptr->fbo) { + static GLfloat const vertices[][2] = { + {-1, -1}, {1, -1}, {1, 1}, {-1, 1} + }; + static GLfloat const texCoords[][2] = { + {0, 0}, {1, 0}, {1, 1}, {0, 1} + }; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glVertexPointer(2, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glBindTexture(GL_TEXTURE_2D, d_ptr->texture); + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } +#endif +#if defined(Q_WS_X11) + glXSwapBuffers(X11->display, d_ptr->drawable); +#endif + context->doneCurrent(); +} + +void ShivaVGWindowSurface::setGeometry(const QRect &rect) +{ + QWindowSurface::setGeometry(rect); + d_ptr->needsResize = true; + d_ptr->size = rect.size(); +} + +bool ShivaVGWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ + return QWindowSurface::scroll(area, dx, dy); +} + +void ShivaVGWindowSurface::beginPaint(const QRegion ®ion) +{ + // Nothing to do here. + Q_UNUSED(region); +} + +void ShivaVGWindowSurface::endPaint(const QRegion ®ion) +{ + // Nothing to do here. + Q_UNUSED(region); +} + +Q_GLOBAL_STATIC(QVGPaintEngine, sharedPaintEngine); + +QPaintEngine *ShivaVGWindowSurface::paintEngine() const +{ + if (!d_ptr->engine) + d_ptr->engine = sharedPaintEngine(); + return d_ptr->engine; +} + +// We need to get access to QWidget::metric() from ShivaVGWindowSurface::metric, +// but it is not a friend of QWidget. To get around this, we create a +// fake QX11PaintEngine class, which is a friend. +class QX11PaintEngine +{ +public: + static int metric(const QWidget *widget, QPaintDevice::PaintDeviceMetric met) + { + return widget->metric(met); + } +}; + +int ShivaVGWindowSurface::metric(PaintDeviceMetric met) const +{ + return QX11PaintEngine::metric(window(), met); +} + +QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.h b/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.h new file mode 100644 index 0000000..5211ba4 --- /dev/null +++ b/src/plugins/graphicssystems/shivavg/shivavgwindowsurface.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHIVAVGWINDOWSURFACE_H +#define SHIVAVGWINDOWSURFACE_H + +#include <QtGui/private/qwindowsurface_p.h> + +QT_BEGIN_NAMESPACE + +class ShivaVGWindowSurfacePrivate; + +class ShivaVGWindowSurface : public QWindowSurface, public QPaintDevice +{ +public: + ShivaVGWindowSurface(QWidget *window); + virtual ~ShivaVGWindowSurface(); + + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + bool scroll(const QRegion &area, int dx, int dy); + + void beginPaint(const QRegion ®ion); + void endPaint(const QRegion ®ion); + + QPaintEngine *paintEngine() const; + +protected: + int metric(PaintDeviceMetric metric) const; + +private: + ShivaVGWindowSurfacePrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/src.pro b/src/src.pro index f40c6ad..9bff20d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -19,6 +19,7 @@ win32:{ } contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles2): SRC_SUBDIRS += src_opengl +contains(QT_CONFIG, openvg): SRC_SUBDIRS += src_openvg contains(QT_CONFIG, xmlpatterns): SRC_SUBDIRS += src_xmlpatterns contains(QT_CONFIG, phonon): SRC_SUBDIRS += src_phonon contains(QT_CONFIG, svg): SRC_SUBDIRS += src_svg @@ -61,6 +62,8 @@ src_scripttools.subdir = $$QT_SOURCE_TREE/src/scripttools src_scripttools.target = sub-scripttools src_opengl.subdir = $$QT_SOURCE_TREE/src/opengl src_opengl.target = sub-opengl +src_openvg.subdir = $$QT_SOURCE_TREE/src/openvg +src_openvg.target = sub-openvg src_qt3support.subdir = $$QT_SOURCE_TREE/src/qt3support src_qt3support.target = sub-qt3support src_phonon.subdir = $$QT_SOURCE_TREE/src/phonon @@ -96,6 +99,7 @@ src_webkit.target = sub-webkit src_scripttools.depends = src_script src_gui src_network src_network.depends = src_corelib src_opengl.depends = src_gui + src_openvg.depends = src_gui src_sql.depends = src_corelib src_testlib.depends = src_corelib src_qt3support.depends = src_gui src_xml src_network src_sql |