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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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