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