src/openvg/qwindowsurface_vgegl.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openvg/qwindowsurface_vgegl.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,694 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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;
+};
+
+QVGSharedContext::QVGSharedContext()
+    : context(0)
+    , refCount(0)
+    , engine(0)
+    , surface(EGL_NO_SURFACE)
+{
+}
+
+QVGSharedContext::~QVGSharedContext()
+{
+    // Don't accidentally destroy the QEglContext if the reference
+    // count falls to zero while deleting the paint engine.
+    ++refCount;
+
+    if (context)
+        context->makeCurrent(qt_vg_shared_surface());
+    delete engine;
+    if (context)
+        context->doneCurrent();
+    if (context && surface != EGL_NO_SURFACE)
+        context->destroySurface(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
+#ifndef QVG_SCISSOR_CLIP
+    // If we are using the mask to clip, then explicitly request a mask.
+    configProps.setValue(EGL_ALPHA_MASK_SIZE, 1);
+#endif
+#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) {
+        shared->context->makeCurrent(qt_vg_shared_surface());
+        delete shared->engine;
+        shared->engine = 0;
+        shared->context->doneCurrent();
+        if (shared->surface != EGL_NO_SURFACE) {
+            eglDestroySurface(shared->context->display(), 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;
+}
+
+#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;
+}
+
+#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.
+            context->makeCurrent(qt_vg_shared_surface());
+            context->destroySurface(backBufferSurface);
+            vgDestroyImage(backBuffer);
+            context->doneCurrent();
+        }
+        if (windowSurface != EGL_NO_SURFACE)
+            context->destroySurface(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)
+            context->doneCurrent();
+        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.
+            context->makeCurrent(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)
+            context->makeCurrent(backBufferSurface);
+        else
+            context->makeCurrent(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();
+            context->lazyDoneCurrent();
+        }
+        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();
+            context->makeCurrent(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]);
+            }
+            context->lazyDoneCurrent();
+        }
+        isPaintingActive = false;
+    }
+}
+
+#endif // QVG_VGIMAGE_BACKBUFFERS
+
+QVGEGLWindowSurfaceDirect::QVGEGLWindowSurfaceDirect(QWindowSurface *win)
+    : QVGEGLWindowSurfacePrivate(win)
+    , context(0)
+    , isPaintingActive(false)
+    , needToSwap(false)
+    , windowSurface(EGL_NO_SURFACE)
+{
+}
+
+QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect()
+{
+    destroyPaintEngine();
+    if (context) {
+        if (windowSurface != EGL_NO_SURFACE)
+            context->destroySurface(windowSurface);
+        qt_vg_destroy_context(context);
+    }
+}
+
+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)
+            context->doneCurrent();
+        context->destroySurface(windowSurface);
+#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
+        windowSurface = context->createSurface(widget, &surfaceProps);
+        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->destroySurface(windowSurface);
+        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
+        EGLSurface surface = context->createSurface(widget, &surfaceProps);
+        if (surface == EGL_NO_SURFACE) {
+            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(), surface,
+                             EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+            if (eglGetError() != EGL_SUCCESS) {
+                qWarning("QVG: could not enable preserved swap");
+            }
+        }
+#endif
+        windowSurface = surface;
+        isPaintingActive = false;
+    }
+    return context;
+}
+
+void QVGEGLWindowSurfaceDirect::beginPaint(QWidget *widget)
+{
+    QEglContext *context = ensureContext(widget);
+    if (context) {
+        context->makeCurrent(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)
+                context->makeCurrent(windowSurface);
+            context->swapBuffers(windowSurface);
+            context->lazyDoneCurrent();
+        } else if (isPaintingActive) {
+            vgFlush();
+            context->lazyDoneCurrent();
+        }
+        isPaintingActive = false;
+    }
+}
+
+QT_END_NAMESPACE
+
+#endif