blob: 3e21a1925dd837ce1c085220615ad9d2f0330410 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qdirectfbblitter.h"
#include "qdirectfbconvenience.h"
#include <QtGui/private/qpixmap_blitter_p.h>
#include <QDebug>
#include <QFile>
#include <directfb.h>
QT_BEGIN_NAMESPACE
static QBlittable::Capabilities dfb_blitter_capabilities()
{
return QBlittable::Capabilities(QBlittable::SolidRectCapability
|QBlittable::SourcePixmapCapability
|QBlittable::SourceOverPixmapCapability
|QBlittable::SourceOverScaledPixmapCapability
|QBlittable::AlphaFillRectCapability
|QBlittable::OpacityPixmapCapability
|QBlittable::DrawScaledCachedGlyphsCapability
);
}
QDirectFbBlitter::QDirectFbBlitter(const QSize &rect, IDirectFBSurface *surface)
: QBlittable(rect, dfb_blitter_capabilities())
, m_surface(surface)
, m_debugPaint(false)
{
m_surface->AddRef(m_surface.data());
DFBSurfaceCapabilities surfaceCaps;
m_surface->GetCapabilities(m_surface.data(), &surfaceCaps);
m_premult = (surfaceCaps & DSCAPS_PREMULTIPLIED);
if (qEnvironmentVariableIntValue("QT_DIRECTFB_BLITTER_DEBUGPAINT"))
m_debugPaint = true;
}
QDirectFbBlitter::QDirectFbBlitter(const QSize &rect, bool alpha)
: QBlittable(rect, dfb_blitter_capabilities())
, m_premult(false)
, m_debugPaint(false)
{
DFBSurfaceDescription surfaceDesc;
memset(&surfaceDesc,0,sizeof(DFBSurfaceDescription));
surfaceDesc.width = rect.width();
surfaceDesc.height = rect.height();
// force alpha format to get AlphaFillRectCapability and ExtendedPixmapCapability support
alpha = true;
if (alpha) {
m_premult = true;
surfaceDesc.caps = DSCAPS_PREMULTIPLIED;
surfaceDesc.pixelformat = QDirectFbBlitter::alphaPixmapFormat();
surfaceDesc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_CAPS | DSDESC_PIXELFORMAT);
} else {
surfaceDesc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT);
surfaceDesc.pixelformat = QDirectFbBlitter::pixmapFormat();
}
if (qEnvironmentVariableIntValue("QT_DIRECTFB_BLITTER_DEBUGPAINT"))
m_debugPaint = true;
IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
dfb->CreateSurface(dfb , &surfaceDesc, m_surface.outPtr());
m_surface->Clear(m_surface.data(), 0, 0, 0, 0);
}
QDirectFbBlitter::~QDirectFbBlitter()
{
unlock();
}
DFBSurfacePixelFormat QDirectFbBlitter::alphaPixmapFormat()
{
return DSPF_ARGB;
}
DFBSurfacePixelFormat QDirectFbBlitter::pixmapFormat()
{
return DSPF_RGB32;
}
DFBSurfacePixelFormat QDirectFbBlitter::selectPixmapFormat(bool withAlpha)
{
return withAlpha ? alphaPixmapFormat() : pixmapFormat();
}
void QDirectFbBlitter::fillRect(const QRectF &rect, const QColor &color)
{
alphaFillRect(rect, color, QPainter::CompositionMode_Source);
}
void QDirectFbBlitter::drawPixmap(const QRectF &rect, const QPixmap &pixmap, const QRectF &srcRect)
{
drawPixmapOpacity(rect, pixmap, srcRect, QPainter::CompositionMode_SourceOver, 1.0);
}
void QDirectFbBlitter::alphaFillRect(const QRectF &rect, const QColor &color, QPainter::CompositionMode cmode)
{
int x, y, w, h;
DFBResult result;
// check parameters
rect.toRect().getRect(&x, &y ,&w, &h);
if ((w <= 0) || (h <= 0)) return;
if ((cmode == QPainter::CompositionMode_Source) || (color.alpha() == 255)) {
// CompositionMode_Source case or CompositionMode_SourceOver with opaque color
m_surface->SetDrawingFlags(m_surface.data(),
DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_NOFX | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_NOFX));
m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC);
} else {
// CompositionMode_SourceOver case
// check if operation is useless
if (color.alpha() == 0)
return;
m_surface->SetDrawingFlags(m_surface.data(),
DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_BLEND));
m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC_OVER);
}
// set color
m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), color.alpha());
// perform fill
result = m_surface->FillRectangle(m_surface.data(), x, y, w, h);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::alphaFillRect()", result);
if (m_debugPaint)
drawDebugRect(QRect(x, y, w, h), QColor(Qt::blue));
}
void QDirectFbBlitter::drawPixmapOpacity(const QRectF &rect, const QPixmap &pixmap, const QRectF &subrect, QPainter::CompositionMode cmode, qreal opacity)
{
QRect sQRect = subrect.toRect();
QRect dQRect = rect.toRect();
DFBRectangle sRect(sQRect.x(), sQRect.y(), sQRect.width(), sQRect.height());
DFBRectangle dRect(dQRect.x(), dQRect.y(), dQRect.width(), dQRect.height());
DFBResult result;
// skip if dst too small
if ((dRect.w <= 0) || (dRect.h <= 0)) return;
// correct roundings if needed
if (sRect.w <= 0) sRect.w = 1;
if (sRect.h <= 0) sRect.h = 1;
QDirectFbBlitterPlatformPixmap *blitPm = static_cast<QDirectFbBlitterPlatformPixmap *>(pixmap.handle());
QDirectFbBlitter *dfbBlitter = static_cast<QDirectFbBlitter *>(blitPm->blittable());
dfbBlitter->unlock();
IDirectFBSurface *s = dfbBlitter->m_surface.data();
DFBSurfaceBlittingFlags blittingFlags = DFBSurfaceBlittingFlags(DSBLIT_BLEND_ALPHACHANNEL);
DFBSurfacePorterDuffRule porterDuff = (cmode == QPainter::CompositionMode_SourceOver) ? DSPD_SRC_OVER : DSPD_SRC;
if (opacity != 1.0)
{
blittingFlags = DFBSurfaceBlittingFlags(blittingFlags | DSBLIT_BLEND_COLORALPHA | (m_premult ? DSBLIT_SRC_PREMULTCOLOR : 0));
m_surface->SetColor(m_surface.data(), 0xff, 0xff, 0xff, (u8) (opacity * 255.0));
}
m_surface->SetBlittingFlags(m_surface.data(), DFBSurfaceBlittingFlags(blittingFlags));
m_surface->SetPorterDuff(m_surface.data(), porterDuff);
if (cmode == QPainter::CompositionMode_SourceOver)
m_surface->SetDstBlendFunction(m_surface.data(), DSBF_INVSRCALPHA);
if ((sRect.w == dRect.w) && (sRect.h == dRect.h)) {
result = m_surface->Blit(m_surface.data(), s, &sRect, dRect.x, dRect.y);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawPixmapOpacity()", result);
if (m_debugPaint)
drawDebugRect(QRect(dRect.x, dRect.y, sRect.w, sRect.h), QColor(Qt::green));
} else {
result = m_surface->StretchBlit(m_surface.data(), s, &sRect, &dRect);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawPixmapOpacity()", result);
if (m_debugPaint)
drawDebugRect(QRect(dRect.x, dRect.y, dRect.w, dRect.h), QColor(Qt::red));
}
}
bool QDirectFbBlitter::drawCachedGlyphs(const QPaintEngineState *state, QFontEngine::GlyphFormat glyphFormat, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine)
{
void *cacheKey = QDirectFbConvenience::dfbInterface();
QDirectFbTextureGlyphCache *cache =
static_cast<QDirectFbTextureGlyphCache *>(fontEngine->glyphCache(cacheKey, glyphFormat, state->transform()));
if (!cache) {
cache = new QDirectFbTextureGlyphCache(glyphFormat, state->transform());
fontEngine->setGlyphCache(cacheKey, cache);
}
cache->populate(fontEngine, numGlyphs, glyphs, positions);
cache->fillInPendingGlyphs();
if (cache->image().width() == 0 || cache->image().height() == 0)
return false;
const int margin = fontEngine->glyphMargin(glyphFormat);
QVarLengthArray<DFBRectangle, 64> sourceRects(numGlyphs);
QVarLengthArray<DFBPoint, 64> destPoints(numGlyphs);
int nGlyphs = 0;
for (int i=0; i<numGlyphs; ++i) {
QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
const QTextureGlyphCache::Coord &c = cache->coords[glyph];
if (c.isNull())
continue;
int x = qFloor(positions[i].x) + c.baseLineX - margin;
int y = qRound(positions[i].y) - c.baseLineY - margin;
// printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
// c.x, c.y,
// c.w, c.h,
// c.baseLineX, c.baseLineY,
// glyphs[i],
// x, y,
// positions[i].x.toInt(), positions[i].y.toInt());
sourceRects[nGlyphs].x = c.x;
sourceRects[nGlyphs].y = c.y;
sourceRects[nGlyphs].w = c.w;
sourceRects[nGlyphs].h = c.h;
destPoints[nGlyphs].x = x;
destPoints[nGlyphs].y = y;
++nGlyphs;
}
const QColor color = state->pen().color();
m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), color.alpha());
m_surface->SetSrcBlendFunction(m_surface.data(), DSBF_SRCALPHA);
m_surface->SetDstBlendFunction(m_surface.data(), DSBF_INVSRCALPHA);
int flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE;
if (color.alpha() != 0xff)
flags |= DSBLIT_BLEND_COLORALPHA;
m_surface->SetBlittingFlags(m_surface.data(), DFBSurfaceBlittingFlags(flags));
const QRasterPaintEngineState *rs = static_cast<const QRasterPaintEngineState*>(state);
if (rs->clip && rs->clip->enabled) {
Q_ASSERT(rs->clip->hasRectClip);
DFBRegion dfbClip;
dfbClip.x1 = rs->clip->clipRect.x();
dfbClip.y1 = rs->clip->clipRect.y();
dfbClip.x2 = rs->clip->clipRect.right();
dfbClip.y2 = rs->clip->clipRect.bottom();
m_surface->SetClip(m_surface.data(), &dfbClip);
}
m_surface->BatchBlit(m_surface.data(), cache->sourceSurface(), sourceRects.constData(), destPoints.constData(), nGlyphs);
if (m_debugPaint) {
for (int i = 0; i < nGlyphs; ++i) {
drawDebugRect(QRect(destPoints[i].x, destPoints[i].y, sourceRects[i].w, sourceRects[i].h), QColor(Qt::yellow));
}
}
if (rs->clip && rs->clip->enabled)
m_surface->SetClip(m_surface.data(), 0);
return true;
}
QImage *QDirectFbBlitter::doLock()
{
Q_ASSERT(m_surface);
Q_ASSERT(size().isValid());
void *mem;
int bpl;
const DFBResult result = m_surface->Lock(m_surface.data(), DFBSurfaceLockFlags(DSLF_WRITE|DSLF_READ), static_cast<void**>(&mem), &bpl);
if (result == DFB_OK) {
DFBSurfacePixelFormat dfbFormat;
DFBSurfaceCapabilities dfbCaps;
m_surface->GetPixelFormat(m_surface.data(), &dfbFormat);
m_surface->GetCapabilities(m_surface.data(), &dfbCaps);
QImage::Format format = QDirectFbConvenience::imageFormatFromSurfaceFormat(dfbFormat, dfbCaps);
int w, h;
m_surface->GetSize(m_surface.data(), &w, &h);
m_image = QImage(static_cast<uchar *>(mem),w,h,bpl,format);
} else {
DirectFBError("Failed to lock image", result);
}
return &m_image;
}
bool QDirectFbBlitterPlatformPixmap::fromDataBufferDescription(const DFBDataBufferDescription &dataBufferDescription)
{
DFBResult result;
IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
// Create a data buffer
QDirectFBPointer<IDirectFBDataBuffer> dataBuffer;
result = dfb->CreateDataBuffer(dfb, &dataBufferDescription, dataBuffer.outPtr());
if (result != DFB_OK) {
DirectFBError(QDFB_PRETTY, result);
return false;
}
// Create the image provider
QDirectFBPointer<IDirectFBImageProvider> provider;
result = dataBuffer->CreateImageProvider(dataBuffer.data(), provider.outPtr());
if (result != DFB_OK) {
DirectFBError(QDFB_PRETTY, result);
return false;
}
// Extract image information
DFBImageDescription imageDescription;
result = provider->GetImageDescription(provider.data(), &imageDescription);
if (result != DFB_OK) {
DirectFBError(QDFB_PRETTY, result);
return false;
}
// Can we handle this directlu?
if (imageDescription.caps & DICAPS_COLORKEY)
return false;
DFBSurfaceDescription surfaceDescription;
result = provider->GetSurfaceDescription(provider.data(), &surfaceDescription);
if (result != DFB_OK) {
DirectFBError(QDFB_PRETTY, result);
return false;
}
m_alpha = imageDescription.caps & DICAPS_ALPHACHANNEL;
resize(surfaceDescription.width, surfaceDescription.height);
// TODO: FIXME; update d
result = provider->RenderTo(provider.data(), dfbBlitter()->dfbSurface(), 0);
if (result != DFB_OK) {
DirectFBError(QDFB_PRETTY, result);
return false;
}
return true;
}
bool QDirectFbBlitterPlatformPixmap::fromFile(const QString &filename, const char *format,
Qt::ImageConversionFlags flags)
{
// If we can't find the file, pass it on to the base class as it is
// trying harder by appending various extensions to the path.
if (!QFile::exists(filename))
return QBlittablePlatformPixmap::fromFile(filename, format, flags);
// Stop if there is a requirement for colors
if (flags != Qt::AutoColor)
return QBlittablePlatformPixmap::fromFile(filename, format, flags);
// Deal with resources
if (filename.startsWith(QLatin1Char(':')))
return QBlittablePlatformPixmap::fromFile(filename, format, flags);
// Try to use directfb to load it.
DFBDataBufferDescription description;
description.flags = DBDESC_FILE;
const QByteArray fileNameData = filename.toLocal8Bit();
description.file = fileNameData.constData();
if (fromDataBufferDescription(description))
return true;
// Fallback
return QBlittablePlatformPixmap::fromFile(filename, format, flags);
}
void QDirectFbBlitter::doUnlock()
{
m_surface->Unlock(m_surface.data());
}
void QDirectFbBlitter::drawDebugRect(const QRect &rect, const QColor &color)
{
int x, y, w, h;
DFBResult result;
// check parameters
rect.getRect(&x, &y ,&w, &h);
if ((w <= 0) || (h <= 0)) return;
m_surface->SetDrawingFlags(m_surface.data(),
DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_BLEND));
m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC_OVER);
// set color
m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), 120);
result = m_surface->DrawLine(m_surface.data(), x, y, x + w-1, y);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
result = m_surface->DrawLine(m_surface.data(), x + w-1, y, x + w-1, y + h-1);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
result = m_surface->DrawLine(m_surface.data(), x + w-1, y + h-1, x, y + h-1);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
result = m_surface->DrawLine(m_surface.data(), x, y + h-1, x, y);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), 10);
result = m_surface->FillRectangle(m_surface.data(), x, y, w, h);
if (result != DFB_OK)
DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
}
void QDirectFbTextureGlyphCache::resizeTextureData(int width, int height)
{
m_surface.reset();;
QImageTextureGlyphCache::resizeTextureData(width, height);
}
IDirectFBSurface *QDirectFbTextureGlyphCache::sourceSurface()
{
if (m_surface.isNull()) {
const QImage &source = image();
DFBSurfaceDescription desc;
memset(&desc, 0, sizeof(desc));
desc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED | DSDESC_CAPS);
desc.width = source.width();
desc.height = source.height();
desc.caps = DSCAPS_SYSTEMONLY;
switch (source.format()) {
case QImage::Format_Mono:
desc.pixelformat = DSPF_A1;
break;
case QImage::Format_Alpha8:
desc.pixelformat = DSPF_A8;
break;
default:
qFatal("QDirectFBTextureGlyphCache: Unsupported source texture image format.");
break;
}
desc.preallocated[0].data = const_cast<void*>(static_cast<const void*>(source.bits()));
desc.preallocated[0].pitch = source.bytesPerLine();
desc.preallocated[1].data = 0;
desc.preallocated[1].pitch = 0;
IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
dfb->CreateSurface(dfb , &desc, m_surface.outPtr());
}
return m_surface.data();
}
QT_END_NAMESPACE