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