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