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

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