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

source code of qtbase/src/gui/painting/qpaintengine_raster.cpp