src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,572 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdirectfbpixmap.h"
+
+#ifndef QT_NO_QWS_DIRECTFB
+
+#include "qdirectfbscreen.h"
+#include "qdirectfbpaintengine.h"
+
+#include <QtGui/qbitmap.h>
+#include <QtCore/qfile.h>
+#include <directfb.h>
+
+
+QT_BEGIN_NAMESPACE
+
+static int global_ser_no = 0;
+
+QDirectFBPixmapData::QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType)
+    : QPixmapData(pixelType, DirectFBClass), QDirectFBPaintDevice(screen),
+      alpha(false)
+{
+    setSerialNumber(0);
+}
+
+QDirectFBPixmapData::~QDirectFBPixmapData()
+{
+}
+
+void QDirectFBPixmapData::resize(int width, int height)
+{
+    if (width <= 0 || height <= 0) {
+        invalidate();
+        return;
+    }
+
+    imageFormat = screen->pixelFormat();
+    dfbSurface = screen->createDFBSurface(QSize(width, height),
+                                          imageFormat,
+                                          QDirectFBScreen::TrackSurface);
+    d = QDirectFBScreen::depth(imageFormat);
+    alpha = false;
+    if (!dfbSurface) {
+        invalidate();
+        qWarning("QDirectFBPixmapData::resize(): Unable to allocate surface");
+        return;
+    }
+
+    w = width;
+    h = height;
+    is_null = (w <= 0 || h <= 0);
+    setSerialNumber(++global_ser_no);
+}
+
+// mostly duplicated from qimage.cpp (QImageData::checkForAlphaPixels)
+static bool checkForAlphaPixels(const QImage &img)
+{
+    const uchar *bits = img.bits();
+    const int bytes_per_line = img.bytesPerLine();
+    const uchar *end_bits = bits + bytes_per_line;
+    const int width = img.width();
+    const int height = img.height();
+    switch (img.format()) {
+    case QImage::Format_Indexed8:
+        return img.hasAlphaChannel();
+    case QImage::Format_ARGB32:
+    case QImage::Format_ARGB32_Premultiplied:
+        for (int y=0; y<height; ++y) {
+            for (int x=0; x<width; ++x) {
+                if ((((uint *)bits)[x] & 0xff000000) != 0xff000000) {
+                    return true;
+                }
+            }
+            bits += bytes_per_line;
+        }
+        break;
+
+    case QImage::Format_ARGB8555_Premultiplied:
+    case QImage::Format_ARGB8565_Premultiplied:
+        for (int y=0; y<height; ++y) {
+            while (bits < end_bits) {
+                if (bits[0] != 0) {
+                    return true;
+                }
+                bits += 3;
+            }
+            bits = end_bits;
+            end_bits += bytes_per_line;
+        }
+        break;
+
+    case QImage::Format_ARGB6666_Premultiplied:
+        for (int y=0; y<height; ++y) {
+            while (bits < end_bits) {
+                if ((bits[0] & 0xfc) != 0) {
+                    return true;
+                }
+                bits += 3;
+            }
+            bits = end_bits;
+            end_bits += bytes_per_line;
+        }
+        break;
+
+    case QImage::Format_ARGB4444_Premultiplied:
+        for (int y=0; y<height; ++y) {
+            while (bits < end_bits) {
+                if ((bits[0] & 0xf0) != 0) {
+                    return true;
+                }
+                bits += 2;
+            }
+            bits = end_bits;
+            end_bits += bytes_per_line;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return false;
+}
+
+bool QDirectFBPixmapData::hasAlphaChannel(const QImage &img)
+{
+#ifndef QT_NO_DIRECTFB_OPAQUE_DETECTION
+    return checkForAlphaPixels(img);
+#else
+    return img.hasAlphaChannel();
+#endif
+}
+
+#ifdef QT_DIRECTFB_IMAGEPROVIDER
+bool QDirectFBPixmapData::fromFile(const QString &filename, const char *format,
+                                   Qt::ImageConversionFlags flags)
+{
+    if (flags == Qt::AutoColor) {
+        if (filename.startsWith(QLatin1Char(':'))) { // resource
+            QFile file(filename);
+            if (!file.open(QIODevice::ReadOnly))
+                return false;
+            const QByteArray data = file.readAll();
+            file.close();
+            return fromData(reinterpret_cast<const uchar*>(data.constData()), data.size(), format, flags);
+        } else {
+            DFBDataBufferDescription description;
+            description.flags = DBDESC_FILE;
+            const QByteArray fileNameData = filename.toLocal8Bit();
+            description.file = fileNameData.constData();
+            if (fromDataBufferDescription(description)) {
+                return true;
+            }
+            // fall back to Qt
+        }
+    }
+    return QPixmapData::fromFile(filename, format, flags);
+}
+
+bool QDirectFBPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+                                   Qt::ImageConversionFlags flags)
+{
+    if (flags == Qt::AutoColor) {
+        DFBDataBufferDescription description;
+        description.flags = DBDESC_MEMORY;
+        description.memory.data = buffer;
+        description.memory.length = len;
+        if (fromDataBufferDescription(description))
+            return true;
+        // fall back to Qt
+    }
+    return QPixmapData::fromData(buffer, len, format, flags);
+}
+
+template <typename T> struct QDirectFBInterfaceCleanupHandler
+{
+    static void cleanup(T *t) { if (t) t->Release(t); }
+};
+
+template <typename T>
+class QDirectFBPointer : public QScopedPointer<T, QDirectFBInterfaceCleanupHandler<T> >
+{
+public:
+    QDirectFBPointer(T *t = 0)
+        : QScopedPointer<T, QDirectFBInterfaceCleanupHandler<T> >(t)
+    {}
+};
+
+bool QDirectFBPixmapData::fromDataBufferDescription(const DFBDataBufferDescription &dataBufferDescription)
+{
+    IDirectFB *dfb = screen->dfb();
+    Q_ASSERT(dfb);
+    DFBResult result = DFB_OK;
+    IDirectFBDataBuffer *dataBufferPtr;
+    if ((result = dfb->CreateDataBuffer(dfb, &dataBufferDescription, &dataBufferPtr)) != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::fromDataBufferDescription()", result);
+        return false;
+    }
+    QDirectFBPointer<IDirectFBDataBuffer> dataBuffer(dataBufferPtr);
+
+    IDirectFBImageProvider *providerPtr;
+    if ((result = dataBuffer->CreateImageProvider(dataBuffer.data(), &providerPtr)) != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::fromDataBufferDescription(): Can't create image provider", result);
+        return false;
+    }
+    QDirectFBPointer<IDirectFBImageProvider> provider(providerPtr);
+
+    DFBSurfaceDescription surfaceDescription;
+    if ((result = provider->GetSurfaceDescription(provider.data(), &surfaceDescription)) != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::fromDataBufferDescription(): Can't get surface description", result);
+        return false;
+    }
+
+    DFBImageDescription imageDescription;
+    result = provider->GetImageDescription(provider.data(), &imageDescription);
+    if (result != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't get image description", result);
+        return false;
+    }
+
+    alpha = imageDescription.caps & (DICAPS_ALPHACHANNEL|DICAPS_COLORKEY);
+    imageFormat = alpha ? screen->alphaPixmapFormat() : screen->pixelFormat();
+
+    dfbSurface = screen->createDFBSurface(QSize(surfaceDescription.width, surfaceDescription.height),
+                                          imageFormat, QDirectFBScreen::TrackSurface);
+
+    result = provider->RenderTo(provider.data(), dfbSurface, 0);
+    if (result != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::fromSurfaceDescription(): Can't render to surface", result);
+        return false;
+    }
+
+    w = surfaceDescription.width;
+    h = surfaceDescription.height;
+    is_null = (w <= 0 || h <= 0);
+    d = QDirectFBScreen::depth(imageFormat);
+    setSerialNumber(++global_ser_no);
+
+#if defined QT_DIRECTFB_IMAGEPROVIDER_KEEPALIVE
+    screen->setDirectFBImageProvider(providerPtr);
+    provider.take();
+#endif
+
+    return true;
+}
+
+#endif
+
+void QDirectFBPixmapData::fromImage(const QImage &img,
+                                    Qt::ImageConversionFlags flags)
+{
+    if (img.depth() == 1 || img.format() == QImage::Format_RGB32) {
+        fromImage(img.convertToFormat(screen->alphaPixmapFormat()), flags);
+        return;
+    }
+
+    if (img.hasAlphaChannel()
+#ifndef QT_NO_DIRECTFB_OPAQUE_DETECTION
+        && (flags & Qt::NoOpaqueDetection || QDirectFBPixmapData::hasAlphaChannel(img))
+#endif
+        ) {
+        alpha = true;
+        imageFormat = screen->alphaPixmapFormat();
+    } else {
+        alpha = false;
+        imageFormat = screen->pixelFormat();
+    }
+    QImage image;
+    if (flags != Qt::AutoColor) {
+        image = img.convertToFormat(imageFormat, flags);
+        flags = Qt::AutoColor;
+    } else if (img.format() == QImage::Format_RGB32) {
+        image = img.convertToFormat(imageFormat, flags);
+    } else {
+        image = img;
+    }
+
+    IDirectFBSurface *imageSurface = screen->createDFBSurface(image, image.format(), QDirectFBScreen::DontTrackSurface);
+    if (!imageSurface) {
+        qWarning("QDirectFBPixmapData::fromImage()");
+        invalidate();
+        return;
+    }
+
+    dfbSurface = screen->createDFBSurface(image.size(), imageFormat, QDirectFBScreen::TrackSurface);
+    if (!dfbSurface) {
+        qWarning("QDirectFBPixmapData::fromImage()");
+        invalidate();
+        return;
+    }
+
+    if (image.hasAlphaChannel()) {
+        dfbSurface->Clear(dfbSurface, 0, 0, 0, 0);
+        dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_BLEND_ALPHACHANNEL);
+    } else {
+        dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX);
+    }
+
+    dfbSurface->Blit(dfbSurface, imageSurface, 0, 0, 0);
+    imageSurface->Release(imageSurface);
+
+    w = image.width();
+    h = image.height();
+    is_null = (w <= 0 || h <= 0);
+    d = QDirectFBScreen::depth(imageFormat);
+    setSerialNumber(++global_ser_no);
+#ifdef QT_NO_DIRECTFB_OPAQUE_DETECTION
+    Q_UNUSED(flags);
+#endif
+}
+
+void QDirectFBPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+    if (data->classId() != DirectFBClass) {
+        QPixmapData::copy(data, rect);
+        return;
+    }
+
+    const QDirectFBPixmapData *otherData = static_cast<const QDirectFBPixmapData*>(data);
+#ifdef QT_NO_DIRECTFB_SUBSURFACE
+    if (otherData->lockFlags()) {
+        const_cast<QDirectFBPixmapData*>(otherData)->unlockSurface();
+    }
+#endif
+    IDirectFBSurface *src = otherData->directFBSurface();
+    alpha = data->hasAlphaChannel();
+    imageFormat = (alpha
+                   ? QDirectFBScreen::instance()->alphaPixmapFormat()
+                   : QDirectFBScreen::instance()->pixelFormat());
+
+
+    dfbSurface = screen->createDFBSurface(rect.size(), imageFormat,
+                                          QDirectFBScreen::TrackSurface);
+    if (!dfbSurface) {
+        qWarning("QDirectFBPixmapData::copy()");
+        invalidate();
+        return;
+    }
+
+    if (alpha) {
+        dfbSurface->Clear(dfbSurface, 0, 0, 0, 0);
+        dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_BLEND_ALPHACHANNEL);
+    } else {
+        dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX);
+    }
+    const DFBRectangle blitRect = { rect.x(), rect.y(),
+                                    rect.width(), rect.height() };
+    w = rect.width();
+    h = rect.height();
+    d = otherData->d;
+    is_null = (w <= 0 || h <= 0);
+    unlockSurface();
+    DFBResult result = dfbSurface->Blit(dfbSurface, src, &blitRect, 0, 0);
+#if (Q_DIRECTFB_VERSION >= 0x010000)
+    dfbSurface->ReleaseSource(dfbSurface);
+#endif
+    if (result != DFB_OK) {
+        DirectFBError("QDirectFBPixmapData::copy()", result);
+        invalidate();
+        return;
+    }
+
+    setSerialNumber(++global_ser_no);
+}
+
+static inline bool isOpaqueFormat(QImage::Format format)
+{
+    switch (format) {
+    case QImage::Format_RGB32:
+    case QImage::Format_RGB16:
+    case QImage::Format_RGB666:
+    case QImage::Format_RGB555:
+    case QImage::Format_RGB888:
+    case QImage::Format_RGB444:
+        return true;
+    default:
+        break;
+    }
+    return false;
+}
+
+void QDirectFBPixmapData::fill(const QColor &color)
+{
+    if (!serialNumber())
+        return;
+
+    Q_ASSERT(dfbSurface);
+
+    alpha = (color.alpha() < 255);
+
+    if (alpha && isOpaqueFormat(imageFormat)) {
+        QSize size;
+        dfbSurface->GetSize(dfbSurface, &size.rwidth(), &size.rheight());
+        screen->releaseDFBSurface(dfbSurface);
+        imageFormat = screen->alphaPixmapFormat();
+        d = QDirectFBScreen::depth(imageFormat);
+        dfbSurface = screen->createDFBSurface(size, screen->alphaPixmapFormat(), QDirectFBScreen::TrackSurface);
+        setSerialNumber(++global_ser_no);
+        if (!dfbSurface) {
+            qWarning("QDirectFBPixmapData::fill()");
+            invalidate();
+            return;
+        }
+    }
+
+    dfbSurface->Clear(dfbSurface, color.red(), color.green(), color.blue(), color.alpha());
+}
+
+QPixmap QDirectFBPixmapData::transformed(const QTransform &transform,
+                                         Qt::TransformationMode mode) const
+{
+    QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
+#ifdef QT_NO_DIRECTFB_SUBSURFACE
+    if (lockFlags())
+        that->unlockSurface();
+#endif
+
+    if (!dfbSurface || transform.type() != QTransform::TxScale
+        || mode != Qt::FastTransformation)
+    {
+        const QImage *image = that->buffer();
+        Q_ASSERT(image);
+        const QImage transformed = image->transformed(transform, mode);
+        QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType);
+        data->fromImage(transformed, Qt::AutoColor);
+        return QPixmap(data);
+    }
+
+    const QSize size = transform.mapRect(QRect(0, 0, w, h)).size();
+    if (size.isEmpty())
+        return QPixmap();
+
+    QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType);
+    DFBSurfaceBlittingFlags flags = DSBLIT_NOFX;
+    data->alpha = alpha;
+    if (alpha) {
+        flags = DSBLIT_BLEND_ALPHACHANNEL;
+    }
+    data->dfbSurface = screen->createDFBSurface(size,
+                                                imageFormat,
+                                                QDirectFBScreen::TrackSurface);
+    if (flags & DSBLIT_BLEND_ALPHACHANNEL) {
+        data->dfbSurface->Clear(data->dfbSurface, 0, 0, 0, 0);
+    }
+    data->dfbSurface->SetBlittingFlags(data->dfbSurface, flags);
+
+    const DFBRectangle destRect = { 0, 0, size.width(), size.height() };
+    data->dfbSurface->StretchBlit(data->dfbSurface, dfbSurface, 0, &destRect);
+    data->w = size.width();
+    data->h = size.height();
+    data->is_null = (data->w <= 0 || data->h <= 0);
+
+#if (Q_DIRECTFB_VERSION >= 0x010000)
+    data->dfbSurface->ReleaseSource(data->dfbSurface);
+#endif
+    return QPixmap(data);
+}
+
+QImage QDirectFBPixmapData::toImage() const
+{
+    if (!dfbSurface)
+        return QImage();
+
+#if 0
+    // In later versions of DirectFB one can set a flag to tell
+    // DirectFB not to move the surface to videomemory. When that
+    // happens we can use this (hopefully faster) codepath
+#ifndef QT_NO_DIRECTFB_PREALLOCATED
+    QImage ret(w, h, QDirectFBScreen::getImageFormat(dfbSurface));
+    if (IDirectFBSurface *imgSurface = screen->createDFBSurface(ret, QDirectFBScreen::DontTrackSurface)) {
+        if (hasAlphaChannel()) {
+            imgSurface->SetBlittingFlags(imgSurface, DSBLIT_BLEND_ALPHACHANNEL);
+            imgSurface->Clear(imgSurface, 0, 0, 0, 0);
+        } else {
+            imgSurface->SetBlittingFlags(imgSurface, DSBLIT_NOFX);
+        }
+        imgSurface->Blit(imgSurface, dfbSurface, 0, 0, 0);
+#if (Q_DIRECTFB_VERSION >= 0x010000)
+        imgSurface->ReleaseSource(imgSurface);
+#endif
+        imgSurface->Release(imgSurface);
+        return ret;
+    }
+#endif
+#endif
+
+    QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
+    const QImage *img = that->buffer();
+    return img->copy();
+}
+
+/* This is QPixmapData::paintEngine(), not QPaintDevice::paintEngine() */
+
+QPaintEngine *QDirectFBPixmapData::paintEngine() const
+{
+    if (!engine) {
+        // QDirectFBPixmapData is also a QCustomRasterPaintDevice, so pass
+        // that to the paint engine:
+        QDirectFBPixmapData *that = const_cast<QDirectFBPixmapData*>(this);
+        that->engine = new QDirectFBPaintEngine(that);
+    }
+    return engine;
+}
+
+QImage *QDirectFBPixmapData::buffer()
+{
+    if (!lockFlgs) {
+        lockSurface(DSLF_READ|DSLF_WRITE);
+    }
+    Q_ASSERT(lockFlgs);
+    Q_ASSERT(!lockedImage.isNull());
+    return &lockedImage;
+}
+
+void QDirectFBPixmapData::invalidate()
+{
+    if (dfbSurface) {
+        screen->releaseDFBSurface(dfbSurface);
+        dfbSurface = 0;
+    }
+    setSerialNumber(0);
+    alpha = false;
+    d = w = h = 0;
+    is_null = true;
+    imageFormat = QImage::Format_Invalid;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QWS_DIRECTFB
+
+
+