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 QtGui module 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 <QtCore/qglobal.h>
41 #include <QtCore/qmutex.h>
42
43 #define QT_FT_BEGIN_HEADER
44 #define QT_FT_END_HEADER
45
46 #include <private/qrasterdefs_p.h>
47 #include <private/qgrayraster_p.h>
48
49 #include <qpainterpath.h>
50 #include <qdebug.h>
51 #include <qbitmap.h>
52 #include <qmath.h>
53 #include <qrandom.h>
54
55 // #include <private/qdatabuffer_p.h>
56 // #include <private/qpainter_p.h>
57 #include <private/qtextengine_p.h>
58 #include <private/qfontengine_p.h>
59 #include <private/qpixmap_raster_p.h>
60 // #include <private/qpolygonclipper_p.h>
61 // #include <private/qrasterizer_p.h>
62 #include <private/qimage_p.h>
63 #include <private/qstatictext_p.h>
64 #include <private/qcosmeticstroker_p.h>
65 #include "qmemrotate_p.h"
66 #include "qrgba64_p.h"
67
68 #include "qpaintengine_raster_p.h"
69 // #include "qbezier_p.h"
70 #include "qoutlinemapper_p.h"
71
72 #include <limits.h>
73 #include <algorithm>
74
75 #ifdef Q_OS_WIN
76 # include <qvarlengtharray.h>
77 # include <private/qfontengine_p.h>
78 # include <qt_windows.h>
79 #ifdef Q_OS_WIN64
80 # include <malloc.h>
81 # endif
82 #endif
83
84 QT_BEGIN_NAMESPACE
85
86 class QRectVectorPath : public QVectorPath {
87 public:
set(const QRect & r)88 inline void set(const QRect &r) {
89 qreal left = r.x();
90 qreal right = r.x() + r.width();
91 qreal top = r.y();
92 qreal bottom = r.y() + r.height();
93 pts[0] = left;
94 pts[1] = top;
95 pts[2] = right;
96 pts[3] = top;
97 pts[4] = right;
98 pts[5] = bottom;
99 pts[6] = left;
100 pts[7] = bottom;
101 }
102
set(const QRectF & r)103 inline void set(const QRectF &r) {
104 qreal left = r.x();
105 qreal right = r.x() + r.width();
106 qreal top = r.y();
107 qreal bottom = r.y() + r.height();
108 pts[0] = left;
109 pts[1] = top;
110 pts[2] = right;
111 pts[3] = top;
112 pts[4] = right;
113 pts[5] = bottom;
114 pts[6] = left;
115 pts[7] = bottom;
116 }
QRectVectorPath(const QRect & r)117 inline QRectVectorPath(const QRect &r)
118 : QVectorPath(pts, 4, nullptr, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
119 {
120 set(r);
121 }
QRectVectorPath(const QRectF & r)122 inline QRectVectorPath(const QRectF &r)
123 : QVectorPath(pts, 4, nullptr, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
124 {
125 set(r);
126 }
QRectVectorPath()127 inline QRectVectorPath()
128 : QVectorPath(pts, 4, nullptr, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
129 { }
130
131 qreal pts[8];
132 };
133
134 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
135
136 #define qreal_to_fixed_26_6(f) (int(f * 64))
137 #define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
138 #define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
139
140 // #define QT_DEBUG_DRAW
141 #ifdef QT_DEBUG_DRAW
142 void dumpClip(int width, int height, const QClipData *clip);
143 #endif
144
145 #define QT_FAST_SPANS
146
147
148 // A little helper macro to get a better approximation of dimensions.
149 // If we have a rect that starting at 0.5 of width 3.5 it should span
150 // 4 pixels.
151 #define int_dim(pos, dim) (int(pos+dim) - int(pos))
152
153 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
154
155 #ifdef Q_OS_WIN
156
winClearTypeFontsEnabled()157 static inline bool winClearTypeFontsEnabled()
158 {
159 #ifdef Q_OS_WINRT
160 return false;
161 #else // Q_OS_WINRT
162 UINT result = 0;
163 #if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
164 # define SPI_GETFONTSMOOTHINGTYPE 0x200A
165 # define FE_FONTSMOOTHINGCLEARTYPE 0x002
166 #endif
167 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
168 return result == FE_FONTSMOOTHINGCLEARTYPE;
169 #endif // !Q_OS_WINRT
170 }
171
172 /*!
173 \internal
174 */
clearTypeFontsEnabled()175 bool QRasterPaintEngine::clearTypeFontsEnabled()
176 {
177 static const bool result = winClearTypeFontsEnabled();
178 return result;
179 }
180
181 #endif // Q_OS_WIN
182
183
184
185 /********************************************************************************
186 * Span functions
187 */
188 static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
189 static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
190 static void qt_span_clip(int count, const QSpan *spans, void *userData);
191
192 struct ClipData
193 {
194 QClipData *oldClip;
195 QClipData *newClip;
196 Qt::ClipOperation operation;
197 };
198
199 enum LineDrawMode {
200 LineDrawClipped,
201 LineDrawNormal,
202 LineDrawIncludeLastPixel
203 };
204
205 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
206 ProcessSpans pen_func, ProcessSpans brush_func,
207 QSpanData *pen_data, QSpanData *brush_data);
208
209 struct QRasterFloatPoint {
210 qreal x;
211 qreal y;
212 };
213
214 #ifdef QT_DEBUG_DRAW
boundingRect(const QPointF * points,int pointCount)215 static const QRectF boundingRect(const QPointF *points, int pointCount)
216 {
217 const QPointF *e = points;
218 const QPointF *last = points + pointCount;
219 qreal minx, maxx, miny, maxy;
220 minx = maxx = e->x();
221 miny = maxy = e->y();
222 while (++e < last) {
223 if (e->x() < minx)
224 minx = e->x();
225 else if (e->x() > maxx)
226 maxx = e->x();
227 if (e->y() < miny)
228 miny = e->y();
229 else if (e->y() > maxy)
230 maxy = e->y();
231 }
232 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
233 }
234 #endif
235
qt_ft_outline_move_to(qfixed x,qfixed y,void * data)236 static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
237 {
238 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
239 }
240
qt_ft_outline_line_to(qfixed x,qfixed y,void * data)241 static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
242 {
243 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
244 }
245
qt_ft_outline_cubic_to(qfixed c1x,qfixed c1y,qfixed c2x,qfixed c2y,qfixed ex,qfixed ey,void * data)246 static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
247 qfixed c2x, qfixed c2y,
248 qfixed ex, qfixed ey,
249 void *data)
250 {
251 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
252 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
253 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
254 }
255
256
257 #if !defined(QT_NO_DEBUG) && 0
qt_debug_path(const QPainterPath & path)258 static void qt_debug_path(const QPainterPath &path)
259 {
260 const char *names[] = {
261 "MoveTo ",
262 "LineTo ",
263 "CurveTo ",
264 "CurveToData"
265 };
266
267 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
268 for (int i=0; i<path.elementCount(); ++i) {
269 const QPainterPath::Element &e = path.elementAt(i);
270 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
271 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
272 }
273 }
274 #endif
275
276 // QRect::normalized() will change the width/height of the rectangle due to
277 // its incusive-integer definition of left/right vs width. This is not
278 // something we want to change in QRect as that would potentially introduce
279 // regressions all over the place, so we implement a straightforward
280 // normalized here. QRectF already does this, so QRectF::normalized() is ok to
281 // use.
qrect_normalized(const QRect & rect)282 static QRect qrect_normalized(const QRect &rect)
283 {
284 int x, y, w, h;
285 if (Q_UNLIKELY(rect.width() < 0)) {
286 x = rect.x() + rect.width();
287 w = -rect.width();
288 } else {
289 x = rect.x();
290 w = rect.width();
291 }
292
293 if (Q_UNLIKELY(rect.height() < 0)) {
294 y = rect.y() + rect.height();
295 h = -rect.height();
296 } else {
297 y = rect.y();
298 h = rect.height();
299 }
300
301 return QRect(x, y, w, h);
302 }
303
304
QRasterPaintEnginePrivate()305 QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
306 QPaintEngineExPrivate(),
307 cachedLines(0)
308 {
309 }
310
311
312 /*!
313 \class QRasterPaintEngine
314 \preliminary
315 \ingroup qws
316 \inmodule QtGui
317 \since 4.2
318
319 \brief The QRasterPaintEngine class enables hardware acceleration
320 of painting operations in Qt for Embedded Linux.
321
322 Note that this functionality is only available in
323 Qt for Embedded Linux.
324
325 In Qt for Embedded Linux, painting is a pure software
326 implementation. But starting with Qt 4.2, it is
327 possible to add an accelerated graphics driver to take advantage
328 of available hardware resources.
329
330 Hardware acceleration is accomplished by creating a custom screen
331 driver, accelerating the copying from memory to the screen, and
332 implementing a custom paint engine accelerating the various
333 painting operations. Then a custom paint device and a custom
334 window surface must be implemented to make
335 Qt for Embedded Linux aware of the accelerated driver.
336
337 \note The QRasterPaintEngine class does not support 8-bit images.
338 Instead, they need to be converted to a supported format, such as
339 QImage::Format_ARGB32_Premultiplied.
340
341 \sa QPaintEngine
342 */
343
344 /*!
345 \fn QPaintEngine::Type QRasterPaintEngine::type() const
346 \reimp
347 */
348
349 /*!
350 \typedef QSpan
351 \relates QRasterPaintEngine
352
353 A struct equivalent to QT_FT_Span, containing a position (x,
354 y), the span's length in pixels and its color/coverage (a value
355 ranging from 0 to 255).
356 */
357
358 /*!
359 \since 4.5
360
361 Creates a raster based paint engine for operating on the given
362 \a device, with the complete set of \l
363 {QPaintEngine::PaintEngineFeature}{paint engine features and
364 capabilities}.
365 */
QRasterPaintEngine(QPaintDevice * device)366 QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
367 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
368 {
369 d_func()->device = device;
370 init();
371 }
372
373 /*!
374 \internal
375 */
QRasterPaintEngine(QRasterPaintEnginePrivate & dd,QPaintDevice * device)376 QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
377 : QPaintEngineEx(dd)
378 {
379 d_func()->device = device;
380 init();
381 }
382
init()383 void QRasterPaintEngine::init()
384 {
385 Q_D(QRasterPaintEngine);
386
387
388 #ifdef Q_OS_WIN
389 d->hdc = 0;
390 #endif
391
392 // The antialiasing raster.
393 d->grayRaster.reset(new QT_FT_Raster);
394 Q_CHECK_PTR(d->grayRaster.data());
395 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
396 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
397
398
399 d->rasterizer.reset(new QRasterizer);
400 d->rasterBuffer.reset(new QRasterBuffer());
401 d->outlineMapper.reset(new QOutlineMapper);
402 d->outlinemapper_xform_dirty = true;
403
404 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
405 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
406 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
407
408 d->baseClip.reset(new QClipData(d->device->height()));
409 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
410
411 d->image_filler.init(d->rasterBuffer.data(), this);
412 d->image_filler.type = QSpanData::Texture;
413
414 d->image_filler_xform.init(d->rasterBuffer.data(), this);
415 d->image_filler_xform.type = QSpanData::Texture;
416
417 d->solid_color_filler.init(d->rasterBuffer.data(), this);
418 d->solid_color_filler.type = QSpanData::Solid;
419
420 d->deviceDepth = d->device->depth();
421
422 d->mono_surface = false;
423 gccaps &= ~PorterDuff;
424
425 QImage::Format format = QImage::Format_Invalid;
426
427 switch (d->device->devType()) {
428 case QInternal::Pixmap:
429 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
430 break;
431 case QInternal::Image:
432 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
433 break;
434 default:
435 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
436 d->device = nullptr;
437 return;
438 }
439
440 switch (format) {
441 case QImage::Format_MonoLSB:
442 case QImage::Format_Mono:
443 d->mono_surface = true;
444 break;
445 default:
446 if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha)
447 gccaps |= PorterDuff;
448 break;
449 }
450 }
451
452
453 /*!
454 Destroys this paint engine.
455 */
~QRasterPaintEngine()456 QRasterPaintEngine::~QRasterPaintEngine()
457 {
458 Q_D(QRasterPaintEngine);
459
460 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
461 }
462
463 /*!
464 \reimp
465 */
begin(QPaintDevice * device)466 bool QRasterPaintEngine::begin(QPaintDevice *device)
467 {
468 Q_D(QRasterPaintEngine);
469
470 if (device->devType() == QInternal::Pixmap) {
471 QPixmap *pixmap = static_cast<QPixmap *>(device);
472 QPlatformPixmap *pd = pixmap->handle();
473 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
474 d->device = pd->buffer();
475 } else {
476 d->device = device;
477 }
478
479 // Make sure QPaintEngine::paintDevice() returns the proper device.
480 d->pdev = d->device;
481
482 Q_ASSERT(d->device->devType() == QInternal::Image
483 || d->device->devType() == QInternal::CustomRaster);
484
485 d->systemStateChanged();
486
487 QRasterPaintEngineState *s = state();
488 ensureOutlineMapper();
489 d->outlineMapper->m_clip_rect = d->deviceRect;
490
491 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
492 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
493 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
494 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
495
496 d->rasterizer->setClipRect(d->deviceRect);
497
498 s->penData.init(d->rasterBuffer.data(), this);
499 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
500 s->stroker = &d->basicStroker;
501 d->basicStroker.setClipRect(d->deviceRect);
502
503 s->brushData.init(d->rasterBuffer.data(), this);
504 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
505
506 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
507
508 setDirty(DirtyBrushOrigin);
509
510 #ifdef QT_DEBUG_DRAW
511 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
512 << ") devType:" << device->devType()
513 << "devRect:" << d->deviceRect;
514 if (d->baseClip) {
515 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
516 }
517 #endif
518
519 if (d->mono_surface)
520 d->glyphCacheFormat = QFontEngine::Format_Mono;
521 #if defined(Q_OS_WIN)
522 else if (clearTypeFontsEnabled())
523 #else
524 else if (false)
525 #endif
526 {
527 QImage::Format format = static_cast<QImage *>(d->device)->format();
528 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
529 d->glyphCacheFormat = QFontEngine::Format_A32;
530 else
531 d->glyphCacheFormat = QFontEngine::Format_A8;
532 } else
533 d->glyphCacheFormat = QFontEngine::Format_A8;
534
535 setActive(true);
536 return true;
537 }
538
539 /*!
540 \reimp
541 */
end()542 bool QRasterPaintEngine::end()
543 {
544 #ifdef QT_DEBUG_DRAW
545 Q_D(QRasterPaintEngine);
546 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
547 if (d->baseClip) {
548 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
549 }
550 #endif
551
552 return true;
553 }
554
555 /*!
556 \internal
557 */
updateMatrix(const QTransform & matrix)558 void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
559 {
560 QRasterPaintEngineState *s = state();
561 // FALCON: get rid of this line, see drawImage call below.
562 s->matrix = matrix;
563 QTransform::TransformationType txop = s->matrix.type();
564
565 switch (txop) {
566
567 case QTransform::TxNone:
568 s->flags.int_xform = true;
569 break;
570
571 case QTransform::TxTranslate:
572 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
573 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 break;
575
576 case QTransform::TxScale:
577 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
578 && qreal(int(s->matrix.dy())) == s->matrix.dy()
579 && qreal(int(s->matrix.m11())) == s->matrix.m11()
580 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 break;
582
583 default: // shear / perspective...
584 s->flags.int_xform = false;
585 break;
586 }
587
588 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
589
590 ensureOutlineMapper();
591 }
592
593
594
~QRasterPaintEngineState()595 QRasterPaintEngineState::~QRasterPaintEngineState()
596 {
597 if (flags.has_clip_ownership)
598 delete clip;
599 }
600
601
QRasterPaintEngineState()602 QRasterPaintEngineState::QRasterPaintEngineState()
603 {
604 stroker = nullptr;
605
606 fillFlags = 0;
607 strokeFlags = 0;
608 pixmapFlags = 0;
609
610 intOpacity = 256;
611
612 txscale = 1.;
613
614 flags.fast_pen = true;
615 flags.non_complex_pen = false;
616 flags.antialiased = false;
617 flags.bilinear = false;
618 flags.legacy_rounding = false;
619 flags.fast_text = true;
620 flags.int_xform = true;
621 flags.tx_noshear = true;
622 flags.fast_images = true;
623
624 clip = nullptr;
625 flags.has_clip_ownership = false;
626
627 dirty = 0;
628 }
629
QRasterPaintEngineState(QRasterPaintEngineState & s)630 QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
631 : QPainterState(s)
632 , lastPen(s.lastPen)
633 , penData(s.penData)
634 , stroker(s.stroker)
635 , strokeFlags(s.strokeFlags)
636 , lastBrush(s.lastBrush)
637 , brushData(s.brushData)
638 , fillFlags(s.fillFlags)
639 , pixmapFlags(s.pixmapFlags)
640 , intOpacity(s.intOpacity)
641 , txscale(s.txscale)
642 , clip(s.clip)
643 , dirty(s.dirty)
644 , flag_bits(s.flag_bits)
645 {
646 brushData.tempImage = nullptr;
647 penData.tempImage = nullptr;
648 flags.has_clip_ownership = false;
649 }
650
651 /*!
652 \internal
653 */
createState(QPainterState * orig) const654 QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
655 {
656 QRasterPaintEngineState *s;
657 if (!orig)
658 s = new QRasterPaintEngineState();
659 else
660 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
661
662 return s;
663 }
664
665 /*!
666 \internal
667 */
setState(QPainterState * s)668 void QRasterPaintEngine::setState(QPainterState *s)
669 {
670 Q_D(QRasterPaintEngine);
671 QPaintEngineEx::setState(s);
672 QRasterPaintEngineState *t = state();
673 if (t->clip && t->clip->enabled != t->clipEnabled) {
674 // Since we do not "detach" clipdata when changing only enabled state, we need to resync state here
675 t->clip->enabled = t->clipEnabled;
676 }
677 d->rasterBuffer->compositionMode = s->composition_mode;
678 }
679
680 /*!
681 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
682 \internal
683 */
684
685 /*!
686 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
687 \internal
688 */
689
690 /*!
691 \internal
692 */
penChanged()693 void QRasterPaintEngine::penChanged()
694 {
695 #ifdef QT_DEBUG_DRAW
696 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
697 #endif
698 QRasterPaintEngineState *s = state();
699 Q_ASSERT(s);
700 s->strokeFlags |= DirtyPen;
701 s->dirty |= DirtyPen;
702 }
703
704 /*!
705 \internal
706 */
updatePen(const QPen & pen)707 void QRasterPaintEngine::updatePen(const QPen &pen)
708 {
709 Q_D(QRasterPaintEngine);
710 QRasterPaintEngineState *s = state();
711 #ifdef QT_DEBUG_DRAW
712 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
713 #endif
714
715 Qt::PenStyle pen_style = qpen_style(pen);
716
717 s->lastPen = pen;
718 s->strokeFlags = 0;
719
720 s->penData.clip = d->clip();
721 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
722
723 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
724 || pen.brush().transform().type() >= QTransform::TxNone) {
725 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
726 }
727
728 // Slightly ugly handling of an uncommon case... We need to change
729 // the pen because it is reused in draw_midpoint to decide dashed
730 // or non-dashed.
731 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
732 pen_style = Qt::SolidLine;
733 s->lastPen.setStyle(Qt::SolidLine);
734 }
735
736 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
737 d->basicStroker.setCapStyle(qpen_capStyle(pen));
738 d->basicStroker.setMiterLimit(pen.miterLimit());
739
740 qreal penWidth = qpen_widthf(pen);
741 if (penWidth == 0)
742 d->basicStroker.setStrokeWidth(1);
743 else
744 d->basicStroker.setStrokeWidth(penWidth);
745
746 if(pen_style == Qt::SolidLine) {
747 s->stroker = &d->basicStroker;
748 } else if (pen_style != Qt::NoPen) {
749 if (!d->dashStroker)
750 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
751 if (qt_pen_is_cosmetic(pen, s->renderHints)) {
752 d->dashStroker->setClipRect(d->deviceRect);
753 } else {
754 // ### I've seen this inverted devrect multiple places now...
755 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
756 d->dashStroker->setClipRect(clipRect);
757 }
758 d->dashStroker->setDashPattern(pen.dashPattern());
759 d->dashStroker->setDashOffset(pen.dashOffset());
760 s->stroker = d->dashStroker.data();
761 } else {
762 s->stroker = nullptr;
763 }
764
765 ensureRasterState(); // needed because of tx_noshear...
766 bool cosmetic = qt_pen_is_cosmetic(pen, s->renderHints);
767 s->flags.fast_pen = pen_style > Qt::NoPen
768 && s->penData.blend
769 && ((cosmetic && penWidth <= 1)
770 || (!cosmetic && (s->flags.tx_noshear || !s->flags.antialiased) && penWidth * s->txscale <= 1));
771
772 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
773
774 s->strokeFlags = 0;
775 }
776
777
778
779 /*!
780 \internal
781 */
brushOriginChanged()782 void QRasterPaintEngine::brushOriginChanged()
783 {
784 QRasterPaintEngineState *s = state();
785 #ifdef QT_DEBUG_DRAW
786 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
787 #endif
788
789 s->fillFlags |= DirtyBrushOrigin;
790 }
791
792
793 /*!
794 \internal
795 */
brushChanged()796 void QRasterPaintEngine::brushChanged()
797 {
798 QRasterPaintEngineState *s = state();
799 #ifdef QT_DEBUG_DRAW
800 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
801 #endif
802 s->fillFlags |= DirtyBrush;
803 }
804
805
806
807
808 /*!
809 \internal
810 */
updateBrush(const QBrush & brush)811 void QRasterPaintEngine::updateBrush(const QBrush &brush)
812 {
813 #ifdef QT_DEBUG_DRAW
814 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
815 #endif
816 Q_D(QRasterPaintEngine);
817 QRasterPaintEngineState *s = state();
818 // must set clip prior to setup, as setup uses it...
819 s->brushData.clip = d->clip();
820 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
821 if (s->fillFlags & DirtyTransform
822 || brush.transform().type() >= QTransform::TxNone)
823 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
824 s->lastBrush = brush;
825 s->fillFlags = 0;
826 }
827
updateOutlineMapper()828 void QRasterPaintEngine::updateOutlineMapper()
829 {
830 Q_D(QRasterPaintEngine);
831 d->outlineMapper->setMatrix(state()->matrix);
832 }
833
updateRasterState()834 void QRasterPaintEngine::updateRasterState()
835 {
836 QRasterPaintEngineState *s = state();
837
838 if (s->dirty & DirtyTransform)
839 updateMatrix(s->matrix);
840
841 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
842 const QPainter::CompositionMode mode = s->composition_mode;
843 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
844 && s->intOpacity == 256
845 && (mode == QPainter::CompositionMode_SourceOver
846 || (mode == QPainter::CompositionMode_Source
847 && s->penData.solidColor.isOpaque()));
848 }
849
850 s->dirty = 0;
851 }
852
853
854 /*!
855 \internal
856 */
opacityChanged()857 void QRasterPaintEngine::opacityChanged()
858 {
859 QRasterPaintEngineState *s = state();
860
861 #ifdef QT_DEBUG_DRAW
862 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
863 #endif
864
865 s->fillFlags |= DirtyOpacity;
866 s->strokeFlags |= DirtyOpacity;
867 s->pixmapFlags |= DirtyOpacity;
868 s->dirty |= DirtyOpacity;
869 s->intOpacity = (int) (s->opacity * 256);
870 }
871
872 /*!
873 \internal
874 */
compositionModeChanged()875 void QRasterPaintEngine::compositionModeChanged()
876 {
877 Q_D(QRasterPaintEngine);
878 QRasterPaintEngineState *s = state();
879
880 #ifdef QT_DEBUG_DRAW
881 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
882 #endif
883
884 s->fillFlags |= DirtyCompositionMode;
885 s->dirty |= DirtyCompositionMode;
886
887 s->strokeFlags |= DirtyCompositionMode;
888 d->rasterBuffer->compositionMode = s->composition_mode;
889
890 d->recalculateFastImages();
891 }
892
893 /*!
894 \internal
895 */
renderHintsChanged()896 void QRasterPaintEngine::renderHintsChanged()
897 {
898 QRasterPaintEngineState *s = state();
899
900 #ifdef QT_DEBUG_DRAW
901 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << Qt::hex << s->renderHints;
902 #endif
903
904 bool was_aa = s->flags.antialiased;
905 bool was_bilinear = s->flags.bilinear;
906
907 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
908 #if QT_DEPRECATED_SINCE(5, 14)
909 QT_WARNING_PUSH
910 QT_WARNING_DISABLE_DEPRECATED
911 if (s->renderHints & QPainter::HighQualityAntialiasing)
912 s->flags.antialiased = true;
913 QT_WARNING_POP
914 #endif
915 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
916 s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
917
918 if (was_aa != s->flags.antialiased)
919 s->strokeFlags |= DirtyHints;
920
921 if (was_bilinear != s->flags.bilinear) {
922 s->strokeFlags |= DirtyPen;
923 s->fillFlags |= DirtyBrush;
924 }
925
926 Q_D(QRasterPaintEngine);
927 d->recalculateFastImages();
928 }
929
930 /*!
931 \internal
932 */
transformChanged()933 void QRasterPaintEngine::transformChanged()
934 {
935 QRasterPaintEngineState *s = state();
936
937 #ifdef QT_DEBUG_DRAW
938 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
939 #endif
940
941 s->fillFlags |= DirtyTransform;
942 s->strokeFlags |= DirtyTransform;
943
944 s->dirty |= DirtyTransform;
945
946 Q_D(QRasterPaintEngine);
947 d->recalculateFastImages();
948 }
949
950 /*!
951 \internal
952 */
clipEnabledChanged()953 void QRasterPaintEngine::clipEnabledChanged()
954 {
955 QRasterPaintEngineState *s = state();
956
957 #ifdef QT_DEBUG_DRAW
958 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
959 #endif
960
961 if (s->clip) {
962 s->clip->enabled = s->clipEnabled;
963 s->fillFlags |= DirtyClipEnabled;
964 s->strokeFlags |= DirtyClipEnabled;
965 s->pixmapFlags |= DirtyClipEnabled;
966 }
967 }
968
drawImage(const QPointF & pt,const QImage & img,SrcOverBlendFunc func,const QRect & clip,int alpha,const QRect & sr)969 void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
970 const QImage &img,
971 SrcOverBlendFunc func,
972 const QRect &clip,
973 int alpha,
974 const QRect &sr)
975 {
976 if (alpha == 0 || !clip.isValid())
977 return;
978 if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
979 return;
980 if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
981 return;
982
983 Q_ASSERT(img.depth() >= 8);
984
985 qsizetype srcBPL = img.bytesPerLine();
986 const uchar *srcBits = img.bits();
987 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
988 int iw = img.width();
989 int ih = img.height();
990
991 if (!sr.isEmpty()) {
992 iw = sr.width();
993 ih = sr.height();
994 // Adjust the image according to the source offset...
995 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
996 }
997
998 // adapt the x parameters
999 int x = qRound(pt.x());
1000 int cx1 = clip.x();
1001 int cx2 = clip.x() + clip.width();
1002 if (x < cx1) {
1003 int d = cx1 - x;
1004 srcBits += srcSize * d;
1005 iw -= d;
1006 x = cx1;
1007 }
1008 if (x + iw > cx2) {
1009 int d = x + iw - cx2;
1010 iw -= d;
1011 }
1012 if (iw <= 0)
1013 return;
1014
1015 // adapt the y paremeters...
1016 int cy1 = clip.y();
1017 int cy2 = clip.y() + clip.height();
1018 int y = qRound(pt.y());
1019 if (y < cy1) {
1020 int d = cy1 - y;
1021 srcBits += srcBPL * d;
1022 ih -= d;
1023 y = cy1;
1024 }
1025 if (y + ih > cy2) {
1026 int d = y + ih - cy2;
1027 ih -= d;
1028 }
1029 if (ih <= 0)
1030 return;
1031
1032 // call the blend function...
1033 int dstSize = rasterBuffer->bytesPerPixel();
1034 qsizetype dstBPL = rasterBuffer->bytesPerLine();
1035 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1036 srcBits, srcBPL,
1037 iw, ih,
1038 alpha);
1039 }
1040
blitImage(const QPointF & pt,const QImage & img,const QRect & clip,const QRect & sr)1041 void QRasterPaintEnginePrivate::blitImage(const QPointF &pt,
1042 const QImage &img,
1043 const QRect &clip,
1044 const QRect &sr)
1045 {
1046 if (!clip.isValid())
1047 return;
1048 if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
1049 return;
1050 if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
1051 return;
1052
1053 Q_ASSERT(img.depth() >= 8);
1054
1055 qsizetype srcBPL = img.bytesPerLine();
1056 const uchar *srcBits = img.bits();
1057 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1058 int iw = img.width();
1059 int ih = img.height();
1060
1061 if (!sr.isEmpty()) {
1062 iw = sr.width();
1063 ih = sr.height();
1064 // Adjust the image according to the source offset...
1065 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1066 }
1067
1068 // adapt the x parameters
1069 int x = qRound(pt.x());
1070 int cx1 = clip.x();
1071 int cx2 = clip.x() + clip.width();
1072 if (x < cx1) {
1073 int d = cx1 - x;
1074 srcBits += srcSize * d;
1075 iw -= d;
1076 x = cx1;
1077 }
1078 if (x + iw > cx2) {
1079 int d = x + iw - cx2;
1080 iw -= d;
1081 }
1082 if (iw <= 0)
1083 return;
1084
1085 // adapt the y paremeters...
1086 int cy1 = clip.y();
1087 int cy2 = clip.y() + clip.height();
1088 int y = qRound(pt.y());
1089 if (y < cy1) {
1090 int d = cy1 - y;
1091 srcBits += srcBPL * d;
1092 ih -= d;
1093 y = cy1;
1094 }
1095 if (y + ih > cy2) {
1096 int d = y + ih - cy2;
1097 ih -= d;
1098 }
1099 if (ih <= 0)
1100 return;
1101
1102 // blit..
1103 int dstSize = rasterBuffer->bytesPerPixel();
1104 qsizetype dstBPL = rasterBuffer->bytesPerLine();
1105 const uint *src = (const uint *) srcBits;
1106 uint *dst = reinterpret_cast<uint *>(rasterBuffer->buffer() + x * dstSize + y * dstBPL);
1107
1108 const int len = iw * (qt_depthForFormat(rasterBuffer->format) >> 3);
1109 for (int y = 0; y < ih; ++y) {
1110 memcpy(dst, src, len);
1111 dst = (quint32 *)(((uchar *) dst) + dstBPL);
1112 src = (const quint32 *)(((const uchar *) src) + srcBPL);
1113 }
1114 }
1115
1116
systemStateChanged()1117 void QRasterPaintEnginePrivate::systemStateChanged()
1118 {
1119 deviceRectUnclipped = QRect(0, 0,
1120 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1121 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1122
1123 if (!systemClip.isEmpty()) {
1124 QRegion clippedDeviceRgn = systemClip & deviceRectUnclipped;
1125 deviceRect = clippedDeviceRgn.boundingRect();
1126 baseClip->setClipRegion(clippedDeviceRgn);
1127 } else {
1128 deviceRect = deviceRectUnclipped;
1129 baseClip->setClipRect(deviceRect);
1130 }
1131 #ifdef QT_DEBUG_DRAW
1132 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << deviceRectUnclipped << systemClip;
1133 #endif
1134
1135 exDeviceRect = deviceRect;
1136
1137 Q_Q(QRasterPaintEngine);
1138 if (q->state()) {
1139 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1140 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1141 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1142 }
1143 }
1144
updateMatrixData(QSpanData * spanData,const QBrush & b,const QTransform & m)1145 void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1146 {
1147 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1148 return;
1149
1150 Q_Q(QRasterPaintEngine);
1151 bool bilinear = q->state()->flags.bilinear;
1152
1153 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1154 spanData->setupMatrix(b.transform() * m, bilinear);
1155 } else {
1156 if (m.type() <= QTransform::TxTranslate) {
1157 // specialize setupMatrix for translation matrices
1158 // to avoid needless matrix inversion
1159 spanData->m11 = 1;
1160 spanData->m12 = 0;
1161 spanData->m13 = 0;
1162 spanData->m21 = 0;
1163 spanData->m22 = 1;
1164 spanData->m23 = 0;
1165 spanData->m33 = 1;
1166 spanData->dx = -m.dx();
1167 spanData->dy = -m.dy();
1168 spanData->txop = m.type();
1169 spanData->bilinear = bilinear;
1170 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1171 spanData->adjustSpanMethods();
1172 } else {
1173 spanData->setupMatrix(m, bilinear);
1174 }
1175 }
1176 }
1177
1178 // #define QT_CLIPPING_RATIOS
1179
1180 #ifdef QT_CLIPPING_RATIOS
1181 int rectClips;
1182 int regionClips;
1183 int totalClips;
1184
checkClipRatios(QRasterPaintEnginePrivate * d)1185 static void checkClipRatios(QRasterPaintEnginePrivate *d)
1186 {
1187 if (d->clip()->hasRectClip)
1188 rectClips++;
1189 if (d->clip()->hasRegionClip)
1190 regionClips++;
1191 totalClips++;
1192
1193 if ((totalClips % 5000) == 0) {
1194 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1195 rectClips * 100.0 / (qreal) totalClips,
1196 regionClips * 100.0 / (qreal) totalClips,
1197 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1198 totalClips = 0;
1199 rectClips = 0;
1200 regionClips = 0;
1201 }
1202
1203 }
1204 #endif
1205
qrasterpaintengine_state_setNoClip(QRasterPaintEngineState * s)1206 static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1207 {
1208 if (s->flags.has_clip_ownership)
1209 delete s->clip;
1210 s->clip = nullptr;
1211 s->flags.has_clip_ownership = false;
1212 }
1213
qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate * d,QRasterPaintEngineState * s)1214 static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1215 {
1216 s->fillFlags |= QPaintEngine::DirtyClipPath;
1217 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1218 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1219
1220 d->solid_color_filler.clip = d->clip();
1221 d->solid_color_filler.adjustSpanMethods();
1222
1223 #ifdef QT_DEBUG_DRAW
1224 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1225 #endif
1226
1227 }
1228
1229
1230 /*!
1231 \internal
1232 */
clip(const QVectorPath & path,Qt::ClipOperation op)1233 void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1234 {
1235 #ifdef QT_DEBUG_DRAW
1236 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1237
1238 if (path.elements()) {
1239 for (int i=0; i<path.elementCount(); ++i) {
1240 qDebug() << " - " << path.elements()[i]
1241 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1242 }
1243 } else {
1244 for (int i=0; i<path.elementCount(); ++i) {
1245 qDebug() << " ---- "
1246 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1247 }
1248 }
1249 #endif
1250
1251 Q_D(QRasterPaintEngine);
1252 QRasterPaintEngineState *s = state();
1253
1254 // There are some cases that are not supported by clip(QRect)
1255 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1256 if (s->matrix.type() <= QTransform::TxScale
1257 && path.isRect()) {
1258 #ifdef QT_DEBUG_DRAW
1259 qDebug(" --- optimizing vector clip to rect clip...");
1260 #endif
1261 const qreal *points = path.points();
1262 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1263 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toAlignedRect(), op))
1264 return;
1265 }
1266 }
1267
1268 if (op == Qt::NoClip) {
1269 qrasterpaintengine_state_setNoClip(s);
1270
1271 } else {
1272 QClipData *base = d->baseClip.data();
1273
1274 // Intersect with current clip when available...
1275 if (op == Qt::IntersectClip && s->clip)
1276 base = s->clip;
1277
1278 // We always intersect, except when there is nothing to
1279 // intersect with, in which case we simplify the operation to
1280 // a replace...
1281 Qt::ClipOperation isectOp = Qt::IntersectClip;
1282 if (base == nullptr)
1283 isectOp = Qt::ReplaceClip;
1284
1285 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1286 newClip->initialize();
1287 ClipData clipData = { base, newClip, isectOp };
1288 ensureOutlineMapper();
1289 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, nullptr);
1290
1291 newClip->fixup();
1292
1293 if (s->flags.has_clip_ownership)
1294 delete s->clip;
1295
1296 s->clip = newClip;
1297 s->flags.has_clip_ownership = true;
1298 }
1299 qrasterpaintengine_dirty_clip(d, s);
1300 }
1301
1302
1303
1304 /*!
1305 \internal
1306 */
clip(const QRect & rect,Qt::ClipOperation op)1307 void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1308 {
1309 #ifdef QT_DEBUG_DRAW
1310 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1311 #endif
1312
1313 QRasterPaintEngineState *s = state();
1314
1315 if (op == Qt::NoClip) {
1316 qrasterpaintengine_state_setNoClip(s);
1317
1318 } else if (s->matrix.type() > QTransform::TxScale) {
1319 QPaintEngineEx::clip(rect, op);
1320 return;
1321
1322 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(QRectF(rect)).toRect(), op)) {
1323 QPaintEngineEx::clip(rect, op);
1324 return;
1325 }
1326 }
1327
1328
setClipRectInDeviceCoords(const QRect & r,Qt::ClipOperation op)1329 bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1330 {
1331 Q_D(QRasterPaintEngine);
1332 // normalize before using the & operator which uses QRect::normalize()
1333 // internally which will give us the wrong values.
1334 QRect clipRect = qrect_normalized(r) & d->deviceRect;
1335 QRasterPaintEngineState *s = state();
1336
1337 if (op == Qt::ReplaceClip || s->clip == nullptr) {
1338
1339 // No current clip, hence we intersect with sysclip and be
1340 // done with it...
1341 QRegion clipRegion = systemClip();
1342 QClipData *clip = new QClipData(d->rasterBuffer->height());
1343
1344 if (clipRegion.isEmpty())
1345 clip->setClipRect(clipRect);
1346 else
1347 clip->setClipRegion(clipRegion & clipRect);
1348
1349 if (s->flags.has_clip_ownership)
1350 delete s->clip;
1351
1352 s->clip = clip;
1353 s->clip->enabled = true;
1354 s->flags.has_clip_ownership = true;
1355
1356 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1357 QClipData *base = s->clip;
1358
1359 Q_ASSERT(base);
1360 if (base->hasRectClip || base->hasRegionClip) {
1361 if (!s->flags.has_clip_ownership) {
1362 s->clip = new QClipData(d->rasterBuffer->height());
1363 s->flags.has_clip_ownership = true;
1364 }
1365 if (base->hasRectClip)
1366 s->clip->setClipRect(base->clipRect & clipRect);
1367 else
1368 s->clip->setClipRegion(base->clipRegion & clipRect);
1369 s->clip->enabled = true;
1370 } else {
1371 return false;
1372 }
1373 } else {
1374 return false;
1375 }
1376
1377 qrasterpaintengine_dirty_clip(d, s);
1378 return true;
1379 }
1380
1381
1382 /*!
1383 \internal
1384 */
clip(const QRegion & region,Qt::ClipOperation op)1385 void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op)
1386 {
1387 #ifdef QT_DEBUG_DRAW
1388 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1389 #endif
1390
1391 Q_D(QRasterPaintEngine);
1392
1393 if (region.rectCount() == 1) {
1394 clip(region.boundingRect(), op);
1395 return;
1396 }
1397
1398 QRasterPaintEngineState *s = state();
1399 const QClipData *clip = d->clip();
1400 const QClipData *baseClip = d->baseClip.data();
1401
1402 if (op == Qt::NoClip) {
1403 qrasterpaintengine_state_setNoClip(s);
1404 } else if (s->matrix.type() > QTransform::TxScale
1405 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1406 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1407 QPaintEngineEx::clip(region, op);
1408 } else {
1409 const QClipData *curClip;
1410 QClipData *newClip;
1411
1412 if (op == Qt::IntersectClip)
1413 curClip = clip;
1414 else
1415 curClip = baseClip;
1416
1417 if (s->flags.has_clip_ownership) {
1418 newClip = s->clip;
1419 Q_ASSERT(newClip);
1420 } else {
1421 newClip = new QClipData(d->rasterBuffer->height());
1422 s->clip = newClip;
1423 s->flags.has_clip_ownership = true;
1424 }
1425
1426 QRegion r = s->matrix.map(region);
1427 if (curClip->hasRectClip)
1428 newClip->setClipRegion(r & curClip->clipRect);
1429 else if (curClip->hasRegionClip)
1430 newClip->setClipRegion(r & curClip->clipRegion);
1431
1432 qrasterpaintengine_dirty_clip(d, s);
1433 }
1434 }
1435
1436 /*!
1437 \fn const QClipData *QRasterPaintEngine::clipData() const
1438
1439 \internal
1440 */
1441
1442
1443 /*!
1444 \internal
1445 */
fillPath(const QPainterPath & path,QSpanData * fillData)1446 void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1447 {
1448 #ifdef QT_DEBUG_DRAW
1449 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1450 #endif
1451
1452 if (!fillData->blend)
1453 return;
1454
1455 Q_D(QRasterPaintEngine);
1456
1457 const QRectF controlPointRect = path.controlPointRect();
1458
1459 QRasterPaintEngineState *s = state();
1460 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1461 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1462 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1463 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1464 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1465 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1466
1467 if (!s->flags.antialiased && !do_clip) {
1468 d->initializeRasterizer(fillData);
1469 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1470 return;
1471 }
1472
1473 ensureOutlineMapper();
1474 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1475 }
1476
fillRect_normalized(const QRect & r,QSpanData * data,QRasterPaintEnginePrivate * pe)1477 static void fillRect_normalized(const QRect &r, QSpanData *data,
1478 QRasterPaintEnginePrivate *pe)
1479 {
1480 int x1, x2, y1, y2;
1481
1482 bool rectClipped = true;
1483
1484 if (data->clip) {
1485 x1 = qMax(r.x(), data->clip->xmin);
1486 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1487 y1 = qMax(r.y(), data->clip->ymin);
1488 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1489 rectClipped = data->clip->hasRectClip;
1490
1491 } else if (pe) {
1492 x1 = qMax(r.x(), pe->deviceRect.x());
1493 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1494 y1 = qMax(r.y(), pe->deviceRect.y());
1495 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1496 } else {
1497 x1 = qMax(r.x(), 0);
1498 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1499 y1 = qMax(r.y(), 0);
1500 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1501 }
1502
1503 if (x2 <= x1 || y2 <= y1)
1504 return;
1505
1506 const int width = x2 - x1;
1507 const int height = y2 - y1;
1508
1509 bool isUnclipped = rectClipped
1510 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1511
1512 if (pe && isUnclipped) {
1513 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1514
1515 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1516 || (mode == QPainter::CompositionMode_SourceOver
1517 && data->solidColor.isOpaque())))
1518 {
1519 data->fillRect(data->rasterBuffer, x1, y1, width, height, data->solidColor);
1520 return;
1521 }
1522 }
1523
1524 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1525
1526 const int nspans = 256;
1527 QT_FT_Span spans[nspans];
1528
1529 Q_ASSERT(data->blend);
1530 int y = y1;
1531 while (y < y2) {
1532 int n = qMin(nspans, y2 - y);
1533 int i = 0;
1534 while (i < n) {
1535 spans[i].x = x1;
1536 spans[i].len = width;
1537 spans[i].y = y + i;
1538 spans[i].coverage = 255;
1539 ++i;
1540 }
1541
1542 blend(n, spans, data);
1543 y += n;
1544 }
1545 }
1546
1547 /*!
1548 \reimp
1549 */
drawRects(const QRect * rects,int rectCount)1550 void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1551 {
1552 #ifdef QT_DEBUG_DRAW
1553 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1554 #endif
1555 Q_D(QRasterPaintEngine);
1556 ensureRasterState();
1557 QRasterPaintEngineState *s = state();
1558
1559 // Fill
1560 ensureBrush();
1561 if (s->brushData.blend) {
1562 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1563 const QRect *r = rects;
1564 const QRect *lastRect = rects + rectCount;
1565
1566 int offset_x = int(s->matrix.dx());
1567 int offset_y = int(s->matrix.dy());
1568 while (r < lastRect) {
1569 QRect rect = qrect_normalized(*r);
1570 QRect rr = rect.translated(offset_x, offset_y);
1571 fillRect_normalized(rr, &s->brushData, d);
1572 ++r;
1573 }
1574 } else {
1575 QRectVectorPath path;
1576 for (int i=0; i<rectCount; ++i) {
1577 path.set(rects[i]);
1578 fill(path, s->brush);
1579 }
1580 }
1581 }
1582
1583 ensurePen();
1584 if (s->penData.blend) {
1585 QRectVectorPath path;
1586 if (s->flags.fast_pen) {
1587 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1588 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1589 for (int i = 0; i < rectCount; ++i) {
1590 path.set(rects[i]);
1591 stroker.drawPath(path);
1592 }
1593 } else {
1594 for (int i = 0; i < rectCount; ++i) {
1595 path.set(rects[i]);
1596 stroke(path, s->pen);
1597 }
1598 }
1599 }
1600 }
1601
1602 /*!
1603 \reimp
1604 */
drawRects(const QRectF * rects,int rectCount)1605 void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1606 {
1607 #ifdef QT_DEBUG_DRAW
1608 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1609 #endif
1610 #ifdef QT_FAST_SPANS
1611 Q_D(QRasterPaintEngine);
1612 ensureRasterState();
1613 QRasterPaintEngineState *s = state();
1614
1615
1616 if (s->flags.tx_noshear) {
1617 ensureBrush();
1618 if (s->brushData.blend) {
1619 d->initializeRasterizer(&s->brushData);
1620 for (int i = 0; i < rectCount; ++i) {
1621 const QRectF &rect = rects[i].normalized();
1622 if (rect.isEmpty())
1623 continue;
1624 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1625 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1626 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1627 }
1628 }
1629
1630 ensurePen();
1631 if (s->penData.blend) {
1632 QRectVectorPath path;
1633 if (s->flags.fast_pen) {
1634 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1635 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1636 for (int i = 0; i < rectCount; ++i) {
1637 path.set(rects[i]);
1638 stroker.drawPath(path);
1639 }
1640 } else {
1641 for (int i = 0; i < rectCount; ++i) {
1642 path.set(rects[i]);
1643 QPaintEngineEx::stroke(path, s->lastPen);
1644 }
1645 }
1646 }
1647
1648 return;
1649 }
1650 #endif // QT_FAST_SPANS
1651 QPaintEngineEx::drawRects(rects, rectCount);
1652 }
1653
1654
1655 /*!
1656 \internal
1657 */
stroke(const QVectorPath & path,const QPen & pen)1658 void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1659 {
1660 Q_D(QRasterPaintEngine);
1661 QRasterPaintEngineState *s = state();
1662
1663 ensurePen(pen);
1664 if (!s->penData.blend)
1665 return;
1666
1667 if (s->flags.fast_pen) {
1668 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1669 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1670 stroker.drawPath(path);
1671 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1672 qreal width = qt_pen_is_cosmetic(s->lastPen, s->renderHints)
1673 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1674 : qpen_widthf(s->lastPen) * s->txscale;
1675 int dashIndex = 0;
1676 qreal dashOffset = s->lastPen.dashOffset();
1677 bool inDash = true;
1678 qreal patternLength = 0;
1679 const QVector<qreal> pattern = s->lastPen.dashPattern();
1680 for (int i = 0; i < pattern.size(); ++i)
1681 patternLength += pattern.at(i);
1682
1683 if (patternLength > 0) {
1684 int n = qFloor(dashOffset / patternLength);
1685 dashOffset -= n * patternLength;
1686 while (dashOffset >= pattern.at(dashIndex)) {
1687 dashOffset -= pattern.at(dashIndex);
1688 if (++dashIndex >= pattern.size())
1689 dashIndex = 0;
1690 inDash = !inDash;
1691 }
1692 }
1693
1694 Q_D(QRasterPaintEngine);
1695 d->initializeRasterizer(&s->penData);
1696 int lineCount = path.elementCount() / 2;
1697 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1698
1699 for (int i = 0; i < lineCount; ++i) {
1700 if (lines[i].p1() == lines[i].p2()) {
1701 if (s->lastPen.capStyle() != Qt::FlatCap) {
1702 QPointF p = lines[i].p1();
1703 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1704 QPointF(p.x() + width*0.5, p.y())));
1705 d->rasterizer->rasterizeLine(line.p1(), line.p2(), width / line.length());
1706 }
1707 continue;
1708 }
1709
1710 const QLineF line = s->matrix.map(lines[i]);
1711 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1712 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1713 width / line.length(),
1714 s->lastPen.capStyle() == Qt::SquareCap);
1715 } else {
1716 // LinesHint means each line is distinct, so restart dashing
1717 int dIndex = dashIndex;
1718 qreal dOffset = dashOffset;
1719 bool inD = inDash;
1720 d->rasterizeLine_dashed(line, width, &dIndex, &dOffset, &inD);
1721 }
1722 }
1723 }
1724 else
1725 QPaintEngineEx::stroke(path, pen);
1726 }
1727
toNormalizedFillRect(const QRectF & rect)1728 QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1729 {
1730 QRasterPaintEngineState *s = state();
1731
1732 qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
1733
1734 int x1 = qRound(rect.x() + delta);
1735 int y1 = qRound(rect.y() + delta);
1736 int x2 = qRound(rect.right() + delta);
1737 int y2 = qRound(rect.bottom() + delta);
1738
1739 if (x2 < x1)
1740 qSwap(x1, x2);
1741 if (y2 < y1)
1742 qSwap(y1, y2);
1743
1744 return QRect(x1, y1, x2 - x1, y2 - y1);
1745 }
1746
1747 /*!
1748 \internal
1749 */
fill(const QVectorPath & path,const QBrush & brush)1750 void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1751 {
1752 if (path.isEmpty())
1753 return;
1754 #ifdef QT_DEBUG_DRAW
1755 QRectF rf = path.controlPointRect();
1756 qDebug() << "QRasterPaintEngine::fill(): "
1757 << "size=" << path.elementCount()
1758 << ", hints=" << Qt::hex << path.hints()
1759 << rf << brush;
1760 #endif
1761
1762 Q_D(QRasterPaintEngine);
1763 QRasterPaintEngineState *s = state();
1764
1765 ensureBrush(brush);
1766 if (!s->brushData.blend)
1767 return;
1768
1769 if (path.shape() == QVectorPath::RectangleHint) {
1770 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1771 const qreal *p = path.points();
1772 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1773 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1774 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1775 return;
1776 }
1777 ensureRasterState();
1778 if (s->flags.tx_noshear) {
1779 d->initializeRasterizer(&s->brushData);
1780 // ### Is normalizing really necessary here?
1781 const qreal *p = path.points();
1782 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1783 if (!r.isEmpty()) {
1784 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1785 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1786 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1787 }
1788 return;
1789 }
1790 }
1791
1792 // ### Optimize for non transformed ellipses and rectangles...
1793 QRectF cpRect = path.controlPointRect();
1794 const QRectF pathDeviceRect = s->matrix.mapRect(cpRect);
1795 // Skip paths that by conservative estimates are completely outside the paint device.
1796 if (!pathDeviceRect.intersects(QRectF(d->deviceRect)))
1797 return;
1798
1799 ProcessSpans blend = d->getBrushFunc(pathDeviceRect, &s->brushData);
1800
1801 // ### Falcon
1802 // const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1803 // || deviceRect.right() > QT_RASTER_COORD_LIMIT
1804 // || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1805 // || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1806
1807 // ### Falonc: implement....
1808 // if (!s->flags.antialiased && !do_clip) {
1809 // d->initializeRasterizer(&s->brushData);
1810 // d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1811 // return;
1812 // }
1813
1814 ensureOutlineMapper();
1815 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1816 }
1817
fillRect(const QRectF & r,QSpanData * data)1818 void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1819 {
1820 Q_D(QRasterPaintEngine);
1821 QRasterPaintEngineState *s = state();
1822
1823 if (!s->flags.antialiased) {
1824 uint txop = s->matrix.type();
1825 if (txop == QTransform::TxNone) {
1826 fillRect_normalized(toNormalizedFillRect(r), data, d);
1827 return;
1828 } else if (txop == QTransform::TxTranslate) {
1829 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1830 fillRect_normalized(rr, data, d);
1831 return;
1832 } else if (txop == QTransform::TxScale) {
1833 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1834 fillRect_normalized(rr, data, d);
1835 return;
1836 }
1837 }
1838 ensureRasterState();
1839 if (s->flags.tx_noshear) {
1840 d->initializeRasterizer(data);
1841 QRectF nr = r.normalized();
1842 if (!nr.isEmpty()) {
1843 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1844 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1845 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1846 }
1847 return;
1848 }
1849
1850 QPainterPath path;
1851 path.addRect(r);
1852 ensureOutlineMapper();
1853 fillPath(path, data);
1854 }
1855
1856 /*!
1857 \reimp
1858 */
fillRect(const QRectF & r,const QBrush & brush)1859 void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1860 {
1861 #ifdef QT_DEBUG_DRAW
1862 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1863 #endif
1864 QRasterPaintEngineState *s = state();
1865
1866 ensureBrush(brush);
1867 if (!s->brushData.blend)
1868 return;
1869
1870 fillRect(r, &s->brushData);
1871 }
1872
1873 /*!
1874 \reimp
1875 */
fillRect(const QRectF & r,const QColor & color)1876 void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1877 {
1878 #ifdef QT_DEBUG_DRAW
1879 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1880 #endif
1881 Q_D(QRasterPaintEngine);
1882 QRasterPaintEngineState *s = state();
1883
1884 d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(color.rgba64(), s->intOpacity));
1885
1886 if (d->solid_color_filler.solidColor.isTransparent()
1887 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1888 return;
1889 }
1890 d->solid_color_filler.clip = d->clip();
1891 d->solid_color_filler.adjustSpanMethods();
1892 fillRect(r, &d->solid_color_filler);
1893 }
1894
isAbove(const QPointF * a,const QPointF * b)1895 static inline bool isAbove(const QPointF *a, const QPointF *b)
1896 {
1897 return a->y() < b->y();
1898 }
1899
splitPolygon(const QPointF * points,int pointCount,QVector<QPointF> * upper,QVector<QPointF> * lower)1900 static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1901 {
1902 Q_ASSERT(upper);
1903 Q_ASSERT(lower);
1904
1905 Q_ASSERT(pointCount >= 2);
1906
1907 QVector<const QPointF *> sorted;
1908 sorted.reserve(pointCount);
1909
1910 upper->reserve(pointCount * 3 / 4);
1911 lower->reserve(pointCount * 3 / 4);
1912
1913 for (int i = 0; i < pointCount; ++i)
1914 sorted << points + i;
1915
1916 std::sort(sorted.begin(), sorted.end(), isAbove);
1917
1918 qreal splitY = sorted.at(sorted.size() / 2)->y();
1919
1920 const QPointF *end = points + pointCount;
1921 const QPointF *last = end - 1;
1922
1923 QVector<QPointF> *bin[2] = { upper, lower };
1924
1925 for (const QPointF *p = points; p < end; ++p) {
1926 int side = p->y() < splitY;
1927 int lastSide = last->y() < splitY;
1928
1929 if (side != lastSide) {
1930 if (qFuzzyCompare(p->y(), splitY)) {
1931 bin[!side]->append(*p);
1932 } else if (qFuzzyCompare(last->y(), splitY)) {
1933 bin[side]->append(*last);
1934 } else {
1935 QPointF delta = *p - *last;
1936 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1937
1938 bin[0]->append(intersection);
1939 bin[1]->append(intersection);
1940 }
1941 }
1942
1943 bin[side]->append(*p);
1944
1945 last = p;
1946 }
1947
1948 // give up if we couldn't reduce the point count
1949 return upper->size() < pointCount && lower->size() < pointCount;
1950 }
1951
1952 /*!
1953 \internal
1954 */
fillPolygon(const QPointF * points,int pointCount,PolygonDrawMode mode)1955 void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1956 {
1957 Q_D(QRasterPaintEngine);
1958 QRasterPaintEngineState *s = state();
1959
1960 const int maxPoints = 0xffff;
1961
1962 // max amount of points that raster engine can reliably handle
1963 if (pointCount > maxPoints) {
1964 QVector<QPointF> upper, lower;
1965
1966 if (splitPolygon(points, pointCount, &upper, &lower)) {
1967 fillPolygon(upper.constData(), upper.size(), mode);
1968 fillPolygon(lower.constData(), lower.size(), mode);
1969 } else
1970 qWarning("Polygon too complex for filling.");
1971
1972 return;
1973 }
1974
1975 // Compose polygon fill..,
1976 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1977 ensureOutlineMapper();
1978 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1979
1980 // scanconvert.
1981 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1982 &s->brushData);
1983 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1984 }
1985
1986 /*!
1987 \reimp
1988 */
drawPolygon(const QPointF * points,int pointCount,PolygonDrawMode mode)1989 void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1990 {
1991 Q_D(QRasterPaintEngine);
1992 QRasterPaintEngineState *s = state();
1993
1994 #ifdef QT_DEBUG_DRAW
1995 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1996 for (int i=0; i<pointCount; ++i)
1997 qDebug() << " - " << points[i];
1998 #endif
1999 Q_ASSERT(pointCount >= 2);
2000
2001 if (mode != PolylineMode && QVectorPath::isRect((const qreal *) points, pointCount)) {
2002 QRectF r(points[0], points[2]);
2003 drawRects(&r, 1);
2004 return;
2005 }
2006
2007 ensurePen();
2008 if (mode != PolylineMode) {
2009 // Do the fill...
2010 ensureBrush();
2011 if (s->brushData.blend)
2012 fillPolygon(points, pointCount, mode);
2013 }
2014
2015 // Do the outline...
2016 if (s->penData.blend) {
2017 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
2018 if (s->flags.fast_pen) {
2019 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
2020 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2021 stroker.drawPath(vp);
2022 } else {
2023 QPaintEngineEx::stroke(vp, s->lastPen);
2024 }
2025 }
2026 }
2027
2028 /*!
2029 \reimp
2030 */
drawPolygon(const QPoint * points,int pointCount,PolygonDrawMode mode)2031 void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2032 {
2033 Q_D(QRasterPaintEngine);
2034 QRasterPaintEngineState *s = state();
2035
2036 #ifdef QT_DEBUG_DRAW
2037 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2038 for (int i=0; i<pointCount; ++i)
2039 qDebug() << " - " << points[i];
2040 #endif
2041 Q_ASSERT(pointCount >= 2);
2042 if (mode != PolylineMode && QVectorPath::isRect((const int *) points, pointCount)) {
2043 QRect r(points[0].x(),
2044 points[0].y(),
2045 points[2].x() - points[0].x(),
2046 points[2].y() - points[0].y());
2047 drawRects(&r, 1);
2048 return;
2049 }
2050
2051 ensurePen();
2052
2053 // Do the fill
2054 if (mode != PolylineMode) {
2055 ensureBrush();
2056 if (s->brushData.blend) {
2057 // Compose polygon fill..,
2058 ensureOutlineMapper();
2059 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2060 d->outlineMapper->moveTo(*points);
2061 const QPoint *p = points;
2062 const QPoint *ep = points + pointCount - 1;
2063 do {
2064 d->outlineMapper->lineTo(*(++p));
2065 } while (p < ep);
2066 d->outlineMapper->endOutline();
2067
2068 // scanconvert.
2069 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2070 &s->brushData);
2071 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2072 }
2073 }
2074
2075 // Do the outline...
2076 if (s->penData.blend) {
2077 int count = pointCount * 2;
2078 QVarLengthArray<qreal> fpoints(count);
2079 for (int i=0; i<count; ++i)
2080 fpoints[i] = ((const int *) points)[i];
2081 QVectorPath vp((qreal *) fpoints.data(), pointCount, nullptr, QVectorPath::polygonFlags(mode));
2082
2083 if (s->flags.fast_pen) {
2084 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
2085 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2086 stroker.drawPath(vp);
2087 } else {
2088 QPaintEngineEx::stroke(vp, s->lastPen);
2089 }
2090 }
2091 }
2092
2093 /*!
2094 \internal
2095 */
drawPixmap(const QPointF & pos,const QPixmap & pixmap)2096 void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2097 {
2098 #ifdef QT_DEBUG_DRAW
2099 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2100 #endif
2101
2102 QPlatformPixmap *pd = pixmap.handle();
2103 if (pd->classId() == QPlatformPixmap::RasterClass) {
2104 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2105 if (image.depth() == 1) {
2106 Q_D(QRasterPaintEngine);
2107 QRasterPaintEngineState *s = state();
2108 if (s->matrix.type() <= QTransform::TxTranslate) {
2109 ensurePen();
2110 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2111 } else {
2112 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2113 }
2114 } else {
2115 QRasterPaintEngine::drawImage(pos, image);
2116 }
2117 } else {
2118 const QImage image = pixmap.toImage();
2119 if (pixmap.depth() == 1) {
2120 Q_D(QRasterPaintEngine);
2121 QRasterPaintEngineState *s = state();
2122 if (s->matrix.type() <= QTransform::TxTranslate) {
2123 ensurePen();
2124 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2125 } else {
2126 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2127 }
2128 } else {
2129 QRasterPaintEngine::drawImage(pos, image);
2130 }
2131 }
2132 }
2133
2134 /*!
2135 \reimp
2136 */
drawPixmap(const QRectF & r,const QPixmap & pixmap,const QRectF & sr)2137 void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2138 {
2139 #ifdef QT_DEBUG_DRAW
2140 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2141 #endif
2142
2143 QPlatformPixmap* pd = pixmap.handle();
2144 if (pd->classId() == QPlatformPixmap::RasterClass) {
2145 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2146 if (image.depth() == 1) {
2147 Q_D(QRasterPaintEngine);
2148 QRasterPaintEngineState *s = state();
2149 if (s->matrix.type() <= QTransform::TxTranslate
2150 && r.size() == sr.size()
2151 && r.size() == pixmap.size()) {
2152 ensurePen();
2153 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2154 return;
2155 } else {
2156 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2157 }
2158 } else {
2159 drawImage(r, image, sr);
2160 }
2161 } else {
2162 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2163 const QImage image = pd->toImage(clippedSource);
2164 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2165 if (image.depth() == 1) {
2166 Q_D(QRasterPaintEngine);
2167 QRasterPaintEngineState *s = state();
2168 if (s->matrix.type() <= QTransform::TxTranslate
2169 && r.size() == sr.size()
2170 && r.size() == pixmap.size()) {
2171 ensurePen();
2172 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2173 return;
2174 } else {
2175 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2176 }
2177 } else {
2178 drawImage(r, image, translatedSource);
2179 }
2180 }
2181 }
2182
fast_ceil_positive(const qreal & v)2183 static inline int fast_ceil_positive(const qreal &v)
2184 {
2185 const int iv = int(v);
2186 if (v - iv == 0)
2187 return iv;
2188 else
2189 return iv + 1;
2190 }
2191
toAlignedRect_positive(const QRectF & rect)2192 static inline const QRect toAlignedRect_positive(const QRectF &rect)
2193 {
2194 const int xmin = int(rect.x());
2195 const int xmax = int(fast_ceil_positive(rect.right()));
2196 const int ymin = int(rect.y());
2197 const int ymax = int(fast_ceil_positive(rect.bottom()));
2198 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2199 }
2200
2201 /*!
2202 \internal
2203 */
drawImage(const QPointF & p,const QImage & img)2204 void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2205 {
2206 #ifdef QT_DEBUG_DRAW
2207 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2208 #endif
2209
2210 Q_D(QRasterPaintEngine);
2211 QRasterPaintEngineState *s = state();
2212 qreal scale = img.devicePixelRatio();
2213
2214 if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
2215 drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2216 img,
2217 QRectF(0, 0, img.width(), img.height()));
2218 } else {
2219
2220 const QClipData *clip = d->clip();
2221 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2222
2223 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, img.rect())) {
2224 if (!clip) {
2225 d->blitImage(pt, img, d->deviceRect);
2226 return;
2227 } else if (clip->hasRectClip) {
2228 d->blitImage(pt, img, clip->clipRect);
2229 return;
2230 }
2231 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2232 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2233 if (func) {
2234 if (!clip) {
2235 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2236 return;
2237 } else if (clip->hasRectClip) {
2238 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2239 return;
2240 }
2241 }
2242 }
2243
2244
2245
2246 d->image_filler.clip = clip;
2247 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2248 if (!d->image_filler.blend)
2249 return;
2250 d->image_filler.dx = -pt.x();
2251 d->image_filler.dy = -pt.y();
2252 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2253
2254 fillRect_normalized(rr, &d->image_filler, d);
2255 }
2256
2257 }
2258
qt_mapRect_non_normalizing(const QRectF & r,const QTransform & t)2259 QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2260 {
2261 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2262 }
2263
2264 namespace {
2265 enum RotationType {
2266 Rotation90,
2267 Rotation180,
2268 Rotation270,
2269 NoRotation
2270 };
2271
qRotationType(const QTransform & transform)2272 inline RotationType qRotationType(const QTransform &transform)
2273 {
2274 QTransform::TransformationType type = transform.type();
2275
2276 if (type > QTransform::TxRotate)
2277 return NoRotation;
2278
2279 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2280 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2281 return Rotation90;
2282
2283 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2284 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2285 return Rotation180;
2286
2287 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2288 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2289 return Rotation270;
2290
2291 return NoRotation;
2292 }
2293
isPixelAligned(const QPointF & pt)2294 inline bool isPixelAligned(const QPointF &pt)
2295 {
2296 return QPointF(pt.toPoint()) == pt;
2297 }
isPixelAligned(const QRectF & rect)2298 inline bool isPixelAligned(const QRectF &rect)
2299 {
2300 return QRectF(rect.toRect()) == rect;
2301 }
2302 }
2303
2304 /*!
2305 \reimp
2306 */
drawImage(const QRectF & r,const QImage & img,const QRectF & sr,Qt::ImageConversionFlags)2307 void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2308 Qt::ImageConversionFlags)
2309 {
2310 #ifdef QT_DEBUG_DRAW
2311 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2312 #endif
2313
2314 if (r.isEmpty())
2315 return;
2316
2317 Q_D(QRasterPaintEngine);
2318 QRasterPaintEngineState *s = state();
2319 Q_ASSERT(s);
2320 int sr_l = qFloor(sr.left());
2321 int sr_r = qCeil(sr.right()) - 1;
2322 int sr_t = qFloor(sr.top());
2323 int sr_b = qCeil(sr.bottom()) - 1;
2324
2325 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2326 // as fillRect will apply the aliased coordinate delta we need to
2327 // subtract it here as we don't use it for image drawing
2328 QTransform old = s->matrix;
2329
2330 if (s->flags.legacy_rounding)
2331 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2332
2333 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2334 QRgb color = img.pixel(sr_l, sr_t);
2335 switch (img.format()) {
2336 case QImage::Format_ARGB32_Premultiplied:
2337 case QImage::Format_ARGB8565_Premultiplied:
2338 case QImage::Format_ARGB6666_Premultiplied:
2339 case QImage::Format_ARGB8555_Premultiplied:
2340 case QImage::Format_ARGB4444_Premultiplied:
2341 case QImage::Format_RGBA8888_Premultiplied:
2342 case QImage::Format_A2BGR30_Premultiplied:
2343 case QImage::Format_A2RGB30_Premultiplied:
2344 // Combine premultiplied color with the opacity set on the painter.
2345 d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity);
2346 break;
2347 default:
2348 d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity));
2349 break;
2350 }
2351
2352 if (d->solid_color_filler.solidColor.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver)
2353 return;
2354
2355 d->solid_color_filler.clip = d->clip();
2356 d->solid_color_filler.adjustSpanMethods();
2357 fillRect(r, &d->solid_color_filler);
2358
2359 s->matrix = old;
2360 return;
2361 }
2362
2363 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2364
2365 const QClipData *clip = d->clip();
2366
2367 if (s->matrix.type() == QTransform::TxRotate
2368 && !stretch_sr
2369 && (!clip || clip->hasRectClip)
2370 && s->intOpacity == 256
2371 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2372 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))
2373 {
2374 RotationType rotationType = qRotationType(s->matrix);
2375 const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2376
2377 if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2378 QRectF transformedTargetRect = s->matrix.mapRect(r);
2379
2380 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, transformedTargetRect.topRight(), sr)) {
2381 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2382 if (clippedTransformedTargetRect.isNull())
2383 return;
2384
2385 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2386
2387 QRect clippedSourceRect
2388 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2389 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2390
2391 clippedSourceRect = clippedSourceRect.intersected(img.rect());
2392
2393 const qsizetype dbpl = d->rasterBuffer->bytesPerLine();
2394 const qsizetype sbpl = img.bytesPerLine();
2395
2396 uchar *dst = d->rasterBuffer->buffer();
2397 uint bpp = img.depth() >> 3;
2398
2399 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2400 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2401
2402 uint cw = clippedSourceRect.width();
2403 uint ch = clippedSourceRect.height();
2404
2405 qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2406
2407 return;
2408 }
2409 }
2410 }
2411
2412 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2413
2414 QRectF targetBounds = s->matrix.mapRect(r);
2415 bool exceedsPrecision = r.width() > 0x7fff
2416 || r.height() > 0x7fff
2417 || targetBounds.width() > 0x7fff
2418 || targetBounds.height() > 0x7fff
2419 || s->matrix.m11() >= 512
2420 || s->matrix.m22() >= 512;
2421
2422 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2423 if (s->matrix.type() > QTransform::TxScale) {
2424 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2425 if (func && (!clip || clip->hasRectClip)) {
2426 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2427 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2428 s->matrix, s->intOpacity);
2429 return;
2430 }
2431 } else {
2432 // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2433 bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2434 bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2435 if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2436 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2437 if (func) {
2438 QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2439 if (!clip) {
2440 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2441 return;
2442 } else if (clip->hasRectClip) {
2443 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2444 return;
2445 }
2446 }
2447 }
2448 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2449 if (func && (!clip || clip->hasRectClip)) {
2450 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2451 img.bits(), img.bytesPerLine(), img.height(),
2452 qt_mapRect_non_normalizing(r, s->matrix), sr,
2453 !clip ? d->deviceRect : clip->clipRect,
2454 s->intOpacity);
2455 return;
2456 }
2457 }
2458 }
2459
2460 QTransform copy = s->matrix;
2461 copy.translate(r.x(), r.y());
2462 if (stretch_sr)
2463 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2464 copy.translate(-sr.x(), -sr.y());
2465
2466 d->image_filler_xform.clip = clip;
2467 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2468 if (!d->image_filler_xform.blend)
2469 return;
2470 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2471
2472 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2473 QRectF rr = s->matrix.mapRect(r);
2474
2475 const int x1 = qRound(rr.x());
2476 const int y1 = qRound(rr.y());
2477 const int x2 = qRound(rr.right());
2478 const int y2 = qRound(rr.bottom());
2479
2480 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2481 return;
2482 }
2483
2484 #ifdef QT_FAST_SPANS
2485 ensureRasterState();
2486 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2487 d->initializeRasterizer(&d->image_filler_xform);
2488 d->rasterizer->setAntialiased(s->flags.antialiased);
2489 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2490
2491 const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
2492
2493 const QRectF &rect = r.normalized();
2494 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2495 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2496
2497 if (s->flags.tx_noshear)
2498 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2499 else
2500 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2501 return;
2502 }
2503 #endif
2504 const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
2505 QPainterPath path;
2506 path.addRect(r);
2507 QTransform m = s->matrix;
2508 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2509 m.m21(), m.m22(), m.m23(),
2510 m.m31() - offs, m.m32() - offs, m.m33());
2511 fillPath(path, &d->image_filler_xform);
2512 s->matrix = m;
2513 } else {
2514 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2515 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) {
2516 if (!clip) {
2517 d->blitImage(pt, img, d->deviceRect, sr.toRect());
2518 return;
2519 } else if (clip->hasRectClip) {
2520 d->blitImage(pt, img, clip->clipRect, sr.toRect());
2521 return;
2522 }
2523 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2524 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2525 if (func) {
2526 if (!clip) {
2527 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2528 return;
2529 } else if (clip->hasRectClip) {
2530 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2531 return;
2532 }
2533 }
2534 }
2535
2536 d->image_filler.clip = clip;
2537 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2538 if (!d->image_filler.blend)
2539 return;
2540 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2541 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2542
2543 QRectF rr = r;
2544 rr.translate(s->matrix.dx(), s->matrix.dy());
2545
2546 const int x1 = qRound(rr.x());
2547 const int y1 = qRound(rr.y());
2548 const int x2 = qRound(rr.right());
2549 const int y2 = qRound(rr.bottom());
2550
2551 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2552 }
2553 }
2554
2555 /*!
2556 \reimp
2557 */
drawTiledPixmap(const QRectF & r,const QPixmap & pixmap,const QPointF & sr)2558 void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2559 {
2560 #ifdef QT_DEBUG_DRAW
2561 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2562 #endif
2563 Q_D(QRasterPaintEngine);
2564 QRasterPaintEngineState *s = state();
2565 Q_ASSERT(s);
2566
2567 QImage image;
2568
2569 QPlatformPixmap *pd = pixmap.handle();
2570 if (pd->classId() == QPlatformPixmap::RasterClass) {
2571 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2572 } else {
2573 image = pixmap.toImage();
2574 }
2575
2576 if (image.depth() == 1)
2577 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2578
2579 const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
2580 if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
2581 QTransform copy = s->matrix;
2582 copy.translate(r.x(), r.y());
2583 copy.translate(-sr.x(), -sr.y());
2584 const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
2585 copy.scale(inverseDpr, inverseDpr);
2586 d->image_filler_xform.clip = d->clip();
2587 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2588 if (!d->image_filler_xform.blend)
2589 return;
2590 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2591
2592 #ifdef QT_FAST_SPANS
2593 ensureRasterState();
2594 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2595 d->initializeRasterizer(&d->image_filler_xform);
2596 d->rasterizer->setAntialiased(s->flags.antialiased);
2597 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2598
2599 const QRectF &rect = r.normalized();
2600 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2601 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2602 if (s->flags.tx_noshear)
2603 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2604 else
2605 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2606 return;
2607 }
2608 #endif
2609 QPainterPath path;
2610 path.addRect(r);
2611 fillPath(path, &d->image_filler_xform);
2612 } else {
2613 d->image_filler.clip = d->clip();
2614
2615 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2616 if (!d->image_filler.blend)
2617 return;
2618 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2619 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2620
2621 QRectF rr = r;
2622 rr.translate(s->matrix.dx(), s->matrix.dy());
2623 fillRect_normalized(rr.normalized().toRect(), &d->image_filler, d);
2624 }
2625 }
2626
2627
2628 //QWS hack
monoVal(const uchar * s,int x)2629 static inline bool monoVal(const uchar* s, int x)
2630 {
2631 return (s[x>>3] << (x&7)) & 0x80;
2632 }
2633
2634 /*!
2635 \internal
2636 */
rasterBuffer()2637 QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2638 {
2639 Q_D(QRasterPaintEngine);
2640 return d->rasterBuffer.data();
2641 }
2642
2643 /*!
2644 \internal
2645 */
alphaPenBlt(const void * src,int bpl,int depth,int rx,int ry,int w,int h,bool useGammaCorrection)2646 void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
2647 {
2648 Q_D(QRasterPaintEngine);
2649 QRasterPaintEngineState *s = state();
2650
2651 if (!s->penData.blend)
2652 return;
2653
2654 QRasterBuffer *rb = d->rasterBuffer.data();
2655
2656 const QRect rect(rx, ry, w, h);
2657 const QClipData *clip = d->clip();
2658 bool unclipped = false;
2659 if (clip) {
2660 // inlined QRect::intersects
2661 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2662 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2663
2664 if (clip->hasRectClip) {
2665 unclipped = rx > clip->xmin
2666 && rx + w < clip->xmax
2667 && ry > clip->ymin
2668 && ry + h < clip->ymax;
2669 }
2670
2671 if (!intersects)
2672 return;
2673 } else {
2674 // inlined QRect::intersects
2675 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2676 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2677 if (!intersects)
2678 return;
2679
2680 // inlined QRect::contains
2681 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2682 && rect.top() >= 0 && rect.bottom() < rb->height();
2683
2684 unclipped = contains && d->isUnclipped_normalized(rect);
2685 }
2686
2687 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2688 const uchar * scanline = static_cast<const uchar *>(src);
2689
2690 if (s->flags.fast_text) {
2691 if (unclipped) {
2692 if (depth == 1) {
2693 if (s->penData.bitmapBlit) {
2694 s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor,
2695 scanline, w, h, bpl);
2696 return;
2697 }
2698 } else if (depth == 8) {
2699 if (s->penData.alphamapBlit) {
2700 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor,
2701 scanline, w, h, bpl, nullptr, useGammaCorrection);
2702 return;
2703 }
2704 } else if (depth == 32) {
2705 // (A)RGB Alpha mask where the alpha component is not used.
2706 if (s->penData.alphaRGBBlit) {
2707 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor,
2708 (const uint *) scanline, w, h, bpl / 4, nullptr, useGammaCorrection);
2709 return;
2710 }
2711 }
2712 } else if ((depth == 8 && s->penData.alphamapBlit) || (depth == 32 && s->penData.alphaRGBBlit)) {
2713 // (A)RGB Alpha mask where the alpha component is not used.
2714 if (!clip) {
2715 int nx = qMax(0, rx);
2716 int ny = qMax(0, ry);
2717
2718 // Move scanline pointer to compensate for moved x and y
2719 int xdiff = nx - rx;
2720 int ydiff = ny - ry;
2721 scanline += ydiff * bpl;
2722 scanline += xdiff * (depth == 32 ? 4 : 1);
2723
2724 w -= xdiff;
2725 h -= ydiff;
2726
2727 if (nx + w > d->rasterBuffer->width())
2728 w = d->rasterBuffer->width() - nx;
2729 if (ny + h > d->rasterBuffer->height())
2730 h = d->rasterBuffer->height() - ny;
2731
2732 rx = nx;
2733 ry = ny;
2734 }
2735 if (depth == 8)
2736 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor,
2737 scanline, w, h, bpl, clip, useGammaCorrection);
2738 else if (depth == 32)
2739 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor,
2740 (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
2741 return;
2742 }
2743 }
2744
2745 int x0 = 0;
2746 if (rx < 0) {
2747 x0 = -rx;
2748 w -= x0;
2749 }
2750
2751 int y0 = 0;
2752 if (ry < 0) {
2753 y0 = -ry;
2754 scanline += bpl * y0;
2755 h -= y0;
2756 }
2757
2758 w = qMin(w, rb->width() - qMax(0, rx));
2759 h = qMin(h, rb->height() - qMax(0, ry));
2760
2761 if (w <= 0 || h <= 0)
2762 return;
2763
2764 const int NSPANS = 256;
2765 QSpan spans[NSPANS];
2766 int current = 0;
2767
2768 const int x1 = x0 + w;
2769 const int y1 = y0 + h;
2770
2771 if (depth == 1) {
2772 for (int y = y0; y < y1; ++y) {
2773 for (int x = x0; x < x1; ) {
2774 if (!monoVal(scanline, x)) {
2775 ++x;
2776 continue;
2777 }
2778
2779 if (current == NSPANS) {
2780 blend(current, spans, &s->penData);
2781 current = 0;
2782 }
2783 spans[current].x = x + rx;
2784 spans[current].y = y + ry;
2785 spans[current].coverage = 255;
2786 int len = 1;
2787 ++x;
2788 // extend span until we find a different one.
2789 while (x < x1 && monoVal(scanline, x)) {
2790 ++x;
2791 ++len;
2792 }
2793 spans[current].len = len;
2794 ++current;
2795 }
2796 scanline += bpl;
2797 }
2798 } else if (depth == 8) {
2799 for (int y = y0; y < y1; ++y) {
2800 for (int x = x0; x < x1; ) {
2801 // Skip those with 0 coverage
2802 if (scanline[x] == 0) {
2803 ++x;
2804 continue;
2805 }
2806
2807 if (current == NSPANS) {
2808 blend(current, spans, &s->penData);
2809 current = 0;
2810 }
2811 int coverage = scanline[x];
2812 spans[current].x = x + rx;
2813 spans[current].y = y + ry;
2814 spans[current].coverage = coverage;
2815 int len = 1;
2816 ++x;
2817
2818 // extend span until we find a different one.
2819 while (x < x1 && scanline[x] == coverage) {
2820 ++x;
2821 ++len;
2822 }
2823 spans[current].len = len;
2824 ++current;
2825 }
2826 scanline += bpl;
2827 }
2828 } else { // 32-bit alpha...
2829 const uint *sl = (const uint *) scanline;
2830 for (int y = y0; y < y1; ++y) {
2831 for (int x = x0; x < x1; ) {
2832 // Skip those with 0 coverage
2833 if ((sl[x] & 0x00ffffff) == 0) {
2834 ++x;
2835 continue;
2836 }
2837
2838 if (current == NSPANS) {
2839 blend(current, spans, &s->penData);
2840 current = 0;
2841 }
2842 uint rgbCoverage = sl[x];
2843 int coverage = qGreen(rgbCoverage);
2844 spans[current].x = x + rx;
2845 spans[current].y = y + ry;
2846 spans[current].coverage = coverage;
2847 int len = 1;
2848 ++x;
2849
2850 // extend span until we find a different one.
2851 while (x < x1 && sl[x] == rgbCoverage) {
2852 ++x;
2853 ++len;
2854 }
2855 spans[current].len = len;
2856 ++current;
2857 }
2858 sl += bpl / sizeof(uint);
2859 }
2860 }
2861 // qDebug() << "alphaPenBlt: num spans=" << current
2862 // << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2863 // Call span func for current set of spans.
2864 if (current != 0)
2865 blend(current, spans, &s->penData);
2866 }
2867
2868 /*!
2869 \internal
2870 */
drawCachedGlyphs(int numGlyphs,const glyph_t * glyphs,const QFixedPoint * positions,QFontEngine * fontEngine)2871 bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2872 const QFixedPoint *positions, QFontEngine *fontEngine)
2873 {
2874 Q_D(QRasterPaintEngine);
2875 QRasterPaintEngineState *s = state();
2876
2877 if (fontEngine->hasInternalCaching()) {
2878 QFontEngine::GlyphFormat neededFormat =
2879 painter()->device()->devType() == QInternal::Widget
2880 ? QFontEngine::Format_None
2881 : QFontEngine::Format_A8;
2882
2883 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2884 neededFormat = QFontEngine::Format_Mono;
2885
2886 for (int i = 0; i < numGlyphs; i++) {
2887 QFixed spp = fontEngine->subPixelPositionForX(positions[i].x);
2888
2889 const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyphs[i], spp, neededFormat, s->matrix);
2890 if (!alphaMap)
2891 continue;
2892
2893 int depth;
2894 int bytesPerLine;
2895 switch (alphaMap->format) {
2896 case QFontEngine::Format_Mono:
2897 depth = 1;
2898 bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
2899 break;
2900 case QFontEngine::Format_A8:
2901 depth = 8;
2902 bytesPerLine = (alphaMap->width + 3) & ~3;
2903 break;
2904 case QFontEngine::Format_A32:
2905 depth = 32;
2906 bytesPerLine = alphaMap->width * 4;
2907 break;
2908 default:
2909 Q_UNREACHABLE();
2910 };
2911
2912 alphaPenBlt(alphaMap->data, bytesPerLine, depth,
2913 qFloor(positions[i].x) + alphaMap->x,
2914 qRound(positions[i].y) - alphaMap->y,
2915 alphaMap->width, alphaMap->height,
2916 fontEngine->expectsGammaCorrectedBlending());
2917 }
2918
2919 } else {
2920 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
2921
2922 QImageTextureGlyphCache *cache =
2923 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(nullptr, glyphFormat, s->matrix, QColor(s->penData.solidColor)));
2924 if (!cache) {
2925 cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, QColor(s->penData.solidColor));
2926 fontEngine->setGlyphCache(nullptr, cache);
2927 }
2928
2929 cache->populate(fontEngine, numGlyphs, glyphs, positions);
2930 cache->fillInPendingGlyphs();
2931
2932 const QImage &image = cache->image();
2933 qsizetype bpl = image.bytesPerLine();
2934
2935 int depth = image.depth();
2936 int rightShift = 0;
2937 int leftShift = 0;
2938 if (depth == 32)
2939 leftShift = 2; // multiply by 4
2940 else if (depth == 1)
2941 rightShift = 3; // divide by 8
2942
2943 int margin = fontEngine->glyphMargin(glyphFormat);
2944 const uchar *bits = image.bits();
2945 for (int i=0; i<numGlyphs; ++i) {
2946
2947 QFixed subPixelPosition = fontEngine->subPixelPositionForX(positions[i].x);
2948 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2949 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2950 if (c.isNull())
2951 continue;
2952
2953 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2954 int y = qRound(positions[i].y) - c.baseLineY - margin;
2955
2956 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2957 // c.x, c.y,
2958 // c.w, c.h,
2959 // c.baseLineX, c.baseLineY,
2960 // glyphs[i],
2961 // x, y,
2962 // positions[i].x.toInt(), positions[i].y.toInt());
2963
2964 const uchar *glyphBits = bits + ((c.x << leftShift) >> rightShift) + c.y * bpl;
2965
2966 if (glyphFormat == QFontEngine::Format_ARGB) {
2967 // The current state transform has already been applied to the positions,
2968 // so we prevent drawImage() from re-applying the transform by clearing
2969 // the state for the duration of the call.
2970 QTransform originalTransform = s->matrix;
2971 s->matrix = QTransform();
2972 drawImage(QPoint(x, y), QImage(glyphBits, c.w, c.h, bpl, image.format()));
2973 s->matrix = originalTransform;
2974 } else {
2975 alphaPenBlt(glyphBits, bpl, depth, x, y, c.w, c.h, fontEngine->expectsGammaCorrectedBlending());
2976 }
2977 }
2978 }
2979 return true;
2980 }
2981
2982
2983 /*!
2984 * Returns \c true if the rectangle is completely within the current clip
2985 * state of the paint engine.
2986 */
isUnclipped_normalized(const QRect & r) const2987 bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2988 {
2989 const QClipData *cl = clip();
2990 if (!cl) {
2991 // inline contains() for performance (we know the rects are normalized)
2992 const QRect &r1 = deviceRect;
2993 return (r.left() >= r1.left() && r.right() <= r1.right()
2994 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2995 }
2996
2997
2998 if (cl->hasRectClip) {
2999 // currently all painting functions clips to deviceRect internally
3000 if (cl->clipRect == deviceRect)
3001 return true;
3002
3003 // inline contains() for performance (we know the rects are normalized)
3004 const QRect &r1 = cl->clipRect;
3005 return (r.left() >= r1.left() && r.right() <= r1.right()
3006 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3007 } else {
3008 return qt_region_strictContains(cl->clipRegion, r);
3009 }
3010 }
3011
isUnclipped(const QRect & rect,int penWidth) const3012 bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
3013 int penWidth) const
3014 {
3015 Q_Q(const QRasterPaintEngine);
3016 const QRasterPaintEngineState *s = q->state();
3017 const QClipData *cl = clip();
3018 if (!cl) {
3019 QRect r = qrect_normalized(rect);
3020 // inline contains() for performance (we know the rects are normalized)
3021 const QRect &r1 = deviceRect;
3022 return (r.left() >= r1.left() && r.right() <= r1.right()
3023 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3024 }
3025
3026
3027 // currently all painting functions that call this function clip to deviceRect internally
3028 if (cl->hasRectClip && cl->clipRect == deviceRect)
3029 return true;
3030
3031 if (s->flags.antialiased)
3032 ++penWidth;
3033
3034 QRect r = qrect_normalized(rect);
3035 if (penWidth > 0) {
3036 r.setX(r.x() - penWidth);
3037 r.setY(r.y() - penWidth);
3038 r.setWidth(r.width() + 2 * penWidth);
3039 r.setHeight(r.height() + 2 * penWidth);
3040 }
3041
3042 if (cl->hasRectClip) {
3043 // inline contains() for performance (we know the rects are normalized)
3044 const QRect &r1 = cl->clipRect;
3045 return (r.left() >= r1.left() && r.right() <= r1.right()
3046 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
3047 } else {
3048 return qt_region_strictContains(cl->clipRegion, r);
3049 }
3050 }
3051
isUnclipped(const QRectF & rect,int penWidth) const3052 inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
3053 int penWidth) const
3054 {
3055 const QRectF norm = rect.normalized();
3056 if (norm.left() < INT_MIN || norm.top() < INT_MIN
3057 || norm.right() > INT_MAX || norm.bottom() > INT_MAX
3058 || norm.width() > INT_MAX || norm.height() > INT_MAX)
3059 return false;
3060 return isUnclipped(norm.toAlignedRect(), penWidth);
3061 }
3062
3063 inline ProcessSpans
getBrushFunc(const QRect & rect,const QSpanData * data) const3064 QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
3065 const QSpanData *data) const
3066 {
3067 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3068 }
3069
3070 inline ProcessSpans
getBrushFunc(const QRectF & rect,const QSpanData * data) const3071 QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
3072 const QSpanData *data) const
3073 {
3074 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
3075 }
3076
3077 inline ProcessSpans
getPenFunc(const QRectF & rect,const QSpanData * data) const3078 QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
3079 const QSpanData *data) const
3080 {
3081 Q_Q(const QRasterPaintEngine);
3082 const QRasterPaintEngineState *s = q->state();
3083
3084 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3085 return data->blend;
3086 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3087 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3088 }
3089
visibleGlyphRange(const QRectF & clip,QFontEngine * fontEngine,glyph_t * glyphs,QFixedPoint * positions,int numGlyphs)3090 static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
3091 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
3092 {
3093 QFixed clipLeft = QFixed::fromReal(clip.left());
3094 QFixed clipRight = QFixed::fromReal(clip.right());
3095 QFixed clipTop = QFixed::fromReal(clip.top());
3096 QFixed clipBottom = QFixed::fromReal(clip.bottom());
3097
3098 int first = 0;
3099 while (first < numGlyphs) {
3100 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
3101 QFixed left = metrics.x + positions[first].x;
3102 QFixed top = metrics.y + positions[first].y;
3103 QFixed right = left + metrics.width;
3104 QFixed bottom = top + metrics.height;
3105 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3106 break;
3107 ++first;
3108 }
3109 int last = numGlyphs - 1;
3110 while (last > first) {
3111 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
3112 QFixed left = metrics.x + positions[last].x;
3113 QFixed top = metrics.y + positions[last].y;
3114 QFixed right = left + metrics.width;
3115 QFixed bottom = top + metrics.height;
3116 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3117 break;
3118 --last;
3119 }
3120 return QPair<int, int>(first, last + 1);
3121 }
3122
3123 /*!
3124 \reimp
3125 */
drawStaticTextItem(QStaticTextItem * textItem)3126 void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3127 {
3128 if (textItem->numGlyphs == 0)
3129 return;
3130
3131 ensurePen();
3132 ensureRasterState();
3133
3134 QTransform matrix = state()->matrix;
3135
3136 QFontEngine *fontEngine = textItem->fontEngine();
3137 if (shouldDrawCachedGlyphs(fontEngine, matrix)) {
3138 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3139 fontEngine);
3140 } else if (matrix.type() < QTransform::TxProject) {
3141 bool invertible;
3142 QTransform invMat = matrix.inverted(&invertible);
3143 if (!invertible)
3144 return;
3145
3146 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3147 textItem->fontEngine(), textItem->glyphs,
3148 textItem->glyphPositions, textItem->numGlyphs);
3149 QStaticTextItem copy = *textItem;
3150 copy.glyphs += range.first;
3151 copy.glyphPositions += range.first;
3152 copy.numGlyphs = range.second - range.first;
3153 QPaintEngineEx::drawStaticTextItem(©);
3154 } else {
3155 QPaintEngineEx::drawStaticTextItem(textItem);
3156 }
3157 }
3158
3159 /*!
3160 \reimp
3161 */
drawTextItem(const QPointF & p,const QTextItem & textItem)3162 void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3163 {
3164 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3165
3166 #ifdef QT_DEBUG_DRAW
3167 Q_D(QRasterPaintEngine);
3168 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3169 p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
3170 d->glyphCacheFormat);
3171 #endif
3172
3173 if (ti.glyphs.numGlyphs == 0)
3174 return;
3175 ensurePen();
3176 ensureRasterState();
3177
3178 QRasterPaintEngineState *s = state();
3179 QTransform matrix = s->matrix;
3180
3181 if (shouldDrawCachedGlyphs(ti.fontEngine, matrix)) {
3182 QVarLengthArray<QFixedPoint> positions;
3183 QVarLengthArray<glyph_t> glyphs;
3184
3185 matrix.translate(p.x(), p.y());
3186 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3187
3188 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3189 } else if (matrix.type() < QTransform::TxProject
3190 && ti.fontEngine->supportsTransformation(matrix)) {
3191 bool invertible;
3192 QTransform invMat = matrix.inverted(&invertible);
3193 if (!invertible)
3194 return;
3195
3196 QVarLengthArray<QFixedPoint> positions;
3197 QVarLengthArray<glyph_t> glyphs;
3198
3199 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3200 ti.flags, glyphs, positions);
3201 QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3202 ti.fontEngine, glyphs.data(), positions.data(),
3203 glyphs.size());
3204
3205 if (range.first >= range.second)
3206 return;
3207
3208 QStaticTextItem staticTextItem;
3209 staticTextItem.color = s->pen.color();
3210 staticTextItem.font = s->font;
3211 staticTextItem.setFontEngine(ti.fontEngine);
3212 staticTextItem.numGlyphs = range.second - range.first;
3213 staticTextItem.glyphs = glyphs.data() + range.first;
3214 staticTextItem.glyphPositions = positions.data() + range.first;
3215 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3216 } else {
3217 QPaintEngineEx::drawTextItem(p, ti);
3218 }
3219 }
3220
3221 /*!
3222 \reimp
3223 */
drawPoints(const QPointF * points,int pointCount)3224 void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3225 {
3226 Q_D(QRasterPaintEngine);
3227 QRasterPaintEngineState *s = state();
3228
3229 ensurePen();
3230 if (!s->penData.blend)
3231 return;
3232
3233 if (!s->flags.fast_pen) {
3234 QPaintEngineEx::drawPoints(points, pointCount);
3235 return;
3236 }
3237
3238 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3239 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3240 stroker.drawPoints(points, pointCount);
3241 }
3242
3243
drawPoints(const QPoint * points,int pointCount)3244 void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3245 {
3246 Q_D(QRasterPaintEngine);
3247 QRasterPaintEngineState *s = state();
3248
3249 ensurePen();
3250 if (!s->penData.blend)
3251 return;
3252
3253 if (!s->flags.fast_pen) {
3254 QPaintEngineEx::drawPoints(points, pointCount);
3255 return;
3256 }
3257
3258 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3259 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3260 stroker.drawPoints(points, pointCount);
3261 }
3262
3263 /*!
3264 \reimp
3265 */
drawLines(const QLine * lines,int lineCount)3266 void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3267 {
3268 #ifdef QT_DEBUG_DRAW
3269 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3270 #endif
3271 Q_D(QRasterPaintEngine);
3272 QRasterPaintEngineState *s = state();
3273
3274 ensurePen();
3275 if (!s->penData.blend)
3276 return;
3277
3278 if (s->flags.fast_pen) {
3279 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3280 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3281 for (int i=0; i<lineCount; ++i) {
3282 const QLine &l = lines[i];
3283 stroker.drawLine(l.p1(), l.p2());
3284 }
3285 } else {
3286 QPaintEngineEx::drawLines(lines, lineCount);
3287 }
3288 }
3289
rasterizeLine_dashed(QLineF line,qreal width,int * dashIndex,qreal * dashOffset,bool * inDash)3290 void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3291 qreal width,
3292 int *dashIndex,
3293 qreal *dashOffset,
3294 bool *inDash)
3295 {
3296 Q_Q(QRasterPaintEngine);
3297 QRasterPaintEngineState *s = q->state();
3298
3299 const QPen &pen = s->lastPen;
3300 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3301 const QVector<qreal> pattern = pen.dashPattern();
3302
3303 qreal patternLength = 0;
3304 for (int i = 0; i < pattern.size(); ++i)
3305 patternLength += pattern.at(i);
3306
3307 if (patternLength <= 0)
3308 return;
3309
3310 qreal length = line.length();
3311 Q_ASSERT(length > 0);
3312 while (length > 0) {
3313 const bool rasterize = *inDash;
3314 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3315 QLineF l = line;
3316
3317 if (dash >= length) {
3318 dash = line.length(); // Avoid accumulated precision error in 'length'
3319 *dashOffset += dash / width;
3320 length = 0;
3321 } else {
3322 *dashOffset = 0;
3323 *inDash = !(*inDash);
3324 if (++*dashIndex >= pattern.size())
3325 *dashIndex = 0;
3326 length -= dash;
3327 l.setLength(dash);
3328 line.setP1(l.p2());
3329 }
3330
3331 if (rasterize && dash > 0)
3332 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3333 }
3334 }
3335
3336 /*!
3337 \reimp
3338 */
drawLines(const QLineF * lines,int lineCount)3339 void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3340 {
3341 #ifdef QT_DEBUG_DRAW
3342 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3343 #endif
3344 Q_D(QRasterPaintEngine);
3345 QRasterPaintEngineState *s = state();
3346
3347 ensurePen();
3348 if (!s->penData.blend)
3349 return;
3350 if (s->flags.fast_pen) {
3351 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3352 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
3353 for (int i=0; i<lineCount; ++i) {
3354 QLineF line = lines[i];
3355 stroker.drawLine(line.p1(), line.p2());
3356 }
3357 } else {
3358 QPaintEngineEx::drawLines(lines, lineCount);
3359 }
3360 }
3361
3362
3363 /*!
3364 \reimp
3365 */
drawEllipse(const QRectF & rect)3366 void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3367 {
3368 Q_D(QRasterPaintEngine);
3369 QRasterPaintEngineState *s = state();
3370
3371 ensurePen();
3372 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3373 || (qpen_style(s->lastPen) == Qt::NoPen))
3374 && !s->flags.antialiased
3375 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3376 && !rect.isEmpty()
3377 && s->matrix.type() <= QTransform::TxScale) // no shear
3378 {
3379 ensureBrush();
3380 const QRectF r = s->matrix.mapRect(rect);
3381 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3382 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3383 const QRect brect = QRect(int(r.x()), int(r.y()),
3384 int_dim(r.x(), r.width()),
3385 int_dim(r.y(), r.height()));
3386 if (brect == r) {
3387 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3388 &s->penData, &s->brushData);
3389 return;
3390 }
3391 }
3392 QPaintEngineEx::drawEllipse(rect);
3393 }
3394
3395
3396 #ifdef Q_OS_WIN
3397 /*!
3398 \internal
3399 */
setDC(HDC hdc)3400 void QRasterPaintEngine::setDC(HDC hdc) {
3401 Q_D(QRasterPaintEngine);
3402 d->hdc = hdc;
3403 }
3404
3405 /*!
3406 \internal
3407 */
getDC() const3408 HDC QRasterPaintEngine::getDC() const
3409 {
3410 Q_D(const QRasterPaintEngine);
3411 return d->hdc;
3412 }
3413
3414 /*!
3415 \internal
3416 */
releaseDC(HDC) const3417 void QRasterPaintEngine::releaseDC(HDC) const
3418 {
3419 }
3420
3421 #endif
3422
3423 /*!
3424 \internal
3425 */
requiresPretransformedGlyphPositions(QFontEngine * fontEngine,const QTransform & m) const3426 bool QRasterPaintEngine::requiresPretransformedGlyphPositions(QFontEngine *fontEngine, const QTransform &m) const
3427 {
3428 // Cached glyphs always require pretransformed positions
3429 if (shouldDrawCachedGlyphs(fontEngine, m))
3430 return true;
3431
3432 // Otherwise let the base-class decide based on the transform
3433 return QPaintEngineEx::requiresPretransformedGlyphPositions(fontEngine, m);
3434 }
3435
3436 /*!
3437 Returns whether glyph caching is supported by the font engine
3438 \a fontEngine with the given transform \a m applied.
3439 */
shouldDrawCachedGlyphs(QFontEngine * fontEngine,const QTransform & m) const3440 bool QRasterPaintEngine::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const
3441 {
3442 // The raster engine does not support projected cached glyph drawing
3443 if (m.type() >= QTransform::TxProject)
3444 return false;
3445
3446 // The font engine might not support filling the glyph cache
3447 // with the given transform applied, in which case we need to
3448 // fall back to the QPainterPath code-path. This does not apply
3449 // for engines with internal caching, as we don't use the engine
3450 // to fill up our cache in that case.
3451 if (!fontEngine->hasInternalCaching() && !fontEngine->supportsTransformation(m))
3452 return false;
3453
3454 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, m);
3455 }
3456
3457 /*!
3458 \internal
3459 */
coordinateOffset() const3460 QPoint QRasterPaintEngine::coordinateOffset() const
3461 {
3462 return QPoint(0, 0);
3463 }
3464
drawBitmap(const QPointF & pos,const QImage & image,QSpanData * fg)3465 void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3466 {
3467 Q_ASSERT(fg);
3468 if (!fg->blend)
3469 return;
3470 Q_D(QRasterPaintEngine);
3471
3472 Q_ASSERT(image.depth() == 1);
3473
3474 const int spanCount = 256;
3475 QT_FT_Span spans[spanCount];
3476 int n = 0;
3477
3478 // Boundaries
3479 int w = image.width();
3480 int h = image.height();
3481 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3482 int ymin = qMax(qRound(pos.y()), 0);
3483 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3484 int xmin = qMax(qRound(pos.x()), 0);
3485
3486 int x_offset = xmin - qRound(pos.x());
3487
3488 QImage::Format format = image.format();
3489 for (int y = ymin; y < ymax; ++y) {
3490 const uchar *src = image.scanLine(y - qRound(pos.y()));
3491 if (format == QImage::Format_MonoLSB) {
3492 for (int x = 0; x < xmax - xmin; ++x) {
3493 int src_x = x + x_offset;
3494 uchar pixel = src[src_x >> 3];
3495 if (!pixel) {
3496 x += 7 - (src_x%8);
3497 continue;
3498 }
3499 if (pixel & (0x1 << (src_x & 7))) {
3500 spans[n].x = xmin + x;
3501 spans[n].y = y;
3502 spans[n].coverage = 255;
3503 int len = 1;
3504 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3505 ++src_x;
3506 ++len;
3507 }
3508 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3509 x += len;
3510 ++n;
3511 if (n == spanCount) {
3512 fg->blend(n, spans, fg);
3513 n = 0;
3514 }
3515 }
3516 }
3517 } else {
3518 for (int x = 0; x < xmax - xmin; ++x) {
3519 int src_x = x + x_offset;
3520 uchar pixel = src[src_x >> 3];
3521 if (!pixel) {
3522 x += 7 - (src_x%8);
3523 continue;
3524 }
3525 if (pixel & (0x80 >> (x & 7))) {
3526 spans[n].x = xmin + x;
3527 spans[n].y = y;
3528 spans[n].coverage = 255;
3529 int len = 1;
3530 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3531 ++src_x;
3532 ++len;
3533 }
3534 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3535 x += len;
3536 ++n;
3537 if (n == spanCount) {
3538 fg->blend(n, spans, fg);
3539 n = 0;
3540 }
3541 }
3542 }
3543 }
3544 }
3545 if (n) {
3546 fg->blend(n, spans, fg);
3547 n = 0;
3548 }
3549 }
3550
3551 /*!
3552 \enum QRasterPaintEngine::ClipType
3553 \internal
3554
3555 \value RectClip Indicates that the currently set clip is a single rectangle.
3556 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3557 */
3558
3559 /*!
3560 \internal
3561 Returns the type of the clip currently set.
3562 */
clipType() const3563 QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3564 {
3565 Q_D(const QRasterPaintEngine);
3566
3567 const QClipData *clip = d->clip();
3568 if (!clip || clip->hasRectClip)
3569 return RectClip;
3570 else
3571 return ComplexClip;
3572 }
3573
3574 /*!
3575 \internal
3576 Returns the bounding rect of the currently set clip.
3577 */
clipBoundingRect() const3578 QRect QRasterPaintEngine::clipBoundingRect() const
3579 {
3580 Q_D(const QRasterPaintEngine);
3581
3582 const QClipData *clip = d->clip();
3583
3584 if (!clip)
3585 return d->deviceRect;
3586
3587 if (clip->hasRectClip)
3588 return clip->clipRect;
3589
3590 return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3591 }
3592
initializeRasterizer(QSpanData * data)3593 void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3594 {
3595 Q_Q(QRasterPaintEngine);
3596 QRasterPaintEngineState *s = q->state();
3597
3598 rasterizer->setAntialiased(s->flags.antialiased);
3599 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3600
3601 QRect clipRect(deviceRect);
3602 ProcessSpans blend;
3603 // ### get from optimized rectbased QClipData
3604
3605 const QClipData *c = clip();
3606 if (c) {
3607 const QRect r(QPoint(c->xmin, c->ymin),
3608 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3609 clipRect = clipRect.intersected(r);
3610 blend = data->blend;
3611 } else {
3612 blend = data->unclipped_blend;
3613 }
3614
3615 rasterizer->setClipRect(clipRect);
3616 rasterizer->initialize(blend, data);
3617 }
3618
rasterize(QT_FT_Outline * outline,ProcessSpans callback,QSpanData * spanData,QRasterBuffer * rasterBuffer)3619 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3620 ProcessSpans callback,
3621 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3622 {
3623 if (!callback || !outline)
3624 return;
3625
3626 Q_Q(QRasterPaintEngine);
3627 QRasterPaintEngineState *s = q->state();
3628
3629 if (!s->flags.antialiased) {
3630 initializeRasterizer(spanData);
3631
3632 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3633 ? Qt::WindingFill
3634 : Qt::OddEvenFill;
3635
3636 rasterizer->rasterize(outline, fillRule);
3637 return;
3638 }
3639
3640 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3641 }
3642
3643 extern "C" {
3644 int q_gray_rendered_spans(QT_FT_Raster raster);
3645 }
3646
alignAddress(uchar * address,quintptr alignmentMask)3647 static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3648 {
3649 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3650 }
3651
rasterize(QT_FT_Outline * outline,ProcessSpans callback,void * userData,QRasterBuffer *)3652 void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3653 ProcessSpans callback,
3654 void *userData, QRasterBuffer *)
3655 {
3656 if (!callback || !outline)
3657 return;
3658
3659 Q_Q(QRasterPaintEngine);
3660 QRasterPaintEngineState *s = q->state();
3661
3662 if (!s->flags.antialiased) {
3663 rasterizer->setAntialiased(s->flags.antialiased);
3664 rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
3665 rasterizer->setClipRect(deviceRect);
3666 rasterizer->initialize(callback, userData);
3667
3668 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3669 ? Qt::WindingFill
3670 : Qt::OddEvenFill;
3671
3672 rasterizer->rasterize(outline, fillRule);
3673 return;
3674 }
3675
3676 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3677 // minimize memory reallocations. However if initial size for
3678 // raster pool is changed for lower value, reallocations will
3679 // occur normally.
3680 int rasterPoolSize = MINIMUM_POOL_SIZE;
3681 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3682 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3683 uchar *rasterPoolOnHeap = nullptr;
3684
3685 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3686
3687 void *data = userData;
3688
3689 QT_FT_BBox clip_box = { deviceRect.x(),
3690 deviceRect.y(),
3691 deviceRect.x() + deviceRect.width(),
3692 deviceRect.y() + deviceRect.height() };
3693
3694 QT_FT_Raster_Params rasterParams;
3695 rasterParams.target = nullptr;
3696 rasterParams.source = outline;
3697 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3698 rasterParams.gray_spans = nullptr;
3699 rasterParams.black_spans = nullptr;
3700 rasterParams.bit_test = nullptr;
3701 rasterParams.bit_set = nullptr;
3702 rasterParams.user = data;
3703 rasterParams.clip_box = clip_box;
3704
3705 bool done = false;
3706 int error;
3707
3708 int rendered_spans = 0;
3709
3710 while (!done) {
3711
3712 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3713 rasterParams.gray_spans = callback;
3714 rasterParams.skip_spans = rendered_spans;
3715 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3716
3717 // Out of memory, reallocate some more and try again...
3718 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3719 rasterPoolSize *= 2;
3720 if (rasterPoolSize > 1024 * 1024) {
3721 qWarning("QPainter: Rasterization of primitive failed");
3722 break;
3723 }
3724
3725 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3726
3727 free(rasterPoolOnHeap);
3728 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3729
3730 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3731
3732 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3733
3734 qt_ft_grays_raster.raster_done(*grayRaster.data());
3735 qt_ft_grays_raster.raster_new(grayRaster.data());
3736 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3737 } else {
3738 done = true;
3739 }
3740 }
3741
3742 free(rasterPoolOnHeap);
3743 }
3744
recalculateFastImages()3745 void QRasterPaintEnginePrivate::recalculateFastImages()
3746 {
3747 Q_Q(QRasterPaintEngine);
3748 QRasterPaintEngineState *s = q->state();
3749
3750 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3751 && s->matrix.type() <= QTransform::TxShear;
3752 }
3753
canUseFastImageBlending(QPainter::CompositionMode mode,const QImage & image) const3754 bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3755 {
3756 Q_Q(const QRasterPaintEngine);
3757 const QRasterPaintEngineState *s = q->state();
3758
3759 return s->flags.fast_images
3760 && (mode == QPainter::CompositionMode_SourceOver
3761 || (mode == QPainter::CompositionMode_Source
3762 && !image.hasAlphaChannel()));
3763 }
3764
canUseImageBlitting(QPainter::CompositionMode mode,const QImage & image,const QPointF & pt,const QRectF & sr) const3765 bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image, const QPointF &pt, const QRectF &sr) const
3766 {
3767 Q_Q(const QRasterPaintEngine);
3768
3769 if (!(mode == QPainter::CompositionMode_Source
3770 || (mode == QPainter::CompositionMode_SourceOver
3771 && !image.hasAlphaChannel())))
3772 return false;
3773
3774 const QRasterPaintEngineState *s = q->state();
3775 Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate);
3776
3777 if (s->intOpacity != 256
3778 || image.depth() < 8
3779 || ((s->renderHints & (QPainter::SmoothPixmapTransform | QPainter::Antialiasing))
3780 && (!isPixelAligned(pt) || !isPixelAligned(sr))))
3781 return false;
3782
3783 QImage::Format dFormat = rasterBuffer->format;
3784 QImage::Format sFormat = image.format();
3785 // Formats must match or source format must be a subset of destination format
3786 if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha) {
3787 if ((sFormat == QImage::Format_RGB32 && dFormat == QImage::Format_ARGB32)
3788 || (sFormat == QImage::Format_RGBX8888 && dFormat == QImage::Format_RGBA8888)
3789 || (sFormat == QImage::Format_RGBX64 && dFormat == QImage::Format_RGBA64))
3790 sFormat = dFormat;
3791 else
3792 sFormat = qt_maybeAlphaVersionWithSameDepth(sFormat); // this returns premul formats
3793 }
3794 return (dFormat == sFormat);
3795 }
3796
colorizeBitmap(const QImage & image,const QColor & color)3797 QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3798 {
3799 Q_ASSERT(image.depth() == 1);
3800
3801 const QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3802 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3803
3804 QRgb fg = qPremultiply(color.rgba());
3805 QRgb bg = 0;
3806
3807 int height = sourceImage.height();
3808 int width = sourceImage.width();
3809 for (int y=0; y<height; ++y) {
3810 const uchar *source = sourceImage.constScanLine(y);
3811 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3812 if (!source || !target)
3813 QT_THROW(std::bad_alloc()); // we must have run out of memory
3814 for (int x=0; x < width; ++x)
3815 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3816 }
3817 return dest;
3818 }
3819
~QRasterBuffer()3820 QRasterBuffer::~QRasterBuffer()
3821 {
3822 }
3823
init()3824 void QRasterBuffer::init()
3825 {
3826 compositionMode = QPainter::CompositionMode_SourceOver;
3827 monoDestinationWithClut = false;
3828 destColor0 = 0;
3829 destColor1 = 0;
3830 }
3831
prepare(QImage * image)3832 QImage::Format QRasterBuffer::prepare(QImage *image)
3833 {
3834 m_buffer = (uchar *)image->bits();
3835 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3836 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3837 bytes_per_pixel = image->depth()/8;
3838 bytes_per_line = image->bytesPerLine();
3839
3840 format = image->format();
3841 if (image->depth() == 1 && image->colorTable().size() == 2) {
3842 monoDestinationWithClut = true;
3843 const QVector<QRgb> colorTable = image->colorTable();
3844 destColor0 = qPremultiply(colorTable[0]);
3845 destColor1 = qPremultiply(colorTable[1]);
3846 }
3847
3848 return format;
3849 }
3850
QClipData(int height)3851 QClipData::QClipData(int height)
3852 {
3853 clipSpanHeight = height;
3854 m_clipLines = nullptr;
3855
3856 allocated = 0;
3857 m_spans = nullptr;
3858 xmin = xmax = ymin = ymax = 0;
3859 count = 0;
3860
3861 enabled = true;
3862 hasRectClip = hasRegionClip = false;
3863 }
3864
~QClipData()3865 QClipData::~QClipData()
3866 {
3867 if (m_clipLines)
3868 free(m_clipLines);
3869 if (m_spans)
3870 free(m_spans);
3871 }
3872
initialize()3873 void QClipData::initialize()
3874 {
3875 if (m_spans)
3876 return;
3877
3878 if (!m_clipLines)
3879 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3880
3881 Q_CHECK_PTR(m_clipLines);
3882 QT_TRY {
3883 allocated = clipSpanHeight;
3884 QT_TRY {
3885 if (hasRegionClip) {
3886 const auto rects = clipRegion.begin();
3887 const int numRects = clipRegion.rectCount();
3888 const int maxSpans = (ymax - ymin) * numRects;
3889 allocated = qMax(allocated, maxSpans);
3890 m_spans = (QSpan *)malloc(allocated * sizeof(QSpan));
3891 Q_CHECK_PTR(m_spans);
3892
3893 int y = 0;
3894 int firstInBand = 0;
3895 count = 0;
3896 while (firstInBand < numRects) {
3897 const int currMinY = rects[firstInBand].y();
3898 const int currMaxY = currMinY + rects[firstInBand].height();
3899
3900 while (y < currMinY) {
3901 m_clipLines[y].spans = nullptr;
3902 m_clipLines[y].count = 0;
3903 ++y;
3904 }
3905
3906 int lastInBand = firstInBand;
3907 while (lastInBand + 1 < numRects && rects[lastInBand+1].top() == y)
3908 ++lastInBand;
3909
3910 while (y < currMaxY) {
3911
3912 m_clipLines[y].spans = m_spans + count;
3913 m_clipLines[y].count = lastInBand - firstInBand + 1;
3914
3915 for (int r = firstInBand; r <= lastInBand; ++r) {
3916 const QRect &currRect = rects[r];
3917 QSpan *span = m_spans + count;
3918 span->x = currRect.x();
3919 span->len = currRect.width();
3920 span->y = y;
3921 span->coverage = 255;
3922 ++count;
3923 }
3924 ++y;
3925 }
3926
3927 firstInBand = lastInBand + 1;
3928 }
3929
3930 Q_ASSERT(count <= allocated);
3931
3932 while (y < clipSpanHeight) {
3933 m_clipLines[y].spans = nullptr;
3934 m_clipLines[y].count = 0;
3935 ++y;
3936 }
3937
3938 return;
3939 }
3940
3941 m_spans = (QSpan *)malloc(allocated * sizeof(QSpan));
3942 Q_CHECK_PTR(m_spans);
3943
3944 if (hasRectClip) {
3945 int y = 0;
3946 while (y < ymin) {
3947 m_clipLines[y].spans = nullptr;
3948 m_clipLines[y].count = 0;
3949 ++y;
3950 }
3951
3952 const int len = clipRect.width();
3953 count = 0;
3954 while (y < ymax) {
3955 QSpan *span = m_spans + count;
3956 span->x = xmin;
3957 span->len = len;
3958 span->y = y;
3959 span->coverage = 255;
3960 ++count;
3961
3962 m_clipLines[y].spans = span;
3963 m_clipLines[y].count = 1;
3964 ++y;
3965 }
3966
3967 while (y < clipSpanHeight) {
3968 m_clipLines[y].spans = nullptr;
3969 m_clipLines[y].count = 0;
3970 ++y;
3971 }
3972 }
3973 } QT_CATCH(...) {
3974 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3975 m_spans = nullptr;
3976 QT_RETHROW;
3977 }
3978 } QT_CATCH(...) {
3979 free(m_clipLines); // same for clipLines
3980 m_clipLines = nullptr;
3981 QT_RETHROW;
3982 }
3983 }
3984
fixup()3985 void QClipData::fixup()
3986 {
3987 Q_ASSERT(m_spans);
3988
3989 if (count == 0) {
3990 ymin = ymax = xmin = xmax = 0;
3991 return;
3992 }
3993
3994 int y = -1;
3995 ymin = m_spans[0].y;
3996 ymax = m_spans[count-1].y + 1;
3997 xmin = INT_MAX;
3998 xmax = 0;
3999
4000 const int firstLeft = m_spans[0].x;
4001 const int firstRight = m_spans[0].x + m_spans[0].len;
4002 bool isRect = true;
4003
4004 for (int i = 0; i < count; ++i) {
4005 QT_FT_Span_& span = m_spans[i];
4006
4007 if (span.y != y) {
4008 if (span.y != y + 1 && y != -1)
4009 isRect = false;
4010 y = span.y;
4011 m_clipLines[y].spans = &span;
4012 m_clipLines[y].count = 1;
4013 } else
4014 ++m_clipLines[y].count;
4015
4016 const int spanLeft = span.x;
4017 const int spanRight = spanLeft + span.len;
4018
4019 if (spanLeft < xmin)
4020 xmin = spanLeft;
4021
4022 if (spanRight > xmax)
4023 xmax = spanRight;
4024
4025 if (spanLeft != firstLeft || spanRight != firstRight)
4026 isRect = false;
4027 }
4028
4029 if (isRect) {
4030 hasRectClip = true;
4031 clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
4032 }
4033 }
4034
4035 /*
4036 Convert \a rect to clip spans.
4037 */
setClipRect(const QRect & rect)4038 void QClipData::setClipRect(const QRect &rect)
4039 {
4040 if (hasRectClip && rect == clipRect)
4041 return;
4042
4043 // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
4044 hasRectClip = true;
4045 hasRegionClip = false;
4046 clipRect = rect;
4047
4048 xmin = rect.x();
4049 xmax = rect.x() + rect.width();
4050 ymin = qMin(rect.y(), clipSpanHeight);
4051 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
4052
4053 if (m_spans) {
4054 free(m_spans);
4055 m_spans = nullptr;
4056 }
4057
4058 // qDebug() << xmin << xmax << ymin << ymax;
4059 }
4060
4061 /*
4062 Convert \a region to clip spans.
4063 */
setClipRegion(const QRegion & region)4064 void QClipData::setClipRegion(const QRegion ®ion)
4065 {
4066 if (region.rectCount() == 1) {
4067 setClipRect(region.boundingRect());
4068 return;
4069 }
4070
4071 hasRegionClip = true;
4072 hasRectClip = false;
4073 clipRegion = region;
4074
4075 { // set bounding rect
4076 const QRect rect = region.boundingRect();
4077 xmin = rect.x();
4078 xmax = rect.x() + rect.width();
4079 ymin = rect.y();
4080 ymax = rect.y() + rect.height();
4081 }
4082
4083 if (m_spans) {
4084 free(m_spans);
4085 m_spans = nullptr;
4086 }
4087
4088 }
4089
4090 /*!
4091 \internal
4092 spans must be sorted on y
4093 */
qt_intersect_spans(const QClipData * clip,int * currentClip,const QSpan * spans,const QSpan * end,QSpan ** outSpans,int available)4094 static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
4095 const QSpan *spans, const QSpan *end,
4096 QSpan **outSpans, int available)
4097 {
4098 const_cast<QClipData *>(clip)->initialize();
4099
4100 QSpan *out = *outSpans;
4101
4102 const QSpan *clipSpans = clip->m_spans + *currentClip;
4103 const QSpan *clipEnd = clip->m_spans + clip->count;
4104
4105 while (available && spans < end ) {
4106 if (clipSpans >= clipEnd) {
4107 spans = end;
4108 break;
4109 }
4110 if (clipSpans->y > spans->y) {
4111 ++spans;
4112 continue;
4113 }
4114 if (spans->y != clipSpans->y) {
4115 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4116 clipSpans = clip->m_clipLines[spans->y].spans;
4117 else
4118 ++clipSpans;
4119 continue;
4120 }
4121 Q_ASSERT(spans->y == clipSpans->y);
4122
4123 int sx1 = spans->x;
4124 int sx2 = sx1 + spans->len;
4125 int cx1 = clipSpans->x;
4126 int cx2 = cx1 + clipSpans->len;
4127
4128 if (cx1 < sx1 && cx2 < sx1) {
4129 ++clipSpans;
4130 continue;
4131 } else if (sx1 < cx1 && sx2 < cx1) {
4132 ++spans;
4133 continue;
4134 }
4135 int x = qMax(sx1, cx1);
4136 int len = qMin(sx2, cx2) - x;
4137 if (len) {
4138 out->x = qMax(sx1, cx1);
4139 out->len = qMin(sx2, cx2) - out->x;
4140 out->y = spans->y;
4141 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4142 ++out;
4143 --available;
4144 }
4145 if (sx2 < cx2) {
4146 ++spans;
4147 } else {
4148 ++clipSpans;
4149 }
4150 }
4151
4152 *outSpans = out;
4153 *currentClip = clipSpans - clip->m_spans;
4154 return spans;
4155 }
4156
qt_span_fill_clipped(int spanCount,const QSpan * spans,void * userData)4157 static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
4158 {
4159 // qDebug() << "qt_span_fill_clipped" << spanCount;
4160 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4161
4162 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4163
4164 const int NSPANS = 256;
4165 QSpan cspans[NSPANS];
4166 int currentClip = 0;
4167 const QSpan *end = spans + spanCount;
4168 while (spans < end) {
4169 QSpan *clipped = cspans;
4170 spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS);
4171 // qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4172 // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4173
4174 if (clipped - cspans)
4175 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4176 }
4177 }
4178
4179 /*
4180 \internal
4181 Clip spans to \a{clip}-rectangle.
4182 Returns number of unclipped spans
4183 */
qt_intersect_spans(QT_FT_Span * & spans,int numSpans,const QRect & clip)4184 static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
4185 const QRect &clip)
4186 {
4187 const short minx = clip.left();
4188 const short miny = clip.top();
4189 const short maxx = clip.right();
4190 const short maxy = clip.bottom();
4191
4192 QT_FT_Span *end = spans + numSpans;
4193 while (spans < end) {
4194 if (spans->y >= miny)
4195 break;
4196 ++spans;
4197 }
4198
4199 QT_FT_Span *s = spans;
4200 while (s < end) {
4201 if (s->y > maxy)
4202 break;
4203 if (s->x > maxx || s->x + s->len <= minx) {
4204 s->len = 0;
4205 ++s;
4206 continue;
4207 }
4208 if (s->x < minx) {
4209 s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
4210 s->x = minx;
4211 } else {
4212 s->len = qMin(s->len, ushort(maxx - s->x + 1));
4213 }
4214 ++s;
4215 }
4216
4217 return s - spans;
4218 }
4219
4220
qt_span_fill_clipRect(int count,const QSpan * spans,void * userData)4221 static void qt_span_fill_clipRect(int count, const QSpan *spans,
4222 void *userData)
4223 {
4224 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4225 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4226
4227 Q_ASSERT(fillData->clip);
4228 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4229
4230 QSpan *s = const_cast<QSpan *>(spans);
4231 // hw: check if this const_cast<> is safe!!!
4232 count = qt_intersect_spans(s, count,
4233 fillData->clip->clipRect);
4234 if (count > 0)
4235 fillData->unclipped_blend(count, s, fillData);
4236 }
4237
qt_span_clip(int count,const QSpan * spans,void * userData)4238 static void qt_span_clip(int count, const QSpan *spans, void *userData)
4239 {
4240 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4241
4242 // qDebug() << " qt_span_clip: " << count << clipData->operation;
4243 // for (int i = 0; i < qMin(count, 10); ++i) {
4244 // qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4245 // }
4246
4247 switch (clipData->operation) {
4248
4249 case Qt::IntersectClip:
4250 {
4251 QClipData *newClip = clipData->newClip;
4252 newClip->initialize();
4253
4254 int currentClip = 0;
4255 const QSpan *end = spans + count;
4256 while (spans < end) {
4257 QSpan *newspans = newClip->m_spans + newClip->count;
4258 spans = qt_intersect_spans(clipData->oldClip, ¤tClip, spans, end,
4259 &newspans, newClip->allocated - newClip->count);
4260 newClip->count = newspans - newClip->m_spans;
4261 if (spans < end) {
4262 newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
4263 newClip->allocated *= 2;
4264 }
4265 }
4266 }
4267 break;
4268
4269 case Qt::ReplaceClip:
4270 clipData->newClip->appendSpans(spans, count);
4271 break;
4272 case Qt::NoClip:
4273 break;
4274 }
4275 }
4276
4277 class QGradientCache
4278 {
4279 public:
4280 struct CacheInfo : QSpanData::Pinnable
4281 {
CacheInfoQGradientCache::CacheInfo4282 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4283 stops(std::move(s)), opacity(op), interpolationMode(mode) {}
4284 QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE];
4285 QRgb buffer32[GRADIENT_STOPTABLE_SIZE];
4286 QGradientStops stops;
4287 int opacity;
4288 QGradient::InterpolationMode interpolationMode;
4289 };
4290
4291 typedef QMultiHash<quint64, QSharedPointer<const CacheInfo>> QGradientColorTableHash;
4292
getBuffer(const QGradient & gradient,int opacity)4293 inline QSharedPointer<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
4294 quint64 hash_val = 0;
4295
4296 const QGradientStops stops = gradient.stops();
4297 for (int i = 0; i < stops.size() && i <= 2; i++)
4298 hash_val += stops[i].second.rgba64();
4299
4300 QMutexLocker lock(&mutex);
4301 QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4302
4303 if (it == cache.constEnd())
4304 return addCacheElement(hash_val, gradient, opacity);
4305 else {
4306 do {
4307 const auto &cache_info = it.value();
4308 if (cache_info->stops == stops && cache_info->opacity == opacity && cache_info->interpolationMode == gradient.interpolationMode())
4309 return cache_info;
4310 ++it;
4311 } while (it != cache.constEnd() && it.key() == hash_val);
4312 // an exact match for these stops and opacity was not found, create new cache
4313 return addCacheElement(hash_val, gradient, opacity);
4314 }
4315 }
4316
paletteSize() const4317 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4318 protected:
maxCacheSize() const4319 inline int maxCacheSize() const { return 60; }
4320 inline void generateGradientColorTable(const QGradient& g,
4321 QRgba64 *colorTable,
4322 int size, int opacity) const;
addCacheElement(quint64 hash_val,const QGradient & gradient,int opacity)4323 QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4324 if (cache.size() == maxCacheSize()) {
4325 // may remove more than 1, but OK
4326 cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize())));
4327 }
4328 auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode());
4329 generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
4330 for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
4331 cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
4332 return cache.insert(hash_val, cache_entry).value();
4333 }
4334
4335 QGradientColorTableHash cache;
4336 QMutex mutex;
4337 };
4338
generateGradientColorTable(const QGradient & gradient,QRgba64 * colorTable,int size,int opacity) const4339 void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
4340 {
4341 const QGradientStops stops = gradient.stops();
4342 int stopCount = stops.count();
4343 Q_ASSERT(stopCount > 0);
4344
4345 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4346
4347 if (stopCount == 2) {
4348 QRgba64 first_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4349 QRgba64 second_color = combineAlpha256(stops[1].second.rgba64(), opacity);
4350
4351 qreal first_stop = stops[0].first;
4352 qreal second_stop = stops[1].first;
4353
4354 if (second_stop < first_stop) {
4355 quint64 tmp = first_color;
4356 first_color = second_color;
4357 second_color = tmp;
4358 qSwap(first_stop, second_stop);
4359 }
4360
4361 if (colorInterpolation) {
4362 first_color = qPremultiply(first_color);
4363 second_color = qPremultiply(second_color);
4364 }
4365
4366 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4367 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4368
4369 uint red_first = uint(first_color.red()) << 16;
4370 uint green_first = uint(first_color.green()) << 16;
4371 uint blue_first = uint(first_color.blue()) << 16;
4372 uint alpha_first = uint(first_color.alpha()) << 16;
4373
4374 uint red_second = uint(second_color.red()) << 16;
4375 uint green_second = uint(second_color.green()) << 16;
4376 uint blue_second = uint(second_color.blue()) << 16;
4377 uint alpha_second = uint(second_color.alpha()) << 16;
4378
4379 int i = 0;
4380 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4381 if (colorInterpolation)
4382 colorTable[i] = first_color;
4383 else
4384 colorTable[i] = qPremultiply(first_color);
4385 }
4386
4387 if (i < second_index) {
4388 qreal reciprocal = qreal(1) / (second_index - first_index);
4389
4390 int red_delta = qRound((qreal(red_second) - red_first) * reciprocal);
4391 int green_delta = qRound((qreal(green_second) - green_first) * reciprocal);
4392 int blue_delta = qRound((qreal(blue_second) - blue_first) * reciprocal);
4393 int alpha_delta = qRound((qreal(alpha_second) - alpha_first) * reciprocal);
4394
4395 // rounding
4396 red_first += 1 << 15;
4397 green_first += 1 << 15;
4398 blue_first += 1 << 15;
4399 alpha_first += 1 << 15;
4400
4401 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4402 red_first += red_delta;
4403 green_first += green_delta;
4404 blue_first += blue_delta;
4405 alpha_first += alpha_delta;
4406
4407 const QRgba64 color = qRgba64(red_first >> 16, green_first >> 16, blue_first >> 16, alpha_first >> 16);
4408
4409 if (colorInterpolation)
4410 colorTable[i] = color;
4411 else
4412 colorTable[i] = qPremultiply(color);
4413 }
4414 }
4415
4416 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4417 if (colorInterpolation)
4418 colorTable[i] = second_color;
4419 else
4420 colorTable[i] = qPremultiply(second_color);
4421 }
4422
4423 return;
4424 }
4425
4426 QRgba64 current_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4427 if (stopCount == 1) {
4428 current_color = qPremultiply(current_color);
4429 for (int i = 0; i < size; ++i)
4430 colorTable[i] = current_color;
4431 return;
4432 }
4433
4434 // The position where the gradient begins and ends
4435 qreal begin_pos = stops[0].first;
4436 qreal end_pos = stops[stopCount-1].first;
4437
4438 int pos = 0; // The position in the color table.
4439 QRgba64 next_color;
4440
4441 qreal incr = 1 / qreal(size); // the double increment.
4442 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4443
4444 // Up to first point
4445 colorTable[pos++] = qPremultiply(current_color);
4446 while (dpos <= begin_pos) {
4447 colorTable[pos] = colorTable[pos - 1];
4448 ++pos;
4449 dpos += incr;
4450 }
4451
4452 int current_stop = 0; // We always interpolate between current and current + 1.
4453
4454 qreal t; // position between current left and right stops
4455 qreal t_delta; // the t increment per entry in the color table
4456
4457 if (dpos < end_pos) {
4458 // Gradient area
4459 while (dpos > stops[current_stop+1].first)
4460 ++current_stop;
4461
4462 if (current_stop != 0)
4463 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4464 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4465
4466 if (colorInterpolation) {
4467 current_color = qPremultiply(current_color);
4468 next_color = qPremultiply(next_color);
4469 }
4470
4471 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4472 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4473 t = (dpos - stops[current_stop].first) * c;
4474 t_delta = incr * c;
4475
4476 while (true) {
4477 Q_ASSERT(current_stop < stopCount);
4478
4479 int dist = qRound(t);
4480 int idist = 256 - dist;
4481
4482 if (colorInterpolation)
4483 colorTable[pos] = interpolate256(current_color, idist, next_color, dist);
4484 else
4485 colorTable[pos] = qPremultiply(interpolate256(current_color, idist, next_color, dist));
4486
4487 ++pos;
4488 dpos += incr;
4489
4490 if (dpos >= end_pos)
4491 break;
4492
4493 t += t_delta;
4494
4495 int skip = 0;
4496 while (dpos > stops[current_stop+skip+1].first)
4497 ++skip;
4498
4499 if (skip != 0) {
4500 current_stop += skip;
4501 if (skip == 1)
4502 current_color = next_color;
4503 else
4504 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4505 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4506
4507 if (colorInterpolation) {
4508 if (skip != 1)
4509 current_color = qPremultiply(current_color);
4510 next_color = qPremultiply(next_color);
4511 }
4512
4513 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4514 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4515 t = (dpos - stops[current_stop].first) * c;
4516 t_delta = incr * c;
4517 }
4518 }
4519 }
4520
4521 // After last point
4522 current_color = qPremultiply(combineAlpha256(stops[stopCount - 1].second.rgba64(), opacity));
4523 while (pos < size - 1) {
4524 colorTable[pos] = current_color;
4525 ++pos;
4526 }
4527
4528 // Make sure the last color stop is represented at the end of the table
4529 colorTable[size - 1] = current_color;
4530 }
4531
Q_GLOBAL_STATIC(QGradientCache,qt_gradient_cache)4532 Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4533
4534
4535 void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4536 {
4537 rasterBuffer = rb;
4538 type = None;
4539 txop = 0;
4540 bilinear = false;
4541 m11 = m22 = m33 = 1.;
4542 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4543 clip = pe ? pe->d_func()->clip() : nullptr;
4544 }
4545
4546 Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4547
setup(const QBrush & brush,int alpha,QPainter::CompositionMode compositionMode)4548 void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
4549 {
4550 Qt::BrushStyle brushStyle = qbrush_style(brush);
4551 cachedGradient.reset();
4552 switch (brushStyle) {
4553 case Qt::SolidPattern: {
4554 type = Solid;
4555 QColor c = qbrush_color(brush);
4556 solidColor = qPremultiply(combineAlpha256(c.rgba64(), alpha));
4557 if (solidColor.isTransparent() && compositionMode == QPainter::CompositionMode_SourceOver)
4558 type = None;
4559 break;
4560 }
4561
4562 case Qt::LinearGradientPattern:
4563 {
4564 type = LinearGradient;
4565 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4566 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4567
4568 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4569 gradient.colorTable32 = cacheInfo->buffer32;
4570 #if QT_CONFIG(raster_64bit)
4571 gradient.colorTable64 = cacheInfo->buffer64;
4572 #endif
4573 cachedGradient = std::move(cacheInfo);
4574
4575 gradient.spread = g->spread();
4576
4577 QLinearGradientData &linearData = gradient.linear;
4578
4579 linearData.origin.x = g->start().x();
4580 linearData.origin.y = g->start().y();
4581 linearData.end.x = g->finalStop().x();
4582 linearData.end.y = g->finalStop().y();
4583 break;
4584 }
4585
4586 case Qt::RadialGradientPattern:
4587 {
4588 type = RadialGradient;
4589 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4590 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4591
4592 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4593 gradient.colorTable32 = cacheInfo->buffer32;
4594 #if QT_CONFIG(raster_64bit)
4595 gradient.colorTable64 = cacheInfo->buffer64;
4596 #endif
4597 cachedGradient = std::move(cacheInfo);
4598
4599 gradient.spread = g->spread();
4600
4601 QRadialGradientData &radialData = gradient.radial;
4602
4603 QPointF center = g->center();
4604 radialData.center.x = center.x();
4605 radialData.center.y = center.y();
4606 radialData.center.radius = g->centerRadius();
4607 QPointF focal = g->focalPoint();
4608 radialData.focal.x = focal.x();
4609 radialData.focal.y = focal.y();
4610 radialData.focal.radius = g->focalRadius();
4611 }
4612 break;
4613
4614 case Qt::ConicalGradientPattern:
4615 {
4616 type = ConicalGradient;
4617 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4618 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4619
4620 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4621 gradient.colorTable32 = cacheInfo->buffer32;
4622 #if QT_CONFIG(raster_64bit)
4623 gradient.colorTable64 = cacheInfo->buffer64;
4624 #endif
4625 cachedGradient = std::move(cacheInfo);
4626
4627 gradient.spread = QGradient::RepeatSpread;
4628
4629 QConicalGradientData &conicalData = gradient.conical;
4630
4631 QPointF center = g->center();
4632 conicalData.center.x = center.x();
4633 conicalData.center.y = center.y();
4634 conicalData.angle = qDegreesToRadians(g->angle());
4635 }
4636 break;
4637
4638 case Qt::Dense1Pattern:
4639 case Qt::Dense2Pattern:
4640 case Qt::Dense3Pattern:
4641 case Qt::Dense4Pattern:
4642 case Qt::Dense5Pattern:
4643 case Qt::Dense6Pattern:
4644 case Qt::Dense7Pattern:
4645 case Qt::HorPattern:
4646 case Qt::VerPattern:
4647 case Qt::CrossPattern:
4648 case Qt::BDiagPattern:
4649 case Qt::FDiagPattern:
4650 case Qt::DiagCrossPattern:
4651 type = Texture;
4652 if (!tempImage)
4653 tempImage = new QImage();
4654 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4655 initTexture(tempImage, alpha, QTextureData::Tiled);
4656 break;
4657 case Qt::TexturePattern:
4658 type = Texture;
4659 if (!tempImage)
4660 tempImage = new QImage();
4661
4662 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4663 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4664 else
4665 *tempImage = brush.textureImage();
4666 initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4667 break;
4668
4669 case Qt::NoBrush:
4670 default:
4671 type = None;
4672 break;
4673 }
4674 adjustSpanMethods();
4675 }
4676
adjustSpanMethods()4677 void QSpanData::adjustSpanMethods()
4678 {
4679 bitmapBlit = nullptr;
4680 alphamapBlit = nullptr;
4681 alphaRGBBlit = nullptr;
4682
4683 fillRect = nullptr;
4684
4685 switch(type) {
4686 case None:
4687 unclipped_blend = nullptr;
4688 break;
4689 case Solid: {
4690 const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format];
4691 unclipped_blend = drawHelper.blendColor;
4692 bitmapBlit = drawHelper.bitmapBlit;
4693 alphamapBlit = drawHelper.alphamapBlit;
4694 alphaRGBBlit = drawHelper.alphaRGBBlit;
4695 fillRect = drawHelper.fillRect;
4696 break;
4697 }
4698 case LinearGradient:
4699 case RadialGradient:
4700 case ConicalGradient:
4701 unclipped_blend = qBlendGradient;
4702 break;
4703 case Texture:
4704 unclipped_blend = qBlendTexture;
4705 if (!texture.imageData)
4706 unclipped_blend = nullptr;
4707
4708 break;
4709 }
4710 // setup clipping
4711 if (!unclipped_blend) {
4712 blend = nullptr;
4713 } else if (!clip) {
4714 blend = unclipped_blend;
4715 } else if (clip->hasRectClip) {
4716 blend = clip->clipRect.isEmpty() ? nullptr : qt_span_fill_clipRect;
4717 } else {
4718 blend = qt_span_fill_clipped;
4719 }
4720 }
4721
setupMatrix(const QTransform & matrix,int bilin)4722 void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4723 {
4724 QTransform delta;
4725 // make sure we round off correctly in qdrawhelper.cpp
4726 delta.translate(1.0 / 65536, 1.0 / 65536);
4727
4728 QTransform inv = (delta * matrix).inverted();
4729 m11 = inv.m11();
4730 m12 = inv.m12();
4731 m13 = inv.m13();
4732 m21 = inv.m21();
4733 m22 = inv.m22();
4734 m23 = inv.m23();
4735 m33 = inv.m33();
4736 dx = inv.dx();
4737 dy = inv.dy();
4738 txop = inv.type();
4739 bilinear = bilin;
4740
4741 const bool affine = inv.isAffine();
4742 const qreal f1 = m11 * m11 + m21 * m21;
4743 const qreal f2 = m12 * m12 + m22 * m22;
4744 fast_matrix = affine
4745 && f1 < 1e4
4746 && f2 < 1e4
4747 && f1 > (1.0 / 65536)
4748 && f2 > (1.0 / 65536)
4749 && qAbs(dx) < 1e4
4750 && qAbs(dy) < 1e4;
4751
4752 adjustSpanMethods();
4753 }
4754
initTexture(const QImage * image,int alpha,QTextureData::Type _type,const QRect & sourceRect)4755 void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4756 {
4757 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4758 if (!d || d->height == 0) {
4759 texture.imageData = nullptr;
4760 texture.width = 0;
4761 texture.height = 0;
4762 texture.x1 = 0;
4763 texture.y1 = 0;
4764 texture.x2 = 0;
4765 texture.y2 = 0;
4766 texture.bytesPerLine = 0;
4767 texture.format = QImage::Format_Invalid;
4768 texture.colorTable = nullptr;
4769 texture.hasAlpha = alpha != 256;
4770 } else {
4771 texture.imageData = d->data;
4772 texture.width = d->width;
4773 texture.height = d->height;
4774
4775 if (sourceRect.isNull()) {
4776 texture.x1 = 0;
4777 texture.y1 = 0;
4778 texture.x2 = texture.width;
4779 texture.y2 = texture.height;
4780 } else {
4781 texture.x1 = sourceRect.x();
4782 texture.y1 = sourceRect.y();
4783 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4784 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4785 }
4786
4787 texture.bytesPerLine = d->bytes_per_line;
4788
4789 texture.format = d->format;
4790 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : nullptr;
4791 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4792 }
4793 texture.const_alpha = alpha;
4794 texture.type = _type;
4795
4796 adjustSpanMethods();
4797 }
4798
4799 /*!
4800 \internal
4801 \a x and \a y is relative to the midpoint of \a rect.
4802 */
drawEllipsePoints(int x,int y,int length,const QRect & rect,const QRect & clip,ProcessSpans pen_func,ProcessSpans brush_func,QSpanData * pen_data,QSpanData * brush_data)4803 static inline void drawEllipsePoints(int x, int y, int length,
4804 const QRect &rect,
4805 const QRect &clip,
4806 ProcessSpans pen_func, ProcessSpans brush_func,
4807 QSpanData *pen_data, QSpanData *brush_data)
4808 {
4809 if (length == 0)
4810 return;
4811
4812 QT_FT_Span _outline[4];
4813 QT_FT_Span *outline = _outline;
4814 const int midx = rect.x() + (rect.width() + 1) / 2;
4815 const int midy = rect.y() + (rect.height() + 1) / 2;
4816
4817 x = x + midx;
4818 y = midy - y;
4819
4820 // topleft
4821 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4822 outline[0].len = qMin(length, x - outline[0].x);
4823 outline[0].y = y;
4824 outline[0].coverage = 255;
4825
4826 // topright
4827 outline[1].x = x;
4828 outline[1].len = length;
4829 outline[1].y = y;
4830 outline[1].coverage = 255;
4831
4832 // bottomleft
4833 outline[2].x = outline[0].x;
4834 outline[2].len = outline[0].len;
4835 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4836 outline[2].coverage = 255;
4837
4838 // bottomright
4839 outline[3].x = x;
4840 outline[3].len = length;
4841 outline[3].y = outline[2].y;
4842 outline[3].coverage = 255;
4843
4844 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4845 QT_FT_Span _fill[2];
4846 QT_FT_Span *fill = _fill;
4847
4848 // top fill
4849 fill[0].x = outline[0].x + outline[0].len - 1;
4850 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4851 fill[0].y = outline[1].y;
4852 fill[0].coverage = 255;
4853
4854 // bottom fill
4855 fill[1].x = outline[2].x + outline[2].len - 1;
4856 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4857 fill[1].y = outline[3].y;
4858 fill[1].coverage = 255;
4859
4860 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4861 n = qt_intersect_spans(fill, n, clip);
4862 if (n > 0)
4863 brush_func(n, fill, brush_data);
4864 }
4865 if (pen_func) {
4866 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4867 n = qt_intersect_spans(outline, n, clip);
4868 if (n > 0)
4869 pen_func(n, outline, pen_data);
4870 }
4871 }
4872
4873 /*!
4874 \internal
4875 Draws an ellipse using the integer point midpoint algorithm.
4876 */
drawEllipse_midpoint_i(const QRect & rect,const QRect & clip,ProcessSpans pen_func,ProcessSpans brush_func,QSpanData * pen_data,QSpanData * brush_data)4877 static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4878 ProcessSpans pen_func, ProcessSpans brush_func,
4879 QSpanData *pen_data, QSpanData *brush_data)
4880 {
4881 const qreal a = qreal(rect.width()) / 2;
4882 const qreal b = qreal(rect.height()) / 2;
4883 qreal d = b*b - (a*a*b) + 0.25*a*a;
4884
4885 int x = 0;
4886 int y = (rect.height() + 1) / 2;
4887 int startx = x;
4888
4889 // region 1
4890 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4891 if (d < 0) { // select E
4892 d += b*b*(2*x + 3);
4893 ++x;
4894 } else { // select SE
4895 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4896 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4897 pen_func, brush_func, pen_data, brush_data);
4898 startx = ++x;
4899 --y;
4900 }
4901 }
4902 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4903 pen_func, brush_func, pen_data, brush_data);
4904
4905 // region 2
4906 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4907 const int miny = rect.height() & 0x1;
4908 while (y > miny) {
4909 if (d < 0) { // select SE
4910 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4911 ++x;
4912 } else { // select S
4913 d += a*a*(-2*y + 3);
4914 }
4915 --y;
4916 drawEllipsePoints(x, y, 1, rect, clip,
4917 pen_func, brush_func, pen_data, brush_data);
4918 }
4919 }
4920
4921 /*!
4922 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4923 \overload
4924 \reimp
4925 */
4926
4927
4928 #ifdef QT_DEBUG_DRAW
dumpClip(int width,int height,const QClipData * clip)4929 void dumpClip(int width, int height, const QClipData *clip)
4930 {
4931 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4932 clipImg.fill(0xffff0000);
4933
4934 int x0 = width;
4935 int x1 = 0;
4936 int y0 = height;
4937 int y1 = 0;
4938
4939 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4940
4941 for (int i = 0; i < clip->count; ++i) {
4942 const QSpan *span = ((QClipData *) clip)->spans() + i;
4943 for (int j = 0; j < span->len; ++j)
4944 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4945 x0 = qMin(x0, int(span->x));
4946 x1 = qMax(x1, int(span->x + span->len - 1));
4947
4948 y0 = qMin(y0, int(span->y));
4949 y1 = qMax(y1, int(span->y));
4950 }
4951
4952 static int counter = 0;
4953
4954 Q_ASSERT(y0 >= 0);
4955 Q_ASSERT(x0 >= 0);
4956 Q_ASSERT(y1 >= 0);
4957 Q_ASSERT(x1 >= 0);
4958
4959 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4960 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
4961 }
4962 #endif
4963
4964
4965 QT_END_NAMESPACE
4966