/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the plugins of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** 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. | |
** | |
** 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. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $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; | |
} | |
int ShivaVGWindowSurface::metric(PaintDeviceMetric met) const | |
{ | |
return qt_paint_device_metric(window(), met); | |
} | |
QT_END_NAMESPACE |