diff options
Diffstat (limited to 'src/openvg/qvg_symbian.cpp')
-rw-r--r-- | src/openvg/qvg_symbian.cpp | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/src/openvg/qvg_symbian.cpp b/src/openvg/qvg_symbian.cpp new file mode 100644 index 0000000..0e6e773 --- /dev/null +++ b/src/openvg/qvg_symbian.cpp @@ -0,0 +1,398 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** 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 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 "qpixmapdata_vg_p.h" +#include "qvgfontglyphcache_p.h" +#include <private/qt_s60_p.h> + +#include <fbs.h> +#include <bitdev.h> + +#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE +# include <sgresource/sgimage.h> +# ifdef SYMBIAN_FBSERV_GLYPHDATA // defined in fbs.h +# define QT_SYMBIAN_HARDWARE_GLYPH_CACHE +# include <graphics/fbsglyphdataiterator.h> +# include <private/qfontengine_s60_p.h> +# endif +#endif + +QT_BEGIN_NAMESPACE + +typedef VGImage (*_vgCreateEGLImageTargetKHR)(VGeglImageKHR); +static _vgCreateEGLImageTargetKHR qt_vgCreateEGLImageTargetKHR = 0; + +namespace QVG +{ + VGImage vgCreateEGLImageTargetKHR(VGeglImageKHR eglImage); +} + +VGImage QVG::vgCreateEGLImageTargetKHR(VGeglImageKHR eglImage) +{ + if (!qt_vgCreateEGLImageTargetKHR && QEgl::hasExtension("EGL_KHR_image")) + qt_vgCreateEGLImageTargetKHR = (_vgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR"); + + return qt_vgCreateEGLImageTargetKHR ? qt_vgCreateEGLImageTargetKHR(eglImage) : 0; +} + +extern int qt_vg_pixmap_serial; + +static CFbsBitmap* createBlitCopy(CFbsBitmap* bitmap) +{ + CFbsBitmap *copy = q_check_ptr(new CFbsBitmap); + if(!copy) + return 0; + + if (copy->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) { + delete copy; + copy = 0; + + return 0; + } + + CFbsBitmapDevice* bitmapDevice = 0; + CFbsBitGc *bitmapGc = 0; + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(copy)); + QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); + bitmapGc->Activate(bitmapDevice); + + bitmapGc->BitBlt(TPoint(), bitmap); + + delete bitmapGc; + delete bitmapDevice; + + return copy; +} + +#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE +static VGImage sgImageToVGImage(QEglContext *context, const RSgImage &sgImage) +{ + // when "0" used as argument then + // default display, context are used + if (!context) + context = qt_vg_create_context(0, QInternal::Pixmap); + + VGImage vgImage = VG_INVALID_HANDLE; + + TInt err = 0; + + RSgDriver driver; + err = driver.Open(); + if (err != KErrNone) { + return vgImage; + } + + if (sgImage.IsNull()) { + driver.Close(); + return vgImage; + } + + TSgImageInfo sgImageInfo; + err = sgImage.GetInfo(sgImageInfo); + if (err != KErrNone) { + driver.Close(); + return vgImage; + } + + const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE}; + EGLImageKHR eglImage = QEgl::eglCreateImageKHR(QEgl::display(), + EGL_NO_CONTEXT, + EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)&sgImage, + (EGLint*)KEglImageAttribs); + + if (!eglImage || eglGetError() != EGL_SUCCESS) { + driver.Close(); + return vgImage; + } + + vgImage = QVG::vgCreateEGLImageTargetKHR(eglImage); + if (!vgImage || vgGetError() != VG_NO_ERROR) { + QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); + driver.Close(); + return vgImage; + } + + //setSerialNumber(++qt_vg_pixmap_serial); + // release stuff + QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); + driver.Close(); + return vgImage; +} +#endif + +void QVGPixmapData::cleanup() +{ + is_null = w = h = 0; + recreate = false; + source = QImage(); +} + +void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) +{ + if (type == QPixmapData::SgImage && pixmap) { +#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL) + RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmap); + destroyImages(); + prevSize = QSize(); + + VGImage vgImage = sgImageToVGImage(context, *sgImage); + if (vgImage != VG_INVALID_HANDLE) { + w = vgGetParameteri(vgImage, VG_IMAGE_WIDTH); + h = vgGetParameteri(vgImage, VG_IMAGE_HEIGHT); + d = 32; // We always use ARGB_Premultiplied for VG pixmaps. + } + + is_null = (w <= 0 || h <= 0); + source = QImage(); // vgGetImageSubData() some day? + recreate = false; + prevSize = QSize(w, h); + //setSerialNumber(++qt_vg_pixmap_serial); +#endif + } else if (type == QPixmapData::FbsBitmap) { + CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap); + + bool deleteSourceBitmap = false; + +#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE + + // Rasterize extended bitmaps + + TUid extendedBitmapType = bitmap->ExtendedBitmapType(); + if (extendedBitmapType != KNullUid) { + bitmap = createBlitCopy(bitmap); + deleteSourceBitmap = true; + } +#endif + + if (bitmap->IsCompressedInRAM()) { + bitmap = createBlitCopy(bitmap); + deleteSourceBitmap = true; + } + + TDisplayMode displayMode = bitmap->DisplayMode(); + QImage::Format format = qt_TDisplayMode2Format(displayMode); + + TSize size = bitmap->SizeInPixels(); + + bitmap->BeginDataAccess(); + uchar *bytes = (uchar*)bitmap->DataAddress(); + QImage img = QImage(bytes, size.iWidth, size.iHeight, format); + img = img.copy(); + bitmap->EndDataAccess(); + + if(displayMode == EGray2) { + //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid + //So invert mono bitmaps so that masks work correctly. + img.invertPixels(); + } else if(displayMode == EColor16M) { + img = img.rgbSwapped(); // EColor16M is BGR + } + + fromImage(img, Qt::AutoColor); + + if(deleteSourceBitmap) + delete bitmap; + } +} + +void* QVGPixmapData::toNativeType(NativeType type) +{ + if (type == QPixmapData::SgImage) { +#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL) + toVGImage(); + + if (!isValid() || vgImage == VG_INVALID_HANDLE) + return 0; + + TInt err = 0; + + RSgDriver driver; + err = driver.Open(); + if (err != KErrNone) + return 0; + + TSgImageInfo sgInfo; + sgInfo.iPixelFormat = EUidPixelFormatARGB_8888_PRE; + sgInfo.iSizeInPixels.SetSize(w, h); + sgInfo.iUsage = ESgUsageBitOpenVgImage | ESgUsageBitOpenVgSurface; + + RSgImage *sgImage = q_check_ptr(new RSgImage()); + err = sgImage->Create(sgInfo, NULL, NULL); + if (err != KErrNone) { + driver.Close(); + return 0; + } + + const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE}; + EGLImageKHR eglImage = QEgl::eglCreateImageKHR(QEgl::display(), + EGL_NO_CONTEXT, + EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)sgImage, + (EGLint*)KEglImageAttribs); + if (!eglImage || eglGetError() != EGL_SUCCESS) { + sgImage->Close(); + driver.Close(); + return 0; + } + + VGImage dstVgImage = QVG::vgCreateEGLImageTargetKHR(eglImage); + if (!dstVgImage || vgGetError() != VG_NO_ERROR) { + QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); + sgImage->Close(); + driver.Close(); + return 0; + } + + vgCopyImage(dstVgImage, 0, 0, + vgImage, 0, 0, + w, h, VG_FALSE); + + if (vgGetError() != VG_NO_ERROR) { + sgImage->Close(); + sgImage = 0; + } + // release stuff + vgDestroyImage(dstVgImage); + QEgl::eglDestroyImageKHR(QEgl::display(), eglImage); + driver.Close(); + return reinterpret_cast<void*>(sgImage); +#endif + } else if (type == QPixmapData::FbsBitmap) { + CFbsBitmap *bitmap = q_check_ptr(new CFbsBitmap); + + if (bitmap) { + if (bitmap->Create(TSize(source.width(), source.height()), + EColor16MAP) == KErrNone) { + const uchar *sptr = const_cast<const QImage&>(source).bits(); + bitmap->BeginDataAccess(); + + uchar *dptr = (uchar*)bitmap->DataAddress(); + Mem::Copy(dptr, sptr, source.byteCount()); + + bitmap->EndDataAccess(); + } else { + delete bitmap; + bitmap = 0; + } + } + + return reinterpret_cast<void*>(bitmap); + } + return 0; +} + +QSymbianVGFontGlyphCache::QSymbianVGFontGlyphCache() : QVGFontGlyphCache() +{ +#ifdef QT_SYMBIAN_HARDWARE_GLYPH_CACHE + invertedGlyphs = true; +#endif +} + +void QSymbianVGFontGlyphCache::cacheGlyphs(QVGPaintEnginePrivate *d, + QFontEngine *fontEngine, + const glyph_t *g, int count) +{ +#ifdef QT_SYMBIAN_HARDWARE_GLYPH_CACHE + QFontEngineS60 *s60fontEngine = static_cast<QFontEngineS60*>(fontEngine); + if (s60fontEngine->m_activeFont->TypeUid() != KCFbsFontUid) + return QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count); + + QVector<glyph_t> uncachedGlyphs; + while (count-- > 0) { + // Skip this glyph if we have already cached it before. + glyph_t glyph = *g++; + if (((glyph < 256) && ((cachedGlyphsMask[glyph / 32] & (1 << (glyph % 32))) != 0)) + || cachedGlyphs.contains(glyph)) + continue; + if (!uncachedGlyphs.contains(glyph)) + uncachedGlyphs.append(glyph); + } + + if (!uncachedGlyphs.isEmpty()) { + CFbsFont *cfbsFont = static_cast<CFbsFont *>(s60fontEngine->m_activeFont); + RFbsGlyphDataIterator iter; + + int err = iter.Open(*cfbsFont, (const unsigned int*)uncachedGlyphs.constData(), uncachedGlyphs.count()); + + if (err == KErrNotSupported || err == KErrInUse) { // Fallback in possibly supported error cases + iter.Close(); + qWarning("Falling back to default QVGFontGlyphCache"); + return QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count); + } + + for (; err == KErrNone; err = iter.Next()) { + const unsigned int glyph = iter.GlyphCode(); + + const RSgImage& image = iter.Image(); + const TOpenFontCharMetrics& metrics = iter.Metrics(); + + TRect glyphBounds; + metrics.GetHorizBounds(glyphBounds); + VGImage vgImage = sgImageToVGImage(0, image); + VGfloat origin[2]; + VGfloat escapement[2]; + origin[0] = -glyphBounds.iTl.iX; + origin[1] = glyphBounds.iBr.iY; + escapement[0] = 0; + escapement[1] = 0; + vgSetGlyphToImage(font, glyph, vgImage, origin, escapement); + vgDestroyImage(vgImage); + + // Add to cache + if (glyph < 256) + cachedGlyphsMask[glyph / 32] |= (1 << (glyph % 32)); + else + cachedGlyphs.insert(glyph); + } + iter.Close(); + + if (err == KErrNoMemory || err == KErrNoGraphicsMemory) + qWarning("Not enough memory to cache glyph"); + else if (err != KErrNotFound) + qWarning("Received error %d from glyph cache", err); + } +#else + QVGFontGlyphCache::cacheGlyphs(d, fontEngine, g, count); +#endif +} + +QT_END_NAMESPACE |