/**************************************************************************** ** ** Copyright (C) 2012 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$ ** ****************************************************************************/ #include "qmeegolivepixmapdata.h" #include "qmeegorasterpixmapdata.h" #include #include #include #include #include #include #include static QMeeGoLivePixmapDataList all_live_pixmaps; static EGLint lock_attribs[] = { EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR | EGL_WRITE_SURFACE_BIT_KHR, EGL_NONE }; static EGLint preserved_attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; // as copied from qwindowsurface.cpp void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset) { // make sure we don't detach uchar *mem = const_cast(const_cast(img).bits()); int lineskip = img.bytesPerLine(); int depth = img.depth() >> 3; const QRect imageRect(0, 0, img.width(), img.height()); const QRect r = rect & imageRect & imageRect.translated(-offset); const QPoint p = rect.topLeft() + offset; if (r.isEmpty()) return; const uchar *src; uchar *dest; if (r.top() < p.y()) { src = mem + r.bottom() * lineskip + r.left() * depth; dest = mem + (p.y() + r.height() - 1) * lineskip + p.x() * depth; lineskip = -lineskip; } else { src = mem + r.top() * lineskip + r.left() * depth; dest = mem + p.y() * lineskip + p.x() * depth; } const int w = r.width(); int h = r.height(); const int bytes = w * depth; // overlapping segments? if (offset.y() == 0 && qAbs(offset.x()) < w) { do { ::memmove(dest, src, bytes); dest += lineskip; src += lineskip; } while (--h); } else { do { ::memcpy(dest, src, bytes); dest += lineskip; src += lineskip; } while (--h); } } /* Public */ QMeeGoLivePixmapData::QMeeGoLivePixmapData(int w, int h, QImage::Format format) : QGLPixmapData(QPixmapData::PixmapType) { QImage image(w, h, format); QX11PixmapData *pmd = new QX11PixmapData(QPixmapData::PixmapType); pmd->fromImage(image, Qt::NoOpaqueDetection); backingX11Pixmap = new QPixmap(pmd); initializeThroughEGLImage(); pos = all_live_pixmaps.insert(all_live_pixmaps.begin(), this); } QMeeGoLivePixmapData::QMeeGoLivePixmapData(Qt::HANDLE h) : QGLPixmapData(QPixmapData::PixmapType) { backingX11Pixmap = new QPixmap(QPixmap::fromX11Pixmap(h)); initializeThroughEGLImage(); pos = all_live_pixmaps.insert(all_live_pixmaps.begin(), this); } QMeeGoLivePixmapData::~QMeeGoLivePixmapData() { delete backingX11Pixmap; all_live_pixmaps.erase(pos); } void QMeeGoLivePixmapData::initializeThroughEGLImage() { if (texture()->id != 0) return; QGLShareContextScope ctx(qt_gl_share_widget()->context()); QMeeGoExtensions::ensureInitialized(); EGLImageKHR eglImage = EGL_NO_IMAGE_KHR; GLuint newTextureId = 0; eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer) backingX11Pixmap->handle(), preserved_attribs); if (eglImage == EGL_NO_IMAGE_KHR) { qWarning("eglCreateImageKHR failed (live texture)!"); return; } glGenTextures(1, &newTextureId); glBindTexture(GL_TEXTURE_2D, newTextureId); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (EGLImageKHR) eglImage); if (glGetError() == GL_NO_ERROR) { resize(backingX11Pixmap->width(), backingX11Pixmap->height()); texture()->id = newTextureId; texture()->options &= ~QGLContext::InvertedYBindOption; m_hasAlpha = backingX11Pixmap->hasAlphaChannel(); } else { qWarning("Failed to create a texture from an egl image (live texture)!"); glDeleteTextures(1, &newTextureId); } QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); } QPixmapData *QMeeGoLivePixmapData::createCompatiblePixmapData() const { qWarning("Create compatible called on live pixmap! Expect fail soon..."); return new QMeeGoRasterPixmapData(pixelType()); } QImage* QMeeGoLivePixmapData::lock(EGLSyncKHR fenceSync) { QGLShareContextScope ctx(qt_gl_share_widget()->context()); QMeeGoExtensions::ensureInitialized(); if (fenceSync) { QMeeGoExtensions::eglClientWaitSyncKHR(QEgl::display(), fenceSync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); } void *data = 0; int pitch = 0; int surfaceWidth = 0; int surfaceHeight = 0; EGLSurface surface = 0; QImage::Format format; lockedImage = QImage(); surface = getSurfaceForBackingPixmap(); if (! QMeeGoExtensions::eglLockSurfaceKHR(QEgl::display(), surface, lock_attribs)) { qWarning("Failed to lock surface (live texture)!"); return &lockedImage; } eglQuerySurface(QEgl::display(), surface, EGL_BITMAP_POINTER_KHR, (EGLint*) &data); eglQuerySurface(QEgl::display(), surface, EGL_BITMAP_PITCH_KHR, (EGLint*) &pitch); eglQuerySurface(QEgl::display(), surface, EGL_WIDTH, (EGLint*) &surfaceWidth); eglQuerySurface(QEgl::display(), surface, EGL_HEIGHT, (EGLint*) &surfaceHeight); // Ok, here we know we just support those two formats. Real solution would be: // query also the format. if (backingX11Pixmap->depth() > 16) format = QImage::Format_ARGB32_Premultiplied; else format = QImage::Format_RGB16; if (data == NULL || pitch == 0) { qWarning("Failed to query the live texture!"); return &lockedImage; } if (width() != surfaceWidth || height() != surfaceHeight) { qWarning("Live texture dimensions don't match!"); QMeeGoExtensions::eglUnlockSurfaceKHR(QEgl::display(), surface); return &lockedImage; } lockedImage = QImage((uchar *) data, width(), height(), pitch, format); return &lockedImage; } bool QMeeGoLivePixmapData::release(QImage* /*img*/) { QGLShareContextScope ctx(qt_gl_share_widget()->context()); QMeeGoExtensions::ensureInitialized(); if (QMeeGoExtensions::eglUnlockSurfaceKHR(QEgl::display(), getSurfaceForBackingPixmap())) { lockedImage = QImage(); return true; } else { lockedImage = QImage(); return false; } } Qt::HANDLE QMeeGoLivePixmapData::handle() { return backingX11Pixmap->handle(); } bool QMeeGoLivePixmapData::scroll(int dx, int dy, const QRect &rect) { lock(NULL); if (!lockedImage.isNull()) qt_scrollRectInImage(lockedImage, rect, QPoint(dx, dy)); release(&lockedImage); return true; } EGLSurface QMeeGoLivePixmapData::getSurfaceForBackingPixmap() { initializeThroughEGLImage(); // This code is a crative remix of the stuff that can be found in the // Qt's TFP implementation in /src/opengl/qgl_x11egl.cpp ::bindiTextureFromNativePixmap QX11PixmapData *pixmapData = static_cast(backingX11Pixmap->data_ptr().data()); Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class); bool hasAlpha = pixmapData->hasAlphaChannel(); if (pixmapData->gl_surface && hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) return pixmapData->gl_surface; // Check to see if the surface is still valid if (pixmapData->gl_surface && hasAlpha != ((pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha) > 0)) { // Surface is invalid! destroySurfaceForPixmapData(pixmapData); } if (pixmapData->gl_surface == 0) { EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, QEgl::OpenGL, hasAlpha ? QEgl::Translucent : QEgl::NoOptions); pixmapData->gl_surface = (void*)QEgl::createSurface(backingX11Pixmap, config); if (hasAlpha) pixmapData->flags |= QX11PixmapData::GlSurfaceCreatedWithAlpha; else pixmapData->flags &= ~QX11PixmapData::GlSurfaceCreatedWithAlpha; if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) return NULL; } return pixmapData->gl_surface; } void QMeeGoLivePixmapData::destroySurfaceForPixmapData(QPixmapData* pmd) { Q_ASSERT(pmd->classId() == QPixmapData::X11Class); QX11PixmapData *pixmapData = static_cast(pmd); if (pixmapData->gl_surface) { eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); pixmapData->gl_surface = 0; } } void QMeeGoLivePixmapData::invalidateSurfaces() { foreach (QMeeGoLivePixmapData *data, all_live_pixmaps) { QX11PixmapData *pixmapData = static_cast(data->backingX11Pixmap->data_ptr().data()); *data->texture() = QGLTexture(); pixmapData->gl_surface = 0; } }