1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdirectfbpixmap.h"
43
44 #ifndef QT_NO_QWS_DIRECTFB
45
46 #include "qdirectfbscreen.h"
47 #include "qdirectfbpaintengine.h"
48
49 #include <QtGui/qbitmap.h>
50 #include <QtCore/qfile.h>
51 #include <directfb.h>
52
53
54 QT_BEGIN_NAMESPACE
55
56 static int global_ser_no = 0;
57
QDirectFBPixmapData(QDirectFBScreen * screen,PixelType pixelType)58 QDirectFBPixmapData::QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType)
59 : QPixmapData(pixelType, DirectFBClass), QDirectFBPaintDevice(screen),
60 alpha(false)
61 {
62 setSerialNumber(0);
63 }
64
~QDirectFBPixmapData()65 QDirectFBPixmapData::~QDirectFBPixmapData()
66 {
67 }
68
resize(int width,int height)69 void QDirectFBPixmapData::resize(int width, int height)
70 {
71 if (width <= 0 || height <= 0) {
72 invalidate();
73 return;
74 }
75
76 imageFormat = screen->pixelFormat();
77 dfbSurface = screen->createDFBSurface(QSize(width, height),
78 imageFormat,
79 QDirectFBScreen::TrackSurface);
80 d = QDirectFBScreen::depth(imageFormat);
81 alpha = false;
82 if (!dfbSurface) {
83 invalidate();
84 qWarning("QDirectFBPixmapData::resize(): Unable to allocate surface");
85 return;
86 }
87
88 w = width;
89 h = height;
90 is_null = (w <= 0 || h <= 0);
91 setSerialNumber(++global_ser_no);
92 }
93
94 #ifdef QT_DIRECTFB_OPAQUE_DETECTION
95 // mostly duplicated from qimage.cpp (QImageData::checkForAlphaPixels)
checkForAlphaPixels(const QImage & img)96 static bool checkForAlphaPixels(const QImage &img)
97 {
98 const uchar *bits = img.bits();
99 const int bytes_per_line = img.bytesPerLine();
100 const uchar *end_bits = bits + bytes_per_line;
101 const int width = img.width();
102 const int height = img.height();
103 switch (img.format()) {
104 case QImage::Format_Indexed8:
105 return img.hasAlphaChannel();
106 case QImage::Format_ARGB32:
107 case QImage::Format_ARGB32_Premultiplied:
108 for (int y=0; y<height; ++y) {
109 for (int x=0; x<width; ++x) {
110 if ((((uint *)bits)[x] & 0xff000000) != 0xff000000) {
111 return true;
112 }
113 }
114 bits += bytes_per_line;
115 }
116 break;
117
118 case QImage::Format_ARGB8555_Premultiplied:
119 case QImage::Format_ARGB8565_Premultiplied:
120 for (int y=0; y<height; ++y) {
121 while (bits < end_bits) {
122 if (bits[0] != 0) {
123 return true;
124 }
125 bits += 3;
126 }
127 bits = end_bits;
128 end_bits += bytes_per_line;
129 }
130 break;
131
132 case QImage::Format_ARGB6666_Premultiplied:
133 for (int y=0; y<height; ++y) {
134 while (bits < end_bits) {
135 if ((bits[0] & 0xfc) != 0) {
136 return true;
137 }
138 bits += 3;
139 }
140 bits = end_bits;
141 end_bits += bytes_per_line;
142 }
143 break;
144
145 case QImage::Format_ARGB4444_Premultiplied:
146 for (int y=0; y<height; ++y) {
147 while (bits < end_bits) {
148 if ((bits[0] & 0xf0) != 0) {
149 return true;
150 }
151 bits += 2;
152 }
153 bits = end_bits;
154 end_bits += bytes_per_line;
155 }
156 break;
157
158 default:
159 break;
160 }
161
162 return false;
163 }
164 #endif // QT_DIRECTFB_OPAQUE_DETECTION
165
hasAlphaChannel(const QImage & img,Qt::ImageConversionFlags flags)166 bool QDirectFBPixmapData::hasAlphaChannel(const QImage &img, Qt::ImageConversionFlags flags)
167 {
168 if (img.depth() == 1)
169 return true;
170 #ifdef QT_DIRECTFB_OPAQUE_DETECTION
171 return ((flags & Qt::NoOpaqueDetection) ? img.hasAlphaChannel() : checkForAlphaPixels(img));
172 #else
173 Q_UNUSED(flags);
174 return img.hasAlphaChannel();
175 #endif
176 }
177
178 #ifdef QT_DIRECTFB_IMAGEPROVIDER
fromFile(const QString & filename,const char * format,Qt::ImageConversionFlags flags)179 bool QDirectFBPixmapData::fromFile(const QString &filename, const char *format,
180 Qt::ImageConversionFlags flags)
181 {
182 if (!QFile::exists(filename))
183 return false;
184 if (flags == Qt::AutoColor) {
185 if (filename.startsWith(QLatin1Char(':'))) { // resource
186 QFile file(filename);
187 if (!file.open(QIODevice::ReadOnly))
188 return false;
189 const QByteArray data = file.readAll();
190 file.close();
191 return fromData(reinterpret_cast<const uchar*>(data.constData()), data.size(), format, flags);
192 } else {
193 DFBDataBufferDescription description;
194 description.flags = DBDESC_FILE;
195 const QByteArray fileNameData = filename.toLocal8Bit();
196 description.file = fileNameData.constData();
197 if (fromDataBufferDescription(description)) {
198 return true;
199 }
200 // fall back to Qt
201 }
202 }
203 return QPixmapData::fromFile(filename, format, flags);
204 }
205
fromData(const uchar * buffer,uint len,const char * format,Qt::ImageConversionFlags flags)206 bool QDirectFBPixmapData::fromData(const uchar *buffer, uint len, const char *format,
207 Qt::ImageConversionFlags flags)
208 {
209 if (flags == Qt::AutoColor) {
210 DFBDataBufferDescription description;
211 description.flags = DBDESC_MEMORY;
212 description.memory.data = buffer;
213 description.memory.length = len;
214 if (fromDataBufferDescription(description))
215 return true;
216 // fall back to Qt
217 }
218 return QPixmapData::fromData(buffer, len, format, flags);
219 }
220
221 template <typename T> struct QDirectFBInterfaceCleanupHandler
222 {
cleanupQDirectFBInterfaceCleanupHandler223 static void cleanup(T *t) { if (t) t->Release(t); }
224 };
225
226 template <typename T>
227 class QDirectFBPointer : public QScopedPointer<T, QDirectFBInterfaceCleanupHandler<T> >
228 {
229 public:
QDirectFBPointer(T * t=0)230 QDirectFBPointer(T *t = 0)
231 : QScopedPointer<T, QDirectFBInterfaceCleanupHandler<T> >(t)
232 {}
233 };
234
fromDataBufferDescription(const DFBDataBufferDescription & dataBufferDescription)235 bool QDirectFBPixmapData::fromDataBufferDescription(const DFBDataBufferDescription &dataBufferDescription)
236 {
237 IDirectFB *dfb = screen->dfb();
238 Q_ASSERT(dfb);
239 DFBResult result = DFB_OK;
240 IDirectFBDataBuffer *dataBufferPtr;
241 if ((result = dfb->CreateDataBuffer(dfb, &dataBufferDescription, &dataBufferPtr)) != DFB_OK) {
242 DirectFBError("QDirectFBPixmapData::fromDataBufferDescription()", result);
243 return false;
244 }
245 QDirectFBPointer<IDirectFBDataBuffer> dataBuffer(dataBufferPtr);
246
247 IDirectFBImageProvider *providerPtr;
248 if ((result = dataBuffer->CreateImageProvider(dataBuffer.data(), &providerPtr)) != DFB_OK)
249 return false;
250
251 QDirectFBPointer<IDirectFBImageProvider> provider(providerPtr);
252
253 DFBImageDescription imageDescription;
254 result = provider->GetImageDescription(provider.data(), &imageDescription);
255 if (result != DFB_OK) {
256 DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't get image description", result);
257 return false;
258 }
259
260 if (imageDescription.caps & DICAPS_COLORKEY) {
261 return false;
262 }
263
264 DFBSurfaceDescription surfaceDescription;
265 if ((result = provider->GetSurfaceDescription(provider.data(), &surfaceDescription)) != DFB_OK) {
266 DirectFBError("QDirectFBPixmapData::fromDataBufferDescription(): Can't get surface description", result);
267 return false;
268 }
269
270 alpha = imageDescription.caps & DICAPS_ALPHACHANNEL;
271 imageFormat = alpha ? screen->alphaPixmapFormat() : screen->pixelFormat();
272
273 dfbSurface = screen->createDFBSurface(QSize(surfaceDescription.width, surfaceDescription.height),
274 imageFormat, QDirectFBScreen::TrackSurface);
275
276 result = provider->RenderTo(provider.data(), dfbSurface, 0);
277 if (result != DFB_OK) {
278 DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't render to surface", result);
279 return false;
280 }
281
282 w = surfaceDescription.width;
283 h = surfaceDescription.height;
284 is_null = (w <= 0 || h <= 0);
285 d = QDirectFBScreen::depth(imageFormat);
286 setSerialNumber(++global_ser_no);
287
288 #if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
289 screen->setDirectFBImageProvider(providerPtr);
290 provider.take();
291 #endif
292
293 return true;
294 }
295
296 #endif
297
fromImage(const QImage & img,Qt::ImageConversionFlags flags)298 void QDirectFBPixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
299 {
300 alpha = QDirectFBPixmapData::hasAlphaChannel(img, flags);
301 imageFormat = alpha ? screen->alphaPixmapFormat() : screen->pixelFormat();
302
303 QImage image;
304 if ((flags & ~Qt::NoOpaqueDetection) != Qt::AutoColor) {
305 image = img.convertToFormat(imageFormat, flags);
306 flags = Qt::AutoColor;
307 } else if (img.format() == QImage::Format_RGB32 || img.depth() == 1) {
308 image = img.convertToFormat(imageFormat, flags);
309 } else if (img.format() != imageFormat) {
310 image = img.convertToFormat(imageFormat, flags);
311 } else {
312 image = img;
313 }
314
315 dfbSurface = screen->createDFBSurface(image, image.format(), QDirectFBScreen::NoPreallocated | QDirectFBScreen::TrackSurface);
316 if (!dfbSurface) {
317 qWarning("QDirectFBPixmapData::fromImage()");
318 invalidate();
319 return;
320 }
321
322 w = image.width();
323 h = image.height();
324 is_null = (w <= 0 || h <= 0);
325 d = QDirectFBScreen::depth(imageFormat);
326 setSerialNumber(++global_ser_no);
327 #ifdef QT_NO_DIRECTFB_OPAQUE_DETECTION
328 Q_UNUSED(flags);
329 #endif
330 }
331
copy(const QPixmapData * data,const QRect & rect)332 void QDirectFBPixmapData::copy(const QPixmapData *data, const QRect &rect)
333 {
334 if (data->classId() != DirectFBClass) {
335 QPixmapData::copy(data, rect);
336 return;
337 }
338
339 const QDirectFBPixmapData *otherData = static_cast<const QDirectFBPixmapData*>(data);
340 #ifdef QT_NO_DIRECTFB_SUBSURFACE
341 if (otherData->lockFlags()) {
342 const_cast<QDirectFBPixmapData*>(otherData)->unlockSurface();
343 }
344 #endif
345 IDirectFBSurface *src = otherData->directFBSurface();
346 alpha = data->hasAlphaChannel();
347 imageFormat = (alpha
348 ? QDirectFBScreen::instance()->alphaPixmapFormat()
349 : QDirectFBScreen::instance()->pixelFormat());
350
351
352 dfbSurface = screen->createDFBSurface(rect.size(), imageFormat,
353 QDirectFBScreen::TrackSurface);
354 if (!dfbSurface) {
355 qWarning("QDirectFBPixmapData::copy()");
356 invalidate();
357 return;
358 }
359
360 if (alpha) {
361 dfbSurface->Clear(dfbSurface, 0, 0, 0, 0);
362 dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_BLEND_ALPHACHANNEL);
363 } else {
364 dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX);
365 }
366 const DFBRectangle blitRect = { rect.x(), rect.y(),
367 rect.width(), rect.height() };
368 w = rect.width();
369 h = rect.height();
370 d = otherData->d;
371 is_null = (w <= 0 || h <= 0);
372 unlockSurface();
373 DFBResult result = dfbSurface->Blit(dfbSurface, src, &blitRect, 0, 0);
374 #if (Q_DIRECTFB_VERSION >= 0x010000)
375 dfbSurface->ReleaseSource(dfbSurface);
376 #endif
377 if (result != DFB_OK) {
378 DirectFBError("QDirectFBPixmapData::copy()", result);
379 invalidate();
380 return;
381 }
382
383 setSerialNumber(++global_ser_no);
384 }
385
isOpaqueFormat(QImage::Format format)386 static inline bool isOpaqueFormat(QImage::Format format)
387 {
388 switch (format) {
389 case QImage::Format_RGB32:
390 case QImage::Format_RGB16:
391 case QImage::Format_RGB666:
392 case QImage::Format_RGB555:
393 case QImage::Format_RGB888:
394 case QImage::Format_RGB444:
395 return true;
396 default:
397 break;
398 }
399 return false;
400 }
401
fill(const QColor & color)402 void QDirectFBPixmapData::fill(const QColor &color)
403 {
404 if (!serialNumber())
405 return;
406
407 Q_ASSERT(dfbSurface);
408
409 alpha |= (color.alpha() < 255);
410
411 if (alpha && isOpaqueFormat(imageFormat)) {
412 QSize size;
413 dfbSurface->GetSize(dfbSurface, &size.rwidth(), &size.rheight());
414 screen->releaseDFBSurface(dfbSurface);
415 imageFormat = screen->alphaPixmapFormat();
416 d = QDirectFBScreen::depth(imageFormat);
417 dfbSurface = screen->createDFBSurface(size, screen->alphaPixmapFormat(), QDirectFBScreen::TrackSurface);
418 setSerialNumber(++global_ser_no);
419 if (!dfbSurface) {
420 qWarning("QDirectFBPixmapData::fill()");
421 invalidate();
422 return;
423 }
424 }
425
426 dfbSurface->Clear(dfbSurface, color.red(), color.green(), color.blue(), color.alpha());
427 }
428
transformed(const QTransform & transform,Qt::TransformationMode mode) const429 QPixmap QDirectFBPixmapData::transformed(const QTransform &transform,
430 Qt::TransformationMode mode) const
431 {
432 QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
433 #ifdef QT_NO_DIRECTFB_SUBSURFACE
434 if (lockFlags())
435 that->unlockSurface();
436 #endif
437
438 if (!dfbSurface || transform.type() != QTransform::TxScale
439 || mode != Qt::FastTransformation)
440 {
441 const QImage *image = that->buffer();
442 Q_ASSERT(image);
443 const QImage transformed = image->transformed(transform, mode);
444 QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType);
445 data->fromImage(transformed, Qt::AutoColor);
446 return QPixmap(data);
447 }
448
449 const QSize size = transform.mapRect(QRect(0, 0, w, h)).size();
450 if (size.isEmpty())
451 return QPixmap();
452
453 QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType);
454 data->setSerialNumber(++global_ser_no);
455 DFBSurfaceBlittingFlags flags = DSBLIT_NOFX;
456 data->alpha = alpha;
457 if (alpha) {
458 flags = DSBLIT_BLEND_ALPHACHANNEL;
459 }
460 data->dfbSurface = screen->createDFBSurface(size,
461 imageFormat,
462 QDirectFBScreen::TrackSurface);
463 if (flags & DSBLIT_BLEND_ALPHACHANNEL) {
464 data->dfbSurface->Clear(data->dfbSurface, 0, 0, 0, 0);
465 }
466 data->dfbSurface->SetBlittingFlags(data->dfbSurface, flags);
467
468 const DFBRectangle destRect = { 0, 0, size.width(), size.height() };
469 data->dfbSurface->StretchBlit(data->dfbSurface, dfbSurface, 0, &destRect);
470 data->w = size.width();
471 data->h = size.height();
472 data->is_null = (data->w <= 0 || data->h <= 0);
473
474 #if (Q_DIRECTFB_VERSION >= 0x010000)
475 data->dfbSurface->ReleaseSource(data->dfbSurface);
476 #endif
477 return QPixmap(data);
478 }
479
toImage() const480 QImage QDirectFBPixmapData::toImage() const
481 {
482 if (!dfbSurface)
483 return QImage();
484
485 #if 0
486 // In later versions of DirectFB one can set a flag to tell
487 // DirectFB not to move the surface to videomemory. When that
488 // happens we can use this (hopefully faster) codepath
489 #ifndef QT_NO_DIRECTFB_PREALLOCATED
490 QImage ret(w, h, QDirectFBScreen::getImageFormat(dfbSurface));
491 if (IDirectFBSurface *imgSurface = screen->createDFBSurface(ret, QDirectFBScreen::DontTrackSurface)) {
492 if (hasAlphaChannel()) {
493 imgSurface->SetBlittingFlags(imgSurface, DSBLIT_BLEND_ALPHACHANNEL);
494 imgSurface->Clear(imgSurface, 0, 0, 0, 0);
495 } else {
496 imgSurface->SetBlittingFlags(imgSurface, DSBLIT_NOFX);
497 }
498 imgSurface->Blit(imgSurface, dfbSurface, 0, 0, 0);
499 #if (Q_DIRECTFB_VERSION >= 0x010000)
500 imgSurface->ReleaseSource(imgSurface);
501 #endif
502 imgSurface->Release(imgSurface);
503 return ret;
504 }
505 #endif
506 #endif
507
508 QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
509 const QImage *img = that->buffer();
510 return img->copy();
511 }
512
513 /* This is QPixmapData::paintEngine(), not QPaintDevice::paintEngine() */
514
paintEngine() const515 QPaintEngine *QDirectFBPixmapData::paintEngine() const
516 {
517 if (!engine) {
518 // QDirectFBPixmapData is also a QCustomRasterPaintDevice, so pass
519 // that to the paint engine:
520 QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
521 that->engine = new QDirectFBPaintEngine(that);
522 }
523 return engine;
524 }
525
buffer()526 QImage *QDirectFBPixmapData::buffer()
527 {
528 if (!lockFlgs) {
529 lockSurface(DSLF_READ|DSLF_WRITE);
530 }
531 Q_ASSERT(lockFlgs);
532 Q_ASSERT(!lockedImage.isNull());
533 return &lockedImage;
534 }
535
536
scroll(int dx,int dy,const QRect & rect)537 bool QDirectFBPixmapData::scroll(int dx, int dy, const QRect &rect)
538 {
539 if (!dfbSurface) {
540 return false;
541 }
542 unlockSurface();
543 DFBResult result = dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX);
544 if (result != DFB_OK) {
545 DirectFBError("QDirectFBPixmapData::scroll", result);
546 return false;
547 }
548 result = dfbSurface->SetPorterDuff(dfbSurface, DSPD_NONE);
549 if (result != DFB_OK) {
550 DirectFBError("QDirectFBPixmapData::scroll", result);
551 return false;
552 }
553
554 const DFBRectangle source = { rect.x(), rect.y(), rect.width(), rect.height() };
555 result = dfbSurface->Blit(dfbSurface, dfbSurface, &source, source.x + dx, source.y + dy);
556 if (result != DFB_OK) {
557 DirectFBError("QDirectFBPixmapData::scroll", result);
558 return false;
559 }
560
561 return true;
562 }
563
invalidate()564 void QDirectFBPixmapData::invalidate()
565 {
566 if (dfbSurface) {
567 screen->releaseDFBSurface(dfbSurface);
568 dfbSurface = 0;
569 }
570 setSerialNumber(0);
571 alpha = false;
572 d = w = h = 0;
573 is_null = true;
574 imageFormat = QImage::Format_Invalid;
575 }
576
qt_directfb_surface_for_pixmap(const QPixmap & pixmap)577 Q_GUI_EXPORT IDirectFBSurface *qt_directfb_surface_for_pixmap(const QPixmap &pixmap)
578 {
579 const QPixmapData *data = pixmap.pixmapData();
580 if (!data || data->classId() != QPixmapData::DirectFBClass)
581 return 0;
582 const QDirectFBPixmapData *dfbData = static_cast<const QDirectFBPixmapData*>(data);
583 return dfbData->directFBSurface();
584 }
585
586 QT_END_NAMESPACE
587
588 #endif // QT_NO_QWS_DIRECTFB
589