source: trunk/src/openvg/qpixmapdata_vg.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 19.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtOpenVG module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qpixmapdata_vg_p.h"
43#include "qpaintengine_vg_p.h"
44#include <QtGui/private/qdrawhelper_p.h>
45#include "qvg_p.h"
46#include "qvgimagepool_p.h"
47
48#if defined(Q_OS_SYMBIAN)
49#include <private/qt_s60_p.h>
50#include <fbs.h>
51#endif
52#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE
53#include <sgresource/sgimage.h>
54typedef EGLImageKHR (*pfnEglCreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, EGLint*);
55typedef EGLBoolean (*pfnEglDestroyImageKHR)(EGLDisplay, EGLImageKHR);
56typedef VGImage (*pfnVgCreateEGLImageTargetKHR)(VGeglImageKHR);
57#endif // QT_SYMBIAN_SUPPORTS_SGIMAGE
58
59QT_BEGIN_NAMESPACE
60
61static int qt_vg_pixmap_serial = 0;
62
63QVGPixmapData::QVGPixmapData(PixelType type)
64 : QPixmapData(type, OpenVGClass)
65{
66 Q_ASSERT(type == QPixmapData::PixmapType);
67 vgImage = VG_INVALID_HANDLE;
68 vgImageOpacity = VG_INVALID_HANDLE;
69 cachedOpacity = 1.0f;
70 recreate = true;
71 inImagePool = false;
72 inLRU = false;
73#if !defined(QT_NO_EGL)
74 context = 0;
75 qt_vg_register_pixmap(this);
76#endif
77 setSerialNumber(++qt_vg_pixmap_serial);
78}
79
80QVGPixmapData::~QVGPixmapData()
81{
82 destroyImageAndContext();
83#if !defined(QT_NO_EGL)
84 qt_vg_unregister_pixmap(this);
85#endif
86}
87
88void QVGPixmapData::destroyImages()
89{
90 if (inImagePool) {
91 QVGImagePool *pool = QVGImagePool::instance();
92 if (vgImage != VG_INVALID_HANDLE)
93 pool->releaseImage(this, vgImage);
94 if (vgImageOpacity != VG_INVALID_HANDLE)
95 pool->releaseImage(this, vgImageOpacity);
96 } else {
97 if (vgImage != VG_INVALID_HANDLE)
98 vgDestroyImage(vgImage);
99 if (vgImageOpacity != VG_INVALID_HANDLE)
100 vgDestroyImage(vgImageOpacity);
101 }
102 vgImage = VG_INVALID_HANDLE;
103 vgImageOpacity = VG_INVALID_HANDLE;
104 inImagePool = false;
105}
106
107void QVGPixmapData::destroyImageAndContext()
108{
109 if (vgImage != VG_INVALID_HANDLE) {
110 // We need to have a context current to destroy the image.
111#if !defined(QT_NO_EGL)
112 if (context->isCurrent()) {
113 destroyImages();
114 } else {
115 // We don't currently have a widget surface active, but we
116 // need a surface to make the context current. So use the
117 // shared pbuffer surface instead.
118 context->makeCurrent(qt_vg_shared_surface());
119 destroyImages();
120 context->lazyDoneCurrent();
121 }
122#else
123 destroyImages();
124#endif
125 }
126#if !defined(QT_NO_EGL)
127 if (context) {
128 qt_vg_destroy_context(context, QInternal::Pixmap);
129 context = 0;
130 }
131#endif
132 recreate = true;
133}
134
135QPixmapData *QVGPixmapData::createCompatiblePixmapData() const
136{
137 return new QVGPixmapData(pixelType());
138}
139
140bool QVGPixmapData::isValid() const
141{
142 return (w > 0 && h > 0);
143}
144
145void QVGPixmapData::resize(int wid, int ht)
146{
147 if (w == wid && h == ht)
148 return;
149
150 w = wid;
151 h = ht;
152 d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
153 is_null = (w <= 0 || h <= 0);
154 source = QImage();
155 recreate = true;
156
157 setSerialNumber(++qt_vg_pixmap_serial);
158}
159
160void QVGPixmapData::fromImage
161 (const QImage &image, Qt::ImageConversionFlags flags)
162{
163 if (image.size() == QSize(w, h))
164 setSerialNumber(++qt_vg_pixmap_serial);
165 else
166 resize(image.width(), image.height());
167 source = image.convertToFormat(sourceFormat(), flags);
168 recreate = true;
169}
170
171void QVGPixmapData::fill(const QColor &color)
172{
173 if (!isValid())
174 return;
175
176 if (source.isNull())
177 source = QImage(w, h, sourceFormat());
178
179 if (source.depth() == 1) {
180 // Pick the best approximate color in the image's colortable.
181 int gray = qGray(color.rgba());
182 if (qAbs(qGray(source.color(0)) - gray) < qAbs(qGray(source.color(1)) - gray))
183 source.fill(0);
184 else
185 source.fill(1);
186 } else {
187 source.fill(PREMUL(color.rgba()));
188 }
189
190 // Re-upload the image to VG the next time toVGImage() is called.
191 recreate = true;
192}
193
194bool QVGPixmapData::hasAlphaChannel() const
195{
196 if (!source.isNull())
197 return source.hasAlphaChannel();
198 else
199 return isValid();
200}
201
202void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
203{
204 forceToImage();
205 source.setAlphaChannel(alphaChannel.toImage());
206}
207
208QImage QVGPixmapData::toImage() const
209{
210 if (!isValid())
211 return QImage();
212
213 if (source.isNull()) {
214 source = QImage(w, h, sourceFormat());
215 recreate = true;
216 }
217
218 return source;
219}
220
221QImage *QVGPixmapData::buffer()
222{
223 forceToImage();
224 return &source;
225}
226
227QPaintEngine* QVGPixmapData::paintEngine() const
228{
229 // If the application wants to paint into the QPixmap, we first
230 // force it to QImage format and then paint into that.
231 // This is simpler than juggling multiple VG contexts.
232 const_cast<QVGPixmapData *>(this)->forceToImage();
233 return source.paintEngine();
234}
235
236// This function works around QImage::bits() making a deep copy if the
237// QImage is not const. We force it to be const and then get the bits.
238// XXX: Should add a QImage::constBits() in the future to replace this.
239const uchar *qt_vg_imageBits(const QImage& image)
240{
241 return image.bits();
242}
243
244VGImage QVGPixmapData::toVGImage()
245{
246 if (!isValid())
247 return VG_INVALID_HANDLE;
248
249#if !defined(QT_NO_EGL)
250 // Increase the reference count on the shared context.
251 if (!context)
252 context = qt_vg_create_context(0, QInternal::Pixmap);
253#endif
254
255 if (recreate && prevSize != QSize(w, h))
256 destroyImages();
257 else if (recreate)
258 cachedOpacity = -1.0f; // Force opacity image to be refreshed later.
259
260 if (vgImage == VG_INVALID_HANDLE) {
261 vgImage = QVGImagePool::instance()->createImageForPixmap
262 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
263
264 // Bail out if we run out of GPU memory - try again next time.
265 if (vgImage == VG_INVALID_HANDLE)
266 return VG_INVALID_HANDLE;
267
268 inImagePool = true;
269 } else if (inImagePool) {
270 QVGImagePool::instance()->useImage(this);
271 }
272
273 if (!source.isNull() && recreate) {
274 vgImageSubData
275 (vgImage,
276 qt_vg_imageBits(source), source.bytesPerLine(),
277 VG_sARGB_8888_PRE, 0, 0, w, h);
278 }
279
280 recreate = false;
281 prevSize = QSize(w, h);
282
283 return vgImage;
284}
285
286VGImage QVGPixmapData::toVGImage(qreal opacity)
287{
288#if !defined(QT_SHIVAVG)
289 // Force the primary VG image to be recreated if necessary.
290 if (toVGImage() == VG_INVALID_HANDLE)
291 return VG_INVALID_HANDLE;
292
293 if (opacity == 1.0f)
294 return vgImage;
295
296 // Create an alternative image for the selected opacity.
297 if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) {
298 if (vgImageOpacity == VG_INVALID_HANDLE) {
299 if (inImagePool) {
300 vgImageOpacity = QVGImagePool::instance()->createImageForPixmap
301 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
302 } else {
303 vgImageOpacity = vgCreateImage
304 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER);
305 }
306
307 // Bail out if we run out of GPU memory - try again next time.
308 if (vgImageOpacity == VG_INVALID_HANDLE)
309 return VG_INVALID_HANDLE;
310 }
311 VGfloat matrix[20] = {
312 1.0f, 0.0f, 0.0f, 0.0f,
313 0.0f, 1.0f, 0.0f, 0.0f,
314 0.0f, 0.0f, 1.0f, 0.0f,
315 0.0f, 0.0f, 0.0f, opacity,
316 0.0f, 0.0f, 0.0f, 0.0f
317 };
318 vgColorMatrix(vgImageOpacity, vgImage, matrix);
319 cachedOpacity = opacity;
320 }
321
322 return vgImageOpacity;
323#else
324 // vgColorMatrix() doesn't work with ShivaVG, so ignore the opacity.
325 Q_UNUSED(opacity);
326 return toVGImage();
327#endif
328}
329
330void QVGPixmapData::detachImageFromPool()
331{
332 if (inImagePool) {
333 QVGImagePool::instance()->detachImage(this);
334 inImagePool = false;
335 }
336}
337
338void QVGPixmapData::hibernate()
339{
340 // If the image was imported (e.g, from an SgImage under Symbian),
341 // then we cannot copy it back to main memory for storage.
342 if (vgImage != VG_INVALID_HANDLE && source.isNull())
343 return;
344
345 forceToImage();
346 destroyImageAndContext();
347}
348
349void QVGPixmapData::reclaimImages()
350{
351 if (!inImagePool)
352 return;
353 forceToImage();
354 destroyImages();
355}
356
357Q_DECL_IMPORT extern int qt_defaultDpiX();
358Q_DECL_IMPORT extern int qt_defaultDpiY();
359
360int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
361{
362 switch (metric) {
363 case QPaintDevice::PdmWidth:
364 return w;
365 case QPaintDevice::PdmHeight:
366 return h;
367 case QPaintDevice::PdmNumColors:
368 return 0;
369 case QPaintDevice::PdmDepth:
370 return d;
371 case QPaintDevice::PdmWidthMM:
372 return qRound(w * 25.4 / qt_defaultDpiX());
373 case QPaintDevice::PdmHeightMM:
374 return qRound(h * 25.4 / qt_defaultDpiY());
375 case QPaintDevice::PdmDpiX:
376 case QPaintDevice::PdmPhysicalDpiX:
377 return qt_defaultDpiX();
378 case QPaintDevice::PdmDpiY:
379 case QPaintDevice::PdmPhysicalDpiY:
380 return qt_defaultDpiY();
381 default:
382 qWarning("QVGPixmapData::metric(): Invalid metric");
383 return 0;
384 }
385}
386
387// Force the pixmap data to be in QImage format.
388void QVGPixmapData::forceToImage()
389{
390 if (!isValid())
391 return;
392
393 if (source.isNull())
394 source = QImage(w, h, sourceFormat());
395
396 recreate = true;
397}
398
399QImage::Format QVGPixmapData::sourceFormat() const
400{
401 return QImage::Format_ARGB32_Premultiplied;
402}
403
404/*
405 \internal
406
407 Returns the VGImage that is storing the contents of \a pixmap.
408 Returns VG_INVALID_HANDLE if \a pixmap is not owned by the OpenVG
409 graphics system or \a pixmap is invalid.
410
411 This function is typically used to access the backing store
412 for a pixmap when executing raw OpenVG calls. It must only
413 be used when a QPainter is active and the OpenVG paint engine
414 is in use by the QPainter.
415
416 \sa {QtOpenVG Module}
417*/
418Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap)
419{
420 QPixmapData *pd = pixmap.pixmapData();
421 if (!pd)
422 return VG_INVALID_HANDLE; // null QPixmap
423 if (pd->classId() == QPixmapData::OpenVGClass) {
424 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
425 if (vgpd->isValid())
426 return vgpd->toVGImage();
427 }
428 return VG_INVALID_HANDLE;
429}
430
431#if defined(Q_OS_SYMBIAN)
432
433static CFbsBitmap* createBlitCopy(CFbsBitmap* bitmap)
434{
435 CFbsBitmap *copy = q_check_ptr(new CFbsBitmap);
436 if(!copy)
437 return 0;
438
439 if (copy->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) {
440 delete copy;
441 copy = 0;
442
443 return 0;
444 }
445
446 CFbsBitmapDevice* bitmapDevice = 0;
447 CFbsBitGc *bitmapGc = 0;
448 QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(copy));
449 QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
450 bitmapGc->Activate(bitmapDevice);
451
452 bitmapGc->BitBlt(TPoint(), bitmap);
453
454 delete bitmapGc;
455 delete bitmapDevice;
456
457 return copy;
458}
459
460void QVGPixmapData::cleanup()
461{
462 is_null = w = h = 0;
463 recreate = false;
464 source = QImage();
465}
466
467void QVGPixmapData::fromNativeType(void* pixmap, NativeType type)
468{
469 if (type == QPixmapData::SgImage && pixmap) {
470#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
471 RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmap);
472
473 destroyImages();
474 prevSize = QSize();
475
476 TInt err = 0;
477
478 RSgDriver driver;
479 err = driver.Open();
480 if (err != KErrNone) {
481 cleanup();
482 return;
483 }
484
485 if (sgImage->IsNull()) {
486 cleanup();
487 driver.Close();
488 return;
489 }
490
491 TSgImageInfo sgImageInfo;
492 err = sgImage->GetInfo(sgImageInfo);
493 if (err != KErrNone) {
494 cleanup();
495 driver.Close();
496 return;
497 }
498
499 pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR");
500 pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR");
501 pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR");
502
503 if (eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) {
504 cleanup();
505 driver.Close();
506 return;
507 }
508
509 const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
510 EGLImageKHR eglImage = eglCreateImageKHR(QEglContext::display(),
511 EGL_NO_CONTEXT,
512 EGL_NATIVE_PIXMAP_KHR,
513 (EGLClientBuffer)sgImage,
514 (EGLint*)KEglImageAttribs);
515
516 if (eglGetError() != EGL_SUCCESS) {
517 cleanup();
518 driver.Close();
519 return;
520 }
521
522 vgImage = vgCreateEGLImageTargetKHR(eglImage);
523 if (vgGetError() != VG_NO_ERROR) {
524 cleanup();
525 eglDestroyImageKHR(QEglContext::display(), eglImage);
526 driver.Close();
527 return;
528 }
529
530 w = sgImageInfo.iSizeInPixels.iWidth;
531 h = sgImageInfo.iSizeInPixels.iHeight;
532 d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
533 is_null = (w <= 0 || h <= 0);
534 source = QImage();
535 recreate = false;
536 prevSize = QSize(w, h);
537 setSerialNumber(++qt_vg_pixmap_serial);
538 // release stuff
539 eglDestroyImageKHR(QEglContext::display(), eglImage);
540 driver.Close();
541#endif
542 } else if (type == QPixmapData::FbsBitmap) {
543 CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap);
544
545 bool deleteSourceBitmap = false;
546
547#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE
548
549 // Rasterize extended bitmaps
550
551 TUid extendedBitmapType = bitmap->ExtendedBitmapType();
552 if (extendedBitmapType != KNullUid) {
553 bitmap = createBlitCopy(bitmap);
554 deleteSourceBitmap = true;
555 }
556#endif
557
558 if (bitmap->IsCompressedInRAM()) {
559 bitmap = createBlitCopy(bitmap);
560 deleteSourceBitmap = true;
561 }
562
563 TDisplayMode displayMode = bitmap->DisplayMode();
564 QImage::Format format = qt_TDisplayMode2Format(displayMode);
565
566 TSize size = bitmap->SizeInPixels();
567
568 bitmap->BeginDataAccess();
569 uchar *bytes = (uchar*)bitmap->DataAddress();
570 QImage img = QImage(bytes, size.iWidth, size.iHeight, format);
571 img = img.copy();
572 bitmap->EndDataAccess();
573
574 if(displayMode == EGray2) {
575 //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
576 //So invert mono bitmaps so that masks work correctly.
577 img.invertPixels();
578 } else if(displayMode == EColor16M) {
579 img = img.rgbSwapped(); // EColor16M is BGR
580 }
581
582 fromImage(img, Qt::AutoColor);
583
584 if(deleteSourceBitmap)
585 delete bitmap;
586 }
587}
588
589void* QVGPixmapData::toNativeType(NativeType type)
590{
591 if (type == QPixmapData::SgImage) {
592#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
593 toVGImage();
594
595 if (!isValid() || vgImage == VG_INVALID_HANDLE)
596 return 0;
597
598 TInt err = 0;
599
600 RSgDriver driver;
601 err = driver.Open();
602 if (err != KErrNone)
603 return 0;
604
605 TSgImageInfo sgInfo;
606 sgInfo.iPixelFormat = EUidPixelFormatARGB_8888_PRE;
607 sgInfo.iSizeInPixels.SetSize(w, h);
608 sgInfo.iUsage = ESgUsageBitOpenVgImage | ESgUsageBitOpenVgSurface;
609
610 RSgImage *sgImage = q_check_ptr(new RSgImage());
611 err = sgImage->Create(sgInfo, NULL, NULL);
612 if (err != KErrNone) {
613 driver.Close();
614 return 0;
615 }
616
617 pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR");
618 pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR");
619 pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR");
620
621 if (eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) {
622 driver.Close();
623 return 0;
624 }
625
626 const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
627 EGLImageKHR eglImage = eglCreateImageKHR(QEglContext::display(),
628 EGL_NO_CONTEXT,
629 EGL_NATIVE_PIXMAP_KHR,
630 (EGLClientBuffer)sgImage,
631 (EGLint*)KEglImageAttribs);
632 if (eglGetError() != EGL_SUCCESS) {
633 sgImage->Close();
634 driver.Close();
635 return 0;
636 }
637
638 VGImage dstVgImage = vgCreateEGLImageTargetKHR(eglImage);
639 if (vgGetError() != VG_NO_ERROR) {
640 eglDestroyImageKHR(QEglContext::display(), eglImage);
641 sgImage->Close();
642 driver.Close();
643 return 0;
644 }
645
646 vgCopyImage(dstVgImage, 0, 0,
647 vgImage, 0, 0,
648 w, h, VG_FALSE);
649
650 if (vgGetError() != VG_NO_ERROR) {
651 sgImage->Close();
652 sgImage = 0;
653 }
654 // release stuff
655 vgDestroyImage(dstVgImage);
656 eglDestroyImageKHR(QEglContext::display(), eglImage);
657 driver.Close();
658 return reinterpret_cast<void*>(sgImage);
659#endif
660 } else if (type == QPixmapData::FbsBitmap) {
661 CFbsBitmap *bitmap = q_check_ptr(new CFbsBitmap);
662
663 if (bitmap) {
664 if (bitmap->Create(TSize(source.width(), source.height()),
665 EColor16MAP) == KErrNone) {
666 const uchar *sptr = qt_vg_imageBits(source);
667 bitmap->BeginDataAccess();
668
669 uchar *dptr = (uchar*)bitmap->DataAddress();
670 Mem::Copy(dptr, sptr, source.byteCount());
671
672 bitmap->EndDataAccess();
673 } else {
674 delete bitmap;
675 bitmap = 0;
676 }
677 }
678
679 return reinterpret_cast<void*>(bitmap);
680 }
681 return 0;
682}
683#endif //Q_OS_SYMBIAN
684
685QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.