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

Last change on this file since 1023 was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

  • Property svn:eol-style set to native
File size: 13.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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#if !defined(QT_NO_EGL)
46#include <QtGui/private/qegl_p.h>
47#endif
48#include "qvg_p.h"
49#include "qvgimagepool_p.h"
50#include <QBuffer>
51#include <QImageReader>
52#include <QtGui/private/qimage_p.h>
53
54QT_BEGIN_NAMESPACE
55
56static int qt_vg_pixmap_serial = 0;
57
58QVGPixmapData::QVGPixmapData(PixelType type)
59 : QPixmapData(type, OpenVGClass)
60{
61 Q_ASSERT(type == QPixmapData::PixmapType);
62 vgImage = VG_INVALID_HANDLE;
63 vgImageOpacity = VG_INVALID_HANDLE;
64 cachedOpacity = 1.0f;
65 recreate = true;
66 inImagePool = false;
67 inLRU = false;
68 failedToAlloc = 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.isNull())
160 return;
161
162 QImage img = image;
163 createPixmapForImage(img, flags, false);
164}
165
166void QVGPixmapData::fromImageReader(QImageReader *imageReader,
167 Qt::ImageConversionFlags flags)
168{
169 QImage image = imageReader->read();
170 if (image.isNull())
171 return;
172
173 createPixmapForImage(image, flags, true);
174}
175
176bool QVGPixmapData::fromFile(const QString &filename, const char *format,
177 Qt::ImageConversionFlags flags)
178{
179 QImage image = QImageReader(filename, format).read();
180 if (image.isNull())
181 return false;
182
183 createPixmapForImage(image, flags, true);
184
185 return !isNull();
186}
187
188bool QVGPixmapData::fromData(const uchar *buffer, uint len, const char *format,
189 Qt::ImageConversionFlags flags)
190{
191 QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
192 QBuffer b(&a);
193 b.open(QIODevice::ReadOnly);
194 QImage image = QImageReader(&b, format).read();
195 if (image.isNull())
196 return false;
197
198 createPixmapForImage(image, flags, true);
199
200 return !isNull();
201}
202
203/*!
204 out-of-place conversion (inPlace == false) will always detach()
205 */
206void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
207{
208 if (image.size() == QSize(w, h))
209 setSerialNumber(++qt_vg_pixmap_serial);
210 else
211 resize(image.width(), image.height());
212
213 QImage::Format format = sourceFormat();
214 int d = image.depth();
215 if (d == 1 || d == 16 || d == 24 || (d == 32 && !image.hasAlphaChannel()))
216 format = QImage::Format_RGB32;
217 else if (!(flags & Qt::NoOpaqueDetection) && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels())
218 format = sourceFormat();
219 else
220 format = image.hasAlphaChannel() ? sourceFormat() : QImage::Format_RGB32;
221
222 if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
223 source = image;
224 } else {
225 source = image.convertToFormat(format);
226
227 // convertToFormat won't detach the image if format stays the same.
228 if (image.format() == format)
229 source.detach();
230 }
231
232 recreate = true;
233}
234
235void QVGPixmapData::fill(const QColor &color)
236{
237 if (!isValid())
238 return;
239
240 if (source.isNull())
241 source = QImage(w, h, sourceFormat());
242
243 if (source.depth() == 1) {
244 // Pick the best approximate color in the image's colortable.
245 int gray = qGray(color.rgba());
246 if (qAbs(qGray(source.color(0)) - gray) < qAbs(qGray(source.color(1)) - gray))
247 source.fill(0);
248 else
249 source.fill(1);
250 } else {
251 source.fill(PREMUL(color.rgba()));
252 }
253
254 // Re-upload the image to VG the next time toVGImage() is called.
255 recreate = true;
256}
257
258bool QVGPixmapData::hasAlphaChannel() const
259{
260 if (!source.isNull())
261 return source.hasAlphaChannel();
262 else
263 return isValid();
264}
265
266void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel)
267{
268 forceToImage();
269 source.setAlphaChannel(alphaChannel.toImage());
270}
271
272QImage QVGPixmapData::toImage() const
273{
274 if (!isValid())
275 return QImage();
276
277 if (source.isNull()) {
278 source = QImage(w, h, sourceFormat());
279 recreate = true;
280 }
281
282 return source;
283}
284
285QImage *QVGPixmapData::buffer()
286{
287 forceToImage();
288 return &source;
289}
290
291QPaintEngine* QVGPixmapData::paintEngine() const
292{
293 // If the application wants to paint into the QPixmap, we first
294 // force it to QImage format and then paint into that.
295 // This is simpler than juggling multiple VG contexts.
296 const_cast<QVGPixmapData *>(this)->forceToImage();
297 return source.paintEngine();
298}
299
300VGImage QVGPixmapData::toVGImage()
301{
302 if (!isValid() || failedToAlloc)
303 return VG_INVALID_HANDLE;
304
305#if !defined(QT_NO_EGL)
306 // Increase the reference count on the shared context.
307 if (!context)
308 context = qt_vg_create_context(0, QInternal::Pixmap);
309#endif
310
311 if (recreate && prevSize != QSize(w, h))
312 destroyImages();
313 else if (recreate)
314 cachedOpacity = -1.0f; // Force opacity image to be refreshed later.
315
316 if (vgImage == VG_INVALID_HANDLE) {
317 vgImage = QVGImagePool::instance()->createImageForPixmap
318 (qt_vg_image_to_vg_format(source.format()), w, h, VG_IMAGE_QUALITY_FASTER, this);
319
320 // Bail out if we run out of GPU memory - try again next time.
321 if (vgImage == VG_INVALID_HANDLE) {
322 failedToAlloc = true;
323 return VG_INVALID_HANDLE;
324 }
325
326 inImagePool = true;
327 } else if (inImagePool) {
328 QVGImagePool::instance()->useImage(this);
329 }
330
331 if (!source.isNull() && recreate) {
332 vgImageSubData
333 (vgImage,
334 source.constBits(), source.bytesPerLine(),
335 qt_vg_image_to_vg_format(source.format()), 0, 0, w, h);
336 }
337
338 recreate = false;
339 prevSize = QSize(w, h);
340
341 return vgImage;
342}
343
344VGImage QVGPixmapData::toVGImage(qreal opacity)
345{
346#if !defined(QT_SHIVAVG)
347 // Force the primary VG image to be recreated if necessary.
348 if (toVGImage() == VG_INVALID_HANDLE)
349 return VG_INVALID_HANDLE;
350
351 if (opacity == 1.0f)
352 return vgImage;
353
354 // Create an alternative image for the selected opacity.
355 if (vgImageOpacity == VG_INVALID_HANDLE || cachedOpacity != opacity) {
356 if (vgImageOpacity == VG_INVALID_HANDLE) {
357 if (inImagePool) {
358 vgImageOpacity = QVGImagePool::instance()->createImageForPixmap
359 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this);
360 } else {
361 vgImageOpacity = vgCreateImage
362 (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER);
363 }
364
365 // Bail out if we run out of GPU memory - try again next time.
366 if (vgImageOpacity == VG_INVALID_HANDLE)
367 return VG_INVALID_HANDLE;
368 }
369 VGfloat matrix[20] = {
370 1.0f, 0.0f, 0.0f, 0.0f,
371 0.0f, 1.0f, 0.0f, 0.0f,
372 0.0f, 0.0f, 1.0f, 0.0f,
373 0.0f, 0.0f, 0.0f, opacity,
374 0.0f, 0.0f, 0.0f, 0.0f
375 };
376 vgColorMatrix(vgImageOpacity, vgImage, matrix);
377 cachedOpacity = opacity;
378 }
379
380 return vgImageOpacity;
381#else
382 // vgColorMatrix() doesn't work with ShivaVG, so ignore the opacity.
383 Q_UNUSED(opacity);
384 return toVGImage();
385#endif
386}
387
388void QVGPixmapData::detachImageFromPool()
389{
390 if (inImagePool) {
391 QVGImagePool::instance()->detachImage(this);
392 inImagePool = false;
393 }
394}
395
396void QVGPixmapData::hibernate()
397{
398 // If the image was imported (e.g, from an SgImage under Symbian),
399 // then we cannot copy it back to main memory for storage.
400 if (vgImage != VG_INVALID_HANDLE && source.isNull())
401 return;
402
403 forceToImage();
404 destroyImageAndContext();
405}
406
407void QVGPixmapData::reclaimImages()
408{
409 if (!inImagePool)
410 return;
411 forceToImage();
412 destroyImages();
413}
414
415Q_DECL_IMPORT extern int qt_defaultDpiX();
416Q_DECL_IMPORT extern int qt_defaultDpiY();
417
418int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
419{
420 switch (metric) {
421 case QPaintDevice::PdmWidth:
422 return w;
423 case QPaintDevice::PdmHeight:
424 return h;
425 case QPaintDevice::PdmNumColors:
426 return 0;
427 case QPaintDevice::PdmDepth:
428 return d;
429 case QPaintDevice::PdmWidthMM:
430 return qRound(w * 25.4 / qt_defaultDpiX());
431 case QPaintDevice::PdmHeightMM:
432 return qRound(h * 25.4 / qt_defaultDpiY());
433 case QPaintDevice::PdmDpiX:
434 case QPaintDevice::PdmPhysicalDpiX:
435 return qt_defaultDpiX();
436 case QPaintDevice::PdmDpiY:
437 case QPaintDevice::PdmPhysicalDpiY:
438 return qt_defaultDpiY();
439 default:
440 qWarning("QVGPixmapData::metric(): Invalid metric");
441 return 0;
442 }
443}
444
445// Force the pixmap data to be in QImage format.
446void QVGPixmapData::forceToImage()
447{
448 if (!isValid())
449 return;
450
451 if (source.isNull())
452 source = QImage(w, h, sourceFormat());
453
454 recreate = true;
455}
456
457QImage::Format QVGPixmapData::sourceFormat() const
458{
459 return QImage::Format_ARGB32_Premultiplied;
460}
461
462/*
463 \internal
464
465 Returns the VGImage that is storing the contents of \a pixmap.
466 Returns VG_INVALID_HANDLE if \a pixmap is not owned by the OpenVG
467 graphics system or \a pixmap is invalid.
468
469 This function is typically used to access the backing store
470 for a pixmap when executing raw OpenVG calls. It must only
471 be used when a QPainter is active and the OpenVG paint engine
472 is in use by the QPainter.
473
474 \sa {QtOpenVG Module}
475*/
476Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap)
477{
478 QPixmapData *pd = pixmap.pixmapData();
479 if (!pd)
480 return VG_INVALID_HANDLE; // null QPixmap
481 if (pd->classId() == QPixmapData::OpenVGClass) {
482 QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd);
483 if (vgpd->isValid())
484 return vgpd->toVGImage();
485 }
486 return VG_INVALID_HANDLE;
487}
488
489QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.