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 Q_DECL_UNINITIALIZED 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 const QPointF delta(width / 2, 0);
1588 d->rasterizer->rasterizeLine(a: line.p1() - delta, b: line.p1() + delta, width: 1);
1589 }
1590 continue;
1591 }
1592
1593 if (qpen_style(p: s->lastPen) == Qt::SolidLine) {
1594 d->rasterizer->rasterizeLine(a: line.p1(), b: line.p2(),
1595 width: width / line.length(),
1596 squareCap: s->lastPen.capStyle() == Qt::SquareCap);
1597 } else {
1598 // LinesHint means each line is distinct, so restart dashing
1599 int dIndex = dashIndex;
1600 qreal dOffset = dashOffset;
1601 bool inD = inDash;
1602 d->rasterizeLine_dashed(line, width, dashIndex: &dIndex, dashOffset: &dOffset, inDash: &inD);
1603 }
1604 }
1605 }
1606 else
1607 QPaintEngineEx::stroke(path, pen);
1608}
1609
1610QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1611{
1612 int x1 = qRound(d: rect.x());
1613 int y1 = qRound(d: rect.y());
1614 int x2 = qRound(d: rect.right());
1615 int y2 = qRound(d: rect.bottom());
1616
1617 if (x2 < x1)
1618 qSwap(value1&: x1, value2&: x2);
1619 if (y2 < y1)
1620 qSwap(value1&: y1, value2&: y2);
1621
1622 return QRect(x1, y1, x2 - x1, y2 - y1);
1623}
1624
1625/*!
1626 \internal
1627*/
1628void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1629{
1630 if (path.isEmpty())
1631 return;
1632#ifdef QT_DEBUG_DRAW
1633 QRectF rf = path.controlPointRect();
1634 qDebug() << "QRasterPaintEngine::fill(): "
1635 << "size=" << path.elementCount()
1636 << ", hints=" << Qt::hex << path.hints()
1637 << rf << brush;
1638#endif
1639
1640 Q_D(QRasterPaintEngine);
1641 QRasterPaintEngineState *s = state();
1642
1643 ensureBrush(brush);
1644 if (!s->brushData.blend)
1645 return;
1646
1647 if (path.shape() == QVectorPath::RectangleHint) {
1648 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1649 const qreal *p = path.points();
1650 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1651 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1652 fillRect_normalized(r: toNormalizedFillRect(rect: QRectF(tl, br)), data: &s->brushData, pe: d);
1653 return;
1654 }
1655 ensureRasterState();
1656 if (s->flags.tx_noshear) {
1657 d->initializeRasterizer(data: &s->brushData);
1658 // ### Is normalizing really necessary here?
1659 const qreal *p = path.points();
1660 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1661 if (!r.isEmpty()) {
1662 const QPointF a = s->matrix.map(p: (r.topLeft() + r.bottomLeft()) * 0.5f);
1663 const QPointF b = s->matrix.map(p: (r.topRight() + r.bottomRight()) * 0.5f);
1664 d->rasterizer->rasterizeLine(a, b, width: r.height() / r.width());
1665 }
1666 return;
1667 }
1668 }
1669
1670 // ### Optimize for non transformed ellipses and rectangles...
1671 QRectF cpRect = path.controlPointRect();
1672 const QRectF pathDeviceRect = s->matrix.mapRect(cpRect);
1673 // Skip paths that by conservative estimates are completely outside the paint device.
1674 if (!pathDeviceRect.intersects(r: QRectF(d->deviceRect)) || !pathDeviceRect.isValid())
1675 return;
1676
1677 ProcessSpans blend = d->getBrushFunc(rect: pathDeviceRect, data: &s->brushData);
1678
1679 // ### Falcon
1680// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1681// || deviceRect.right() > QT_RASTER_COORD_LIMIT
1682// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1683// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1684
1685 // ### Falonc: implement....
1686// if (!s->flags.antialiased && !do_clip) {
1687// d->initializeRasterizer(&s->brushData);
1688// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1689// return;
1690// }
1691
1692 ensureOutlineMapper();
1693 d->rasterize(outline: d->outlineMapper->convertPath(path), callback: blend, spanData: &s->brushData, rasterBuffer: d->rasterBuffer.data());
1694}
1695
1696void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1697{
1698 Q_D(QRasterPaintEngine);
1699 QRasterPaintEngineState *s = state();
1700
1701 if (!s->flags.antialiased) {
1702 uint txop = s->matrix.type();
1703 if (txop == QTransform::TxNone) {
1704 fillRect_normalized(r: toNormalizedFillRect(rect: r), data, pe: d);
1705 return;
1706 } else if (txop == QTransform::TxTranslate) {
1707 const QRect rr = toNormalizedFillRect(rect: r.translated(dx: s->matrix.dx(), dy: s->matrix.dy()));
1708 fillRect_normalized(r: rr, data, pe: d);
1709 return;
1710 } else if (txop == QTransform::TxScale) {
1711 const QRect rr = toNormalizedFillRect(rect: s->matrix.mapRect(r));
1712 fillRect_normalized(r: rr, data, pe: d);
1713 return;
1714 }
1715 }
1716 ensureRasterState();
1717 if (s->flags.tx_noshear) {
1718 d->initializeRasterizer(data);
1719 QRectF nr = r.normalized();
1720 if (!nr.isEmpty()) {
1721 const QPointF a = s->matrix.map(p: (nr.topLeft() + nr.bottomLeft()) * 0.5f);
1722 const QPointF b = s->matrix.map(p: (nr.topRight() + nr.bottomRight()) * 0.5f);
1723 d->rasterizer->rasterizeLine(a, b, width: nr.height() / nr.width());
1724 }
1725 return;
1726 }
1727
1728 QPainterPath path;
1729 path.addRect(rect: r);
1730 ensureOutlineMapper();
1731 fillPath(path, fillData: data);
1732}
1733
1734/*!
1735 \reimp
1736*/
1737void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1738{
1739#ifdef QT_DEBUG_DRAW
1740 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1741#endif
1742 QRasterPaintEngineState *s = state();
1743
1744 ensureBrush(brush);
1745 if (!s->brushData.blend)
1746 return;
1747
1748 fillRect(r, data: &s->brushData);
1749}
1750
1751static QColor qPremultiplyWithExtraAlpha(const QColor &c, int alpha)
1752{
1753 if (alpha == 0)
1754 return Qt::transparent;
1755 if (c.spec() == QColor::ExtendedRgb) {
1756 float r, g, b, a;
1757 c.getRgbF(r: &r, g: &g, b: &b, a: &a);
1758 a = a * alpha * (1.f / 256.f);
1759 return QColor::fromRgbF(r: r * a, g: g * a, b: b * a, a);
1760 }
1761 return qPremultiply(c: combineAlpha256(rgba64: c.rgba64(), alpha256: alpha));
1762}
1763
1764/*!
1765 \reimp
1766*/
1767void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1768{
1769#ifdef QT_DEBUG_DRAW
1770 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1771#endif
1772 Q_D(QRasterPaintEngine);
1773 QRasterPaintEngineState *s = state();
1774
1775 d->solid_color_filler.solidColor = qPremultiplyWithExtraAlpha(c: color, alpha: s->intOpacity);
1776
1777 if (d->solid_color_filler.solidColor.alphaF() <= 0.0f
1778 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1779 return;
1780 }
1781 d->solid_color_filler.clip = d->clip();
1782 d->solid_color_filler.adjustSpanMethods();
1783 fillRect(r, data: &d->solid_color_filler);
1784}
1785
1786static inline bool isAbove(const QPointF *a, const QPointF *b)
1787{
1788 return a->y() < b->y();
1789}
1790
1791static bool splitPolygon(const QPointF *points, int pointCount, QList<QPointF> *upper, QList<QPointF> *lower)
1792{
1793 Q_ASSERT(upper);
1794 Q_ASSERT(lower);
1795
1796 Q_ASSERT(pointCount >= 2);
1797
1798 QList<const QPointF *> sorted;
1799 sorted.reserve(asize: pointCount);
1800
1801 upper->reserve(asize: pointCount * 3 / 4);
1802 lower->reserve(asize: pointCount * 3 / 4);
1803
1804 for (int i = 0; i < pointCount; ++i)
1805 sorted << points + i;
1806
1807 std::sort(first: sorted.begin(), last: sorted.end(), comp: isAbove);
1808
1809 qreal splitY = sorted.at(i: sorted.size() / 2)->y();
1810
1811 const QPointF *end = points + pointCount;
1812 const QPointF *last = end - 1;
1813
1814 QList<QPointF> *bin[2] = { upper, lower };
1815
1816 for (const QPointF *p = points; p < end; ++p) {
1817 int side = p->y() < splitY;
1818 int lastSide = last->y() < splitY;
1819
1820 if (side != lastSide) {
1821 if (qFuzzyCompare(p1: p->y(), p2: splitY)) {
1822 bin[!side]->append(t: *p);
1823 } else if (qFuzzyCompare(p1: last->y(), p2: splitY)) {
1824 bin[side]->append(t: *last);
1825 } else {
1826 QPointF delta = *p - *last;
1827 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1828
1829 bin[0]->append(t: intersection);
1830 bin[1]->append(t: intersection);
1831 }
1832 }
1833
1834 bin[side]->append(t: *p);
1835
1836 last = p;
1837 }
1838
1839 // give up if we couldn't reduce the point count
1840 return upper->size() < pointCount && lower->size() < pointCount;
1841}
1842
1843/*!
1844 \internal
1845 */
1846void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1847{
1848 Q_D(QRasterPaintEngine);
1849 QRasterPaintEngineState *s = state();
1850
1851 const int maxPoints = 0xffff;
1852
1853 // max amount of points that raster engine can reliably handle
1854 if (pointCount > maxPoints) {
1855 QList<QPointF> upper, lower;
1856
1857 if (splitPolygon(points, pointCount, upper: &upper, lower: &lower)) {
1858 fillPolygon(points: upper.constData(), pointCount: upper.size(), mode);
1859 fillPolygon(points: lower.constData(), pointCount: lower.size(), mode);
1860 } else
1861 qWarning(msg: "Polygon too complex for filling.");
1862
1863 return;
1864 }
1865
1866 // Compose polygon fill..,
1867 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1868 ensureOutlineMapper();
1869 QT_FT_Outline *outline = d->outlineMapper->convertPath(path: vp);
1870
1871 // scanconvert.
1872 ProcessSpans brushBlend = d->getBrushFunc(rect: d->outlineMapper->controlPointRect,
1873 data: &s->brushData);
1874 d->rasterize(outline, callback: brushBlend, spanData: &s->brushData, rasterBuffer: d->rasterBuffer.data());
1875}
1876
1877/*!
1878 \reimp
1879*/
1880void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1881{
1882 Q_D(QRasterPaintEngine);
1883 QRasterPaintEngineState *s = state();
1884
1885#ifdef QT_DEBUG_DRAW
1886 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1887 for (int i=0; i<pointCount; ++i)
1888 qDebug() << " - " << points[i];
1889#endif
1890 Q_ASSERT(pointCount >= 2);
1891
1892 if (mode != PolylineMode && QVectorPath::isRect(pts: (const qreal *) points, elementCount: pointCount)) {
1893 QRectF r(points[0], points[2]);
1894 drawRects(rects: &r, rectCount: 1);
1895 return;
1896 }
1897
1898 ensurePen();
1899 if (mode != PolylineMode) {
1900 // Do the fill...
1901 ensureBrush();
1902 if (s->brushData.blend)
1903 fillPolygon(points, pointCount, mode);
1904 }
1905
1906 // Do the outline...
1907 if (s->penData.blend) {
1908 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1909 if (s->flags.fast_pen) {
1910 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1911 stroker.drawPath(path: vp);
1912 } else {
1913 QPaintEngineEx::stroke(path: vp, pen: s->lastPen);
1914 }
1915 }
1916}
1917
1918/*!
1919 \reimp
1920*/
1921void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1922{
1923 Q_D(QRasterPaintEngine);
1924 QRasterPaintEngineState *s = state();
1925
1926#ifdef QT_DEBUG_DRAW
1927 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1928 for (int i=0; i<pointCount; ++i)
1929 qDebug() << " - " << points[i];
1930#endif
1931 Q_ASSERT(pointCount >= 2);
1932 if (mode != PolylineMode && QVectorPath::isRect(pts: (const int *) points, elementCount: pointCount)) {
1933 QRect r(points[0].x(),
1934 points[0].y(),
1935 points[2].x() - points[0].x(),
1936 points[2].y() - points[0].y());
1937 drawRects(rects: &r, rectCount: 1);
1938 return;
1939 }
1940
1941 ensurePen();
1942
1943 // Do the fill
1944 if (mode != PolylineMode) {
1945 ensureBrush();
1946 if (s->brushData.blend) {
1947 // Compose polygon fill..,
1948 ensureOutlineMapper();
1949 d->outlineMapper->beginOutline(fillRule: mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1950 d->outlineMapper->moveTo(pt: *points);
1951 const QPoint *p = points;
1952 const QPoint *ep = points + pointCount - 1;
1953 do {
1954 d->outlineMapper->lineTo(pt: *(++p));
1955 } while (p < ep);
1956 d->outlineMapper->endOutline();
1957
1958 // scanconvert.
1959 ProcessSpans brushBlend = d->getBrushFunc(rect: d->outlineMapper->controlPointRect,
1960 data: &s->brushData);
1961 d->rasterize(outline: d->outlineMapper->outline(), callback: brushBlend, spanData: &s->brushData, rasterBuffer: d->rasterBuffer.data());
1962 }
1963 }
1964
1965 // Do the outline...
1966 if (s->penData.blend) {
1967 int count = pointCount * 2;
1968 QVarLengthArray<qreal> fpoints(count);
1969 for (int i=0; i<count; ++i)
1970 fpoints[i] = ((const int *) points)[i];
1971 QVectorPath vp((qreal *) fpoints.data(), pointCount, nullptr, QVectorPath::polygonFlags(mode));
1972
1973 if (s->flags.fast_pen) {
1974 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1975 stroker.drawPath(path: vp);
1976 } else {
1977 QPaintEngineEx::stroke(path: vp, pen: s->lastPen);
1978 }
1979 }
1980}
1981
1982/*!
1983 \internal
1984*/
1985void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1986{
1987#ifdef QT_DEBUG_DRAW
1988 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1989#endif
1990
1991 QPlatformPixmap *pd = pixmap.handle();
1992 if (pd->classId() == QPlatformPixmap::RasterClass) {
1993 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
1994 if (image.depth() == 1) {
1995 Q_D(QRasterPaintEngine);
1996 QRasterPaintEngineState *s = state();
1997 if (s->matrix.type() <= QTransform::TxTranslate) {
1998 ensurePen();
1999 drawBitmap(pos: pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, fill: &s->penData);
2000 } else {
2001 drawImage(p: pos, img: d->rasterBuffer->colorizeBitmap(image, color: s->pen.color()));
2002 }
2003 } else {
2004 QRasterPaintEngine::drawImage(p: pos, img: image);
2005 }
2006 } else {
2007 const QImage image = pixmap.toImage();
2008 if (pixmap.depth() == 1) {
2009 Q_D(QRasterPaintEngine);
2010 QRasterPaintEngineState *s = state();
2011 if (s->matrix.type() <= QTransform::TxTranslate) {
2012 ensurePen();
2013 drawBitmap(pos: pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, fill: &s->penData);
2014 } else {
2015 drawImage(p: pos, img: d->rasterBuffer->colorizeBitmap(image, color: s->pen.color()));
2016 }
2017 } else {
2018 QRasterPaintEngine::drawImage(p: pos, img: image);
2019 }
2020 }
2021}
2022
2023/*!
2024 \reimp
2025*/
2026void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2027{
2028#ifdef QT_DEBUG_DRAW
2029 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2030#endif
2031
2032 QPlatformPixmap* pd = pixmap.handle();
2033 if (pd->classId() == QPlatformPixmap::RasterClass) {
2034 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2035 if (image.depth() == 1) {
2036 Q_D(QRasterPaintEngine);
2037 QRasterPaintEngineState *s = state();
2038 if (s->matrix.type() <= QTransform::TxTranslate
2039 && r.size() == sr.size()
2040 && r.size() == pixmap.size()) {
2041 ensurePen();
2042 drawBitmap(pos: r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, fill: &s->penData);
2043 return;
2044 } else {
2045 drawImage(r, pm: d->rasterBuffer->colorizeBitmap(image, color: s->pen.color()), sr);
2046 }
2047 } else {
2048 drawImage(r, pm: image, sr);
2049 }
2050 } else {
2051 QRect clippedSource = sr.toAlignedRect().intersected(other: pixmap.rect());
2052 const QImage image = pd->toImage(rect: clippedSource);
2053 QRectF translatedSource = sr.translated(p: -clippedSource.topLeft());
2054 if (image.depth() == 1) {
2055 Q_D(QRasterPaintEngine);
2056 QRasterPaintEngineState *s = state();
2057 if (s->matrix.type() <= QTransform::TxTranslate
2058 && r.size() == sr.size()
2059 && r.size() == pixmap.size()) {
2060 ensurePen();
2061 drawBitmap(pos: r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, fill: &s->penData);
2062 return;
2063 } else {
2064 drawImage(r, pm: d->rasterBuffer->colorizeBitmap(image, color: s->pen.color()), sr: translatedSource);
2065 }
2066 } else {
2067 drawImage(r, pm: image, sr: translatedSource);
2068 }
2069 }
2070}
2071
2072static inline int fast_ceil_positive(const qreal &v)
2073{
2074 const int iv = int(v);
2075 if (v - iv == 0)
2076 return iv;
2077 else
2078 return iv + 1;
2079}
2080
2081static inline const QRect toAlignedRect_positive(const QRectF &rect)
2082{
2083 const int xmin = int(rect.x());
2084 const int xmax = int(fast_ceil_positive(v: rect.right()));
2085 const int ymin = int(rect.y());
2086 const int ymax = int(fast_ceil_positive(v: rect.bottom()));
2087 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2088}
2089
2090/*!
2091 \internal
2092*/
2093void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2094{
2095#ifdef QT_DEBUG_DRAW
2096 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2097#endif
2098
2099 Q_D(QRasterPaintEngine);
2100 QRasterPaintEngineState *s = state();
2101 qreal scale = img.devicePixelRatio();
2102
2103 if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
2104 drawImage(r: QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2105 pm: img,
2106 sr: QRectF(0, 0, img.width(), img.height()));
2107 } else {
2108
2109 const QClipData *clip = d->clip();
2110 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2111
2112 if (d->canUseImageBlitting(mode: d->rasterBuffer->compositionMode, image: img, pt, sr: img.rect())) {
2113 if (!clip) {
2114 d->blitImage(pt, img, clip: d->deviceRect);
2115 return;
2116 } else if (clip->hasRectClip) {
2117 d->blitImage(pt, img, clip: clip->clipRect);
2118 return;
2119 }
2120 } else if (d->canUseFastImageBlending(mode: d->rasterBuffer->compositionMode, image: img)) {
2121 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2122 if (func) {
2123 if (!clip) {
2124 d->drawImage(pt, img, func, clip: d->deviceRect, alpha: s->intOpacity);
2125 return;
2126 } else if (clip->hasRectClip) {
2127 d->drawImage(pt, img, func, clip: clip->clipRect, alpha: s->intOpacity);
2128 return;
2129 }
2130 }
2131 }
2132
2133
2134
2135 d->image_filler.clip = clip;
2136 d->image_filler.initTexture(image: &img, alpha: s->intOpacity, QTextureData::Plain, sourceRect: img.rect());
2137 if (!d->image_filler.blend)
2138 return;
2139 d->image_filler.dx = -pt.x();
2140 d->image_filler.dy = -pt.y();
2141 QRect rr = img.rect().translated(dx: qRound(d: pt.x()), dy: qRound(d: pt.y()));
2142
2143 fillRect_normalized(r: rr, data: &d->image_filler, pe: d);
2144 }
2145
2146}
2147
2148QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2149{
2150 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2151}
2152
2153namespace {
2154 enum RotationType {
2155 Rotation90,
2156 Rotation180,
2157 Rotation270,
2158 NoRotation
2159 };
2160
2161 inline RotationType qRotationType(const QTransform &transform)
2162 {
2163 QTransform::TransformationType type = transform.type();
2164
2165 if (type > QTransform::TxRotate)
2166 return NoRotation;
2167
2168 if (type == QTransform::TxRotate && qFuzzyIsNull(d: transform.m11()) && qFuzzyCompare(p1: transform.m12(), p2: qreal(-1))
2169 && qFuzzyCompare(p1: transform.m21(), p2: qreal(1)) && qFuzzyIsNull(d: transform.m22()))
2170 return Rotation90;
2171
2172 if (type == QTransform::TxScale && qFuzzyCompare(p1: transform.m11(), p2: qreal(-1)) && qFuzzyIsNull(d: transform.m12())
2173 && qFuzzyIsNull(d: transform.m21()) && qFuzzyCompare(p1: transform.m22(), p2: qreal(-1)))
2174 return Rotation180;
2175
2176 if (type == QTransform::TxRotate && qFuzzyIsNull(d: transform.m11()) && qFuzzyCompare(p1: transform.m12(), p2: qreal(1))
2177 && qFuzzyCompare(p1: transform.m21(), p2: qreal(-1)) && qFuzzyIsNull(d: transform.m22()))
2178 return Rotation270;
2179
2180 return NoRotation;
2181 }
2182
2183 inline bool isPixelAligned(const QPointF &pt)
2184 {
2185 return QPointF(pt.toPoint()) == pt;
2186 }
2187 inline bool isPixelAligned(const QRectF &rect)
2188 {
2189 return QRectF(rect.toRect()) == rect;
2190 }
2191}
2192
2193/*!
2194 \reimp
2195*/
2196void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2197 Qt::ImageConversionFlags)
2198{
2199#ifdef QT_DEBUG_DRAW
2200 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2201#endif
2202
2203 if (r.isEmpty())
2204 return;
2205
2206 Q_D(QRasterPaintEngine);
2207 QRasterPaintEngineState *s = state();
2208 Q_ASSERT(s);
2209 int sr_l = qFloor(v: sr.left());
2210 int sr_r = qCeil(v: sr.right()) - 1;
2211 int sr_t = qFloor(v: sr.top());
2212 int sr_b = qCeil(v: sr.bottom()) - 1;
2213
2214 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2215 // as fillRect will apply the aliased coordinate delta we need to
2216 // subtract it here as we don't use it for image drawing
2217 QTransform old = s->matrix;
2218
2219 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2220 QRgb color = img.pixel(x: sr_l, y: sr_t);
2221 if (img.pixelFormat().premultiplied() == QPixelFormat::Premultiplied) {
2222 // Combine premultiplied color with the opacity set on the painter.
2223 d->solid_color_filler.solidColor = multiplyAlpha256(rgba64: QRgba64::fromArgb32(rgb: color), alpha256: s->intOpacity);
2224 } else {
2225 d->solid_color_filler.solidColor = qPremultiply(c: combineAlpha256(rgba64: QRgba64::fromArgb32(rgb: color), alpha256: s->intOpacity));
2226 }
2227
2228 if (d->solid_color_filler.solidColor.alphaF() <= 0.0f && s->composition_mode == QPainter::CompositionMode_SourceOver)
2229 return;
2230
2231 d->solid_color_filler.clip = d->clip();
2232 d->solid_color_filler.adjustSpanMethods();
2233 fillRect(r, data: &d->solid_color_filler);
2234
2235 s->matrix = old;
2236 return;
2237 }
2238
2239 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2240
2241 const QClipData *clip = d->clip();
2242
2243 if (s->matrix.type() == QTransform::TxRotate
2244 && !stretch_sr
2245 && (!clip || clip->hasRectClip)
2246 && s->intOpacity == 256
2247 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2248 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))
2249 {
2250 RotationType rotationType = qRotationType(transform: s->matrix);
2251 Q_ASSERT(d->rasterBuffer->format < QImage::NImageFormats);
2252 const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2253
2254 if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(r: sr.toAlignedRect())) {
2255 QRectF transformedTargetRect = s->matrix.mapRect(r);
2256
2257 if (d->canUseImageBlitting(mode: d->rasterBuffer->compositionMode, image: img, pt: transformedTargetRect.topRight(), sr)) {
2258 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(other: clip ? clip->clipRect : d->deviceRect);
2259 if (clippedTransformedTargetRect.isNull())
2260 return;
2261
2262 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2263
2264 QRect clippedSourceRect
2265 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2266 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2267
2268 clippedSourceRect = clippedSourceRect.intersected(other: img.rect());
2269
2270 const qsizetype dbpl = d->rasterBuffer->bytesPerLine();
2271 const qsizetype sbpl = img.bytesPerLine();
2272
2273 uchar *dst = d->rasterBuffer->buffer();
2274 uint bpp = img.depth() >> 3;
2275
2276 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2277 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2278
2279 uint cw = clippedSourceRect.width();
2280 uint ch = clippedSourceRect.height();
2281
2282 qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2283
2284 return;
2285 }
2286 }
2287 }
2288
2289 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2290
2291 QRectF targetBounds = s->matrix.mapRect(r);
2292 bool exceedsPrecision = r.width() > 0x7fff
2293 || r.height() > 0x7fff
2294 || targetBounds.left() < -0x7fff
2295 || targetBounds.top() < -0x7fff
2296 || targetBounds.right() > 0x7fff
2297 || targetBounds.bottom() > 0x7fff
2298 || targetBounds.width() > 0x7fff
2299 || targetBounds.height() > 0x7fff
2300 || s->matrix.m11() >= 512
2301 || s->matrix.m22() >= 512;
2302 if (!exceedsPrecision && d->canUseFastImageBlending(mode: d->rasterBuffer->compositionMode, image: img)) {
2303 if (s->matrix.type() > QTransform::TxScale) {
2304 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2305 // The fast transform methods doesn't really work on small targets, see QTBUG-93475
2306 // And it can't antialias the edges
2307 if (func && (!clip || clip->hasRectClip) && !s->flags.antialiased && targetBounds.width() >= 16 && targetBounds.height() >= 16) {
2308 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2309 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2310 s->matrix, s->intOpacity);
2311 return;
2312 }
2313 } else {
2314 // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2315 bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2316 bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2317 if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2318 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2319 if (func) {
2320 QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2321 if (!clip) {
2322 d->drawImage(pt, img, func, clip: d->deviceRect, alpha: s->intOpacity, sr: sr.toRect());
2323 return;
2324 } else if (clip->hasRectClip) {
2325 d->drawImage(pt, img, func, clip: clip->clipRect, alpha: s->intOpacity, sr: sr.toRect());
2326 return;
2327 }
2328 }
2329 }
2330 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2331 if (func && (!clip || clip->hasRectClip)) {
2332 QRectF tr = qt_mapRect_non_normalizing(r, t: s->matrix);
2333 if (!s->flags.antialiased) {
2334 tr.setX(qRound(d: tr.x()));
2335 tr.setY(qRound(d: tr.y()));
2336 tr.setWidth(qRound(d: tr.width()));
2337 tr.setHeight(qRound(d: tr.height()));
2338 }
2339 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2340 img.bits(), img.bytesPerLine(), img.height(),
2341 tr, sr,
2342 !clip ? d->deviceRect : clip->clipRect,
2343 s->intOpacity);
2344 return;
2345 }
2346 }
2347 }
2348
2349 QTransform copy = s->matrix;
2350 copy.translate(dx: r.x(), dy: r.y());
2351 if (stretch_sr)
2352 copy.scale(sx: r.width() / sr.width(), sy: r.height() / sr.height());
2353 copy.translate(dx: -sr.x(), dy: -sr.y());
2354
2355 d->image_filler_xform.clip = clip;
2356 d->image_filler_xform.initTexture(image: &img, alpha: s->intOpacity, QTextureData::Plain, sourceRect: toAlignedRect_positive(rect: sr));
2357 if (!d->image_filler_xform.blend)
2358 return;
2359 d->image_filler_xform.setupMatrix(matrix: copy, bilinear: s->flags.bilinear);
2360
2361 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2362 QRectF rr = s->matrix.mapRect(r);
2363
2364 const int x1 = qRound(d: rr.x());
2365 const int y1 = qRound(d: rr.y());
2366 const int x2 = qRound(d: rr.right());
2367 const int y2 = qRound(d: rr.bottom());
2368
2369 fillRect_normalized(r: QRect(x1, y1, x2-x1, y2-y1), data: &d->image_filler_xform, pe: d);
2370 return;
2371 }
2372
2373#ifdef QT_FAST_SPANS
2374 ensureRasterState();
2375 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2376 d->initializeRasterizer(data: &d->image_filler_xform);
2377 d->rasterizer->setAntialiased(s->flags.antialiased);
2378
2379 const QRectF &rect = r.normalized();
2380 const QPointF a = s->matrix.map(p: (rect.topLeft() + rect.bottomLeft()) * 0.5f);
2381 const QPointF b = s->matrix.map(p: (rect.topRight() + rect.bottomRight()) * 0.5f);
2382
2383 if (s->flags.tx_noshear)
2384 d->rasterizer->rasterizeLine(a, b, width: rect.height() / rect.width());
2385 else
2386 d->rasterizer->rasterizeLine(a, b, width: qAbs(t: (s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2387 return;
2388 }
2389#endif
2390 QPainterPath path;
2391 path.addRect(rect: r);
2392 QTransform m = s->matrix;
2393 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2394 m.m21(), m.m22(), m.m23(),
2395 m.m31(), m.m32(), m.m33());
2396 fillPath(path, fillData: &d->image_filler_xform);
2397 s->matrix = m;
2398 } else {
2399 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2400 if (d->canUseImageBlitting(mode: d->rasterBuffer->compositionMode, image: img, pt, sr)) {
2401 if (!clip) {
2402 d->blitImage(pt, img, clip: d->deviceRect, sr: sr.toRect());
2403 return;
2404 } else if (clip->hasRectClip) {
2405 d->blitImage(pt, img, clip: clip->clipRect, sr: sr.toRect());
2406 return;
2407 }
2408 } else if (d->canUseFastImageBlending(mode: d->rasterBuffer->compositionMode, image: img)) {
2409 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2410 if (func) {
2411 if (!clip) {
2412 d->drawImage(pt, img, func, clip: d->deviceRect, alpha: s->intOpacity, sr: sr.toRect());
2413 return;
2414 } else if (clip->hasRectClip) {
2415 d->drawImage(pt, img, func, clip: clip->clipRect, alpha: s->intOpacity, sr: sr.toRect());
2416 return;
2417 }
2418 }
2419 }
2420
2421 d->image_filler.clip = clip;
2422 d->image_filler.initTexture(image: &img, alpha: s->intOpacity, QTextureData::Plain, sourceRect: toAlignedRect_positive(rect: sr));
2423 if (!d->image_filler.blend)
2424 return;
2425 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2426 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2427
2428 QRectF rr = r;
2429 rr.translate(dx: s->matrix.dx(), dy: s->matrix.dy());
2430
2431 const int x1 = qRound(d: rr.x());
2432 const int y1 = qRound(d: rr.y());
2433 const int x2 = qRound(d: rr.right());
2434 const int y2 = qRound(d: rr.bottom());
2435
2436 fillRect_normalized(r: QRect(x1, y1, x2-x1, y2-y1), data: &d->image_filler, pe: d);
2437 }
2438}
2439
2440/*!
2441 \reimp
2442*/
2443void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2444{
2445#ifdef QT_DEBUG_DRAW
2446 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2447#endif
2448 Q_D(QRasterPaintEngine);
2449 QRasterPaintEngineState *s = state();
2450 Q_ASSERT(s);
2451
2452 QImage image;
2453
2454 QPlatformPixmap *pd = pixmap.handle();
2455 if (pd->classId() == QPlatformPixmap::RasterClass) {
2456 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2457 } else {
2458 image = pixmap.toImage();
2459 }
2460
2461 if (image.depth() == 1)
2462 image = d->rasterBuffer->colorizeBitmap(image, color: s->pen.color());
2463
2464 const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
2465 if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
2466 QTransform copy = s->matrix;
2467 copy.translate(dx: r.x(), dy: r.y());
2468 copy.translate(dx: -sr.x(), dy: -sr.y());
2469 const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
2470 copy.scale(sx: inverseDpr, sy: inverseDpr);
2471 d->image_filler_xform.clip = d->clip();
2472 d->image_filler_xform.initTexture(image: &image, alpha: s->intOpacity, QTextureData::Tiled);
2473 if (!d->image_filler_xform.blend)
2474 return;
2475 d->image_filler_xform.setupMatrix(matrix: copy, bilinear: s->flags.bilinear);
2476
2477#ifdef QT_FAST_SPANS
2478 ensureRasterState();
2479 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2480 d->initializeRasterizer(data: &d->image_filler_xform);
2481 d->rasterizer->setAntialiased(s->flags.antialiased);
2482
2483 const QRectF &rect = r.normalized();
2484 const QPointF a = s->matrix.map(p: (rect.topLeft() + rect.bottomLeft()) * 0.5f);
2485 const QPointF b = s->matrix.map(p: (rect.topRight() + rect.bottomRight()) * 0.5f);
2486 if (s->flags.tx_noshear)
2487 d->rasterizer->rasterizeLine(a, b, width: rect.height() / rect.width());
2488 else
2489 d->rasterizer->rasterizeLine(a, b, width: qAbs(t: (s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2490 return;
2491 }
2492#endif
2493 QPainterPath path;
2494 path.addRect(rect: r);
2495 fillPath(path, fillData: &d->image_filler_xform);
2496 } else {
2497 d->image_filler.clip = d->clip();
2498
2499 d->image_filler.initTexture(image: &image, alpha: s->intOpacity, QTextureData::Tiled);
2500 if (!d->image_filler.blend)
2501 return;
2502 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2503 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2504
2505 QRectF rr = r;
2506 rr.translate(dx: s->matrix.dx(), dy: s->matrix.dy());
2507 fillRect_normalized(r: rr.normalized().toRect(), data: &d->image_filler, pe: d);
2508 }
2509}
2510
2511
2512//QWS hack
2513static inline bool monoVal(const uchar* s, int x)
2514{
2515 return (s[x>>3] << (x&7)) & 0x80;
2516}
2517
2518/*!
2519 \internal
2520 */
2521QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2522{
2523 Q_D(QRasterPaintEngine);
2524 return d->rasterBuffer.data();
2525}
2526
2527/*!
2528 \internal
2529*/
2530void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
2531{
2532 Q_D(QRasterPaintEngine);
2533 QRasterPaintEngineState *s = state();
2534
2535 if (!s->penData.blend)
2536 return;
2537
2538 QRasterBuffer *rb = d->rasterBuffer.data();
2539 if (rb->colorSpace.transferFunction() == QColorSpace::TransferFunction::Linear)
2540 useGammaCorrection = false;
2541
2542 const QRect rect(rx, ry, w, h);
2543 const QClipData *clip = d->clip();
2544 bool unclipped = false;
2545 if (clip) {
2546 // inlined QRect::intersects
2547 const bool intersects = qMax(a: clip->xmin, b: rect.left()) <= qMin(a: clip->xmax - 1, b: rect.right())
2548 && qMax(a: clip->ymin, b: rect.top()) <= qMin(a: clip->ymax - 1, b: rect.bottom());
2549
2550 if (clip->hasRectClip) {
2551 unclipped = rx > clip->xmin
2552 && rx + w < clip->xmax
2553 && ry > clip->ymin
2554 && ry + h < clip->ymax;
2555 }
2556
2557 if (!intersects)
2558 return;
2559 } else {
2560 // inlined QRect::intersects
2561 const bool intersects = qMax(a: 0, b: rect.left()) <= qMin(a: rb->width() - 1, b: rect.right())
2562 && qMax(a: 0, b: rect.top()) <= qMin(a: rb->height() - 1, b: rect.bottom());
2563 if (!intersects)
2564 return;
2565
2566 // inlined QRect::contains
2567 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2568 && rect.top() >= 0 && rect.bottom() < rb->height();
2569
2570 unclipped = contains && d->isUnclipped_normalized(rect);
2571 }
2572
2573 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2574 const uchar * scanline = static_cast<const uchar *>(src);
2575
2576 if (s->flags.fast_text) {
2577 if (unclipped) {
2578 if (depth == 1) {
2579 if (s->penData.bitmapBlit) {
2580 s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2581 scanline, w, h, bpl);
2582 return;
2583 }
2584 } else if (depth == 8) {
2585 if (s->penData.alphamapBlit) {
2586 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2587 scanline, w, h, bpl, nullptr, useGammaCorrection);
2588 return;
2589 }
2590 } else if (depth == 32) {
2591 // (A)RGB Alpha mask where the alpha component is not used.
2592 if (s->penData.alphaRGBBlit) {
2593 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2594 (const uint *) scanline, w, h, bpl / 4, nullptr, useGammaCorrection);
2595 return;
2596 }
2597 }
2598 } else if ((depth == 8 && s->penData.alphamapBlit) || (depth == 32 && s->penData.alphaRGBBlit)) {
2599 // (A)RGB Alpha mask where the alpha component is not used.
2600 if (!clip) {
2601 int nx = qMax(a: 0, b: rx);
2602 int ny = qMax(a: 0, b: ry);
2603
2604 // Move scanline pointer to compensate for moved x and y
2605 int xdiff = nx - rx;
2606 int ydiff = ny - ry;
2607 scanline += ydiff * bpl;
2608 scanline += xdiff * (depth == 32 ? 4 : 1);
2609
2610 w -= xdiff;
2611 h -= ydiff;
2612
2613 if (nx + w > d->rasterBuffer->width())
2614 w = d->rasterBuffer->width() - nx;
2615 if (ny + h > d->rasterBuffer->height())
2616 h = d->rasterBuffer->height() - ny;
2617
2618 rx = nx;
2619 ry = ny;
2620 }
2621 if (depth == 8)
2622 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2623 scanline, w, h, bpl, clip, useGammaCorrection);
2624 else if (depth == 32)
2625 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2626 (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
2627 return;
2628 }
2629 }
2630
2631 int x0 = 0;
2632 if (rx < 0) {
2633 x0 = -rx;
2634 w -= x0;
2635 }
2636
2637 int y0 = 0;
2638 if (ry < 0) {
2639 y0 = -ry;
2640 scanline += bpl * y0;
2641 h -= y0;
2642 }
2643
2644 w = qMin(a: w, b: rb->width() - qMax(a: 0, b: rx));
2645 h = qMin(a: h, b: rb->height() - qMax(a: 0, b: ry));
2646
2647 if (w <= 0 || h <= 0)
2648 return;
2649
2650 const int NSPANS = 512;
2651 QT_FT_Span spans[NSPANS];
2652 int current = 0;
2653
2654 const int x1 = x0 + w;
2655 const int y1 = y0 + h;
2656
2657 if (depth == 1) {
2658 for (int y = y0; y < y1; ++y) {
2659 for (int x = x0; x < x1; ) {
2660 if (!monoVal(s: scanline, x)) {
2661 ++x;
2662 continue;
2663 }
2664
2665 if (current == NSPANS) {
2666 blend(current, spans, &s->penData);
2667 current = 0;
2668 }
2669 spans[current].x = x + rx;
2670 spans[current].y = y + ry;
2671 spans[current].coverage = 255;
2672 int len = 1;
2673 ++x;
2674 // extend span until we find a different one.
2675 while (x < x1 && monoVal(s: scanline, x)) {
2676 ++x;
2677 ++len;
2678 }
2679 spans[current].len = len;
2680 ++current;
2681 }
2682 scanline += bpl;
2683 }
2684 } else if (depth == 8) {
2685 for (int y = y0; y < y1; ++y) {
2686 for (int x = x0; x < x1; ) {
2687 // Skip those with 0 coverage
2688 if (scanline[x] == 0) {
2689 ++x;
2690 continue;
2691 }
2692
2693 if (current == NSPANS) {
2694 blend(current, spans, &s->penData);
2695 current = 0;
2696 }
2697 int coverage = scanline[x];
2698 spans[current].x = x + rx;
2699 spans[current].y = y + ry;
2700 spans[current].coverage = coverage;
2701 int len = 1;
2702 ++x;
2703
2704 // extend span until we find a different one.
2705 while (x < x1 && scanline[x] == coverage) {
2706 ++x;
2707 ++len;
2708 }
2709 spans[current].len = len;
2710 ++current;
2711 }
2712 scanline += bpl;
2713 }
2714 } else { // 32-bit alpha...
2715 const uint *sl = (const uint *) scanline;
2716 for (int y = y0; y < y1; ++y) {
2717 for (int x = x0; x < x1; ) {
2718 // Skip those with 0 coverage
2719 if ((sl[x] & 0x00ffffff) == 0) {
2720 ++x;
2721 continue;
2722 }
2723
2724 if (current == NSPANS) {
2725 blend(current, spans, &s->penData);
2726 current = 0;
2727 }
2728 uint rgbCoverage = sl[x];
2729 int coverage = qGreen(rgb: rgbCoverage);
2730 spans[current].x = x + rx;
2731 spans[current].y = y + ry;
2732 spans[current].coverage = coverage;
2733 int len = 1;
2734 ++x;
2735
2736 // extend span until we find a different one.
2737 while (x < x1 && sl[x] == rgbCoverage) {
2738 ++x;
2739 ++len;
2740 }
2741 spans[current].len = len;
2742 ++current;
2743 }
2744 sl += bpl / sizeof(uint);
2745 }
2746 }
2747// qDebug() << "alphaPenBlt: num spans=" << current
2748// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2749 // Call span func for current set of spans.
2750 if (current != 0)
2751 blend(current, spans, &s->penData);
2752}
2753
2754/*!
2755 \internal
2756*/
2757bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2758 const QFixedPoint *positions, QFontEngine *fontEngine)
2759{
2760 Q_D(QRasterPaintEngine);
2761 QRasterPaintEngineState *s = state();
2762
2763 bool verticalSubPixelPositions = fontEngine->supportsVerticalSubPixelPositions()
2764 && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
2765
2766 if (fontEngine->hasInternalCaching()) {
2767 QFontEngine::GlyphFormat neededFormat =
2768 painter()->device()->devType() == QInternal::Widget
2769 ? QFontEngine::Format_None
2770 : QFontEngine::Format_A8;
2771
2772 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2773 neededFormat = QFontEngine::Format_Mono;
2774
2775 for (int i = 0; i < numGlyphs; i++) {
2776 QFixedPoint spp = fontEngine->subPixelPositionFor(position: positions[i]);
2777 if (!verticalSubPixelPositions)
2778 spp.y = 0;
2779
2780 const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyph: glyphs[i], subPixelPosition: spp, neededFormat, t: s->matrix);
2781 if (!alphaMap)
2782 continue;
2783
2784 int depth;
2785 int bytesPerLine;
2786 switch (alphaMap->format) {
2787 case QFontEngine::Format_Mono:
2788 depth = 1;
2789 bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
2790 break;
2791 case QFontEngine::Format_A8:
2792 depth = 8;
2793 bytesPerLine = (alphaMap->width + 3) & ~3;
2794 break;
2795 case QFontEngine::Format_A32:
2796 depth = 32;
2797 bytesPerLine = alphaMap->width * 4;
2798 break;
2799 default:
2800 Q_UNREACHABLE();
2801 };
2802
2803 QFixed y = verticalSubPixelPositions
2804 ? qFloor(f: positions[i].y)
2805 : qRound(f: positions[i].y);
2806
2807 alphaPenBlt(src: alphaMap->data, bpl: bytesPerLine, depth,
2808 rx: qFloor(f: positions[i].x) + alphaMap->x,
2809 ry: qFloor(f: y) - alphaMap->y,
2810 w: alphaMap->width, h: alphaMap->height,
2811 useGammaCorrection: fontEngine->expectsGammaCorrectedBlending());
2812 }
2813
2814 } else {
2815 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
2816
2817 QImageTextureGlyphCache *cache =
2818 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(key: nullptr, format: glyphFormat, transform: s->matrix, color: s->penData.solidColor));
2819 if (!cache) {
2820 cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, s->penData.solidColor);
2821 fontEngine->setGlyphCache(key: nullptr, data: cache);
2822 }
2823
2824 cache->populate(fontEngine, numGlyphs, glyphs, positions, renderHints: s->renderHints);
2825 cache->fillInPendingGlyphs();
2826
2827 const QImage &image = cache->image();
2828 qsizetype bpl = image.bytesPerLine();
2829
2830 int depth = image.depth();
2831 int rightShift = 0;
2832 int leftShift = 0;
2833 if (depth == 32)
2834 leftShift = 2; // multiply by 4
2835 else if (depth == 1)
2836 rightShift = 3; // divide by 8
2837
2838 int margin = fontEngine->glyphMargin(format: glyphFormat);
2839 const uchar *bits = image.bits();
2840 for (int i=0; i<numGlyphs; ++i) {
2841 QFixedPoint subPixelPosition = fontEngine->subPixelPositionFor(position: positions[i]);
2842 if (!verticalSubPixelPositions)
2843 subPixelPosition.y = 0;
2844
2845 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2846 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2847 if (c.isNull())
2848 continue;
2849
2850 int x = qFloor(f: positions[i].x) + c.baseLineX - margin;
2851 int y = (verticalSubPixelPositions
2852 ? qFloor(f: positions[i].y)
2853 : qRound(f: positions[i].y));
2854 y -= c.baseLineY + margin;
2855
2856 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2857 // c.x, c.y,
2858 // c.w, c.h,
2859 // c.baseLineX, c.baseLineY,
2860 // glyphs[i],
2861 // x, y,
2862 // positions[i].x.toInt(), positions[i].y.toInt());
2863
2864 const uchar *glyphBits = bits + ((c.x << leftShift) >> rightShift) + c.y * bpl;
2865
2866 if (glyphFormat == QFontEngine::Format_ARGB) {
2867 // The current state transform has already been applied to the positions,
2868 // so we prevent drawImage() from re-applying the transform by clearing
2869 // the state for the duration of the call.
2870 QTransform originalTransform = s->matrix;
2871 s->matrix = QTransform();
2872 drawImage(p: QPoint(x, y), img: QImage(glyphBits, c.w, c.h, bpl, image.format()));
2873 s->matrix = originalTransform;
2874 } else {
2875 alphaPenBlt(src: glyphBits, bpl, depth, rx: x, ry: y, w: c.w, h: c.h, useGammaCorrection: fontEngine->expectsGammaCorrectedBlending());
2876 }
2877 }
2878 }
2879 return true;
2880}
2881
2882
2883/*!
2884 * Returns \c true if the rectangle is completely within the current clip
2885 * state of the paint engine.
2886 */
2887bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2888{
2889 const QClipData *cl = clip();
2890 if (!cl) {
2891 // inline contains() for performance (we know the rects are normalized)
2892 const QRect &r1 = deviceRect;
2893 return (r.left() >= r1.left() && r.right() <= r1.right()
2894 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2895 }
2896
2897
2898 if (cl->hasRectClip) {
2899 // currently all painting functions clips to deviceRect internally
2900 if (cl->clipRect == deviceRect)
2901 return true;
2902
2903 // inline contains() for performance (we know the rects are normalized)
2904 const QRect &r1 = cl->clipRect;
2905 return (r.left() >= r1.left() && r.right() <= r1.right()
2906 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2907 } else {
2908 return qt_region_strictContains(region: cl->clipRegion, rect: r);
2909 }
2910}
2911
2912bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2913 int penWidth) const
2914{
2915 Q_Q(const QRasterPaintEngine);
2916 const QRasterPaintEngineState *s = q->state();
2917 const QClipData *cl = clip();
2918 QRect r = rect.normalized();
2919 if (!cl) {
2920 // inline contains() for performance (we know the rects are normalized)
2921 const QRect &r1 = deviceRect;
2922 return (r.left() >= r1.left() && r.right() <= r1.right()
2923 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2924 }
2925
2926
2927 // currently all painting functions that call this function clip to deviceRect internally
2928 if (cl->hasRectClip && cl->clipRect == deviceRect)
2929 return true;
2930
2931 if (s->flags.antialiased)
2932 ++penWidth;
2933
2934 if (penWidth > 0) {
2935 r.setX(r.x() - penWidth);
2936 r.setY(r.y() - penWidth);
2937 r.setWidth(r.width() + 2 * penWidth);
2938 r.setHeight(r.height() + 2 * penWidth);
2939 }
2940
2941 if (cl->hasRectClip) {
2942 // inline contains() for performance (we know the rects are normalized)
2943 const QRect &r1 = cl->clipRect;
2944 return (r.left() >= r1.left() && r.right() <= r1.right()
2945 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2946 } else {
2947 return qt_region_strictContains(region: cl->clipRegion, rect: r);
2948 }
2949}
2950
2951inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2952 int penWidth) const
2953{
2954 const QRectF norm = rect.normalized();
2955 if (norm.left() <= INT_MIN || norm.top() <= INT_MIN
2956 || norm.right() > INT_MAX || norm.bottom() > INT_MAX
2957 || norm.width() > INT_MAX || norm.height() > INT_MAX)
2958 return false;
2959 return isUnclipped(rect: norm.toAlignedRect(), penWidth);
2960}
2961
2962inline ProcessSpans
2963QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2964 const QSpanData *data) const
2965{
2966 return isUnclipped(rect, penWidth: 0) ? data->unclipped_blend : data->blend;
2967}
2968
2969inline ProcessSpans
2970QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2971 const QSpanData *data) const
2972{
2973 return isUnclipped(rect, penWidth: 0) ? data->unclipped_blend : data->blend;
2974}
2975
2976inline ProcessSpans
2977QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2978 const QSpanData *data) const
2979{
2980 Q_Q(const QRasterPaintEngine);
2981 const QRasterPaintEngineState *s = q->state();
2982
2983 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2984 return data->blend;
2985 const int penWidth = s->flags.fast_pen ? 1 : qCeil(v: s->lastPen.widthF());
2986 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2987}
2988
2989struct VisibleGlyphRange
2990{
2991 int begin;
2992 int end;
2993};
2994
2995static VisibleGlyphRange visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
2996 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
2997{
2998 QFixed clipLeft = QFixed::fromReal(r: clip.left() - 1);
2999 QFixed clipRight = QFixed::fromReal(r: clip.right() + 1);
3000 QFixed clipTop = QFixed::fromReal(r: clip.top() - 1);
3001 QFixed clipBottom = QFixed::fromReal(r: clip.bottom() + 1);
3002
3003 int first = 0;
3004 while (first < numGlyphs) {
3005 glyph_metrics_t metrics = fontEngine->boundingBox(glyph: glyphs[first]);
3006 QFixed left = metrics.x + positions[first].x;
3007 QFixed top = metrics.y + positions[first].y;
3008 QFixed right = left + metrics.width;
3009 QFixed bottom = top + metrics.height;
3010 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3011 break;
3012 ++first;
3013 }
3014 int last = numGlyphs - 1;
3015 while (last > first) {
3016 glyph_metrics_t metrics = fontEngine->boundingBox(glyph: glyphs[last]);
3017 QFixed left = metrics.x + positions[last].x;
3018 QFixed top = metrics.y + positions[last].y;
3019 QFixed right = left + metrics.width;
3020 QFixed bottom = top + metrics.height;
3021 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3022 break;
3023 --last;
3024 }
3025 return {.begin: first, .end: last + 1};
3026}
3027
3028/*!
3029 \reimp
3030*/
3031void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3032{
3033 if (textItem->numGlyphs == 0)
3034 return;
3035
3036 ensurePen();
3037 ensureRasterState();
3038
3039 QTransform matrix = state()->matrix;
3040
3041 QFontEngine *fontEngine = textItem->fontEngine();
3042 if (shouldDrawCachedGlyphs(fontEngine, m: matrix)) {
3043 drawCachedGlyphs(numGlyphs: textItem->numGlyphs, glyphs: textItem->glyphs, positions: textItem->glyphPositions,
3044 fontEngine);
3045 } else if (matrix.type() < QTransform::TxProject) {
3046 bool invertible;
3047 QTransform invMat = matrix.inverted(invertible: &invertible);
3048 if (!invertible)
3049 return;
3050
3051 const auto range = visibleGlyphRange(clip: invMat.mapRect(clipBoundingRect()),
3052 fontEngine: textItem->fontEngine(), glyphs: textItem->glyphs,
3053 positions: textItem->glyphPositions, numGlyphs: textItem->numGlyphs);
3054 QStaticTextItem copy = *textItem;
3055 copy.glyphs += range.begin;
3056 copy.glyphPositions += range.begin;
3057 copy.numGlyphs = range.end - range.begin;
3058 QPaintEngineEx::drawStaticTextItem(&copy);
3059 } else {
3060 QPaintEngineEx::drawStaticTextItem(textItem);
3061 }
3062}
3063
3064/*!
3065 \reimp
3066*/
3067void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3068{
3069 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3070
3071#ifdef QT_DEBUG_DRAW
3072 Q_D(QRasterPaintEngine);
3073 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3074 p.x(), p.y(), QStringView(ti.chars, ti.num_chars).toLatin1().data(),
3075 d->glyphCacheFormat);
3076#endif
3077
3078 if (ti.glyphs.numGlyphs == 0)
3079 return;
3080 ensurePen();
3081 ensureRasterState();
3082
3083 QRasterPaintEngineState *s = state();
3084 QTransform matrix = s->matrix;
3085
3086 if (shouldDrawCachedGlyphs(fontEngine: ti.fontEngine, m: matrix)) {
3087 QVarLengthArray<QFixedPoint> positions;
3088 QVarLengthArray<glyph_t> glyphs;
3089
3090 matrix.translate(dx: p.x(), dy: p.y());
3091 ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions);
3092
3093 drawCachedGlyphs(numGlyphs: glyphs.size(), glyphs: glyphs.constData(), positions: positions.constData(), fontEngine: ti.fontEngine);
3094 } else if (matrix.type() < QTransform::TxProject
3095 && ti.fontEngine->supportsTransformation(transform: matrix)) {
3096 bool invertible;
3097 QTransform invMat = matrix.inverted(invertible: &invertible);
3098 if (!invertible)
3099 return;
3100
3101 QVarLengthArray<QFixedPoint> positions;
3102 QVarLengthArray<glyph_t> glyphs;
3103
3104 ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix: QTransform::fromTranslate(dx: p.x(), dy: p.y()),
3105 flags: ti.flags, glyphs_out&: glyphs, positions);
3106 const auto range = visibleGlyphRange(clip: invMat.mapRect(clipBoundingRect()),
3107 fontEngine: ti.fontEngine, glyphs: glyphs.data(), positions: positions.data(),
3108 numGlyphs: glyphs.size());
3109
3110 if (range.begin >= range.end)
3111 return;
3112
3113 QStaticTextItem staticTextItem;
3114 staticTextItem.color = s->pen.color();
3115 staticTextItem.font = s->font;
3116 staticTextItem.setFontEngine(ti.fontEngine);
3117 staticTextItem.numGlyphs = range.end - range.begin;
3118 staticTextItem.glyphs = glyphs.data() + range.begin;
3119 staticTextItem.glyphPositions = positions.data() + range.begin;
3120 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3121 } else {
3122 QPaintEngineEx::drawTextItem(p, textItem: ti);
3123 }
3124}
3125
3126/*!
3127 \reimp
3128*/
3129void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3130{
3131 Q_D(QRasterPaintEngine);
3132 QRasterPaintEngineState *s = state();
3133
3134 ensurePen();
3135 if (!s->penData.blend)
3136 return;
3137
3138 if (!s->flags.fast_pen) {
3139 QPaintEngineEx::drawPoints(points, pointCount);
3140 return;
3141 }
3142
3143 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3144 stroker.drawPoints(points, num: pointCount);
3145}
3146
3147
3148void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3149{
3150 Q_D(QRasterPaintEngine);
3151 QRasterPaintEngineState *s = state();
3152
3153 ensurePen();
3154 if (!s->penData.blend)
3155 return;
3156
3157 if (!s->flags.fast_pen) {
3158 QPaintEngineEx::drawPoints(points, pointCount);
3159 return;
3160 }
3161
3162 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3163 stroker.drawPoints(points, num: pointCount);
3164}
3165
3166/*!
3167 \reimp
3168*/
3169void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3170{
3171#ifdef QT_DEBUG_DRAW
3172 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3173#endif
3174 Q_D(QRasterPaintEngine);
3175 QRasterPaintEngineState *s = state();
3176
3177 ensurePen();
3178 if (!s->penData.blend)
3179 return;
3180
3181 if (s->flags.fast_pen) {
3182 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3183 for (int i=0; i<lineCount; ++i) {
3184 const QLine &l = lines[i];
3185 stroker.drawLine(p1: l.p1(), p2: l.p2());
3186 }
3187 } else {
3188 QPaintEngineEx::drawLines(lines, lineCount);
3189 }
3190}
3191
3192void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3193 qreal width,
3194 int *dashIndex,
3195 qreal *dashOffset,
3196 bool *inDash)
3197{
3198 Q_Q(QRasterPaintEngine);
3199 QRasterPaintEngineState *s = q->state();
3200
3201 const QPen &pen = s->lastPen;
3202 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3203 const QList<qreal> pattern = pen.dashPattern();
3204
3205 qreal patternLength = 0;
3206 for (int i = 0; i < pattern.size(); ++i)
3207 patternLength += pattern.at(i);
3208
3209 if (patternLength <= 0)
3210 return;
3211
3212 qreal length = line.length();
3213 Q_ASSERT(length > 0);
3214 if (length / (patternLength * width) > QDashStroker::repetitionLimit()) {
3215 rasterizer->rasterizeLine(a: line.p1(), b: line.p2(), width: width / length, squareCap);
3216 return;
3217 }
3218
3219 while (length > 0) {
3220 const bool rasterize = *inDash;
3221 qreal dash = (pattern.at(i: *dashIndex) - *dashOffset) * width;
3222 QLineF l = line;
3223
3224 if (dash >= length) {
3225 dash = line.length(); // Avoid accumulated precision error in 'length'
3226 *dashOffset += dash / width;
3227 length = 0;
3228 } else {
3229 *dashOffset = 0;
3230 *inDash = !(*inDash);
3231 if (++*dashIndex >= pattern.size())
3232 *dashIndex = 0;
3233 length -= dash;
3234 l.setLength(dash);
3235 line.setP1(l.p2());
3236 }
3237
3238 if (rasterize && dash > 0)
3239 rasterizer->rasterizeLine(a: l.p1(), b: l.p2(), width: width / dash, squareCap);
3240 }
3241}
3242
3243/*!
3244 \reimp
3245*/
3246void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3247{
3248#ifdef QT_DEBUG_DRAW
3249 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3250#endif
3251 Q_D(QRasterPaintEngine);
3252 QRasterPaintEngineState *s = state();
3253
3254 ensurePen();
3255 if (!s->penData.blend)
3256 return;
3257 if (s->flags.fast_pen) {
3258 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3259 for (int i=0; i<lineCount; ++i) {
3260 QLineF line = lines[i];
3261 stroker.drawLine(p1: line.p1(), p2: line.p2());
3262 }
3263 } else {
3264 QPaintEngineEx::drawLines(lines, lineCount);
3265 }
3266}
3267
3268
3269/*!
3270 \reimp
3271*/
3272void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3273{
3274 Q_D(QRasterPaintEngine);
3275 QRasterPaintEngineState *s = state();
3276
3277 ensurePen();
3278 if (((qpen_style(p: s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3279 || (qpen_style(p: s->lastPen) == Qt::NoPen))
3280 && !s->flags.antialiased
3281 && qMax(a: rect.width(), b: rect.height()) < QT_RASTER_COORD_LIMIT
3282 && !rect.isEmpty()
3283 && s->matrix.type() <= QTransform::TxScale) // no shear
3284 {
3285 ensureBrush();
3286 const QRectF r = s->matrix.mapRect(rect);
3287 ProcessSpans penBlend = d->getPenFunc(rect: r, data: &s->penData);
3288 ProcessSpans brushBlend = d->getBrushFunc(rect: r, data: &s->brushData);
3289 const QRect brect = QRect(int(r.x()), int(r.y()),
3290 int_dim(r.x(), r.width()),
3291 int_dim(r.y(), r.height()));
3292 if (brect == r) {
3293 drawEllipse_midpoint_i(rect: brect, clip: d->deviceRect, pen_func: penBlend, brush_func: brushBlend,
3294 pen_data: &s->penData, brush_data: &s->brushData);
3295 return;
3296 }
3297 }
3298 QPaintEngineEx::drawEllipse(r: rect);
3299}
3300
3301
3302#ifdef Q_OS_WIN
3303/*!
3304 \internal
3305*/
3306void QRasterPaintEngine::setDC(HDC hdc) {
3307 Q_D(QRasterPaintEngine);
3308 d->hdc = hdc;
3309}
3310
3311/*!
3312 \internal
3313*/
3314HDC QRasterPaintEngine::getDC() const
3315{
3316 Q_D(const QRasterPaintEngine);
3317 return d->hdc;
3318}
3319
3320/*!
3321 \internal
3322*/
3323void QRasterPaintEngine::releaseDC(HDC) const
3324{
3325}
3326
3327#endif
3328
3329/*!
3330 \internal
3331*/
3332bool QRasterPaintEngine::requiresPretransformedGlyphPositions(QFontEngine *fontEngine, const QTransform &m) const
3333{
3334 // Cached glyphs always require pretransformed positions
3335 if (shouldDrawCachedGlyphs(fontEngine, m))
3336 return true;
3337
3338 // Otherwise let the base-class decide based on the transform
3339 return QPaintEngineEx::requiresPretransformedGlyphPositions(fontEngine, m);
3340}
3341
3342/*!
3343 Returns whether glyph caching is supported by the font engine
3344 \a fontEngine with the given transform \a m applied.
3345*/
3346bool QRasterPaintEngine::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const
3347{
3348 // The raster engine does not support projected cached glyph drawing
3349 if (m.type() >= QTransform::TxProject)
3350 return false;
3351
3352 // The font engine might not support filling the glyph cache
3353 // with the given transform applied, in which case we need to
3354 // fall back to the QPainterPath code-path. This does not apply
3355 // for engines with internal caching, as we don't use the engine
3356 // to fill up our cache in that case.
3357 if (!fontEngine->hasInternalCaching() && !fontEngine->supportsTransformation(transform: m))
3358 return false;
3359
3360 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, m);
3361}
3362
3363/*!
3364 \internal
3365*/
3366QPoint QRasterPaintEngine::coordinateOffset() const
3367{
3368 return QPoint(0, 0);
3369}
3370
3371void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3372{
3373 Q_ASSERT(fg);
3374 if (!fg->blend)
3375 return;
3376 Q_D(QRasterPaintEngine);
3377
3378 Q_ASSERT(image.depth() == 1);
3379
3380 const int spanCount = 512;
3381 QT_FT_Span spans[spanCount];
3382 int n = 0;
3383
3384 // Boundaries
3385 int w = image.width();
3386 int h = image.height();
3387 int px = qRound(d: pos.x());
3388 int py = qRound(d: pos.y());
3389 int ymax = qMin(a: py + h, b: d->rasterBuffer->height());
3390 int ymin = qMax(a: py, b: 0);
3391 int xmax = qMin(a: px + w, b: d->rasterBuffer->width());
3392 int xmin = qMax(a: px, b: 0);
3393
3394 int x_offset = xmin - px;
3395
3396 QImage::Format format = image.format();
3397 for (int y = ymin; y < ymax; ++y) {
3398 const uchar *src = image.scanLine(y - py);
3399 if (format == QImage::Format_MonoLSB) {
3400 for (int x = 0; x < xmax - xmin; ++x) {
3401 int src_x = x + x_offset;
3402 uchar pixel = src[src_x >> 3];
3403 if (!pixel) {
3404 x += 7 - (src_x%8);
3405 continue;
3406 }
3407 if (pixel & (0x1 << (src_x & 7))) {
3408 spans[n].x = xmin + x;
3409 spans[n].y = y;
3410 spans[n].coverage = 255;
3411 int len = 1;
3412 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3413 ++src_x;
3414 ++len;
3415 }
3416 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3417 x += len;
3418 ++n;
3419 if (n == spanCount) {
3420 fg->blend(n, spans, fg);
3421 n = 0;
3422 }
3423 }
3424 }
3425 } else {
3426 for (int x = 0; x < xmax - xmin; ++x) {
3427 int src_x = x + x_offset;
3428 uchar pixel = src[src_x >> 3];
3429 if (!pixel) {
3430 x += 7 - (src_x%8);
3431 continue;
3432 }
3433 if (pixel & (0x80 >> (x & 7))) {
3434 spans[n].x = xmin + x;
3435 spans[n].y = y;
3436 spans[n].coverage = 255;
3437 int len = 1;
3438 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3439 ++src_x;
3440 ++len;
3441 }
3442 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3443 x += len;
3444 ++n;
3445 if (n == spanCount) {
3446 fg->blend(n, spans, fg);
3447 n = 0;
3448 }
3449 }
3450 }
3451 }
3452 }
3453 if (n) {
3454 fg->blend(n, spans, fg);
3455 n = 0;
3456 }
3457}
3458
3459/*!
3460 \enum QRasterPaintEngine::ClipType
3461 \internal
3462
3463 \value RectClip Indicates that the currently set clip is a single rectangle.
3464 \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3465*/
3466
3467/*!
3468 \internal
3469 Returns the type of the clip currently set.
3470*/
3471QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3472{
3473 Q_D(const QRasterPaintEngine);
3474
3475 const QClipData *clip = d->clip();
3476 if (!clip || clip->hasRectClip)
3477 return RectClip;
3478 else
3479 return ComplexClip;
3480}
3481
3482/*!
3483 \internal
3484 Returns the bounding rect of the currently set clip.
3485*/
3486QRectF QRasterPaintEngine::clipBoundingRect() const
3487{
3488 Q_D(const QRasterPaintEngine);
3489
3490 const QClipData *clip = d->clip();
3491
3492 if (!clip)
3493 return d->deviceRect;
3494
3495 if (clip->hasRectClip)
3496 return clip->clipRect;
3497
3498 return QRectF(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3499}
3500
3501void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3502{
3503 Q_Q(QRasterPaintEngine);
3504 QRasterPaintEngineState *s = q->state();
3505
3506 rasterizer->setAntialiased(s->flags.antialiased);
3507
3508 QRect clipRect(deviceRect);
3509 ProcessSpans blend;
3510 // ### get from optimized rectbased QClipData
3511
3512 const QClipData *c = clip();
3513 if (c) {
3514 const QRect r(QPoint(c->xmin, c->ymin),
3515 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3516 clipRect = clipRect.intersected(other: r);
3517 blend = data->blend;
3518 } else {
3519 blend = data->unclipped_blend;
3520 }
3521
3522 rasterizer->setClipRect(clipRect);
3523 rasterizer->initialize(blend, data);
3524}
3525
3526void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3527 ProcessSpans callback,
3528 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3529{
3530 if (!callback || !outline)
3531 return;
3532
3533 Q_Q(QRasterPaintEngine);
3534 QRasterPaintEngineState *s = q->state();
3535
3536 if (!s->flags.antialiased) {
3537 initializeRasterizer(data: spanData);
3538
3539 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3540 ? Qt::WindingFill
3541 : Qt::OddEvenFill;
3542
3543 rasterizer->rasterize(outline, fillRule);
3544 return;
3545 }
3546
3547 rasterize(outline, callback, userData: (void *)spanData, rasterBuffer);
3548}
3549
3550extern "C" {
3551 int q_gray_rendered_spans(QT_FT_Raster raster);
3552}
3553
3554static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3555{
3556 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3557}
3558
3559void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3560 ProcessSpans callback,
3561 void *userData, QRasterBuffer *)
3562{
3563 if (!callback || !outline)
3564 return;
3565
3566 Q_Q(QRasterPaintEngine);
3567 QRasterPaintEngineState *s = q->state();
3568
3569 if (!s->flags.antialiased) {
3570 rasterizer->setAntialiased(s->flags.antialiased);
3571 rasterizer->setClipRect(deviceRect);
3572 rasterizer->initialize(blend: callback, data: userData);
3573
3574 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3575 ? Qt::WindingFill
3576 : Qt::OddEvenFill;
3577
3578 rasterizer->rasterize(outline, fillRule);
3579 return;
3580 }
3581
3582 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3583 // minimize memory reallocations. However if initial size for
3584 // raster pool is changed for lower value, reallocations will
3585 // occur normally.
3586 int rasterPoolSize = MINIMUM_POOL_SIZE;
3587 Q_DECL_UNINITIALIZED uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3588 uchar *rasterPoolBase = alignAddress(address: rasterPoolOnStack, alignmentMask: 0xf);
3589 uchar *rasterPoolOnHeap = nullptr;
3590
3591 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3592
3593 void *data = userData;
3594
3595 QT_FT_BBox clip_box = { .xMin: deviceRect.x(),
3596 .yMin: deviceRect.y(),
3597 .xMax: deviceRect.x() + deviceRect.width(),
3598 .yMax: deviceRect.y() + deviceRect.height() };
3599
3600 QT_FT_Raster_Params rasterParams;
3601 rasterParams.target = nullptr;
3602 rasterParams.source = outline;
3603 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3604 rasterParams.gray_spans = nullptr;
3605 rasterParams.black_spans = nullptr;
3606 rasterParams.bit_test = nullptr;
3607 rasterParams.bit_set = nullptr;
3608 rasterParams.user = data;
3609 rasterParams.clip_box = clip_box;
3610
3611 bool done = false;
3612 int error;
3613
3614 int rendered_spans = 0;
3615
3616 while (!done) {
3617
3618 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3619 rasterParams.gray_spans = callback;
3620 rasterParams.skip_spans = rendered_spans;
3621 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3622
3623 // Out of memory, reallocate some more and try again...
3624 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3625 rasterPoolSize *= 2;
3626 if (rasterPoolSize > 1024 * 1024) {
3627 qWarning(msg: "QPainter: Rasterization of primitive failed");
3628 break;
3629 }
3630
3631 rendered_spans += q_gray_rendered_spans(raster: *grayRaster.data());
3632
3633 free(ptr: rasterPoolOnHeap);
3634 rasterPoolOnHeap = (uchar *)malloc(size: rasterPoolSize + 0xf);
3635
3636 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3637
3638 rasterPoolBase = alignAddress(address: rasterPoolOnHeap, alignmentMask: 0xf);
3639
3640 qt_ft_grays_raster.raster_done(*grayRaster.data());
3641 qt_ft_grays_raster.raster_new(grayRaster.data());
3642 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3643 } else {
3644 done = true;
3645 }
3646 }
3647
3648 free(ptr: rasterPoolOnHeap);
3649}
3650
3651void QRasterPaintEnginePrivate::updateClipping()
3652{
3653 Q_Q(QRasterPaintEngine);
3654 QRasterPaintEngineState *s = q->state();
3655
3656 if (!s->clipEnabled)
3657 return;
3658
3659 qrasterpaintengine_state_setNoClip(s);
3660 replayClipOperations();
3661}
3662
3663void QRasterPaintEnginePrivate::recalculateFastImages()
3664{
3665 Q_Q(QRasterPaintEngine);
3666 QRasterPaintEngineState *s = q->state();
3667
3668 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3669 && s->matrix.type() <= QTransform::TxShear;
3670}
3671
3672bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3673{
3674 Q_Q(const QRasterPaintEngine);
3675 const QRasterPaintEngineState *s = q->state();
3676
3677 return s->flags.fast_images
3678 && (mode == QPainter::CompositionMode_SourceOver
3679 || (mode == QPainter::CompositionMode_Source
3680 && !image.hasAlphaChannel()));
3681}
3682
3683bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image, const QPointF &pt, const QRectF &sr) const
3684{
3685 Q_Q(const QRasterPaintEngine);
3686
3687 if (!(mode == QPainter::CompositionMode_Source
3688 || (mode == QPainter::CompositionMode_SourceOver
3689 && !image.hasAlphaChannel())))
3690 return false;
3691
3692 const QRasterPaintEngineState *s = q->state();
3693 Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate);
3694
3695 if (s->intOpacity != 256
3696 || image.depth() < 8
3697 || ((s->renderHints & (QPainter::SmoothPixmapTransform | QPainter::Antialiasing))
3698 && (!isPixelAligned(pt) || !isPixelAligned(rect: sr))))
3699 return false;
3700
3701 QImage::Format dFormat = rasterBuffer->format;
3702 QImage::Format sFormat = image.format();
3703 // Formats must match or source format must be an opaque version of destination format
3704 if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha)
3705 dFormat = qt_maybeDataCompatibleOpaqueVersion(format: dFormat);
3706 return (dFormat == sFormat);
3707}
3708
3709QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3710{
3711 Q_ASSERT(image.depth() == 1);
3712
3713 const QImage sourceImage = image.convertToFormat(f: QImage::Format_MonoLSB);
3714 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3715 if (sourceImage.isNull() || dest.isNull())
3716 return image; // we must have run out of memory
3717
3718 QRgb fg = qPremultiply(x: color.rgba());
3719 QRgb bg = 0;
3720
3721 int height = sourceImage.height();
3722 int width = sourceImage.width();
3723 for (int y=0; y<height; ++y) {
3724 const uchar *source = sourceImage.constScanLine(y);
3725 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3726 for (int x=0; x < width; ++x)
3727 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3728 }
3729 return dest;
3730}
3731
3732QRasterBuffer::~QRasterBuffer()
3733{
3734}
3735
3736void QRasterBuffer::init()
3737{
3738 compositionMode = QPainter::CompositionMode_SourceOver;
3739 monoDestinationWithClut = false;
3740 destColor0 = 0;
3741 destColor1 = 0;
3742}
3743
3744QImage::Format QRasterBuffer::prepare(QImage *image)
3745{
3746 m_buffer = (uchar *)image->bits();
3747 m_width = qMin(a: QT_RASTER_COORD_LIMIT, b: image->width());
3748 m_height = qMin(a: QT_RASTER_COORD_LIMIT, b: image->height());
3749 bytes_per_pixel = image->depth()/8;
3750 bytes_per_line = image->bytesPerLine();
3751
3752 format = image->format();
3753 colorSpace = image->colorSpace();
3754 if (image->depth() == 1 && image->colorTable().size() == 2) {
3755 monoDestinationWithClut = true;
3756 const QList<QRgb> colorTable = image->colorTable();
3757 destColor0 = qPremultiply(x: colorTable[0]);
3758 destColor1 = qPremultiply(x: colorTable[1]);
3759 }
3760
3761 return format;
3762}
3763
3764QClipData::QClipData(int height)
3765{
3766 clipSpanHeight = height;
3767 m_clipLines = nullptr;
3768
3769 allocated = 0;
3770 m_spans = nullptr;
3771 xmin = xmax = ymin = ymax = 0;
3772 count = 0;
3773
3774 enabled = true;
3775 hasRectClip = hasRegionClip = false;
3776}
3777
3778QClipData::~QClipData()
3779{
3780 if (m_clipLines)
3781 free(ptr: m_clipLines);
3782 if (m_spans)
3783 free(ptr: m_spans);
3784}
3785
3786void QClipData::initialize()
3787{
3788 if (m_spans)
3789 return;
3790
3791 if (!m_clipLines)
3792 m_clipLines = (ClipLine *)calloc(nmemb: clipSpanHeight, size: sizeof(ClipLine));
3793
3794 Q_CHECK_PTR(m_clipLines);
3795 QT_TRY {
3796 allocated = clipSpanHeight;
3797 count = 0;
3798 QT_TRY {
3799 if (hasRegionClip) {
3800 const auto rects = clipRegion.begin();
3801 const int numRects = clipRegion.rectCount();
3802 const int maxSpans = (ymax - ymin) * numRects;
3803 allocated = qMax(a: allocated, b: maxSpans);
3804 m_spans = (QT_FT_Span *)malloc(size: allocated * sizeof(QT_FT_Span));
3805 Q_CHECK_PTR(m_spans);
3806
3807 int y = 0;
3808 int firstInBand = 0;
3809 while (firstInBand < numRects) {
3810 const int currMinY = rects[firstInBand].y();
3811 const int currMaxY = currMinY + rects[firstInBand].height();
3812
3813 while (y < currMinY) {
3814 m_clipLines[y].spans = nullptr;
3815 m_clipLines[y].count = 0;
3816 ++y;
3817 }
3818
3819 int lastInBand = firstInBand;
3820 while (lastInBand + 1 < numRects && rects[lastInBand+1].top() == y)
3821 ++lastInBand;
3822
3823 while (y < currMaxY) {
3824
3825 m_clipLines[y].spans = m_spans + count;
3826 m_clipLines[y].count = lastInBand - firstInBand + 1;
3827
3828 for (int r = firstInBand; r <= lastInBand; ++r) {
3829 const QRect &currRect = rects[r];
3830 QT_FT_Span *span = m_spans + count;
3831 span->x = currRect.x();
3832 span->len = currRect.width();
3833 span->y = y;
3834 span->coverage = 255;
3835 ++count;
3836 }
3837 ++y;
3838 }
3839
3840 firstInBand = lastInBand + 1;
3841 }
3842
3843 Q_ASSERT(count <= allocated);
3844
3845 while (y < clipSpanHeight) {
3846 m_clipLines[y].spans = nullptr;
3847 m_clipLines[y].count = 0;
3848 ++y;
3849 }
3850
3851 return;
3852 }
3853
3854 m_spans = (QT_FT_Span *)malloc(size: allocated * sizeof(QT_FT_Span));
3855 Q_CHECK_PTR(m_spans);
3856
3857 if (hasRectClip) {
3858 int y = 0;
3859 while (y < ymin) {
3860 m_clipLines[y].spans = nullptr;
3861 m_clipLines[y].count = 0;
3862 ++y;
3863 }
3864
3865 const int len = clipRect.width();
3866 while (y < ymax) {
3867 QT_FT_Span *span = m_spans + count;
3868 span->x = xmin;
3869 span->len = len;
3870 span->y = y;
3871 span->coverage = 255;
3872 ++count;
3873
3874 m_clipLines[y].spans = span;
3875 m_clipLines[y].count = 1;
3876 ++y;
3877 }
3878
3879 while (y < clipSpanHeight) {
3880 m_clipLines[y].spans = nullptr;
3881 m_clipLines[y].count = 0;
3882 ++y;
3883 }
3884 }
3885 } QT_CATCH(...) {
3886 free(ptr: m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3887 m_spans = nullptr;
3888 QT_RETHROW;
3889 }
3890 } QT_CATCH(...) {
3891 free(ptr: m_clipLines); // same for clipLines
3892 m_clipLines = nullptr;
3893 QT_RETHROW;
3894 }
3895}
3896
3897void QClipData::fixup()
3898{
3899 Q_ASSERT(m_spans);
3900
3901 if (count == 0) {
3902 ymin = ymax = xmin = xmax = 0;
3903 return;
3904 }
3905
3906 int y = -1;
3907 ymin = m_spans[0].y;
3908 ymax = m_spans[count-1].y + 1;
3909 xmin = INT_MAX;
3910 xmax = 0;
3911
3912 const int firstLeft = m_spans[0].x;
3913 const int firstRight = m_spans[0].x + m_spans[0].len;
3914 bool isRect = true;
3915
3916 for (int i = 0; i < count; ++i) {
3917 QT_FT_Span_& span = m_spans[i];
3918
3919 if (span.y != y) {
3920 if (span.y != y + 1 && y != -1)
3921 isRect = false;
3922 y = span.y;
3923 m_clipLines[y].spans = &span;
3924 m_clipLines[y].count = 1;
3925 } else
3926 ++m_clipLines[y].count;
3927
3928 const int spanLeft = span.x;
3929 const int spanRight = spanLeft + span.len;
3930
3931 if (spanLeft < xmin)
3932 xmin = spanLeft;
3933
3934 if (spanRight > xmax)
3935 xmax = spanRight;
3936
3937 if (spanLeft != firstLeft || spanRight != firstRight)
3938 isRect = false;
3939 }
3940
3941 if (isRect) {
3942 hasRectClip = true;
3943 clipRect.setRect(ax: xmin, ay: ymin, aw: xmax - xmin, ah: ymax - ymin);
3944 }
3945}
3946
3947/*
3948 Convert \a rect to clip spans.
3949 */
3950void QClipData::setClipRect(const QRect &rect)
3951{
3952 if (hasRectClip && rect == clipRect)
3953 return;
3954
3955// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3956 hasRectClip = true;
3957 hasRegionClip = false;
3958 clipRect = rect;
3959
3960 xmin = rect.x();
3961 xmax = rect.x() + rect.width();
3962 ymin = qMin(a: rect.y(), b: clipSpanHeight);
3963 ymax = qMin(a: rect.y() + rect.height(), b: clipSpanHeight);
3964
3965 if (m_spans) {
3966 free(ptr: m_spans);
3967 m_spans = nullptr;
3968 }
3969
3970// qDebug() << xmin << xmax << ymin << ymax;
3971}
3972
3973/*
3974 Convert \a region to clip spans.
3975 */
3976void QClipData::setClipRegion(const QRegion &region)
3977{
3978 if (region.rectCount() == 1) {
3979 setClipRect(region.boundingRect());
3980 return;
3981 }
3982
3983 hasRegionClip = true;
3984 hasRectClip = false;
3985 clipRegion = region;
3986
3987 { // set bounding rect
3988 const QRect rect = region.boundingRect();
3989 xmin = rect.x();
3990 xmax = rect.x() + rect.width();
3991 ymin = rect.y();
3992 ymax = rect.y() + rect.height();
3993 }
3994
3995 if (m_spans) {
3996 free(ptr: m_spans);
3997 m_spans = nullptr;
3998 }
3999
4000}
4001
4002/*!
4003 \internal
4004 spans must be sorted on y
4005*/
4006static const QT_FT_Span *qt_intersect_spans(const QClipData *clip, int *currentClip,
4007 const QT_FT_Span *spans, const QT_FT_Span *end,
4008 QT_FT_Span **outSpans, int available)
4009{
4010 const_cast<QClipData *>(clip)->initialize();
4011
4012 QT_FT_Span *out = *outSpans;
4013
4014 const QT_FT_Span *clipSpans = clip->m_spans + *currentClip;
4015 const QT_FT_Span *clipEnd = clip->m_spans + clip->count;
4016
4017 while (available && spans < end ) {
4018 if (clipSpans >= clipEnd) {
4019 spans = end;
4020 break;
4021 }
4022 if (clipSpans->y > spans->y) {
4023 ++spans;
4024 continue;
4025 }
4026 if (spans->y != clipSpans->y) {
4027 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4028 clipSpans = clip->m_clipLines[spans->y].spans;
4029 else
4030 ++clipSpans;
4031 continue;
4032 }
4033 Q_ASSERT(spans->y == clipSpans->y);
4034
4035 int sx1 = spans->x;
4036 int sx2 = sx1 + spans->len;
4037 int cx1 = clipSpans->x;
4038 int cx2 = cx1 + clipSpans->len;
4039
4040 if (cx1 < sx1 && cx2 < sx1) {
4041 ++clipSpans;
4042 continue;
4043 } else if (sx1 < cx1 && sx2 < cx1) {
4044 ++spans;
4045 continue;
4046 }
4047 int x = qMax(a: sx1, b: cx1);
4048 int len = qMin(a: sx2, b: cx2) - x;
4049 if (len) {
4050 out->x = qMax(a: sx1, b: cx1);
4051 out->len = qMin(a: sx2, b: cx2) - out->x;
4052 out->y = spans->y;
4053 out->coverage = qt_div_255(x: spans->coverage * clipSpans->coverage);
4054 ++out;
4055 --available;
4056 }
4057 if (sx2 < cx2) {
4058 ++spans;
4059 } else {
4060 ++clipSpans;
4061 }
4062 }
4063
4064 *outSpans = out;
4065 *currentClip = clipSpans - clip->m_spans;
4066 return spans;
4067}
4068
4069static void qt_span_fill_clipped(int spanCount, const QT_FT_Span *spans, void *userData)
4070{
4071// qDebug() << "qt_span_fill_clipped" << spanCount;
4072 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4073
4074 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4075
4076 const int NSPANS = 512;
4077 Q_DECL_UNINITIALIZED QT_FT_Span cspans[NSPANS];
4078 int currentClip = 0;
4079 const QT_FT_Span *end = spans + spanCount;
4080 while (spans < end) {
4081 QT_FT_Span *clipped = cspans;
4082 spans = qt_intersect_spans(clip: fillData->clip, currentClip: &currentClip, spans, end, outSpans: &clipped, available: NSPANS);
4083// qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4084// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4085
4086 if (clipped - cspans)
4087 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4088 }
4089}
4090
4091/*
4092 \internal
4093 Clip spans to \a{clip}-rectangle.
4094 Returns number of unclipped spans
4095*/
4096static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
4097 const QRect &clip)
4098{
4099 const int minx = clip.left();
4100 const int miny = clip.top();
4101 const int maxx = clip.right();
4102 const int maxy = clip.bottom();
4103
4104 QT_FT_Span *end = spans + numSpans;
4105 while (spans < end) {
4106 if (spans->y >= miny)
4107 break;
4108 ++spans;
4109 }
4110
4111 QT_FT_Span *s = spans;
4112 while (s < end) {
4113 if (s->y > maxy)
4114 break;
4115 if (s->x > maxx || s->x + s->len <= minx) {
4116 s->len = 0;
4117 ++s;
4118 continue;
4119 }
4120 if (s->x < minx) {
4121 s->len = qMin(a: s->len - (minx - s->x), b: maxx - minx + 1);
4122 s->x = minx;
4123 } else {
4124 s->len = qMin(a: s->len, b: (maxx - s->x + 1));
4125 }
4126 ++s;
4127 }
4128
4129 return s - spans;
4130}
4131
4132
4133static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans,
4134 void *userData)
4135{
4136 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4137 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4138
4139 Q_ASSERT(fillData->clip);
4140 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4141
4142 QT_FT_Span *s = const_cast<QT_FT_Span *>(spans);
4143 // hw: check if this const_cast<> is safe!!!
4144 count = qt_intersect_spans(spans&: s, numSpans: count,
4145 clip: fillData->clip->clipRect);
4146 if (count > 0)
4147 fillData->unclipped_blend(count, s, fillData);
4148}
4149
4150static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData)
4151{
4152 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4153
4154// qDebug() << " qt_span_clip: " << count << clipData->operation;
4155// for (int i = 0; i < qMin(count, 10); ++i) {
4156// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4157// }
4158
4159 switch (clipData->operation) {
4160
4161 case Qt::IntersectClip:
4162 {
4163 QClipData *newClip = clipData->newClip;
4164 newClip->initialize();
4165
4166 int currentClip = 0;
4167 const QT_FT_Span *end = spans + count;
4168 while (spans < end) {
4169 QT_FT_Span *newspans = newClip->m_spans + newClip->count;
4170 spans = qt_intersect_spans(clip: clipData->oldClip, currentClip: &currentClip, spans, end,
4171 outSpans: &newspans, available: newClip->allocated - newClip->count);
4172 newClip->count = newspans - newClip->m_spans;
4173 if (spans < end) {
4174 newClip->m_spans = q_check_ptr(p: (QT_FT_Span *)realloc(ptr: newClip->m_spans, size: newClip->allocated * 2 * sizeof(QT_FT_Span)));
4175 newClip->allocated *= 2;
4176 }
4177 }
4178 }
4179 break;
4180
4181 case Qt::ReplaceClip:
4182 clipData->newClip->appendSpans(s: spans, num: count);
4183 break;
4184 case Qt::NoClip:
4185 break;
4186 }
4187}
4188
4189class QGradientCache
4190{
4191public:
4192 struct CacheInfo
4193 {
4194 inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4195 stops(std::move(s)), opacity(op), interpolationMode(mode) {}
4196 QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE];
4197 QRgb buffer32[GRADIENT_STOPTABLE_SIZE];
4198 QGradientStops stops;
4199 int opacity;
4200 QGradient::InterpolationMode interpolationMode;
4201 };
4202
4203 using QGradientColorTableHash = QMultiHash<quint64, std::shared_ptr<const CacheInfo>>;
4204
4205 std::shared_ptr<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
4206 quint64 hash_val = 0;
4207
4208 const QGradientStops stops = gradient.stops();
4209 for (int i = 0; i < stops.size() && i <= 2; i++)
4210 hash_val += stops[i].second.rgba64();
4211
4212 QMutexLocker lock(&mutex);
4213 QGradientColorTableHash::const_iterator it = cache.constFind(key: hash_val);
4214
4215 if (it == cache.constEnd())
4216 return addCacheElement(hash_val, gradient, opacity);
4217 else {
4218 do {
4219 const auto &cache_info = it.value();
4220 if (cache_info->stops == stops && cache_info->opacity == opacity && cache_info->interpolationMode == gradient.interpolationMode())
4221 return cache_info;
4222 ++it;
4223 } while (it != cache.constEnd() && it.key() == hash_val);
4224 // an exact match for these stops and opacity was not found, create new cache
4225 return addCacheElement(hash_val, gradient, opacity);
4226 }
4227 }
4228
4229 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4230protected:
4231 inline int maxCacheSize() const { return 60; }
4232 inline void generateGradientColorTable(const QGradient& g,
4233 QRgba64 *colorTable,
4234 int size, int opacity) const;
4235 std::shared_ptr<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4236 if (cache.size() == maxCacheSize()) {
4237 // may remove more than 1, but OK
4238 cache.erase(it: std::next(x: cache.begin(), n: QRandomGenerator::global()->bounded(highest: maxCacheSize())));
4239 }
4240 auto cache_entry = std::make_shared<CacheInfo>(args: gradient.stops(), args&: opacity, args: gradient.interpolationMode());
4241 generateGradientColorTable(g: gradient, colorTable: cache_entry->buffer64, size: paletteSize(), opacity);
4242 for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
4243 cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
4244 return cache.insert(key: hash_val, value: std::move(cache_entry)).value();
4245 }
4246
4247 QGradientColorTableHash cache;
4248 QMutex mutex;
4249};
4250
4251void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
4252{
4253 const QGradientStops stops = gradient.stops();
4254 int stopCount = stops.size();
4255 Q_ASSERT(stopCount > 0);
4256
4257 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4258
4259 if (stopCount == 2) {
4260 QRgba64 first_color = combineAlpha256(rgba64: stops[0].second.rgba64(), alpha256: opacity);
4261 QRgba64 second_color = combineAlpha256(rgba64: stops[1].second.rgba64(), alpha256: opacity);
4262
4263 qreal first_stop = stops[0].first;
4264 qreal second_stop = stops[1].first;
4265
4266 if (second_stop < first_stop) {
4267 quint64 tmp = first_color;
4268 first_color = second_color;
4269 second_color = tmp;
4270 qSwap(value1&: first_stop, value2&: second_stop);
4271 }
4272
4273 if (colorInterpolation) {
4274 first_color = qPremultiply(c: first_color);
4275 second_color = qPremultiply(c: second_color);
4276 }
4277
4278 int first_index = qRound(d: first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4279 int second_index = qRound(d: second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4280
4281 uint red_first = uint(first_color.red()) << 16;
4282 uint green_first = uint(first_color.green()) << 16;
4283 uint blue_first = uint(first_color.blue()) << 16;
4284 uint alpha_first = uint(first_color.alpha()) << 16;
4285
4286 uint red_second = uint(second_color.red()) << 16;
4287 uint green_second = uint(second_color.green()) << 16;
4288 uint blue_second = uint(second_color.blue()) << 16;
4289 uint alpha_second = uint(second_color.alpha()) << 16;
4290
4291 int i = 0;
4292 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, b: first_index); ++i) {
4293 if (colorInterpolation)
4294 colorTable[i] = first_color;
4295 else
4296 colorTable[i] = qPremultiply(c: first_color);
4297 }
4298
4299 if (i < second_index) {
4300 qreal reciprocal = qreal(1) / (second_index - first_index);
4301
4302 int red_delta = qRound(d: (qreal(red_second) - red_first) * reciprocal);
4303 int green_delta = qRound(d: (qreal(green_second) - green_first) * reciprocal);
4304 int blue_delta = qRound(d: (qreal(blue_second) - blue_first) * reciprocal);
4305 int alpha_delta = qRound(d: (qreal(alpha_second) - alpha_first) * reciprocal);
4306
4307 // rounding
4308 red_first += 1 << 15;
4309 green_first += 1 << 15;
4310 blue_first += 1 << 15;
4311 alpha_first += 1 << 15;
4312
4313 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, b: second_index); ++i) {
4314 red_first += red_delta;
4315 green_first += green_delta;
4316 blue_first += blue_delta;
4317 alpha_first += alpha_delta;
4318
4319 const QRgba64 color = qRgba64(r: red_first >> 16, g: green_first >> 16, b: blue_first >> 16, a: alpha_first >> 16);
4320
4321 if (colorInterpolation)
4322 colorTable[i] = color;
4323 else
4324 colorTable[i] = qPremultiply(c: color);
4325 }
4326 }
4327
4328 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4329 if (colorInterpolation)
4330 colorTable[i] = second_color;
4331 else
4332 colorTable[i] = qPremultiply(c: second_color);
4333 }
4334
4335 return;
4336 }
4337
4338 QRgba64 current_color = combineAlpha256(rgba64: stops[0].second.rgba64(), alpha256: opacity);
4339 if (stopCount == 1) {
4340 current_color = qPremultiply(c: current_color);
4341 for (int i = 0; i < size; ++i)
4342 colorTable[i] = current_color;
4343 return;
4344 }
4345
4346 // The position where the gradient begins and ends
4347 qreal begin_pos = stops[0].first;
4348 qreal end_pos = stops[stopCount-1].first;
4349
4350 int pos = 0; // The position in the color table.
4351 QRgba64 next_color;
4352
4353 qreal incr = 1 / qreal(size); // the double increment.
4354 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4355
4356 // Up to first point
4357 colorTable[pos++] = qPremultiply(c: current_color);
4358 while (dpos <= begin_pos) {
4359 colorTable[pos] = colorTable[pos - 1];
4360 ++pos;
4361 dpos += incr;
4362 }
4363
4364 int current_stop = 0; // We always interpolate between current and current + 1.
4365
4366 qreal t; // position between current left and right stops
4367 qreal t_delta; // the t increment per entry in the color table
4368
4369 if (dpos < end_pos) {
4370 // Gradient area
4371 while (dpos > stops[current_stop+1].first)
4372 ++current_stop;
4373
4374 if (current_stop != 0)
4375 current_color = combineAlpha256(rgba64: stops[current_stop].second.rgba64(), alpha256: opacity);
4376 next_color = combineAlpha256(rgba64: stops[current_stop+1].second.rgba64(), alpha256: opacity);
4377
4378 if (colorInterpolation) {
4379 current_color = qPremultiply(c: current_color);
4380 next_color = qPremultiply(c: next_color);
4381 }
4382
4383 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4384 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4385 t = (dpos - stops[current_stop].first) * c;
4386 t_delta = incr * c;
4387
4388 while (true) {
4389 Q_ASSERT(current_stop < stopCount);
4390
4391 int dist = qRound(d: t);
4392 int idist = 256 - dist;
4393
4394 if (colorInterpolation)
4395 colorTable[pos] = interpolate256(x: current_color, alpha1: idist, y: next_color, alpha2: dist);
4396 else
4397 colorTable[pos] = qPremultiply(c: interpolate256(x: current_color, alpha1: idist, y: next_color, alpha2: dist));
4398
4399 ++pos;
4400 dpos += incr;
4401
4402 if (dpos >= end_pos)
4403 break;
4404
4405 t += t_delta;
4406
4407 int skip = 0;
4408 while (dpos > stops[current_stop+skip+1].first)
4409 ++skip;
4410
4411 if (skip != 0) {
4412 current_stop += skip;
4413 if (skip == 1)
4414 current_color = next_color;
4415 else
4416 current_color = combineAlpha256(rgba64: stops[current_stop].second.rgba64(), alpha256: opacity);
4417 next_color = combineAlpha256(rgba64: stops[current_stop+1].second.rgba64(), alpha256: opacity);
4418
4419 if (colorInterpolation) {
4420 if (skip != 1)
4421 current_color = qPremultiply(c: current_color);
4422 next_color = qPremultiply(c: next_color);
4423 }
4424
4425 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4426 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4427 t = (dpos - stops[current_stop].first) * c;
4428 t_delta = incr * c;
4429 }
4430 }
4431 }
4432
4433 // After last point
4434 current_color = qPremultiply(c: combineAlpha256(rgba64: stops[stopCount - 1].second.rgba64(), alpha256: opacity));
4435 while (pos < size - 1) {
4436 colorTable[pos] = current_color;
4437 ++pos;
4438 }
4439
4440 // Make sure the last color stop is represented at the end of the table
4441 colorTable[size - 1] = current_color;
4442}
4443
4444Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4445
4446
4447void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4448{
4449 rasterBuffer = rb;
4450 type = None;
4451 txop = 0;
4452 bilinear = false;
4453 m11 = m22 = m33 = 1.;
4454 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4455 clip = pe ? pe->d_func()->clip() : nullptr;
4456}
4457
4458Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4459
4460void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode,
4461 bool isCosmetic)
4462{
4463 Qt::BrushStyle brushStyle = qbrush_style(b: brush);
4464 cachedGradient.reset();
4465 switch (brushStyle) {
4466 case Qt::SolidPattern: {
4467 type = Solid;
4468 QColor c = qbrush_color(b: brush);
4469 solidColor = qPremultiplyWithExtraAlpha(c, alpha);
4470 if (solidColor.alphaF() <= 0.0f && compositionMode == QPainter::CompositionMode_SourceOver)
4471 type = None;
4472 break;
4473 }
4474
4475 case Qt::LinearGradientPattern:
4476 {
4477 type = LinearGradient;
4478 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4479 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4480
4481 auto cacheInfo = qt_gradient_cache()->getBuffer(gradient: *g, opacity: alpha);
4482 gradient.colorTable32 = cacheInfo->buffer32;
4483#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4484 gradient.colorTable64 = cacheInfo->buffer64;
4485#endif
4486 cachedGradient = std::move(cacheInfo);
4487
4488 gradient.spread = g->spread();
4489
4490 QLinearGradientData &linearData = gradient.linear;
4491
4492 linearData.origin.x = g->start().x();
4493 linearData.origin.y = g->start().y();
4494 linearData.end.x = g->finalStop().x();
4495 linearData.end.y = g->finalStop().y();
4496 break;
4497 }
4498
4499 case Qt::RadialGradientPattern:
4500 {
4501 type = RadialGradient;
4502 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4503 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4504
4505 auto cacheInfo = qt_gradient_cache()->getBuffer(gradient: *g, opacity: alpha);
4506 gradient.colorTable32 = cacheInfo->buffer32;
4507#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4508 gradient.colorTable64 = cacheInfo->buffer64;
4509#endif
4510 cachedGradient = std::move(cacheInfo);
4511
4512 gradient.spread = g->spread();
4513
4514 QRadialGradientData &radialData = gradient.radial;
4515
4516 QPointF center = g->center();
4517 radialData.center.x = center.x();
4518 radialData.center.y = center.y();
4519 radialData.center.radius = g->centerRadius();
4520 QPointF focal = g->focalPoint();
4521 radialData.focal.x = focal.x();
4522 radialData.focal.y = focal.y();
4523 radialData.focal.radius = g->focalRadius();
4524 }
4525 break;
4526
4527 case Qt::ConicalGradientPattern:
4528 {
4529 type = ConicalGradient;
4530 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4531 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4532
4533 auto cacheInfo = qt_gradient_cache()->getBuffer(gradient: *g, opacity: alpha);
4534 gradient.colorTable32 = cacheInfo->buffer32;
4535#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4536 gradient.colorTable64 = cacheInfo->buffer64;
4537#endif
4538 cachedGradient = std::move(cacheInfo);
4539
4540 gradient.spread = QGradient::RepeatSpread;
4541
4542 QConicalGradientData &conicalData = gradient.conical;
4543
4544 QPointF center = g->center();
4545 conicalData.center.x = center.x();
4546 conicalData.center.y = center.y();
4547 conicalData.angle = qDegreesToRadians(degrees: g->angle());
4548 }
4549 break;
4550
4551 case Qt::Dense1Pattern:
4552 case Qt::Dense2Pattern:
4553 case Qt::Dense3Pattern:
4554 case Qt::Dense4Pattern:
4555 case Qt::Dense5Pattern:
4556 case Qt::Dense6Pattern:
4557 case Qt::Dense7Pattern:
4558 case Qt::HorPattern:
4559 case Qt::VerPattern:
4560 case Qt::CrossPattern:
4561 case Qt::BDiagPattern:
4562 case Qt::FDiagPattern:
4563 case Qt::DiagCrossPattern:
4564 type = Texture;
4565 if (!tempImage)
4566 tempImage = new QImage();
4567 *tempImage = rasterBuffer->colorizeBitmap(image: qt_imageForBrush(brushStyle, invert: true), color: brush.color());
4568 initTexture(image: tempImage, alpha, isCosmetic ? QTextureData::Pattern : QTextureData::Tiled);
4569 break;
4570 case Qt::TexturePattern:
4571 type = Texture;
4572 if (!tempImage)
4573 tempImage = new QImage();
4574
4575 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4576 *tempImage = rasterBuffer->colorizeBitmap(image: brush.textureImage(), color: brush.color());
4577 else
4578 *tempImage = brush.textureImage();
4579 initTexture(image: tempImage, alpha, QTextureData::Tiled, sourceRect: tempImage->rect());
4580 break;
4581
4582 case Qt::NoBrush:
4583 default:
4584 type = None;
4585 break;
4586 }
4587 adjustSpanMethods();
4588}
4589
4590void QSpanData::adjustSpanMethods()
4591{
4592 bitmapBlit = nullptr;
4593 alphamapBlit = nullptr;
4594 alphaRGBBlit = nullptr;
4595
4596 fillRect = nullptr;
4597
4598 switch(type) {
4599 case None:
4600 unclipped_blend = nullptr;
4601 break;
4602 case Solid: {
4603 const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format];
4604 unclipped_blend = drawHelper.blendColor;
4605 bitmapBlit = drawHelper.bitmapBlit;
4606 alphamapBlit = drawHelper.alphamapBlit;
4607 alphaRGBBlit = drawHelper.alphaRGBBlit;
4608 fillRect = drawHelper.fillRect;
4609 break;
4610 }
4611 case LinearGradient:
4612 case RadialGradient:
4613 case ConicalGradient:
4614 unclipped_blend = qBlendGradient;
4615 break;
4616 case Texture:
4617 unclipped_blend = qBlendTexture;
4618 if (!texture.imageData)
4619 unclipped_blend = nullptr;
4620
4621 break;
4622 }
4623 // setup clipping
4624 if (!unclipped_blend) {
4625 blend = nullptr;
4626 } else if (!clip) {
4627 blend = unclipped_blend;
4628 } else if (clip->hasRectClip) {
4629 blend = clip->clipRect.isEmpty() ? nullptr : qt_span_fill_clipRect;
4630 } else {
4631 blend = qt_span_fill_clipped;
4632 }
4633}
4634
4635void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4636{
4637 QTransform delta;
4638 // make sure we round off correctly in qdrawhelper.cpp
4639 delta.translate(dx: 1.0 / 65536, dy: 1.0 / 65536);
4640
4641 QTransform inv = (delta * matrix).inverted();
4642 m11 = inv.m11();
4643 m12 = inv.m12();
4644 m13 = inv.m13();
4645 m21 = inv.m21();
4646 m22 = inv.m22();
4647 m23 = inv.m23();
4648 m33 = inv.m33();
4649 dx = inv.dx();
4650 dy = inv.dy();
4651 txop = inv.type();
4652 bilinear = bilin;
4653
4654 const bool affine = inv.isAffine();
4655 const qreal f1 = m11 * m11 + m21 * m21;
4656 const qreal f2 = m12 * m12 + m22 * m22;
4657 fast_matrix = affine
4658 && f1 < 1e4
4659 && f2 < 1e4
4660 && f1 > (1.0 / 65536)
4661 && f2 > (1.0 / 65536)
4662 && qAbs(t: dx) < 1e4
4663 && qAbs(t: dy) < 1e4;
4664
4665 adjustSpanMethods();
4666}
4667
4668void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4669{
4670 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4671 if (!d || d->height == 0) {
4672 texture.imageData = nullptr;
4673 texture.width = 0;
4674 texture.height = 0;
4675 texture.x1 = 0;
4676 texture.y1 = 0;
4677 texture.x2 = 0;
4678 texture.y2 = 0;
4679 texture.bytesPerLine = 0;
4680 texture.format = QImage::Format_Invalid;
4681 texture.colorTable = nullptr;
4682 texture.hasAlpha = alpha != 256;
4683 } else {
4684 texture.imageData = d->data;
4685 texture.width = d->width;
4686 texture.height = d->height;
4687
4688 if (sourceRect.isNull()) {
4689 texture.x1 = 0;
4690 texture.y1 = 0;
4691 texture.x2 = texture.width;
4692 texture.y2 = texture.height;
4693 } else {
4694 texture.x1 = sourceRect.x();
4695 texture.y1 = sourceRect.y();
4696 texture.x2 = qMin(a: texture.x1 + sourceRect.width(), b: d->width);
4697 texture.y2 = qMin(a: texture.y1 + sourceRect.height(), b: d->height);
4698 }
4699
4700 texture.bytesPerLine = d->bytes_per_line;
4701
4702 texture.format = d->format;
4703 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : nullptr;
4704 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4705 }
4706 texture.const_alpha = alpha;
4707 texture.type = _type;
4708
4709 adjustSpanMethods();
4710}
4711
4712/*!
4713 \internal
4714 \a x and \a y is relative to the midpoint of \a rect.
4715*/
4716static inline void drawEllipsePoints(int x, int y, int length,
4717 const QRect &rect,
4718 const QRect &clip,
4719 ProcessSpans pen_func, ProcessSpans brush_func,
4720 QSpanData *pen_data, QSpanData *brush_data)
4721{
4722 if (length == 0)
4723 return;
4724
4725 QT_FT_Span _outline[4];
4726 QT_FT_Span *outline = _outline;
4727 const int midx = rect.x() + (rect.width() + 1) / 2;
4728 const int midy = rect.y() + (rect.height() + 1) / 2;
4729
4730 x = x + midx;
4731 y = midy - y;
4732
4733 // topleft
4734 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4735 outline[0].len = qMin(a: length, b: x - outline[0].x);
4736 outline[0].y = y;
4737 outline[0].coverage = 255;
4738
4739 // topright
4740 outline[1].x = x;
4741 outline[1].len = length;
4742 outline[1].y = y;
4743 outline[1].coverage = 255;
4744
4745 // bottomleft
4746 outline[2].x = outline[0].x;
4747 outline[2].len = outline[0].len;
4748 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4749 outline[2].coverage = 255;
4750
4751 // bottomright
4752 outline[3].x = x;
4753 outline[3].len = length;
4754 outline[3].y = outline[2].y;
4755 outline[3].coverage = 255;
4756
4757 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4758 QT_FT_Span _fill[2];
4759 QT_FT_Span *fill = _fill;
4760
4761 // top fill
4762 fill[0].x = outline[0].x + outline[0].len - 1;
4763 fill[0].len = qMax(a: 0, b: outline[1].x - fill[0].x);
4764 fill[0].y = outline[1].y;
4765 fill[0].coverage = 255;
4766
4767 // bottom fill
4768 fill[1].x = outline[2].x + outline[2].len - 1;
4769 fill[1].len = qMax(a: 0, b: outline[3].x - fill[1].x);
4770 fill[1].y = outline[3].y;
4771 fill[1].coverage = 255;
4772
4773 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4774 n = qt_intersect_spans(spans&: fill, numSpans: n, clip);
4775 if (n > 0)
4776 brush_func(n, fill, brush_data);
4777 }
4778 if (pen_func) {
4779 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4780 n = qt_intersect_spans(spans&: outline, numSpans: n, clip);
4781 if (n > 0)
4782 pen_func(n, outline, pen_data);
4783 }
4784}
4785
4786/*!
4787 \internal
4788 Draws an ellipse using the integer point midpoint algorithm.
4789*/
4790static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4791 ProcessSpans pen_func, ProcessSpans brush_func,
4792 QSpanData *pen_data, QSpanData *brush_data)
4793{
4794 const qreal a = qreal(rect.width()) / 2;
4795 const qreal b = qreal(rect.height()) / 2;
4796 qreal d = b*b - (a*a*b) + 0.25*a*a;
4797
4798 int x = 0;
4799 int y = (rect.height() + 1) / 2;
4800 int startx = x;
4801
4802 // region 1
4803 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4804 if (d < 0) { // select E
4805 d += b*b*(2*x + 3);
4806 ++x;
4807 } else { // select SE
4808 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4809 drawEllipsePoints(x: startx, y, length: x - startx + 1, rect, clip,
4810 pen_func, brush_func, pen_data, brush_data);
4811 startx = ++x;
4812 --y;
4813 }
4814 }
4815 drawEllipsePoints(x: startx, y, length: x - startx + 1, rect, clip,
4816 pen_func, brush_func, pen_data, brush_data);
4817
4818 // region 2
4819 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4820 const int miny = rect.height() & 0x1;
4821 while (y > miny) {
4822 if (d < 0) { // select SE
4823 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4824 ++x;
4825 } else { // select S
4826 d += a*a*(-2*y + 3);
4827 }
4828 --y;
4829 drawEllipsePoints(x, y, length: 1, rect, clip,
4830 pen_func, brush_func, pen_data, brush_data);
4831 }
4832}
4833
4834/*
4835 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4836 \overload
4837 \reimp
4838*/
4839
4840
4841#ifdef QT_DEBUG_DRAW
4842void dumpClip(int width, int height, const QClipData *clip)
4843{
4844 QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4845 clipImg.fill(0xffff0000);
4846
4847 int x0 = width;
4848 int x1 = 0;
4849 int y0 = height;
4850 int y1 = 0;
4851
4852 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4853
4854 for (int i = 0; i < clip->count; ++i) {
4855 const QT_FT_Span *span = ((QClipData *) clip)->spans() + i;
4856 for (int j = 0; j < span->len; ++j)
4857 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4858 x0 = qMin(x0, int(span->x));
4859 x1 = qMax(x1, int(span->x + span->len - 1));
4860
4861 y0 = qMin(y0, int(span->y));
4862 y1 = qMax(y1, int(span->y));
4863 }
4864
4865 static int counter = 0;
4866
4867 Q_ASSERT(y0 >= 0);
4868 Q_ASSERT(x0 >= 0);
4869 Q_ASSERT(y1 >= 0);
4870 Q_ASSERT(x1 >= 0);
4871
4872 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4873 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
4874}
4875#endif
4876
4877
4878QT_END_NAMESPACE
4879

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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