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

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

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 17.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
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#ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE
49#include <graphics/sgimage.h>
50typedef EGLImageKHR (*pfnEglCreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, EGLint*);
51typedef EGLBoolean (*pfnEglDestroyImageKHR)(EGLDisplay, EGLImageKHR);
52typedef VGImage (*pfnVgCreateEGLImageTargetKHR)(VGeglImageKHR);
53#endif
54
55QT_BEGIN_NAMESPACE
56
57static int qt_vg_pixmap_serial = 0;
58
59QVGPixmapData::QVGPixmapData(PixelType type)
60 : QPixmapData(type, OpenVGClass)
61{
62 Q_ASSERT(type == QPixmapData::PixmapType);
63 vgImage = VG_INVALID_HANDLE;
64 vgImageOpacity = VG_INVALID_HANDLE;
65 cachedOpacity = 1.0f;
66 recreate = true;
67 inImagePool = false;
68 inLRU = false;
69#if !defined(QT_NO_EGL)
70 context = 0;
71 qt_vg_register_pixmap(this);
72#endif
73 setSerialNumber(++qt_vg_pixmap_serial);
74}
75
76QVGPixmapData::~QVGPixmapData()
77{
78 destroyImageAndContext();
79#if !defined(QT_NO_EGL)
80 qt_vg_unregister_pixmap(this);
81#endif
82}
83
84void QVGPixmapData::destroyImages()
85{
86 if (inImagePool) {
87 QVGImagePool *pool = QVGImagePool::instance();
88 if (vgImage != VG_INVALID_HANDLE)
89 pool->releaseImage(this, vgImage);
90 if (vgImageOpacity != VG_INVALID_HANDLE)
91 pool->releaseImage(this, vgImageOpacity);
92 } else {
93 if (vgImage != VG_INVALID_HANDLE)
94 vgDestroyImage(vgImage);
95 if (vgImageOpacity != VG_INVALID_HANDLE)
96 vgDestroyImage(vgImageOpacity);
97 }
98 vgImage = VG_INVALID_HANDLE;
99 vgImageOpacity = VG_INVALID_HANDLE;
100 inImagePool = false;
101}
102
103void QVGPixmapData::destroyImageAndContext()
104{
105 if (vgImage != VG_INVALID_HANDLE) {
106 // We need to have a context current to destroy the image.
107#if !defined(QT_NO_EGL)
108 if (context->isCurrent()) {
109 destroyImages();
110 } else {
111 // We don't currently have a widget surface active, but we
112 // need a surface to make the context current. So use the
113 // shared pbuffer surface instead.
114 context->makeCurrent(qt_vg_shared_surface());
115 destroyImages();
116 context->lazyDoneCurrent();
117 }
118#else
119 destroyImages();
120#endif
121 }
122#if !defined(QT_NO_EGL)
123 if (context) {
124 qt_vg_destroy_context(context, QInternal::Pixmap);
125 context = 0;
126 }
127#endif
128 recreate = true;
129}
130
131QPixmapData *QVGPixmapData::createCompatiblePixmapData() const
132{
133 return new QVGPixmapData(pixelType());
134}
135
136bool QVGPixmapData::isValid() const
137{
138 return (w > 0 && h > 0);
139}
140
141void QVGPixmapData::resize(int wid, int ht)
142{
143 if (w == wid && h == ht)
144 return;
145
146 w = wid;
147 h = ht;
148 d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
149 is_null = (w <= 0 || h <= 0);
150 source = QImage();
151 recreate = true;
152
153 setSerialNumber(++qt_vg_pixmap_serial);
154}
155
156void QVGPixmapData::fromImage
157 (const QImage &image, Qt::ImageConversionFlags flags)
158{
159 if (image.size() == QSize(w, h))
160 setSerialNumber(++qt_vg_pixmap_serial);
161 else
162 resize(image.width(), image.height());
163 source = image.convertToFormat(sourceFormat(), flags);
164 recreate = true;
165}
166
167void QVGPixmapData::fill(const QColor &color)
168{
169 if (!isValid())
170 return;
171
172 if (source.isNull())
173 source = QImage(w, h, sourceFormat());
174
175 if (source.depth() == 1) {
176 // Pick the best approximate color in the image's colortable.
177 int gray = qGray(color.rgba());
178 if (qAbs(qGray(source.color(0)) - gray) < qAbs(qGray(source.color(1)) - gray))
179 source.fill(0);
180 else
181 source.fill(1);
182 } else {
183 source.fill(PREMUL(color.rgba()));
184 }
185
186 // Re-upload the image to VG the next time toVGImage() is called.
187 recreate = true;
188}
189
190bool QVGPixmapData::hasAlphaChannel() const
191{
192 if (!source.isNull())
193 return source.hasAlphaChannel();
194 else
195 return isValid();
196}
197
198void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
199{
200 forceToImage();
201 source.setAlphaChannel(alphaChannel.toImage());
202}
203
204QImage QVGPixmapData::toImage() const
205{
206 if (!isValid())
207 return QImage();
208
209 if (source.isNull()) {
210 source = QImage(w, h, sourceFormat());
211 recreate = true;
212 }
213
214 return source;
215}
216
217QImage *QVGPixmapData::buffer()
218{
219 forceToImage();
220 return &source;
221}
222
223QPaintEngine* QVGPixmapData::paintEngine() const
224{
225 // If the application wants to paint into the QPixmap, we first
226 // force it to QImage format and then paint into that.
227 // This is simpler than juggling multiple VG contexts.
228 const_cast<QVGPixmapData *>(this)->forceToImage();
229 return source.paintEngine();
230}
231
232// This function works around QImage::bits() making a deep copy if the
233// QImage is not const. We force it to be const and then get the bits.
234// XXX: Should add a QImage::constBits() in the future to replace this.
235const uchar *qt_vg_imageBits(const QImage& image)
236{
237 return image.bits();
238}
239
240VGImage QVGPixmapData::toVGImage()
241{
242 if (!isValid())
243 return VG_INVALID_HANDLE;
244
245#if !defined(QT_NO_EGL)
246 // Increase the reference count on the shared context.
247 if (!context)
248 context = qt_vg_create_context(0, QInternal::Pixmap);
249#endif
250
251 if (recreate && prevSize != QSize(w, h))
252 destroyImages();
253 else if (recreate)
254 cachedOpacity = -1.0f; // Force opacity image to be refreshed later.
255
256 if (vgImage == VG_INVALID_HANDLE) {
257 vgImage = QVGImagePool::instance()->createImageForPixmap
258 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
259
260 // Bail out if we run out of GPU memory - try again next time.
261 if (vgImage == VG_INVALID_HANDLE)
262 return VG_INVALID_HANDLE;
263
264 inImagePool = true;
265 } else if (inImagePool) {
266 QVGImagePool::instance()->useImage(this);
267 }
268
269 if (!source.isNull() && recreate) {
270 vgImageSubData
271 (vgImage,
272 qt_vg_imageBits(source), source.bytesPerLine(),
273 VG_sARGB_8888_PRE, 0, 0, w, h);
274 }
275
276 recreate = false;
277 prevSize = QSize(w, h);
278
279 return vgImage;
280}
281
282VGImage QVGPixmapData::toVGImage(qreal opacity)
283{
284#if !defined(QT_SHIVAVG)
285 // Force the primary VG image to be recreated if necessary.
286 if (toVGImage() == VG_INVALID_HANDLE)
287 return VG_INVALID_HANDLE;
288
289 if (opacity == 1.0f)
290 return vgImage;
291
292 // Create an alternative image for the selected opacity.
293 if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) {
294 if (vgImageOpacity == VG_INVALID_HANDLE) {
295 if (inImagePool) {
296 vgImageOpacity = QVGImagePool::instance()->createImageForPixmap
297 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
298 } else {
299 vgImageOpacity = vgCreateImage
300 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER);
301 }
302
303 // Bail out if we run out of GPU memory - try again next time.
304 if (vgImageOpacity == VG_INVALID_HANDLE)
305 return VG_INVALID_HANDLE;
306 }
307 VGfloat matrix[20] = {
308 1.0f, 0.0f, 0.0f, 0.0f,
309 0.0f, 1.0f, 0.0f, 0.0f,
310 0.0f, 0.0f, 1.0f, 0.0f,
311 0.0f, 0.0f, 0.0f, opacity,
312 0.0f, 0.0f, 0.0f, 0.0f
313 };
314 vgColorMatrix(vgImageOpacity, vgImage, matrix);
315 cachedOpacity = opacity;
316 }
317
318 return vgImageOpacity;
319#else
320 // vgColorMatrix() doesn't work with ShivaVG, so ignore the opacity.
321 Q_UNUSED(opacity);
322 return toVGImage();
323#endif
324}
325
326void QVGPixmapData::detachImageFromPool()
327{
328 if (inImagePool) {
329 QVGImagePool::instance()->detachImage(this);
330 inImagePool = false;
331 }
332}
333
334void QVGPixmapData::hibernate()
335{
336 // If the image was imported (e.g, from an SgImage under Symbian),
337 // then we cannot copy it back to main memory for storage.
338 if (vgImage != VG_INVALID_HANDLE && source.isNull())
339 return;
340
341 forceToImage();
342 destroyImageAndContext();
343}
344
345void QVGPixmapData::reclaimImages()
346{
347 if (!inImagePool)
348 return;
349 forceToImage();
350 destroyImages();
351}
352
353extern int qt_defaultDpiX();
354extern int qt_defaultDpiY();
355
356int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
357{
358 switch (metric) {
359 case QPaintDevice::PdmWidth:
360 return w;
361 case QPaintDevice::PdmHeight:
362 return h;
363 case QPaintDevice::PdmNumColors:
364 return 0;
365 case QPaintDevice::PdmDepth:
366 return d;
367 case QPaintDevice::PdmWidthMM:
368 return qRound(w * 25.4 / qt_defaultDpiX());
369 case QPaintDevice::PdmHeightMM:
370 return qRound(h * 25.4 / qt_defaultDpiY());
371 case QPaintDevice::PdmDpiX:
372 case QPaintDevice::PdmPhysicalDpiX:
373 return qt_defaultDpiX();
374 case QPaintDevice::PdmDpiY:
375 case QPaintDevice::PdmPhysicalDpiY:
376 return qt_defaultDpiY();
377 default:
378 qWarning("QVGPixmapData::metric(): Invalid metric");
379 return 0;
380 }
381}
382
383// Force the pixmap data to be in QImage format.
384void QVGPixmapData::forceToImage()
385{
386 if (!isValid())
387 return;
388
389 if (source.isNull())
390 source = QImage(w, h, sourceFormat());
391
392 recreate = true;
393}
394
395QImage::Format QVGPixmapData::sourceFormat() const
396{
397 return QImage::Format_ARGB32_Premultiplied;
398}
399
400/*
401 \internal
402
403 Returns the VGImage that is storing the contents of \a pixmap.
404 Returns VG_INVALID_HANDLE if \a pixmap is not owned by the OpenVG
405 graphics system or \a pixmap is invalid.
406
407 This function is typically used to access the backing store
408 for a pixmap when executing raw OpenVG calls. It must only
409 be used when a QPainter is active and the OpenVG paint engine
410 is in use by the QPainter.
411
412 \sa {QtOpenVG Module}
413*/
414Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap)
415{
416 QPixmapData *pd = pixmap.pixmapData();
417 if (!pd)
418 return VG_INVALID_HANDLE; // null QPixmap
419 if (pd->classId() == QPixmapData::OpenVGClass) {
420 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
421 if (vgpd->isValid())
422 return vgpd->toVGImage();
423 }
424 return VG_INVALID_HANDLE;
425}
426
427#if defined(Q_OS_SYMBIAN)
428void QVGPixmapData::cleanup()
429{
430 is_null = w = h = 0;
431 recreate = false;
432 source = QImage();
433}
434
435void QVGPixmapData::fromNativeType(void* pixmap, NativeType type)
436{
437#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
438 if (type == QPixmapData::SgImage && pixmap) {
439 RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmap);
440 // when "0" used as argument then
441 // default display, context are used
442 if (!context)
443 context = qt_vg_create_context(0, QInternal::Pixmap);
444
445 destroyImages();
446 prevSize = QSize();
447
448 TInt err = 0;
449
450 err = SgDriver::Open();
451 if(err != KErrNone) {
452 cleanup();
453 return;
454 }
455
456 if(sgImage->IsNull()) {
457 cleanup();
458 SgDriver::Close();
459 return;
460 }
461
462 TSgImageInfo sgImageInfo;
463 err = sgImage->GetInfo(sgImageInfo);
464 if(err != KErrNone) {
465 cleanup();
466 SgDriver::Close();
467 return;
468 }
469
470 pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR");
471 pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR");
472 pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR");
473
474 if(eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) {
475 cleanup();
476 SgDriver::Close();
477 return;
478 }
479
480 const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
481 EGLImageKHR eglImage = eglCreateImageKHR(context->display(),
482 EGL_NO_CONTEXT,
483 EGL_NATIVE_PIXMAP_KHR,
484 (EGLClientBuffer)sgImage,
485 (EGLint*)KEglImageAttribs);
486
487 if(eglGetError() != EGL_SUCCESS) {
488 cleanup();
489 SgDriver::Close();
490 return;
491 }
492
493 vgImage = vgCreateEGLImageTargetKHR(eglImage);
494 if(vgGetError() != VG_NO_ERROR) {
495 cleanup();
496 eglDestroyImageKHR(context->display(), eglImage);
497 SgDriver::Close();
498 return;
499 }
500
501 w = sgImageInfo.iSizeInPixels.iWidth;
502 h = sgImageInfo.iSizeInPixels.iHeight;
503 d = 32; // We always use ARGB_Premultiplied for VG pixmaps.
504 is_null = (w <= 0 || h <= 0);
505 source = QImage();
506 recreate = false;
507 prevSize = QSize(w, h);
508 setSerialNumber(++qt_vg_pixmap_serial);
509 // release stuff
510 eglDestroyImageKHR(context->display(), eglImage);
511 SgDriver::Close();
512 } else if (type == QPixmapData::FbsBitmap) {
513
514 }
515#else
516 Q_UNUSED(pixmap);
517 Q_UNUSED(type);
518#endif
519}
520
521void* QVGPixmapData::toNativeType(NativeType type)
522{
523#if defined(QT_SYMBIAN_SUPPORTS_SGIMAGE) && !defined(QT_NO_EGL)
524 if (type == QPixmapData::SgImage) {
525 toVGImage();
526
527 if(!isValid() || vgImage == VG_INVALID_HANDLE)
528 return 0;
529
530 TInt err = 0;
531
532 err = SgDriver::Open();
533 if(err != KErrNone)
534 return 0;
535
536 TSgImageInfo sgInfo;
537 sgInfo.iPixelFormat = EUidPixelFormatARGB_8888_PRE;
538 sgInfo.iSizeInPixels.SetSize(w, h);
539 sgInfo.iUsage = ESgUsageOpenVgImage | ESgUsageOpenVgTarget;
540 sgInfo.iShareable = ETrue;
541 sgInfo.iCpuAccess = ESgCpuAccessNone;
542 sgInfo.iScreenId = KSgScreenIdMain; //KSgScreenIdAny;
543 sgInfo.iUserAttributes = NULL;
544 sgInfo.iUserAttributeCount = 0;
545
546 RSgImage *sgImage = q_check_ptr(new RSgImage());
547 err = sgImage->Create(sgInfo, NULL, NULL);
548 if(err != KErrNone) {
549 SgDriver::Close();
550 return 0;
551 }
552
553 pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR");
554 pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR");
555 pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR");
556
557 if(eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) {
558 SgDriver::Close();
559 return 0;
560 }
561
562 const EGLint KEglImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, EGL_NONE};
563 EGLImageKHR eglImage = eglCreateImageKHR(context->display(),
564 EGL_NO_CONTEXT,
565 EGL_NATIVE_PIXMAP_KHR,
566 (EGLClientBuffer)sgImage,
567 (EGLint*)KEglImageAttribs);
568 if(eglGetError() != EGL_SUCCESS) {
569 sgImage->Close();
570 SgDriver::Close();
571 return 0;
572 }
573
574 VGImage dstVgImage = vgCreateEGLImageTargetKHR(eglImage);
575 if(vgGetError() != VG_NO_ERROR) {
576 eglDestroyImageKHR(context->display(), eglImage);
577 sgImage->Close();
578 SgDriver::Close();
579 return 0;
580 }
581
582 vgCopyImage(dstVgImage, 0, 0,
583 vgImage, 0, 0,
584 w, h, VG_FALSE);
585
586 if(vgGetError() != VG_NO_ERROR) {
587 sgImage->Close();
588 sgImage = 0;
589 }
590 // release stuff
591 vgDestroyImage(dstVgImage);
592 eglDestroyImageKHR(context->display(), eglImage);
593 SgDriver::Close();
594 return reinterpret_cast<void*>(sgImage);
595 } else if (type == QPixmapData::FbsBitmap) {
596 return 0;
597 }
598#else
599 Q_UNUSED(type);
600 return 0;
601#endif
602}
603#endif //Q_OS_SYMBIAN
604
605QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.