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 &region, 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(&copy);
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 &region)
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, &currentClip, 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, &currentClip, 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