1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qdirectfbblitter.h"
41 #include "qdirectfbconvenience.h"
42 
43 #include <QtGui/private/qpixmap_blitter_p.h>
44 
45 #include <QDebug>
46 #include <QFile>
47 
48 #include <directfb.h>
49 
50 QT_BEGIN_NAMESPACE
51 
dfb_blitter_capabilities()52 static QBlittable::Capabilities dfb_blitter_capabilities()
53 {
54     return QBlittable::Capabilities(QBlittable::SolidRectCapability
55                                     |QBlittable::SourcePixmapCapability
56                                     |QBlittable::SourceOverPixmapCapability
57                                     |QBlittable::SourceOverScaledPixmapCapability
58                                     |QBlittable::AlphaFillRectCapability
59                                     |QBlittable::OpacityPixmapCapability
60                                     |QBlittable::DrawScaledCachedGlyphsCapability
61                                     );
62 }
63 
QDirectFbBlitter(const QSize & rect,IDirectFBSurface * surface)64 QDirectFbBlitter::QDirectFbBlitter(const QSize &rect, IDirectFBSurface *surface)
65     : QBlittable(rect, dfb_blitter_capabilities())
66     , m_surface(surface)
67     , m_debugPaint(false)
68 {
69     m_surface->AddRef(m_surface.data());
70 
71     DFBSurfaceCapabilities surfaceCaps;
72     m_surface->GetCapabilities(m_surface.data(), &surfaceCaps);
73     m_premult = (surfaceCaps & DSCAPS_PREMULTIPLIED);
74     if (qEnvironmentVariableIntValue("QT_DIRECTFB_BLITTER_DEBUGPAINT"))
75         m_debugPaint = true;
76 }
77 
QDirectFbBlitter(const QSize & rect,bool alpha)78 QDirectFbBlitter::QDirectFbBlitter(const QSize &rect, bool alpha)
79     : QBlittable(rect, dfb_blitter_capabilities())
80     , m_premult(false)
81     , m_debugPaint(false)
82 {
83     DFBSurfaceDescription surfaceDesc;
84     memset(&surfaceDesc,0,sizeof(DFBSurfaceDescription));
85     surfaceDesc.width = rect.width();
86     surfaceDesc.height = rect.height();
87 
88     // force alpha format to get AlphaFillRectCapability and ExtendedPixmapCapability support
89     alpha = true;
90 
91     if (alpha) {
92         m_premult = true;
93         surfaceDesc.caps = DSCAPS_PREMULTIPLIED;
94         surfaceDesc.pixelformat = QDirectFbBlitter::alphaPixmapFormat();
95         surfaceDesc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_CAPS | DSDESC_PIXELFORMAT);
96     } else {
97         surfaceDesc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT);
98         surfaceDesc.pixelformat = QDirectFbBlitter::pixmapFormat();
99     }
100 
101     if (qEnvironmentVariableIntValue("QT_DIRECTFB_BLITTER_DEBUGPAINT"))
102         m_debugPaint = true;
103 
104     IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
105     dfb->CreateSurface(dfb , &surfaceDesc, m_surface.outPtr());
106     m_surface->Clear(m_surface.data(), 0, 0, 0, 0);
107 }
108 
~QDirectFbBlitter()109 QDirectFbBlitter::~QDirectFbBlitter()
110 {
111     unlock();
112 }
113 
alphaPixmapFormat()114 DFBSurfacePixelFormat QDirectFbBlitter::alphaPixmapFormat()
115 {
116     return DSPF_ARGB;
117 }
118 
pixmapFormat()119 DFBSurfacePixelFormat QDirectFbBlitter::pixmapFormat()
120 {
121     return DSPF_RGB32;
122 }
123 
selectPixmapFormat(bool withAlpha)124 DFBSurfacePixelFormat QDirectFbBlitter::selectPixmapFormat(bool withAlpha)
125 {
126     return withAlpha ? alphaPixmapFormat() : pixmapFormat();
127 }
128 
fillRect(const QRectF & rect,const QColor & color)129 void QDirectFbBlitter::fillRect(const QRectF &rect, const QColor &color)
130 {
131     alphaFillRect(rect, color, QPainter::CompositionMode_Source);
132 }
133 
drawPixmap(const QRectF & rect,const QPixmap & pixmap,const QRectF & srcRect)134 void QDirectFbBlitter::drawPixmap(const QRectF &rect, const QPixmap &pixmap, const QRectF &srcRect)
135 {
136     drawPixmapOpacity(rect, pixmap, srcRect, QPainter::CompositionMode_SourceOver, 1.0);
137 }
138 
alphaFillRect(const QRectF & rect,const QColor & color,QPainter::CompositionMode cmode)139 void QDirectFbBlitter::alphaFillRect(const QRectF &rect, const QColor &color, QPainter::CompositionMode cmode)
140 {
141     int x, y, w, h;
142     DFBResult result;
143 
144     // check parameters
145     rect.toRect().getRect(&x, &y ,&w, &h);
146     if ((w <= 0) || (h <= 0)) return;
147 
148     if ((cmode == QPainter::CompositionMode_Source) || (color.alpha() == 255)) {
149         // CompositionMode_Source case or CompositionMode_SourceOver with opaque color
150 
151         m_surface->SetDrawingFlags(m_surface.data(),
152             DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_NOFX | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_NOFX));
153         m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC);
154 
155     } else {
156         // CompositionMode_SourceOver case
157 
158         // check if operation is useless
159         if (color.alpha() == 0)
160             return;
161 
162         m_surface->SetDrawingFlags(m_surface.data(),
163             DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_BLEND));
164         m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC_OVER);
165     }
166 
167     // set color
168     m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), color.alpha());
169 
170     // perform fill
171     result = m_surface->FillRectangle(m_surface.data(), x, y, w, h);
172     if (result != DFB_OK)
173         DirectFBError("QDirectFBBlitter::alphaFillRect()", result);
174     if (m_debugPaint)
175         drawDebugRect(QRect(x, y, w, h), QColor(Qt::blue));
176 }
177 
drawPixmapOpacity(const QRectF & rect,const QPixmap & pixmap,const QRectF & subrect,QPainter::CompositionMode cmode,qreal opacity)178 void QDirectFbBlitter::drawPixmapOpacity(const QRectF &rect, const QPixmap &pixmap, const QRectF &subrect, QPainter::CompositionMode cmode, qreal opacity)
179 {
180     QRect sQRect = subrect.toRect();
181     QRect dQRect = rect.toRect();
182     DFBRectangle sRect(sQRect.x(), sQRect.y(), sQRect.width(), sQRect.height());
183     DFBRectangle dRect(dQRect.x(), dQRect.y(), dQRect.width(), dQRect.height());
184     DFBResult result;
185 
186     // skip if dst too small
187     if ((dRect.w <= 0) || (dRect.h <= 0)) return;
188 
189     // correct roundings if needed
190     if (sRect.w <= 0) sRect.w = 1;
191     if (sRect.h <= 0) sRect.h = 1;
192 
193     QDirectFbBlitterPlatformPixmap *blitPm = static_cast<QDirectFbBlitterPlatformPixmap *>(pixmap.handle());
194     QDirectFbBlitter *dfbBlitter = static_cast<QDirectFbBlitter *>(blitPm->blittable());
195     dfbBlitter->unlock();
196 
197     IDirectFBSurface *s = dfbBlitter->m_surface.data();
198 
199     DFBSurfaceBlittingFlags blittingFlags = DFBSurfaceBlittingFlags(DSBLIT_BLEND_ALPHACHANNEL);
200     DFBSurfacePorterDuffRule porterDuff = (cmode == QPainter::CompositionMode_SourceOver) ? DSPD_SRC_OVER : DSPD_SRC;
201 
202     if (opacity != 1.0)
203     {
204         blittingFlags = DFBSurfaceBlittingFlags(blittingFlags | DSBLIT_BLEND_COLORALPHA | (m_premult ? DSBLIT_SRC_PREMULTCOLOR : 0));
205         m_surface->SetColor(m_surface.data(), 0xff, 0xff, 0xff, (u8) (opacity * 255.0));
206     }
207 
208     m_surface->SetBlittingFlags(m_surface.data(), DFBSurfaceBlittingFlags(blittingFlags));
209     m_surface->SetPorterDuff(m_surface.data(), porterDuff);
210 
211     if (cmode == QPainter::CompositionMode_SourceOver)
212         m_surface->SetDstBlendFunction(m_surface.data(), DSBF_INVSRCALPHA);
213 
214     if ((sRect.w == dRect.w) && (sRect.h == dRect.h)) {
215         result = m_surface->Blit(m_surface.data(), s, &sRect, dRect.x, dRect.y);
216         if (result != DFB_OK)
217             DirectFBError("QDirectFBBlitter::drawPixmapOpacity()", result);
218         if (m_debugPaint)
219             drawDebugRect(QRect(dRect.x, dRect.y, sRect.w, sRect.h), QColor(Qt::green));
220     } else {
221         result = m_surface->StretchBlit(m_surface.data(), s, &sRect, &dRect);
222         if (result != DFB_OK)
223             DirectFBError("QDirectFBBlitter::drawPixmapOpacity()", result);
224         if (m_debugPaint)
225             drawDebugRect(QRect(dRect.x, dRect.y, dRect.w, dRect.h), QColor(Qt::red));
226     }
227 }
228 
drawCachedGlyphs(const QPaintEngineState * state,QFontEngine::GlyphFormat glyphFormat,int numGlyphs,const glyph_t * glyphs,const QFixedPoint * positions,QFontEngine * fontEngine)229 bool QDirectFbBlitter::drawCachedGlyphs(const QPaintEngineState *state, QFontEngine::GlyphFormat glyphFormat, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine)
230 {
231     void *cacheKey = QDirectFbConvenience::dfbInterface();
232 
233     QDirectFbTextureGlyphCache *cache =
234             static_cast<QDirectFbTextureGlyphCache *>(fontEngine->glyphCache(cacheKey, glyphFormat, state->transform()));
235     if (!cache) {
236         cache = new QDirectFbTextureGlyphCache(glyphFormat, state->transform());
237         fontEngine->setGlyphCache(cacheKey, cache);
238     }
239 
240     cache->populate(fontEngine, numGlyphs, glyphs, positions);
241     cache->fillInPendingGlyphs();
242 
243     if (cache->image().width() == 0 || cache->image().height() == 0)
244         return false;
245 
246     const int margin = fontEngine->glyphMargin(glyphFormat);
247 
248     QVarLengthArray<DFBRectangle, 64> sourceRects(numGlyphs);
249     QVarLengthArray<DFBPoint, 64> destPoints(numGlyphs);
250     int nGlyphs = 0;
251 
252     for (int i=0; i<numGlyphs; ++i) {
253 
254         QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
255         QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
256         const QTextureGlyphCache::Coord &c = cache->coords[glyph];
257         if (c.isNull())
258             continue;
259 
260         int x = qFloor(positions[i].x) + c.baseLineX - margin;
261         int y = qRound(positions[i].y) - c.baseLineY - margin;
262 
263         // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
264         //        c.x, c.y,
265         //        c.w, c.h,
266         //        c.baseLineX, c.baseLineY,
267         //        glyphs[i],
268         //        x, y,
269         //        positions[i].x.toInt(), positions[i].y.toInt());
270 
271         sourceRects[nGlyphs].x = c.x;
272         sourceRects[nGlyphs].y = c.y;
273         sourceRects[nGlyphs].w = c.w;
274         sourceRects[nGlyphs].h = c.h;
275         destPoints[nGlyphs].x = x;
276         destPoints[nGlyphs].y = y;
277         ++nGlyphs;
278     }
279 
280     const QColor color = state->pen().color();
281     m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), color.alpha());
282 
283     m_surface->SetSrcBlendFunction(m_surface.data(), DSBF_SRCALPHA);
284     m_surface->SetDstBlendFunction(m_surface.data(), DSBF_INVSRCALPHA);
285 
286     int flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE;
287     if (color.alpha() != 0xff)
288         flags |= DSBLIT_BLEND_COLORALPHA;
289     m_surface->SetBlittingFlags(m_surface.data(), DFBSurfaceBlittingFlags(flags));
290 
291     const QRasterPaintEngineState *rs = static_cast<const QRasterPaintEngineState*>(state);
292     if (rs->clip && rs->clip->enabled) {
293         Q_ASSERT(rs->clip->hasRectClip);
294         DFBRegion dfbClip;
295         dfbClip.x1 = rs->clip->clipRect.x();
296         dfbClip.y1 = rs->clip->clipRect.y();
297         dfbClip.x2 = rs->clip->clipRect.right();
298         dfbClip.y2 = rs->clip->clipRect.bottom();
299         m_surface->SetClip(m_surface.data(), &dfbClip);
300     }
301 
302     m_surface->BatchBlit(m_surface.data(), cache->sourceSurface(), sourceRects.constData(), destPoints.constData(), nGlyphs);
303 
304     if (m_debugPaint) {
305         for (int i = 0; i < nGlyphs; ++i) {
306             drawDebugRect(QRect(destPoints[i].x, destPoints[i].y, sourceRects[i].w, sourceRects[i].h), QColor(Qt::yellow));
307         }
308     }
309 
310     if (rs->clip && rs->clip->enabled)
311         m_surface->SetClip(m_surface.data(), 0);
312     return true;
313 }
314 
doLock()315 QImage *QDirectFbBlitter::doLock()
316 {
317     Q_ASSERT(m_surface);
318     Q_ASSERT(size().isValid());
319 
320     void *mem;
321     int bpl;
322     const DFBResult result = m_surface->Lock(m_surface.data(), DFBSurfaceLockFlags(DSLF_WRITE|DSLF_READ), static_cast<void**>(&mem), &bpl);
323     if (result == DFB_OK) {
324         DFBSurfacePixelFormat dfbFormat;
325         DFBSurfaceCapabilities dfbCaps;
326         m_surface->GetPixelFormat(m_surface.data(), &dfbFormat);
327         m_surface->GetCapabilities(m_surface.data(), &dfbCaps);
328         QImage::Format format = QDirectFbConvenience::imageFormatFromSurfaceFormat(dfbFormat, dfbCaps);
329         int w, h;
330         m_surface->GetSize(m_surface.data(), &w, &h);
331         m_image = QImage(static_cast<uchar *>(mem),w,h,bpl,format);
332     } else {
333         DirectFBError("Failed to lock image", result);
334     }
335 
336     return &m_image;
337 }
338 
fromDataBufferDescription(const DFBDataBufferDescription & dataBufferDescription)339 bool QDirectFbBlitterPlatformPixmap::fromDataBufferDescription(const DFBDataBufferDescription &dataBufferDescription)
340 {
341     DFBResult result;
342     IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
343 
344     // Create a data buffer
345     QDirectFBPointer<IDirectFBDataBuffer> dataBuffer;
346     result = dfb->CreateDataBuffer(dfb, &dataBufferDescription, dataBuffer.outPtr());
347     if (result != DFB_OK) {
348         DirectFBError(QDFB_PRETTY, result);
349         return false;
350     }
351 
352     // Create the image provider
353     QDirectFBPointer<IDirectFBImageProvider> provider;
354     result = dataBuffer->CreateImageProvider(dataBuffer.data(), provider.outPtr());
355     if (result != DFB_OK) {
356         DirectFBError(QDFB_PRETTY, result);
357         return false;
358     }
359 
360     // Extract image information
361     DFBImageDescription imageDescription;
362     result = provider->GetImageDescription(provider.data(), &imageDescription);
363     if (result != DFB_OK) {
364         DirectFBError(QDFB_PRETTY, result);
365         return false;
366     }
367 
368     // Can we handle this directlu?
369     if (imageDescription.caps & DICAPS_COLORKEY)
370         return false;
371 
372     DFBSurfaceDescription surfaceDescription;
373     result = provider->GetSurfaceDescription(provider.data(), &surfaceDescription);
374     if (result != DFB_OK) {
375         DirectFBError(QDFB_PRETTY, result);
376         return false;
377     }
378 
379     m_alpha = imageDescription.caps & DICAPS_ALPHACHANNEL;
380     resize(surfaceDescription.width, surfaceDescription.height);
381     // TODO: FIXME; update d
382 
383 
384     result = provider->RenderTo(provider.data(), dfbBlitter()->dfbSurface(), 0);
385     if (result != DFB_OK) {
386         DirectFBError(QDFB_PRETTY, result);
387         return false;
388     }
389 
390     return true;
391 }
392 
fromFile(const QString & filename,const char * format,Qt::ImageConversionFlags flags)393 bool QDirectFbBlitterPlatformPixmap::fromFile(const QString &filename, const char *format,
394                                               Qt::ImageConversionFlags flags)
395 {
396     // If we can't find the file, pass it on to the base class as it is
397     // trying harder by appending various extensions to the path.
398     if (!QFile::exists(filename))
399         return QBlittablePlatformPixmap::fromFile(filename, format, flags);
400 
401     // Stop if there is a requirement for colors
402     if (flags != Qt::AutoColor)
403         return QBlittablePlatformPixmap::fromFile(filename, format, flags);
404 
405     // Deal with resources
406     if (filename.startsWith(QLatin1Char(':')))
407         return QBlittablePlatformPixmap::fromFile(filename, format, flags);
408 
409     // Try to use directfb to load it.
410     DFBDataBufferDescription description;
411     description.flags = DBDESC_FILE;
412     const QByteArray fileNameData = filename.toLocal8Bit();
413     description.file = fileNameData.constData();
414     if (fromDataBufferDescription(description))
415         return true;
416 
417     // Fallback
418     return QBlittablePlatformPixmap::fromFile(filename, format, flags);
419 }
420 
doUnlock()421 void QDirectFbBlitter::doUnlock()
422 {
423     m_surface->Unlock(m_surface.data());
424 }
425 
drawDebugRect(const QRect & rect,const QColor & color)426 void QDirectFbBlitter::drawDebugRect(const QRect &rect, const QColor &color)
427 {
428     int x, y, w, h;
429     DFBResult result;
430 
431     // check parameters
432     rect.getRect(&x, &y ,&w, &h);
433     if ((w <= 0) || (h <= 0)) return;
434 
435     m_surface->SetDrawingFlags(m_surface.data(),
436     DFBSurfaceDrawingFlags(m_premult ? (DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY) : DSDRAW_BLEND));
437     m_surface->SetPorterDuff(m_surface.data(), DSPD_SRC_OVER);
438 
439     // set color
440     m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), 120);
441 
442     result = m_surface->DrawLine(m_surface.data(), x, y, x + w-1, y);
443     if (result != DFB_OK)
444         DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
445     result = m_surface->DrawLine(m_surface.data(), x + w-1, y, x + w-1, y + h-1);
446     if (result != DFB_OK)
447         DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
448     result = m_surface->DrawLine(m_surface.data(), x + w-1, y + h-1, x, y + h-1);
449     if (result != DFB_OK)
450         DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
451     result = m_surface->DrawLine(m_surface.data(), x, y + h-1, x, y);
452     if (result != DFB_OK)
453         DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
454 
455     m_surface->SetColor(m_surface.data(), color.red(), color.green(), color.blue(), 10);
456     result = m_surface->FillRectangle(m_surface.data(), x, y, w, h);
457     if (result != DFB_OK)
458         DirectFBError("QDirectFBBlitter::drawDebugRect()", result);
459 }
460 
resizeTextureData(int width,int height)461 void QDirectFbTextureGlyphCache::resizeTextureData(int width, int height)
462 {
463     m_surface.reset();;
464     QImageTextureGlyphCache::resizeTextureData(width, height);
465 }
466 
sourceSurface()467 IDirectFBSurface *QDirectFbTextureGlyphCache::sourceSurface()
468 {
469     if (m_surface.isNull()) {
470         const QImage &source = image();
471         DFBSurfaceDescription desc;
472         memset(&desc, 0, sizeof(desc));
473         desc.flags = DFBSurfaceDescriptionFlags(DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED | DSDESC_CAPS);
474         desc.width = source.width();
475         desc.height = source.height();
476         desc.caps = DSCAPS_SYSTEMONLY;
477 
478         switch (source.format()) {
479         case QImage::Format_Mono:
480             desc.pixelformat = DSPF_A1;
481             break;
482         case QImage::Format_Alpha8:
483             desc.pixelformat = DSPF_A8;
484             break;
485         default:
486             qFatal("QDirectFBTextureGlyphCache: Unsupported source texture image format.");
487             break;
488         }
489 
490         desc.preallocated[0].data = const_cast<void*>(static_cast<const void*>(source.bits()));
491         desc.preallocated[0].pitch = source.bytesPerLine();
492         desc.preallocated[1].data = 0;
493         desc.preallocated[1].pitch = 0;
494 
495         IDirectFB *dfb = QDirectFbConvenience::dfbInterface();
496         dfb->CreateSurface(dfb , &desc, m_surface.outPtr());
497     }
498     return m_surface.data();
499 }
500 
501 QT_END_NAMESPACE
502