Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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"
35#include "qoutlinemapper_p.h"
36
37#include <limits.h>
38#include <algorithm>
39
40#ifdef Q_OS_WIN
41# include <qvarlengtharray.h>
42# include <private/qfontengine_p.h>
43# include <qt_windows.h>
44#ifdef Q_OS_WIN64
45# include <malloc.h>
46# endif
47#endif
48
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 }
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
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
195{
197}
198
200{
202}
203
205 qfixed c2x, qfixed c2y,
206 qfixed ex, qfixed ey,
207 void *data)
208{
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);
229 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
230 }
231}
232#endif
233
239
240
272/*
273 \fn QPaintEngine::Type QRasterPaintEngine::type() const
274 \reimp
275*/
276
291
301
302void QRasterPaintEngine::init()
303{
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_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
345
346 switch (d->device->devType()) {
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) {
362 d->mono_surface = true;
363 break;
364 default:
367 break;
368 }
369}
370
371
381
386{
388
389 if (device->devType() == QInternal::Pixmap) {
390 QPixmap *pixmap = static_cast<QPixmap *>(device);
391 QPlatformPixmap *pd = pixmap->handle();
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
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
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();
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
456{
457#ifdef QT_DEBUG_DRAW
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
472{
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
484{
485 if (flags.has_clip_ownership)
486 delete clip;
487}
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)
523 , strokeFlags(s.strokeFlags)
524 , lastBrush(s.lastBrush)
525 , brushData(s.brushData)
526 , fillFlags(s.fillFlags)
527 , pixmapFlags(s.pixmapFlags)
528 , intOpacity(s.intOpacity)
529 , txscale(s.txscale)
530 , clip(s.clip)
531 , dirty(s.dirty)
532 , flag_bits(s.flag_bits)
533{
534 brushData.tempImage = nullptr;
535 penData.tempImage = nullptr;
536 flags.has_clip_ownership = false;
537}
538
543{
545 if (!orig)
547 else
548 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
549
550 return s;
551}
552
557{
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
582{
583#ifdef QT_DEBUG_DRAW
584 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
585#endif
587 Q_ASSERT(s);
588 s->strokeFlags |= DirtyPen;
589 s->dirty |= DirtyPen;
590}
591
596{
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
672{
674#ifdef QT_DEBUG_DRAW
675 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
676#endif
677
678 s->fillFlags |= DirtyBrushOrigin;
679}
680
681
686{
688#ifdef QT_DEBUG_DRAW
689 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
690#endif
691 s->fillFlags |= DirtyBrush;
692}
693
694
695
696
701{
702#ifdef QT_DEBUG_DRAW
703 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
704#endif
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{
720 d->outlineMapper->setMatrix(state()->matrix);
721}
722
723void QRasterPaintEngine::updateRasterState()
724{
726
727 if (s->dirty & DirtyTransform)
728 updateMatrix(s->matrix);
729
731 const QPainter::CompositionMode mode = s->composition_mode;
732 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
733 && s->intOpacity == 256
736 && (s->penData.solidColor.spec() != QColor::ExtendedRgb &&
737 s->penData.solidColor.alphaF() >= 1.0f)));
738 }
739
740 s->dirty = 0;
741}
742
743
748{
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
766{
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
787{
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
811 d->recalculateFastImages();
812
813 if (was_aa != s->flags.antialiased)
814 d->updateClipping();
815}
816
821{
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
834 d->recalculateFastImages();
835}
836
841{
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
857 const QImage &img,
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();
922 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
923 srcBits, srcBPL,
924 iw, ih,
925 alpha);
926}
927
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();
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{
1009
1010 if (!systemClip.isEmpty()) {
1011 QRegion clippedDeviceRgn = systemClip & deviceRectUnclipped;
1012 deviceRect = clippedDeviceRgn.boundingRect();
1013 baseClip->setClipRegion(clippedDeviceRgn);
1014 } else {
1017 }
1018#ifdef QT_DEBUG_DRAW
1019 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << deviceRectUnclipped << systemClip;
1020#endif
1021
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
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{
1095 if (s->flags.has_clip_ownership)
1096 delete s->clip;
1097 s->clip = nullptr;
1098 s->flags.has_clip_ownership = false;
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
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);
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) {
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...
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 }
1187}
1188
1189
1190
1195{
1196#ifdef QT_DEBUG_DRAW
1197 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1198#endif
1199
1201
1202 if (op == Qt::NoClip) {
1204
1205 } else if (s->matrix.type() > QTransform::TxScale) {
1207 return;
1208
1209 } else if (!setClipRectInDeviceCoords(qt_mapFillRect(rect, s->matrix), 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;
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
1263 return true;
1264}
1265
1266
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
1284 const QClipData *clip = d->clip();
1285 const QClipData *baseClip = d->baseClip.data();
1286
1287 if (op == Qt::NoClip) {
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
1318 }
1319}
1320
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
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
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
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
1402 && (data->solidColor.spec() != QColor::ExtendedRgb &&
1403 data->solidColor.alphaF() >= 1.0f))))
1404 {
1405 data->fillRect(data->rasterBuffer, x1, y1, width, height, data->solidColor.rgba64());
1406 return;
1407 }
1408 }
1409
1410 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1411
1412 const int nspans = 512;
1413 QT_FT_Span spans[nspans];
1414
1415 Q_ASSERT(data->blend);
1416 int y = y1;
1417 while (y < y2) {
1418 int n = qMin(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
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();
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 {
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) {
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
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();
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) {
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
1543{
1544 Q_D(QRasterPaintEngine);
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 QPointF p = lines[i].p1();
1588 QLineF mappedline = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1589 QPointF(p.x() + width*0.5, p.y())));
1590 d->rasterizer->rasterizeLine(mappedline.p1(), mappedline.p2(),
1591 width / mappedline.length());
1592 }
1593 continue;
1594 }
1595
1596 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1597 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1598 width / line.length(),
1599 s->lastPen.capStyle() == Qt::SquareCap);
1600 } else {
1601 // LinesHint means each line is distinct, so restart dashing
1602 int dIndex = dashIndex;
1603 qreal dOffset = dashOffset;
1604 bool inD = inDash;
1605 d->rasterizeLine_dashed(line, width, &dIndex, &dOffset, &inD);
1606 }
1607 }
1608 }
1609 else
1611}
1612
1613QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1614{
1615 int x1 = qRound(rect.x());
1616 int y1 = qRound(rect.y());
1617 int x2 = qRound(rect.right());
1618 int y2 = qRound(rect.bottom());
1619
1620 if (x2 < x1)
1621 qSwap(x1, x2);
1622 if (y2 < y1)
1623 qSwap(y1, y2);
1624
1625 return QRect(x1, y1, x2 - x1, y2 - y1);
1626}
1627
1632{
1633 if (path.isEmpty())
1634 return;
1635#ifdef QT_DEBUG_DRAW
1636 QRectF rf = path.controlPointRect();
1637 qDebug() << "QRasterPaintEngine::fill(): "
1638 << "size=" << path.elementCount()
1639 << ", hints=" << Qt::hex << path.hints()
1640 << rf << brush;
1641#endif
1642
1643 Q_D(QRasterPaintEngine);
1645
1646 ensureBrush(brush);
1647 if (!s->brushData.blend)
1648 return;
1649
1650 if (path.shape() == QVectorPath::RectangleHint) {
1651 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1652 const qreal *p = path.points();
1653 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1654 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1655 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1656 return;
1657 }
1658 ensureRasterState();
1659 if (s->flags.tx_noshear) {
1660 d->initializeRasterizer(&s->brushData);
1661 // ### Is normalizing really necessary here?
1662 const qreal *p = path.points();
1663 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1664 if (!r.isEmpty()) {
1665 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1666 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1667 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1668 }
1669 return;
1670 }
1671 }
1672
1673 // ### Optimize for non transformed ellipses and rectangles...
1674 QRectF cpRect = path.controlPointRect();
1675 const QRectF pathDeviceRect = s->matrix.mapRect(cpRect);
1676 // Skip paths that by conservative estimates are completely outside the paint device.
1677 if (!pathDeviceRect.intersects(QRectF(d->deviceRect)) || !pathDeviceRect.isValid())
1678 return;
1679
1680 ProcessSpans blend = d->getBrushFunc(pathDeviceRect, &s->brushData);
1681
1682 // ### Falcon
1683// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1684// || deviceRect.right() > QT_RASTER_COORD_LIMIT
1685// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1686// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1687
1688 // ### Falonc: implement....
1689// if (!s->flags.antialiased && !do_clip) {
1690// d->initializeRasterizer(&s->brushData);
1691// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1692// return;
1693// }
1694
1695 ensureOutlineMapper();
1696 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1697}
1698
1700{
1701 Q_D(QRasterPaintEngine);
1703
1704 if (!s->flags.antialiased) {
1705 uint txop = s->matrix.type();
1706 if (txop == QTransform::TxNone) {
1707 fillRect_normalized(toNormalizedFillRect(r), data, d);
1708 return;
1709 } else if (txop == QTransform::TxTranslate) {
1710 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1712 return;
1713 } else if (txop == QTransform::TxScale) {
1714 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1716 return;
1717 }
1718 }
1719 ensureRasterState();
1720 if (s->flags.tx_noshear) {
1721 d->initializeRasterizer(data);
1722 QRectF nr = r.normalized();
1723 if (!nr.isEmpty()) {
1724 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1725 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1726 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1727 }
1728 return;
1729 }
1730
1732 path.addRect(r);
1733 ensureOutlineMapper();
1734 fillPath(path, data);
1735}
1736
1741{
1742#ifdef QT_DEBUG_DRAW
1743 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1744#endif
1746
1747 ensureBrush(brush);
1748 if (!s->brushData.blend)
1749 return;
1750
1751 fillRect(r, &s->brushData);
1752}
1753
1755{
1756 if (alpha == 0)
1757 return Qt::transparent;
1758 if (c.spec() == QColor::ExtendedRgb) {
1759 float r, g, b, a;
1760 c.getRgbF(&r, &g, &b, &a);
1761 a = a * alpha * (1.f / 256.f);
1762 return QColor::fromRgbF(r * a, g * a, b * a, a);
1763 }
1764 return qPremultiply(combineAlpha256(c.rgba64(), alpha));
1765}
1766
1771{
1772#ifdef QT_DEBUG_DRAW
1773 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1774#endif
1775 Q_D(QRasterPaintEngine);
1777
1778 d->solid_color_filler.solidColor = qPremultiplyWithExtraAlpha(color, s->intOpacity);
1779
1780 if (d->solid_color_filler.solidColor.alphaF() <= 0.0f
1781 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1782 return;
1783 }
1784 d->solid_color_filler.clip = d->clip();
1785 d->solid_color_filler.adjustSpanMethods();
1786 fillRect(r, &d->solid_color_filler);
1787}
1788
1789static inline bool isAbove(const QPointF *a, const QPointF *b)
1790{
1791 return a->y() < b->y();
1792}
1793
1794static bool splitPolygon(const QPointF *points, int pointCount, QList<QPointF> *upper, QList<QPointF> *lower)
1795{
1796 Q_ASSERT(upper);
1797 Q_ASSERT(lower);
1798
1799 Q_ASSERT(pointCount >= 2);
1800
1801 QList<const QPointF *> sorted;
1802 sorted.reserve(pointCount);
1803
1804 upper->reserve(pointCount * 3 / 4);
1805 lower->reserve(pointCount * 3 / 4);
1806
1807 for (int i = 0; i < pointCount; ++i)
1808 sorted << points + i;
1809
1810 std::sort(sorted.begin(), sorted.end(), isAbove);
1811
1812 qreal splitY = sorted.at(sorted.size() / 2)->y();
1813
1814 const QPointF *end = points + pointCount;
1815 const QPointF *last = end - 1;
1816
1817 QList<QPointF> *bin[2] = { upper, lower };
1818
1819 for (const QPointF *p = points; p < end; ++p) {
1820 int side = p->y() < splitY;
1821 int lastSide = last->y() < splitY;
1822
1823 if (side != lastSide) {
1824 if (qFuzzyCompare(p->y(), splitY)) {
1825 bin[!side]->append(*p);
1826 } else if (qFuzzyCompare(last->y(), splitY)) {
1827 bin[side]->append(*last);
1828 } else {
1829 QPointF delta = *p - *last;
1830 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1831
1832 bin[0]->append(intersection);
1833 bin[1]->append(intersection);
1834 }
1835 }
1836
1837 bin[side]->append(*p);
1838
1839 last = p;
1840 }
1841
1842 // give up if we couldn't reduce the point count
1843 return upper->size() < pointCount && lower->size() < pointCount;
1844}
1845
1850{
1851 Q_D(QRasterPaintEngine);
1853
1854 const int maxPoints = 0xffff;
1855
1856 // max amount of points that raster engine can reliably handle
1857 if (pointCount > maxPoints) {
1858 QList<QPointF> upper, lower;
1859
1860 if (splitPolygon(points, pointCount, &upper, &lower)) {
1861 fillPolygon(upper.constData(), upper.size(), mode);
1862 fillPolygon(lower.constData(), lower.size(), mode);
1863 } else
1864 qWarning("Polygon too complex for filling.");
1865
1866 return;
1867 }
1868
1869 // Compose polygon fill..,
1870 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1871 ensureOutlineMapper();
1872 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1873
1874 // scanconvert.
1875 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1876 &s->brushData);
1877 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1878}
1879
1884{
1885 Q_D(QRasterPaintEngine);
1887
1888#ifdef QT_DEBUG_DRAW
1889 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1890 for (int i=0; i<pointCount; ++i)
1891 qDebug() << " - " << points[i];
1892#endif
1893 Q_ASSERT(pointCount >= 2);
1894
1895 if (mode != PolylineMode && QVectorPath::isRect((const qreal *) points, pointCount)) {
1896 QRectF r(points[0], points[2]);
1897 drawRects(&r, 1);
1898 return;
1899 }
1900
1901 ensurePen();
1902 if (mode != PolylineMode) {
1903 // Do the fill...
1904 ensureBrush();
1905 if (s->brushData.blend)
1906 fillPolygon(points, pointCount, mode);
1907 }
1908
1909 // Do the outline...
1910 if (s->penData.blend) {
1911 QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1912 if (s->flags.fast_pen) {
1913 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1914 stroker.drawPath(vp);
1915 } else {
1916 QPaintEngineEx::stroke(vp, s->lastPen);
1917 }
1918 }
1919}
1920
1925{
1926 Q_D(QRasterPaintEngine);
1928
1929#ifdef QT_DEBUG_DRAW
1930 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1931 for (int i=0; i<pointCount; ++i)
1932 qDebug() << " - " << points[i];
1933#endif
1934 Q_ASSERT(pointCount >= 2);
1935 if (mode != PolylineMode && QVectorPath::isRect((const int *) points, pointCount)) {
1936 QRect r(points[0].x(),
1937 points[0].y(),
1938 points[2].x() - points[0].x(),
1939 points[2].y() - points[0].y());
1940 drawRects(&r, 1);
1941 return;
1942 }
1943
1944 ensurePen();
1945
1946 // Do the fill
1947 if (mode != PolylineMode) {
1948 ensureBrush();
1949 if (s->brushData.blend) {
1950 // Compose polygon fill..,
1951 ensureOutlineMapper();
1952 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1953 d->outlineMapper->moveTo(*points);
1954 const QPoint *p = points;
1955 const QPoint *ep = points + pointCount - 1;
1956 do {
1957 d->outlineMapper->lineTo(*(++p));
1958 } while (p < ep);
1959 d->outlineMapper->endOutline();
1960
1961 // scanconvert.
1962 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1963 &s->brushData);
1964 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1965 }
1966 }
1967
1968 // Do the outline...
1969 if (s->penData.blend) {
1970 int count = pointCount * 2;
1971 QVarLengthArray<qreal> fpoints(count);
1972 for (int i=0; i<count; ++i)
1973 fpoints[i] = ((const int *) points)[i];
1974 QVectorPath vp((qreal *) fpoints.data(), pointCount, nullptr, QVectorPath::polygonFlags(mode));
1975
1976 if (s->flags.fast_pen) {
1977 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1978 stroker.drawPath(vp);
1979 } else {
1980 QPaintEngineEx::stroke(vp, s->lastPen);
1981 }
1982 }
1983}
1984
1989{
1990#ifdef QT_DEBUG_DRAW
1991 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1992#endif
1993
1994 QPlatformPixmap *pd = pixmap.handle();
1996 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
1997 if (image.depth() == 1) {
1998 Q_D(QRasterPaintEngine);
2000 if (s->matrix.type() <= QTransform::TxTranslate) {
2001 ensurePen();
2002 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2003 } else {
2004 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2005 }
2006 } else {
2008 }
2009 } else {
2010 const QImage image = pixmap.toImage();
2011 if (pixmap.depth() == 1) {
2012 Q_D(QRasterPaintEngine);
2014 if (s->matrix.type() <= QTransform::TxTranslate) {
2015 ensurePen();
2016 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2017 } else {
2018 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2019 }
2020 } else {
2022 }
2023 }
2024}
2025
2030{
2031#ifdef QT_DEBUG_DRAW
2032 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2033#endif
2034
2035 QPlatformPixmap* pd = pixmap.handle();
2037 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2038 if (image.depth() == 1) {
2039 Q_D(QRasterPaintEngine);
2041 if (s->matrix.type() <= QTransform::TxTranslate
2042 && r.size() == sr.size()
2043 && r.size() == pixmap.size()) {
2044 ensurePen();
2045 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2046 return;
2047 } else {
2048 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2049 }
2050 } else {
2051 drawImage(r, image, sr);
2052 }
2053 } else {
2054 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2055 const QImage image = pd->toImage(clippedSource);
2056 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2057 if (image.depth() == 1) {
2058 Q_D(QRasterPaintEngine);
2060 if (s->matrix.type() <= QTransform::TxTranslate
2061 && r.size() == sr.size()
2062 && r.size() == pixmap.size()) {
2063 ensurePen();
2064 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2065 return;
2066 } else {
2067 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2068 }
2069 } else {
2070 drawImage(r, image, translatedSource);
2071 }
2072 }
2073}
2074
2075static inline int fast_ceil_positive(const qreal &v)
2076{
2077 const int iv = int(v);
2078 if (v - iv == 0)
2079 return iv;
2080 else
2081 return iv + 1;
2082}
2083
2084static inline const QRect toAlignedRect_positive(const QRectF &rect)
2085{
2086 const int xmin = int(rect.x());
2087 const int xmax = int(fast_ceil_positive(rect.right()));
2088 const int ymin = int(rect.y());
2089 const int ymax = int(fast_ceil_positive(rect.bottom()));
2090 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2091}
2092
2097{
2098#ifdef QT_DEBUG_DRAW
2099 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2100#endif
2101
2102 Q_D(QRasterPaintEngine);
2104 qreal scale = img.devicePixelRatio();
2105
2106 if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
2107 drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2108 img,
2109 QRectF(0, 0, img.width(), img.height()));
2110 } else {
2111
2112 const QClipData *clip = d->clip();
2113 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2114
2115 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, img.rect())) {
2116 if (!clip) {
2117 d->blitImage(pt, img, d->deviceRect);
2118 return;
2119 } else if (clip->hasRectClip) {
2120 d->blitImage(pt, img, clip->clipRect);
2121 return;
2122 }
2123 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2124 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2125 if (func) {
2126 if (!clip) {
2127 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2128 return;
2129 } else if (clip->hasRectClip) {
2130 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2131 return;
2132 }
2133 }
2134 }
2135
2136
2137
2138 d->image_filler.clip = clip;
2139 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2140 if (!d->image_filler.blend)
2141 return;
2142 d->image_filler.dx = -pt.x();
2143 d->image_filler.dy = -pt.y();
2144 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2145
2146 fillRect_normalized(rr, &d->image_filler, d);
2147 }
2148
2149}
2150
2152{
2153 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2154}
2155
2156namespace {
2157 enum RotationType {
2158 Rotation90,
2159 Rotation180,
2160 Rotation270,
2161 NoRotation
2162 };
2163
2164 inline RotationType qRotationType(const QTransform &transform)
2165 {
2167
2169 return NoRotation;
2170
2172 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2173 return Rotation90;
2174
2176 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2177 return Rotation180;
2178
2180 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2181 return Rotation270;
2182
2183 return NoRotation;
2184 }
2185
2186 inline bool isPixelAligned(const QPointF &pt)
2187 {
2188 return QPointF(pt.toPoint()) == pt;
2189 }
2190 inline bool isPixelAligned(const QRectF &rect)
2191 {
2192 return QRectF(rect.toRect()) == rect;
2193 }
2194}
2195
2200 Qt::ImageConversionFlags)
2201{
2202#ifdef QT_DEBUG_DRAW
2203 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2204#endif
2205
2206 if (r.isEmpty())
2207 return;
2208
2209 Q_D(QRasterPaintEngine);
2211 Q_ASSERT(s);
2212 int sr_l = qFloor(sr.left());
2213 int sr_r = qCeil(sr.right()) - 1;
2214 int sr_t = qFloor(sr.top());
2215 int sr_b = qCeil(sr.bottom()) - 1;
2216
2217 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2218 // as fillRect will apply the aliased coordinate delta we need to
2219 // subtract it here as we don't use it for image drawing
2220 QTransform old = s->matrix;
2221
2222 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2223 QRgb color = img.pixel(sr_l, sr_t);
2224 switch (img.format()) {
2233 // Combine premultiplied color with the opacity set on the painter.
2234 d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity);
2235 break;
2236 default:
2237 d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity));
2238 break;
2239 }
2240
2241 if (d->solid_color_filler.solidColor.alphaF() <= 0.0f && s->composition_mode == QPainter::CompositionMode_SourceOver)
2242 return;
2243
2244 d->solid_color_filler.clip = d->clip();
2245 d->solid_color_filler.adjustSpanMethods();
2246 fillRect(r, &d->solid_color_filler);
2247
2248 s->matrix = old;
2249 return;
2250 }
2251
2252 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2253
2254 const QClipData *clip = d->clip();
2255
2256 if (s->matrix.type() == QTransform::TxRotate
2257 && !stretch_sr
2258 && (!clip || clip->hasRectClip)
2259 && s->intOpacity == 256
2260 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2261 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))
2262 {
2263 RotationType rotationType = qRotationType(s->matrix);
2264 Q_ASSERT(d->rasterBuffer->format < QImage::NImageFormats);
2265 const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2266
2267 if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2268 QRectF transformedTargetRect = s->matrix.mapRect(r);
2269
2270 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, transformedTargetRect.topRight(), sr)) {
2271 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2272 if (clippedTransformedTargetRect.isNull())
2273 return;
2274
2275 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2276
2277 QRect clippedSourceRect
2278 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2279 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2280
2281 clippedSourceRect = clippedSourceRect.intersected(img.rect());
2282
2283 const qsizetype dbpl = d->rasterBuffer->bytesPerLine();
2284 const qsizetype sbpl = img.bytesPerLine();
2285
2286 uchar *dst = d->rasterBuffer->buffer();
2287 uint bpp = img.depth() >> 3;
2288
2289 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2290 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2291
2292 uint cw = clippedSourceRect.width();
2293 uint ch = clippedSourceRect.height();
2294
2295 qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2296
2297 return;
2298 }
2299 }
2300 }
2301
2302 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2303
2304 QRectF targetBounds = s->matrix.mapRect(r);
2305 bool exceedsPrecision = r.width() > 0x7fff
2306 || r.height() > 0x7fff
2307 || targetBounds.left() < -0x7fff
2308 || targetBounds.top() < -0x7fff
2309 || targetBounds.right() > 0x7fff
2310 || targetBounds.bottom() > 0x7fff
2311 || targetBounds.width() > 0x7fff
2312 || targetBounds.height() > 0x7fff
2313 || s->matrix.m11() >= 512
2314 || s->matrix.m22() >= 512;
2315 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2316 if (s->matrix.type() > QTransform::TxScale) {
2317 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2318 // The fast transform methods doesn't really work on small targets, see QTBUG-93475
2319 // And it can't antialias the edges
2320 if (func && (!clip || clip->hasRectClip) && !s->flags.antialiased && targetBounds.width() >= 16 && targetBounds.height() >= 16) {
2321 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2322 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2323 s->matrix, s->intOpacity);
2324 return;
2325 }
2326 } else {
2327 // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2328 bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2329 bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2330 if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2331 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2332 if (func) {
2333 QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2334 if (!clip) {
2335 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2336 return;
2337 } else if (clip->hasRectClip) {
2338 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2339 return;
2340 }
2341 }
2342 }
2343 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2344 if (func && (!clip || clip->hasRectClip)) {
2346 if (!s->flags.antialiased) {
2347 tr.setX(qRound(tr.x()));
2348 tr.setY(qRound(tr.y()));
2349 tr.setWidth(qRound(tr.width()));
2350 tr.setHeight(qRound(tr.height()));
2351 }
2352 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2353 img.bits(), img.bytesPerLine(), img.height(),
2354 tr, sr,
2355 !clip ? d->deviceRect : clip->clipRect,
2356 s->intOpacity);
2357 return;
2358 }
2359 }
2360 }
2361
2362 QTransform copy = s->matrix;
2363 copy.translate(r.x(), r.y());
2364 if (stretch_sr)
2365 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2366 copy.translate(-sr.x(), -sr.y());
2367
2368 d->image_filler_xform.clip = clip;
2369 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2370 if (!d->image_filler_xform.blend)
2371 return;
2372 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2373
2374 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2375 QRectF rr = s->matrix.mapRect(r);
2376
2377 const int x1 = qRound(rr.x());
2378 const int y1 = qRound(rr.y());
2379 const int x2 = qRound(rr.right());
2380 const int y2 = qRound(rr.bottom());
2381
2382 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2383 return;
2384 }
2385
2386#ifdef QT_FAST_SPANS
2387 ensureRasterState();
2388 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2389 d->initializeRasterizer(&d->image_filler_xform);
2390 d->rasterizer->setAntialiased(s->flags.antialiased);
2391
2392 const QRectF &rect = r.normalized();
2393 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2394 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2395
2396 if (s->flags.tx_noshear)
2397 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2398 else
2399 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2400 return;
2401 }
2402#endif
2404 path.addRect(r);
2405 QTransform m = s->matrix;
2406 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2407 m.m21(), m.m22(), m.m23(),
2408 m.m31(), m.m32(), m.m33());
2409 fillPath(path, &d->image_filler_xform);
2410 s->matrix = m;
2411 } else {
2412 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2413 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) {
2414 if (!clip) {
2415 d->blitImage(pt, img, d->deviceRect, sr.toRect());
2416 return;
2417 } else if (clip->hasRectClip) {
2418 d->blitImage(pt, img, clip->clipRect, sr.toRect());
2419 return;
2420 }
2421 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2422 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2423 if (func) {
2424 if (!clip) {
2425 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2426 return;
2427 } else if (clip->hasRectClip) {
2428 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2429 return;
2430 }
2431 }
2432 }
2433
2434 d->image_filler.clip = clip;
2435 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2436 if (!d->image_filler.blend)
2437 return;
2438 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2439 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2440
2441 QRectF rr = r;
2442 rr.translate(s->matrix.dx(), s->matrix.dy());
2443
2444 const int x1 = qRound(rr.x());
2445 const int y1 = qRound(rr.y());
2446 const int x2 = qRound(rr.right());
2447 const int y2 = qRound(rr.bottom());
2448
2449 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2450 }
2451}
2452
2457{
2458#ifdef QT_DEBUG_DRAW
2459 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2460#endif
2461 Q_D(QRasterPaintEngine);
2463 Q_ASSERT(s);
2464
2465 QImage image;
2466
2467 QPlatformPixmap *pd = pixmap.handle();
2469 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2470 } else {
2471 image = pixmap.toImage();
2472 }
2473
2474 if (image.depth() == 1)
2475 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2476
2477 const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
2478 if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
2479 QTransform copy = s->matrix;
2480 copy.translate(r.x(), r.y());
2481 copy.translate(-sr.x(), -sr.y());
2482 const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
2483 copy.scale(inverseDpr, inverseDpr);
2484 d->image_filler_xform.clip = d->clip();
2485 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2486 if (!d->image_filler_xform.blend)
2487 return;
2488 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2489
2490#ifdef QT_FAST_SPANS
2491 ensureRasterState();
2492 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2493 d->initializeRasterizer(&d->image_filler_xform);
2494 d->rasterizer->setAntialiased(s->flags.antialiased);
2495
2496 const QRectF &rect = r.normalized();
2497 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2498 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2499 if (s->flags.tx_noshear)
2500 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2501 else
2502 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2503 return;
2504 }
2505#endif
2507 path.addRect(r);
2508 fillPath(path, &d->image_filler_xform);
2509 } else {
2510 d->image_filler.clip = d->clip();
2511
2512 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2513 if (!d->image_filler.blend)
2514 return;
2515 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2516 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2517
2518 QRectF rr = r;
2519 rr.translate(s->matrix.dx(), s->matrix.dy());
2520 fillRect_normalized(rr.normalized().toRect(), &d->image_filler, d);
2521 }
2522}
2523
2524
2525//QWS hack
2526static inline bool monoVal(const uchar* s, int x)
2527{
2528 return (s[x>>3] << (x&7)) & 0x80;
2529}
2530
2535{
2536 Q_D(QRasterPaintEngine);
2537 return d->rasterBuffer.data();
2538}
2539
2543void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
2544{
2545 Q_D(QRasterPaintEngine);
2547
2548 if (!s->penData.blend)
2549 return;
2550
2551 QRasterBuffer *rb = d->rasterBuffer.data();
2553 useGammaCorrection = false;
2554
2555 const QRect rect(rx, ry, w, h);
2556 const QClipData *clip = d->clip();
2557 bool unclipped = false;
2558 if (clip) {
2559 // inlined QRect::intersects
2560 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2561 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2562
2563 if (clip->hasRectClip) {
2564 unclipped = rx > clip->xmin
2565 && rx + w < clip->xmax
2566 && ry > clip->ymin
2567 && ry + h < clip->ymax;
2568 }
2569
2570 if (!intersects)
2571 return;
2572 } else {
2573 // inlined QRect::intersects
2574 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2575 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2576 if (!intersects)
2577 return;
2578
2579 // inlined QRect::contains
2580 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2581 && rect.top() >= 0 && rect.bottom() < rb->height();
2582
2583 unclipped = contains && d->isUnclipped_normalized(rect);
2584 }
2585
2586 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2587 const uchar * scanline = static_cast<const uchar *>(src);
2588
2589 if (s->flags.fast_text) {
2590 if (unclipped) {
2591 if (depth == 1) {
2592 if (s->penData.bitmapBlit) {
2593 s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2594 scanline, w, h, bpl);
2595 return;
2596 }
2597 } else if (depth == 8) {
2598 if (s->penData.alphamapBlit) {
2599 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2600 scanline, w, h, bpl, nullptr, useGammaCorrection);
2601 return;
2602 }
2603 } else if (depth == 32) {
2604 // (A)RGB Alpha mask where the alpha component is not used.
2605 if (s->penData.alphaRGBBlit) {
2606 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2607 (const uint *) scanline, w, h, bpl / 4, nullptr, useGammaCorrection);
2608 return;
2609 }
2610 }
2611 } else if ((depth == 8 && s->penData.alphamapBlit) || (depth == 32 && s->penData.alphaRGBBlit)) {
2612 // (A)RGB Alpha mask where the alpha component is not used.
2613 if (!clip) {
2614 int nx = qMax(0, rx);
2615 int ny = qMax(0, ry);
2616
2617 // Move scanline pointer to compensate for moved x and y
2618 int xdiff = nx - rx;
2619 int ydiff = ny - ry;
2620 scanline += ydiff * bpl;
2621 scanline += xdiff * (depth == 32 ? 4 : 1);
2622
2623 w -= xdiff;
2624 h -= ydiff;
2625
2626 if (nx + w > d->rasterBuffer->width())
2627 w = d->rasterBuffer->width() - nx;
2628 if (ny + h > d->rasterBuffer->height())
2629 h = d->rasterBuffer->height() - ny;
2630
2631 rx = nx;
2632 ry = ny;
2633 }
2634 if (depth == 8)
2635 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2636 scanline, w, h, bpl, clip, useGammaCorrection);
2637 else if (depth == 32)
2638 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2639 (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
2640 return;
2641 }
2642 }
2643
2644 int x0 = 0;
2645 if (rx < 0) {
2646 x0 = -rx;
2647 w -= x0;
2648 }
2649
2650 int y0 = 0;
2651 if (ry < 0) {
2652 y0 = -ry;
2653 scanline += bpl * y0;
2654 h -= y0;
2655 }
2656
2657 w = qMin(w, rb->width() - qMax(0, rx));
2658 h = qMin(h, rb->height() - qMax(0, ry));
2659
2660 if (w <= 0 || h <= 0)
2661 return;
2662
2663 const int NSPANS = 512;
2664 QT_FT_Span spans[NSPANS];
2665 int current = 0;
2666
2667 const int x1 = x0 + w;
2668 const int y1 = y0 + h;
2669
2670 if (depth == 1) {
2671 for (int y = y0; y < y1; ++y) {
2672 for (int x = x0; x < x1; ) {
2673 if (!monoVal(scanline, x)) {
2674 ++x;
2675 continue;
2676 }
2677
2678 if (current == NSPANS) {
2679 blend(current, spans, &s->penData);
2680 current = 0;
2681 }
2682 spans[current].x = x + rx;
2683 spans[current].y = y + ry;
2684 spans[current].coverage = 255;
2685 int len = 1;
2686 ++x;
2687 // extend span until we find a different one.
2688 while (x < x1 && monoVal(scanline, x)) {
2689 ++x;
2690 ++len;
2691 }
2692 spans[current].len = len;
2693 ++current;
2694 }
2695 scanline += bpl;
2696 }
2697 } else if (depth == 8) {
2698 for (int y = y0; y < y1; ++y) {
2699 for (int x = x0; x < x1; ) {
2700 // Skip those with 0 coverage
2701 if (scanline[x] == 0) {
2702 ++x;
2703 continue;
2704 }
2705
2706 if (current == NSPANS) {
2707 blend(current, spans, &s->penData);
2708 current = 0;
2709 }
2710 int coverage = scanline[x];
2711 spans[current].x = x + rx;
2712 spans[current].y = y + ry;
2713 spans[current].coverage = coverage;
2714 int len = 1;
2715 ++x;
2716
2717 // extend span until we find a different one.
2718 while (x < x1 && scanline[x] == coverage) {
2719 ++x;
2720 ++len;
2721 }
2722 spans[current].len = len;
2723 ++current;
2724 }
2725 scanline += bpl;
2726 }
2727 } else { // 32-bit alpha...
2728 const uint *sl = (const uint *) scanline;
2729 for (int y = y0; y < y1; ++y) {
2730 for (int x = x0; x < x1; ) {
2731 // Skip those with 0 coverage
2732 if ((sl[x] & 0x00ffffff) == 0) {
2733 ++x;
2734 continue;
2735 }
2736
2737 if (current == NSPANS) {
2738 blend(current, spans, &s->penData);
2739 current = 0;
2740 }
2741 uint rgbCoverage = sl[x];
2742 int coverage = qGreen(rgbCoverage);
2743 spans[current].x = x + rx;
2744 spans[current].y = y + ry;
2745 spans[current].coverage = coverage;
2746 int len = 1;
2747 ++x;
2748
2749 // extend span until we find a different one.
2750 while (x < x1 && sl[x] == rgbCoverage) {
2751 ++x;
2752 ++len;
2753 }
2754 spans[current].len = len;
2755 ++current;
2756 }
2757 sl += bpl / sizeof(uint);
2758 }
2759 }
2760// qDebug() << "alphaPenBlt: num spans=" << current
2761// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2762 // Call span func for current set of spans.
2763 if (current != 0)
2764 blend(current, spans, &s->penData);
2765}
2766
2771 const QFixedPoint *positions, QFontEngine *fontEngine)
2772{
2773 Q_D(QRasterPaintEngine);
2775
2776 bool verticalSubPixelPositions = fontEngine->supportsVerticalSubPixelPositions()
2777 && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
2778
2779 if (fontEngine->hasInternalCaching()) {
2780 QFontEngine::GlyphFormat neededFormat =
2784
2785 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2786 neededFormat = QFontEngine::Format_Mono;
2787
2788 for (int i = 0; i < numGlyphs; i++) {
2789 QFixedPoint spp = fontEngine->subPixelPositionFor(positions[i]);
2790 if (!verticalSubPixelPositions)
2791 spp.y = 0;
2792
2793 const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyphs[i], spp, neededFormat, s->matrix);
2794 if (!alphaMap)
2795 continue;
2796
2797 int depth;
2798 int bytesPerLine;
2799 switch (alphaMap->format) {
2801 depth = 1;
2802 bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
2803 break;
2805 depth = 8;
2806 bytesPerLine = (alphaMap->width + 3) & ~3;
2807 break;
2809 depth = 32;
2810 bytesPerLine = alphaMap->width * 4;
2811 break;
2812 default:
2813 Q_UNREACHABLE();
2814 };
2815
2816 QFixed y = verticalSubPixelPositions
2817 ? qFloor(positions[i].y)
2818 : qRound(positions[i].y);
2819
2820 alphaPenBlt(alphaMap->data, bytesPerLine, depth,
2821 qFloor(positions[i].x) + alphaMap->x,
2822 qFloor(y) - alphaMap->y,
2823 alphaMap->width, alphaMap->height,
2824 fontEngine->expectsGammaCorrectedBlending());
2825 }
2826
2827 } else {
2828 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
2829
2831 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(nullptr, glyphFormat, s->matrix, s->penData.solidColor));
2832 if (!cache) {
2833 cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, s->penData.solidColor);
2834 fontEngine->setGlyphCache(nullptr, cache);
2835 }
2836
2837 cache->populate(fontEngine, numGlyphs, glyphs, positions, s->renderHints);
2838 cache->fillInPendingGlyphs();
2839
2840 const QImage &image = cache->image();
2841 qsizetype bpl = image.bytesPerLine();
2842
2843 int depth = image.depth();
2844 int rightShift = 0;
2845 int leftShift = 0;
2846 if (depth == 32)
2847 leftShift = 2; // multiply by 4
2848 else if (depth == 1)
2849 rightShift = 3; // divide by 8
2850
2851 int margin = fontEngine->glyphMargin(glyphFormat);
2852 const uchar *bits = image.bits();
2853 for (int i=0; i<numGlyphs; ++i) {
2854 QFixedPoint subPixelPosition = fontEngine->subPixelPositionFor(positions[i]);
2855 if (!verticalSubPixelPositions)
2856 subPixelPosition.y = 0;
2857
2858 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2859 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2860 if (c.isNull())
2861 continue;
2862
2863 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2864 int y = (verticalSubPixelPositions
2865 ? qFloor(positions[i].y)
2866 : qRound(positions[i].y));
2867 y -= c.baseLineY + margin;
2868
2869 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2870 // c.x, c.y,
2871 // c.w, c.h,
2872 // c.baseLineX, c.baseLineY,
2873 // glyphs[i],
2874 // x, y,
2875 // positions[i].x.toInt(), positions[i].y.toInt());
2876
2877 const uchar *glyphBits = bits + ((c.x << leftShift) >> rightShift) + c.y * bpl;
2878
2879 if (glyphFormat == QFontEngine::Format_ARGB) {
2880 // The current state transform has already been applied to the positions,
2881 // so we prevent drawImage() from re-applying the transform by clearing
2882 // the state for the duration of the call.
2883 QTransform originalTransform = s->matrix;
2884 s->matrix = QTransform();
2885 drawImage(QPoint(x, y), QImage(glyphBits, c.w, c.h, bpl, image.format()));
2886 s->matrix = originalTransform;
2887 } else {
2888 alphaPenBlt(glyphBits, bpl, depth, x, y, c.w, c.h, fontEngine->expectsGammaCorrectedBlending());
2889 }
2890 }
2891 }
2892 return true;
2893}
2894
2895
2901{
2902 const QClipData *cl = clip();
2903 if (!cl) {
2904 // inline contains() for performance (we know the rects are normalized)
2905 const QRect &r1 = deviceRect;
2906 return (r.left() >= r1.left() && r.right() <= r1.right()
2907 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2908 }
2909
2910
2911 if (cl->hasRectClip) {
2912 // currently all painting functions clips to deviceRect internally
2913 if (cl->clipRect == deviceRect)
2914 return true;
2915
2916 // inline contains() for performance (we know the rects are normalized)
2917 const QRect &r1 = cl->clipRect;
2918 return (r.left() >= r1.left() && r.right() <= r1.right()
2919 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2920 } else {
2921 return qt_region_strictContains(cl->clipRegion, r);
2922 }
2923}
2924
2926 int penWidth) const
2927{
2928 Q_Q(const QRasterPaintEngine);
2929 const QRasterPaintEngineState *s = q->state();
2930 const QClipData *cl = clip();
2931 QRect r = rect.normalized();
2932 if (!cl) {
2933 // inline contains() for performance (we know the rects are normalized)
2934 const QRect &r1 = deviceRect;
2935 return (r.left() >= r1.left() && r.right() <= r1.right()
2936 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2937 }
2938
2939
2940 // currently all painting functions that call this function clip to deviceRect internally
2941 if (cl->hasRectClip && cl->clipRect == deviceRect)
2942 return true;
2943
2944 if (s->flags.antialiased)
2945 ++penWidth;
2946
2947 if (penWidth > 0) {
2948 r.setX(r.x() - penWidth);
2949 r.setY(r.y() - penWidth);
2950 r.setWidth(r.width() + 2 * penWidth);
2951 r.setHeight(r.height() + 2 * penWidth);
2952 }
2953
2954 if (cl->hasRectClip) {
2955 // inline contains() for performance (we know the rects are normalized)
2956 const QRect &r1 = cl->clipRect;
2957 return (r.left() >= r1.left() && r.right() <= r1.right()
2958 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2959 } else {
2960 return qt_region_strictContains(cl->clipRegion, r);
2961 }
2962}
2963
2965 int penWidth) const
2966{
2967 const QRectF norm = rect.normalized();
2968 if (norm.left() <= INT_MIN || norm.top() <= INT_MIN
2969 || norm.right() > INT_MAX || norm.bottom() > INT_MAX
2970 || norm.width() > INT_MAX || norm.height() > INT_MAX)
2971 return false;
2972 return isUnclipped(norm.toAlignedRect(), penWidth);
2973}
2974
2975inline ProcessSpans
2977 const QSpanData *data) const
2978{
2979 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2980}
2981
2982inline ProcessSpans
2984 const QSpanData *data) const
2985{
2986 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2987}
2988
2989inline ProcessSpans
2991 const QSpanData *data) const
2992{
2993 Q_Q(const QRasterPaintEngine);
2994 const QRasterPaintEngineState *s = q->state();
2995
2996 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2997 return data->blend;
2998 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2999 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3000}
3001
3003{
3005 int end;
3006};
3007
3009 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
3010{
3011 QFixed clipLeft = QFixed::fromReal(clip.left() - 1);
3012 QFixed clipRight = QFixed::fromReal(clip.right() + 1);
3013 QFixed clipTop = QFixed::fromReal(clip.top() - 1);
3014 QFixed clipBottom = QFixed::fromReal(clip.bottom() + 1);
3015
3016 int first = 0;
3017 while (first < numGlyphs) {
3018 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
3020 QFixed top = metrics.y + positions[first].y;
3021 QFixed right = left + metrics.width;
3022 QFixed bottom = top + metrics.height;
3023 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3024 break;
3025 ++first;
3026 }
3027 int last = numGlyphs - 1;
3028 while (last > first) {
3029 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
3030 QFixed left = metrics.x + positions[last].x;
3031 QFixed top = metrics.y + positions[last].y;
3032 QFixed right = left + metrics.width;
3033 QFixed bottom = top + metrics.height;
3034 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3035 break;
3036 --last;
3037 }
3038 return {first, last + 1};
3039}
3040
3045{
3046 if (textItem->numGlyphs == 0)
3047 return;
3048
3049 ensurePen();
3050 ensureRasterState();
3051
3053
3054 QFontEngine *fontEngine = textItem->fontEngine();
3055 if (shouldDrawCachedGlyphs(fontEngine, matrix)) {
3056 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3057 fontEngine);
3058 } else if (matrix.type() < QTransform::TxProject) {
3059 bool invertible;
3060 QTransform invMat = matrix.inverted(&invertible);
3061 if (!invertible)
3062 return;
3063
3064 const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3065 textItem->fontEngine(), textItem->glyphs,
3066 textItem->glyphPositions, textItem->numGlyphs);
3067 QStaticTextItem copy = *textItem;
3068 copy.glyphs += range.begin;
3069 copy.glyphPositions += range.begin;
3070 copy.numGlyphs = range.end - range.begin;
3072 } else {
3074 }
3075}
3076
3081{
3082 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3083
3084#ifdef QT_DEBUG_DRAW
3085 Q_D(QRasterPaintEngine);
3086 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3087 p.x(), p.y(), QStringView(ti.chars, ti.num_chars).toLatin1().data(),
3088 d->glyphCacheFormat);
3089#endif
3090
3091 if (ti.glyphs.numGlyphs == 0)
3092 return;
3093 ensurePen();
3094 ensureRasterState();
3095
3097 QTransform matrix = s->matrix;
3098
3099 if (shouldDrawCachedGlyphs(ti.fontEngine, matrix)) {
3100 QVarLengthArray<QFixedPoint> positions;
3101 QVarLengthArray<glyph_t> glyphs;
3102
3103 matrix.translate(p.x(), p.y());
3104 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3105
3106 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3107 } else if (matrix.type() < QTransform::TxProject
3108 && ti.fontEngine->supportsTransformation(matrix)) {
3109 bool invertible;
3110 QTransform invMat = matrix.inverted(&invertible);
3111 if (!invertible)
3112 return;
3113
3114 QVarLengthArray<QFixedPoint> positions;
3115 QVarLengthArray<glyph_t> glyphs;
3116
3117 ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3118 ti.flags, glyphs, positions);
3119 const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3120 ti.fontEngine, glyphs.data(), positions.data(),
3121 glyphs.size());
3122
3123 if (range.begin >= range.end)
3124 return;
3125
3126 QStaticTextItem staticTextItem;
3127 staticTextItem.color = s->pen.color();
3128 staticTextItem.font = s->font;
3129 staticTextItem.setFontEngine(ti.fontEngine);
3130 staticTextItem.numGlyphs = range.end - range.begin;
3131 staticTextItem.glyphs = glyphs.data() + range.begin;
3132 staticTextItem.glyphPositions = positions.data() + range.begin;
3133 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3134 } else {
3136 }
3137}
3138
3143{
3144 Q_D(QRasterPaintEngine);
3146
3147 ensurePen();
3148 if (!s->penData.blend)
3149 return;
3150
3151 if (!s->flags.fast_pen) {
3153 return;
3154 }
3155
3156 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3157 stroker.drawPoints(points, pointCount);
3158}
3159
3160
3162{
3163 Q_D(QRasterPaintEngine);
3165
3166 ensurePen();
3167 if (!s->penData.blend)
3168 return;
3169
3170 if (!s->flags.fast_pen) {
3172 return;
3173 }
3174
3175 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3176 stroker.drawPoints(points, pointCount);
3177}
3178
3182void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3183{
3184#ifdef QT_DEBUG_DRAW
3185 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3186#endif
3187 Q_D(QRasterPaintEngine);
3189
3190 ensurePen();
3191 if (!s->penData.blend)
3192 return;
3193
3194 if (s->flags.fast_pen) {
3195 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3196 for (int i=0; i<lineCount; ++i) {
3197 const QLine &l = lines[i];
3198 stroker.drawLine(l.p1(), l.p2());
3199 }
3200 } else {
3201 QPaintEngineEx::drawLines(lines, lineCount);
3202 }
3203}
3204
3206 qreal width,
3207 int *dashIndex,
3208 qreal *dashOffset,
3209 bool *inDash)
3210{
3211 Q_Q(QRasterPaintEngine);
3213
3214 const QPen &pen = s->lastPen;
3215 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3216 const QList<qreal> pattern = pen.dashPattern();
3217
3218 qreal patternLength = 0;
3219 for (int i = 0; i < pattern.size(); ++i)
3220 patternLength += pattern.at(i);
3221
3222 if (patternLength <= 0)
3223 return;
3224
3225 qreal length = line.length();
3226 Q_ASSERT(length > 0);
3227 if (length / (patternLength * width) > QDashStroker::repetitionLimit()) {
3228 rasterizer->rasterizeLine(line.p1(), line.p2(), width / length, squareCap);
3229 return;
3230 }
3231
3232 while (length > 0) {
3233 const bool rasterize = *inDash;
3234 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3235 QLineF l = line;
3236
3237 if (dash >= length) {
3238 dash = line.length(); // Avoid accumulated precision error in 'length'
3239 *dashOffset += dash / width;
3240 length = 0;
3241 } else {
3242 *dashOffset = 0;
3243 *inDash = !(*inDash);
3244 if (++*dashIndex >= pattern.size())
3245 *dashIndex = 0;
3246 length -= dash;
3247 l.setLength(dash);
3248 line.setP1(l.p2());
3249 }
3250
3251 if (rasterize && dash > 0)
3252 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3253 }
3254}
3255
3259void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3260{
3261#ifdef QT_DEBUG_DRAW
3262 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3263#endif
3264 Q_D(QRasterPaintEngine);
3266
3267 ensurePen();
3268 if (!s->penData.blend)
3269 return;
3270 if (s->flags.fast_pen) {
3271 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3272 for (int i=0; i<lineCount; ++i) {
3273 QLineF line = lines[i];
3274 stroker.drawLine(line.p1(), line.p2());
3275 }
3276 } else {
3277 QPaintEngineEx::drawLines(lines, lineCount);
3278 }
3279}
3280
3281
3286{
3287 Q_D(QRasterPaintEngine);
3289
3290 ensurePen();
3291 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3292 || (qpen_style(s->lastPen) == Qt::NoPen))
3293 && !s->flags.antialiased
3294 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3295 && !rect.isEmpty()
3296 && s->matrix.type() <= QTransform::TxScale) // no shear
3297 {
3298 ensureBrush();
3299 const QRectF r = s->matrix.mapRect(rect);
3300 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3301 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3302 const QRect brect = QRect(int(r.x()), int(r.y()),
3303 int_dim(r.x(), r.width()),
3304 int_dim(r.y(), r.height()));
3305 if (brect == r) {
3306 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3307 &s->penData, &s->brushData);
3308 return;
3309 }
3310 }
3312}
3313
3314
3315#ifdef Q_OS_WIN
3319void QRasterPaintEngine::setDC(HDC hdc) {
3320 Q_D(QRasterPaintEngine);
3321 d->hdc = hdc;
3322}
3323
3327HDC QRasterPaintEngine::getDC() const
3328{
3329 Q_D(const QRasterPaintEngine);
3330 return d->hdc;
3331}
3332
3336void QRasterPaintEngine::releaseDC(HDC) const
3337{
3338}
3339
3340#endif
3341
3346{
3347 // Cached glyphs always require pretransformed positions
3348 if (shouldDrawCachedGlyphs(fontEngine, m))
3349 return true;
3350
3351 // Otherwise let the base-class decide based on the transform
3353}
3354
3360{
3361 // The raster engine does not support projected cached glyph drawing
3362 if (m.type() >= QTransform::TxProject)
3363 return false;
3364
3365 // The font engine might not support filling the glyph cache
3366 // with the given transform applied, in which case we need to
3367 // fall back to the QPainterPath code-path. This does not apply
3368 // for engines with internal caching, as we don't use the engine
3369 // to fill up our cache in that case.
3370 if (!fontEngine->hasInternalCaching() && !fontEngine->supportsTransformation(m))
3371 return false;
3372
3373 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, m);
3374}
3375
3380{
3381 return QPoint(0, 0);
3382}
3383
3384void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3385{
3386 Q_ASSERT(fg);
3387 if (!fg->blend)
3388 return;
3389 Q_D(QRasterPaintEngine);
3390
3391 Q_ASSERT(image.depth() == 1);
3392
3393 const int spanCount = 512;
3394 QT_FT_Span spans[spanCount];
3395 int n = 0;
3396
3397 // Boundaries
3398 int w = image.width();
3399 int h = image.height();
3400 int px = qRound(pos.x());
3401 int py = qRound(pos.y());
3402 int ymax = qMin(py + h, d->rasterBuffer->height());
3403 int ymin = qMax(py, 0);
3404 int xmax = qMin(px + w, d->rasterBuffer->width());
3405 int xmin = qMax(px, 0);
3406
3407 int x_offset = xmin - px;
3408
3409 QImage::Format format = image.format();
3410 for (int y = ymin; y < ymax; ++y) {
3411 const uchar *src = image.scanLine(y - py);
3413 for (int x = 0; x < xmax - xmin; ++x) {
3414 int src_x = x + x_offset;
3415 uchar pixel = src[src_x >> 3];
3416 if (!pixel) {
3417 x += 7 - (src_x%8);
3418 continue;
3419 }
3420 if (pixel & (0x1 << (src_x & 7))) {
3421 spans[n].x = xmin + x;
3422 spans[n].y = y;
3423 spans[n].coverage = 255;
3424 int len = 1;
3425 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3426 ++src_x;
3427 ++len;
3428 }
3429 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3430 x += len;
3431 ++n;
3432 if (n == spanCount) {
3433 fg->blend(n, spans, fg);
3434 n = 0;
3435 }
3436 }
3437 }
3438 } else {
3439 for (int x = 0; x < xmax - xmin; ++x) {
3440 int src_x = x + x_offset;
3441 uchar pixel = src[src_x >> 3];
3442 if (!pixel) {
3443 x += 7 - (src_x%8);
3444 continue;
3445 }
3446 if (pixel & (0x80 >> (x & 7))) {
3447 spans[n].x = xmin + x;
3448 spans[n].y = y;
3449 spans[n].coverage = 255;
3450 int len = 1;
3451 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3452 ++src_x;
3453 ++len;
3454 }
3455 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3456 x += len;
3457 ++n;
3458 if (n == spanCount) {
3459 fg->blend(n, spans, fg);
3460 n = 0;
3461 }
3462 }
3463 }
3464 }
3465 }
3466 if (n) {
3467 fg->blend(n, spans, fg);
3468 n = 0;
3469 }
3470}
3471
3485{
3486 Q_D(const QRasterPaintEngine);
3487
3488 const QClipData *clip = d->clip();
3489 if (!clip || clip->hasRectClip)
3490 return RectClip;
3491 else
3492 return ComplexClip;
3493}
3494
3500{
3501 Q_D(const QRasterPaintEngine);
3502
3503 const QClipData *clip = d->clip();
3504
3505 if (!clip)
3506 return d->deviceRect;
3507
3508 if (clip->hasRectClip)
3509 return clip->clipRect;
3510
3511 return QRectF(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3512}
3513
3515{
3516 Q_Q(QRasterPaintEngine);
3518
3519 rasterizer->setAntialiased(s->flags.antialiased);
3520
3521 QRect clipRect(deviceRect);
3522 ProcessSpans blend;
3523 // ### get from optimized rectbased QClipData
3524
3525 const QClipData *c = clip();
3526 if (c) {
3527 const QRect r(QPoint(c->xmin, c->ymin),
3528 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3529 clipRect = clipRect.intersected(r);
3530 blend = data->blend;
3531 } else {
3532 blend = data->unclipped_blend;
3533 }
3534
3535 rasterizer->setClipRect(clipRect);
3536 rasterizer->initialize(blend, data);
3537}
3538
3540 ProcessSpans callback,
3541 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3542{
3543 if (!callback || !outline)
3544 return;
3545
3546 Q_Q(QRasterPaintEngine);
3548
3549 if (!s->flags.antialiased) {
3550 initializeRasterizer(spanData);
3551
3552 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3555
3556 rasterizer->rasterize(outline, fillRule);
3557 return;
3558 }
3559
3560 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3561}
3562
3563extern "C" {
3565}
3566
3567static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3568{
3569 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3570}
3571
3573 ProcessSpans callback,
3574 void *userData, QRasterBuffer *)
3575{
3576 if (!callback || !outline)
3577 return;
3578
3579 Q_Q(QRasterPaintEngine);
3581
3582 if (!s->flags.antialiased) {
3583 rasterizer->setAntialiased(s->flags.antialiased);
3585 rasterizer->initialize(callback, userData);
3586
3587 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3590
3591 rasterizer->rasterize(outline, fillRule);
3592 return;
3593 }
3594
3595 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3596 // minimize memory reallocations. However if initial size for
3597 // raster pool is changed for lower value, reallocations will
3598 // occur normally.
3599 int rasterPoolSize = MINIMUM_POOL_SIZE;
3600 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3601 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3602 uchar *rasterPoolOnHeap = nullptr;
3603
3604 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3605
3606 void *data = userData;
3607
3608 QT_FT_BBox clip_box = { deviceRect.x(),
3609 deviceRect.y(),
3611 deviceRect.y() + deviceRect.height() };
3612
3613 QT_FT_Raster_Params rasterParams;
3614 rasterParams.target = nullptr;
3615 rasterParams.source = outline;
3616 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3617 rasterParams.gray_spans = nullptr;
3618 rasterParams.black_spans = nullptr;
3619 rasterParams.bit_test = nullptr;
3620 rasterParams.bit_set = nullptr;
3621 rasterParams.user = data;
3622 rasterParams.clip_box = clip_box;
3623
3624 bool done = false;
3625 int error;
3626
3627 int rendered_spans = 0;
3628
3629 while (!done) {
3630
3631 rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
3632 rasterParams.gray_spans = callback;
3633 rasterParams.skip_spans = rendered_spans;
3634 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3635
3636 // Out of memory, reallocate some more and try again...
3637 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3638 rasterPoolSize *= 2;
3639 if (rasterPoolSize > 1024 * 1024) {
3640 qWarning("QPainter: Rasterization of primitive failed");
3641 break;
3642 }
3643
3644 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3645
3646 free(rasterPoolOnHeap);
3647 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3648
3649 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3650
3651 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3652
3655 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3656 } else {
3657 done = true;
3658 }
3659 }
3660
3661 free(rasterPoolOnHeap);
3662}
3663
3665{
3666 Q_Q(QRasterPaintEngine);
3668
3669 if (!s->clipEnabled)
3670 return;
3671
3674}
3675
3677{
3678 Q_Q(QRasterPaintEngine);
3680
3681 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3682 && s->matrix.type() <= QTransform::TxShear;
3683}
3684
3686{
3687 Q_Q(const QRasterPaintEngine);
3688 const QRasterPaintEngineState *s = q->state();
3689
3690 return s->flags.fast_images
3693 && !image.hasAlphaChannel()));
3694}
3695
3697{
3698 Q_Q(const QRasterPaintEngine);
3699
3702 && !image.hasAlphaChannel())))
3703 return false;
3704
3705 const QRasterPaintEngineState *s = q->state();
3706 Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate);
3707
3708 if (s->intOpacity != 256
3709 || image.depth() < 8
3711 && (!isPixelAligned(pt) || !isPixelAligned(sr))))
3712 return false;
3713
3715 QImage::Format sFormat = image.format();
3716 // Formats must match or source format must be an opaque version of destination format
3717 if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha)
3718 dFormat = qt_maybeDataCompatibleOpaqueVersion(dFormat);
3719 return (dFormat == sFormat);
3720}
3721
3723{
3724 Q_ASSERT(image.depth() == 1);
3725
3726 const QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3727 QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3728 if (sourceImage.isNull() || dest.isNull())
3729 return image; // we must have run out of memory
3730
3731 QRgb fg = qPremultiply(color.rgba());
3732 QRgb bg = 0;
3733
3734 int height = sourceImage.height();
3735 int width = sourceImage.width();
3736 for (int y=0; y<height; ++y) {
3737 const uchar *source = sourceImage.constScanLine(y);
3738 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3739 for (int x=0; x < width; ++x)
3740 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3741 }
3742 return dest;
3743}
3744
3748
3756
3758{
3759 m_buffer = (uchar *)image->bits();
3760 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3761 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3762 bytes_per_pixel = image->depth()/8;
3763 bytes_per_line = image->bytesPerLine();
3764
3765 format = image->format();
3766 colorSpace = image->colorSpace();
3767 if (image->depth() == 1 && image->colorTable().size() == 2) {
3769 const QList<QRgb> colorTable = image->colorTable();
3770 destColor0 = qPremultiply(colorTable[0]);
3771 destColor1 = qPremultiply(colorTable[1]);
3772 }
3773
3774 return format;
3775}
3776
3778{
3780 m_clipLines = nullptr;
3781
3782 allocated = 0;
3783 m_spans = nullptr;
3784 xmin = xmax = ymin = ymax = 0;
3785 count = 0;
3786
3787 enabled = true;
3788 hasRectClip = hasRegionClip = false;
3789}
3790
3792{
3793 if (m_clipLines)
3794 free(m_clipLines);
3795 if (m_spans)
3796 free(m_spans);
3797}
3798
3800{
3801 if (m_spans)
3802 return;
3803
3804 if (!m_clipLines)
3805 m_clipLines = (ClipLine *)calloc(clipSpanHeight, sizeof(ClipLine));
3806
3808 QT_TRY {
3810 count = 0;
3811 QT_TRY {
3812 if (hasRegionClip) {
3813 const auto rects = clipRegion.begin();
3814 const int numRects = clipRegion.rectCount();
3815 const int maxSpans = (ymax - ymin) * numRects;
3816 allocated = qMax(allocated, maxSpans);
3817 m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3819
3820 int y = 0;
3821 int firstInBand = 0;
3822 while (firstInBand < numRects) {
3823 const int currMinY = rects[firstInBand].y();
3824 const int currMaxY = currMinY + rects[firstInBand].height();
3825
3826 while (y < currMinY) {
3827 m_clipLines[y].spans = nullptr;
3828 m_clipLines[y].count = 0;
3829 ++y;
3830 }
3831
3832 int lastInBand = firstInBand;
3833 while (lastInBand + 1 < numRects && rects[lastInBand+1].top() == y)
3834 ++lastInBand;
3835
3836 while (y < currMaxY) {
3837
3838 m_clipLines[y].spans = m_spans + count;
3839 m_clipLines[y].count = lastInBand - firstInBand + 1;
3840
3841 for (int r = firstInBand; r <= lastInBand; ++r) {
3842 const QRect &currRect = rects[r];
3844 span->x = currRect.x();
3845 span->len = currRect.width();
3846 span->y = y;
3847 span->coverage = 255;
3848 ++count;
3849 }
3850 ++y;
3851 }
3852
3853 firstInBand = lastInBand + 1;
3854 }
3855
3857
3858 while (y < clipSpanHeight) {
3859 m_clipLines[y].spans = nullptr;
3860 m_clipLines[y].count = 0;
3861 ++y;
3862 }
3863
3864 return;
3865 }
3866
3867 m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3869
3870 if (hasRectClip) {
3871 int y = 0;
3872 while (y < ymin) {
3873 m_clipLines[y].spans = nullptr;
3874 m_clipLines[y].count = 0;
3875 ++y;
3876 }
3877
3878 const int len = clipRect.width();
3879 while (y < ymax) {
3881 span->x = xmin;
3882 span->len = len;
3883 span->y = y;
3884 span->coverage = 255;
3885 ++count;
3886
3887 m_clipLines[y].spans = span;
3888 m_clipLines[y].count = 1;
3889 ++y;
3890 }
3891
3892 while (y < clipSpanHeight) {
3893 m_clipLines[y].spans = nullptr;
3894 m_clipLines[y].count = 0;
3895 ++y;
3896 }
3897 }
3898 } QT_CATCH(...) {
3899 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3900 m_spans = nullptr;
3901 QT_RETHROW;
3902 }
3903 } QT_CATCH(...) {
3904 free(m_clipLines); // same for clipLines
3905 m_clipLines = nullptr;
3906 QT_RETHROW;
3907 }
3908}
3909
3911{
3913
3914 if (count == 0) {
3915 ymin = ymax = xmin = xmax = 0;
3916 return;
3917 }
3918
3919 int y = -1;
3920 ymin = m_spans[0].y;
3921 ymax = m_spans[count-1].y + 1;
3922 xmin = INT_MAX;
3923 xmax = 0;
3924
3925 const int firstLeft = m_spans[0].x;
3926 const int firstRight = m_spans[0].x + m_spans[0].len;
3927 bool isRect = true;
3928
3929 for (int i = 0; i < count; ++i) {
3931
3932 if (span.y != y) {
3933 if (span.y != y + 1 && y != -1)
3934 isRect = false;
3935 y = span.y;
3936 m_clipLines[y].spans = &span;
3937 m_clipLines[y].count = 1;
3938 } else
3939 ++m_clipLines[y].count;
3940
3941 const int spanLeft = span.x;
3942 const int spanRight = spanLeft + span.len;
3943
3944 if (spanLeft < xmin)
3945 xmin = spanLeft;
3946
3947 if (spanRight > xmax)
3948 xmax = spanRight;
3949
3950 if (spanLeft != firstLeft || spanRight != firstRight)
3951 isRect = false;
3952 }
3953
3954 if (isRect) {
3955 hasRectClip = true;
3957 }
3958}
3959
3960/*
3961 Convert \a rect to clip spans.
3962 */
3964{
3965 if (hasRectClip && rect == clipRect)
3966 return;
3967
3968// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3969 hasRectClip = true;
3970 hasRegionClip = false;
3971 clipRect = rect;
3972
3973 xmin = rect.x();
3974 xmax = rect.x() + rect.width();
3975 ymin = qMin(rect.y(), clipSpanHeight);
3976 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3977
3978 if (m_spans) {
3979 free(m_spans);
3980 m_spans = nullptr;
3981 }
3982
3983// qDebug() << xmin << xmax << ymin << ymax;
3984}
3985
3986/*
3987 Convert \a region to clip spans.
3988 */
3990{
3991 if (region.rectCount() == 1) {
3992 setClipRect(region.boundingRect());
3993 return;
3994 }
3995
3996 hasRegionClip = true;
3997 hasRectClip = false;
3998 clipRegion = region;
3999
4000 { // set bounding rect
4001 const QRect rect = region.boundingRect();
4002 xmin = rect.x();
4003 xmax = rect.x() + rect.width();
4004 ymin = rect.y();
4005 ymax = rect.y() + rect.height();
4006 }
4007
4008 if (m_spans) {
4009 free(m_spans);
4010 m_spans = nullptr;
4011 }
4012
4013}
4014
4019static const QT_FT_Span *qt_intersect_spans(const QClipData *clip, int *currentClip,
4020 const QT_FT_Span *spans, const QT_FT_Span *end,
4021 QT_FT_Span **outSpans, int available)
4022{
4023 const_cast<QClipData *>(clip)->initialize();
4024
4025 QT_FT_Span *out = *outSpans;
4026
4027 const QT_FT_Span *clipSpans = clip->m_spans + *currentClip;
4028 const QT_FT_Span *clipEnd = clip->m_spans + clip->count;
4029
4030 while (available && spans < end ) {
4031 if (clipSpans >= clipEnd) {
4032 spans = end;
4033 break;
4034 }
4035 if (clipSpans->y > spans->y) {
4036 ++spans;
4037 continue;
4038 }
4039 if (spans->y != clipSpans->y) {
4040 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4041 clipSpans = clip->m_clipLines[spans->y].spans;
4042 else
4043 ++clipSpans;
4044 continue;
4045 }
4046 Q_ASSERT(spans->y == clipSpans->y);
4047
4048 int sx1 = spans->x;
4049 int sx2 = sx1 + spans->len;
4050 int cx1 = clipSpans->x;
4051 int cx2 = cx1 + clipSpans->len;
4052
4053 if (cx1 < sx1 && cx2 < sx1) {
4054 ++clipSpans;
4055 continue;
4056 } else if (sx1 < cx1 && sx2 < cx1) {
4057 ++spans;
4058 continue;
4059 }
4060 int x = qMax(sx1, cx1);
4061 int len = qMin(sx2, cx2) - x;
4062 if (len) {
4063 out->x = qMax(sx1, cx1);
4064 out->len = qMin(sx2, cx2) - out->x;
4065 out->y = spans->y;
4066 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4067 ++out;
4068 --available;
4069 }
4070 if (sx2 < cx2) {
4071 ++spans;
4072 } else {
4073 ++clipSpans;
4074 }
4075 }
4076
4077 *outSpans = out;
4078 *currentClip = clipSpans - clip->m_spans;
4079 return spans;
4080}
4081
4082static void qt_span_fill_clipped(int spanCount, const QT_FT_Span *spans, void *userData)
4083{
4084// qDebug() << "qt_span_fill_clipped" << spanCount;
4085 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4086
4087 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4088
4089 const int NSPANS = 512;
4090 QT_FT_Span cspans[NSPANS];
4091 int currentClip = 0;
4092 const QT_FT_Span *end = spans + spanCount;
4093 while (spans < end) {
4094 QT_FT_Span *clipped = cspans;
4095 spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
4096// qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4097// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4098
4099 if (clipped - cspans)
4100 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4101 }
4102}
4103
4104/*
4105 \internal
4106 Clip spans to \a{clip}-rectangle.
4107 Returns number of unclipped spans
4108*/
4109static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
4110 const QRect &clip)
4111{
4112 const int minx = clip.left();
4113 const int miny = clip.top();
4114 const int maxx = clip.right();
4115 const int maxy = clip.bottom();
4116
4117 QT_FT_Span *end = spans + numSpans;
4118 while (spans < end) {
4119 if (spans->y >= miny)
4120 break;
4121 ++spans;
4122 }
4123
4124 QT_FT_Span *s = spans;
4125 while (s < end) {
4126 if (s->y > maxy)
4127 break;
4128 if (s->x > maxx || s->x + s->len <= minx) {
4129 s->len = 0;
4130 ++s;
4131 continue;
4132 }
4133 if (s->x < minx) {
4134 s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
4135 s->x = minx;
4136 } else {
4137 s->len = qMin(s->len, (maxx - s->x + 1));
4138 }
4139 ++s;
4140 }
4141
4142 return s - spans;
4143}
4144
4145
4146static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans,
4147 void *userData)
4148{
4149 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4150 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4151
4152 Q_ASSERT(fillData->clip);
4153 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4154
4155 QT_FT_Span *s = const_cast<QT_FT_Span *>(spans);
4156 // hw: check if this const_cast<> is safe!!!
4158 fillData->clip->clipRect);
4159 if (count > 0)
4160 fillData->unclipped_blend(count, s, fillData);
4161}
4162
4163static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData)
4164{
4165 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4166
4167// qDebug() << " qt_span_clip: " << count << clipData->operation;
4168// for (int i = 0; i < qMin(count, 10); ++i) {
4169// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4170// }
4171
4172 switch (clipData->operation) {
4173
4174 case Qt::IntersectClip:
4175 {
4176 QClipData *newClip = clipData->newClip;
4177 newClip->initialize();
4178
4179 int currentClip = 0;
4180 const QT_FT_Span *end = spans + count;
4181 while (spans < end) {
4182 QT_FT_Span *newspans = newClip->m_spans + newClip->count;
4183 spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
4184 &newspans, newClip->allocated - newClip->count);
4185 newClip->count = newspans - newClip->m_spans;
4186 if (spans < end) {
4187 newClip->m_spans = q_check_ptr((QT_FT_Span *)realloc(newClip->m_spans, newClip->allocated * 2 * sizeof(QT_FT_Span)));
4188 newClip->allocated *= 2;
4189 }
4190 }
4191 }
4192 break;
4193
4194 case Qt::ReplaceClip:
4195 clipData->newClip->appendSpans(spans, count);
4196 break;
4197 case Qt::NoClip:
4198 break;
4199 }
4200}
4201
4203{
4204public:
4215
4216 using QGradientColorTableHash = QMultiHash<quint64, std::shared_ptr<const CacheInfo>>;
4217
4218 std::shared_ptr<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
4219 quint64 hash_val = 0;
4220
4221 const QGradientStops stops = gradient.stops();
4222 for (int i = 0; i < stops.size() && i <= 2; i++)
4223 hash_val += stops[i].second.rgba64();
4224
4227
4228 if (it == cache.constEnd())
4229 return addCacheElement(hash_val, gradient, opacity);
4230 else {
4231 do {
4232 const auto &cache_info = it.value();
4233 if (cache_info->stops == stops && cache_info->opacity == opacity && cache_info->interpolationMode == gradient.interpolationMode())
4234 return cache_info;
4235 ++it;
4236 } while (it != cache.constEnd() && it.key() == hash_val);
4237 // an exact match for these stops and opacity was not found, create new cache
4238 return addCacheElement(hash_val, gradient, opacity);
4239 }
4240 }
4241
4242 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4243protected:
4244 inline int maxCacheSize() const { return 60; }
4245 inline void generateGradientColorTable(const QGradient& g,
4246 QRgba64 *colorTable,
4247 int size, int opacity) const;
4248 std::shared_ptr<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4249 if (cache.size() == maxCacheSize()) {
4250 // may remove more than 1, but OK
4251 cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize())));
4252 }
4253 auto cache_entry = std::make_shared<CacheInfo>(gradient.stops(), opacity, gradient.interpolationMode());
4254 generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
4255 for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
4256 cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
4257 return cache.insert(hash_val, std::move(cache_entry)).value();
4258 }
4259
4262};
4263
4264void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
4265{
4266 const QGradientStops stops = gradient.stops();
4267 int stopCount = stops.size();
4268 Q_ASSERT(stopCount > 0);
4269
4270 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4271
4272 if (stopCount == 2) {
4273 QRgba64 first_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4274 QRgba64 second_color = combineAlpha256(stops[1].second.rgba64(), opacity);
4275
4276 qreal first_stop = stops[0].first;
4277 qreal second_stop = stops[1].first;
4278
4279 if (second_stop < first_stop) {
4280 quint64 tmp = first_color;
4281 first_color = second_color;
4282 second_color = tmp;
4283 qSwap(first_stop, second_stop);
4284 }
4285
4286 if (colorInterpolation) {
4287 first_color = qPremultiply(first_color);
4288 second_color = qPremultiply(second_color);
4289 }
4290
4291 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4292 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4293
4294 uint red_first = uint(first_color.red()) << 16;
4295 uint green_first = uint(first_color.green()) << 16;
4296 uint blue_first = uint(first_color.blue()) << 16;
4297 uint alpha_first = uint(first_color.alpha()) << 16;
4298
4299 uint red_second = uint(second_color.red()) << 16;
4300 uint green_second = uint(second_color.green()) << 16;
4301 uint blue_second = uint(second_color.blue()) << 16;
4302 uint alpha_second = uint(second_color.alpha()) << 16;
4303
4304 int i = 0;
4305 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4306 if (colorInterpolation)
4307 colorTable[i] = first_color;
4308 else
4309 colorTable[i] = qPremultiply(first_color);
4310 }
4311
4312 if (i < second_index) {
4313 qreal reciprocal = qreal(1) / (second_index - first_index);
4314
4315 int red_delta = qRound((qreal(red_second) - red_first) * reciprocal);
4316 int green_delta = qRound((qreal(green_second) - green_first) * reciprocal);
4317 int blue_delta = qRound((qreal(blue_second) - blue_first) * reciprocal);
4318 int alpha_delta = qRound((qreal(alpha_second) - alpha_first) * reciprocal);
4319
4320 // rounding
4321 red_first += 1 << 15;
4322 green_first += 1 << 15;
4323 blue_first += 1 << 15;
4324 alpha_first += 1 << 15;
4325
4326 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4327 red_first += red_delta;
4328 green_first += green_delta;
4329 blue_first += blue_delta;
4330 alpha_first += alpha_delta;
4331
4332 const QRgba64 color = qRgba64(red_first >> 16, green_first >> 16, blue_first >> 16, alpha_first >> 16);
4333
4334 if (colorInterpolation)
4335 colorTable[i] = color;
4336 else
4337 colorTable[i] = qPremultiply(color);
4338 }
4339 }
4340
4341 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4342 if (colorInterpolation)
4343 colorTable[i] = second_color;
4344 else
4345 colorTable[i] = qPremultiply(second_color);
4346 }
4347
4348 return;
4349 }
4350
4351 QRgba64 current_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4352 if (stopCount == 1) {
4353 current_color = qPremultiply(current_color);
4354 for (int i = 0; i < size; ++i)
4355 colorTable[i] = current_color;
4356 return;
4357 }
4358
4359 // The position where the gradient begins and ends
4360 qreal begin_pos = stops[0].first;
4361 qreal end_pos = stops[stopCount-1].first;
4362
4363 int pos = 0; // The position in the color table.
4364 QRgba64 next_color;
4365
4366 qreal incr = 1 / qreal(size); // the double increment.
4367 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4368
4369 // Up to first point
4370 colorTable[pos++] = qPremultiply(current_color);
4371 while (dpos <= begin_pos) {
4372 colorTable[pos] = colorTable[pos - 1];
4373 ++pos;
4374 dpos += incr;
4375 }
4376
4377 int current_stop = 0; // We always interpolate between current and current + 1.
4378
4379 qreal t; // position between current left and right stops
4380 qreal t_delta; // the t increment per entry in the color table
4381
4382 if (dpos < end_pos) {
4383 // Gradient area
4384 while (dpos > stops[current_stop+1].first)
4385 ++current_stop;
4386
4387 if (current_stop != 0)
4388 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4389 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4390
4391 if (colorInterpolation) {
4392 current_color = qPremultiply(current_color);
4393 next_color = qPremultiply(next_color);
4394 }
4395
4396 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4397 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4398 t = (dpos - stops[current_stop].first) * c;
4399 t_delta = incr * c;
4400
4401 while (true) {
4402 Q_ASSERT(current_stop < stopCount);
4403
4404 int dist = qRound(t);
4405 int idist = 256 - dist;
4406
4407 if (colorInterpolation)
4408 colorTable[pos] = interpolate256(current_color, idist, next_color, dist);
4409 else
4410 colorTable[pos] = qPremultiply(interpolate256(current_color, idist, next_color, dist));
4411
4412 ++pos;
4413 dpos += incr;
4414
4415 if (dpos >= end_pos)
4416 break;
4417
4418 t += t_delta;
4419
4420 int skip = 0;
4421 while (dpos > stops[current_stop+skip+1].first)
4422 ++skip;
4423
4424 if (skip != 0) {
4425 current_stop += skip;
4426 if (skip == 1)
4427 current_color = next_color;
4428 else
4429 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4430 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4431
4432 if (colorInterpolation) {
4433 if (skip != 1)
4434 current_color = qPremultiply(current_color);
4435 next_color = qPremultiply(next_color);
4436 }
4437
4438 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4439 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4440 t = (dpos - stops[current_stop].first) * c;
4441 t_delta = incr * c;
4442 }
4443 }
4444 }
4445
4446 // After last point
4447 current_color = qPremultiply(combineAlpha256(stops[stopCount - 1].second.rgba64(), opacity));
4448 while (pos < size - 1) {
4449 colorTable[pos] = current_color;
4450 ++pos;
4451 }
4452
4453 // Make sure the last color stop is represented at the end of the table
4454 colorTable[size - 1] = current_color;
4455}
4456
4457Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4458
4459
4461{
4462 rasterBuffer = rb;
4463 type = None;
4464 txop = 0;
4465 bilinear = false;
4466 m11 = m22 = m33 = 1.;
4467 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4468 clip = pe ? pe->d_func()->clip() : nullptr;
4469}
4470
4471Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4472
4474 bool isCosmetic)
4475{
4476 Qt::BrushStyle brushStyle = qbrush_style(brush);
4477 cachedGradient.reset();
4478 switch (brushStyle) {
4479 case Qt::SolidPattern: {
4480 type = Solid;
4483 if (solidColor.alphaF() <= 0.0f && compositionMode == QPainter::CompositionMode_SourceOver)
4484 type = None;
4485 break;
4486 }
4487
4489 {
4491 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4492 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4493
4494 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4495 gradient.colorTable32 = cacheInfo->buffer32;
4496#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4497 gradient.colorTable64 = cacheInfo->buffer64;
4498#endif
4499 cachedGradient = std::move(cacheInfo);
4500
4501 gradient.spread = g->spread();
4502
4503 QLinearGradientData &linearData = gradient.linear;
4504
4505 linearData.origin.x = g->start().x();
4506 linearData.origin.y = g->start().y();
4507 linearData.end.x = g->finalStop().x();
4508 linearData.end.y = g->finalStop().y();
4509 break;
4510 }
4511
4513 {
4515 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4516 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4517
4518 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4519 gradient.colorTable32 = cacheInfo->buffer32;
4520#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4521 gradient.colorTable64 = cacheInfo->buffer64;
4522#endif
4523 cachedGradient = std::move(cacheInfo);
4524
4525 gradient.spread = g->spread();
4526
4527 QRadialGradientData &radialData = gradient.radial;
4528
4529 QPointF center = g->center();
4530 radialData.center.x = center.x();
4531 radialData.center.y = center.y();
4532 radialData.center.radius = g->centerRadius();
4533 QPointF focal = g->focalPoint();
4534 radialData.focal.x = focal.x();
4535 radialData.focal.y = focal.y();
4536 radialData.focal.radius = g->focalRadius();
4537 }
4538 break;
4539
4541 {
4543 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4544 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4545
4546 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4547 gradient.colorTable32 = cacheInfo->buffer32;
4548#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4549 gradient.colorTable64 = cacheInfo->buffer64;
4550#endif
4551 cachedGradient = std::move(cacheInfo);
4552
4554
4555 QConicalGradientData &conicalData = gradient.conical;
4556
4557 QPointF center = g->center();
4558 conicalData.center.x = center.x();
4559 conicalData.center.y = center.y();
4560 conicalData.angle = qDegreesToRadians(g->angle());
4561 }
4562 break;
4563
4564 case Qt::Dense1Pattern:
4565 case Qt::Dense2Pattern:
4566 case Qt::Dense3Pattern:
4567 case Qt::Dense4Pattern:
4568 case Qt::Dense5Pattern:
4569 case Qt::Dense6Pattern:
4570 case Qt::Dense7Pattern:
4571 case Qt::HorPattern:
4572 case Qt::VerPattern:
4573 case Qt::CrossPattern:
4574 case Qt::BDiagPattern:
4575 case Qt::FDiagPattern:
4577 type = Texture;
4578 if (!tempImage)
4579 tempImage = new QImage();
4580 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4582 break;
4583 case Qt::TexturePattern:
4584 type = Texture;
4585 if (!tempImage)
4586 tempImage = new QImage();
4587
4588 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4589 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4590 else
4591 *tempImage = brush.textureImage();
4593 break;
4594
4595 case Qt::NoBrush:
4596 default:
4597 type = None;
4598 break;
4599 }
4601}
4602
4604{
4605 bitmapBlit = nullptr;
4606 alphamapBlit = nullptr;
4607 alphaRGBBlit = nullptr;
4608
4609 fillRect = nullptr;
4610
4611 switch(type) {
4612 case None:
4613 unclipped_blend = nullptr;
4614 break;
4615 case Solid: {
4616 const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format];
4617 unclipped_blend = drawHelper.blendColor;
4618 bitmapBlit = drawHelper.bitmapBlit;
4619 alphamapBlit = drawHelper.alphamapBlit;
4620 alphaRGBBlit = drawHelper.alphaRGBBlit;
4621 fillRect = drawHelper.fillRect;
4622 break;
4623 }
4624 case LinearGradient:
4625 case RadialGradient:
4626 case ConicalGradient:
4628 break;
4629 case Texture:
4631 if (!texture.imageData)
4632 unclipped_blend = nullptr;
4633
4634 break;
4635 }
4636 // setup clipping
4637 if (!unclipped_blend) {
4638 blend = nullptr;
4639 } else if (!clip) {
4641 } else if (clip->hasRectClip) {
4643 } else {
4645 }
4646}
4647
4649{
4650 QTransform delta;
4651 // make sure we round off correctly in qdrawhelper.cpp
4652 delta.translate(1.0 / 65536, 1.0 / 65536);
4653
4654 QTransform inv = (delta * matrix).inverted();
4655 m11 = inv.m11();
4656 m12 = inv.m12();
4657 m13 = inv.m13();
4658 m21 = inv.m21();
4659 m22 = inv.m22();
4660 m23 = inv.m23();
4661 m33 = inv.m33();
4662 dx = inv.dx();
4663 dy = inv.dy();
4664 txop = inv.type();
4665 bilinear = bilin;
4666
4667 const bool affine = inv.isAffine();
4668 const qreal f1 = m11 * m11 + m21 * m21;
4669 const qreal f2 = m12 * m12 + m22 * m22;
4670 fast_matrix = affine
4671 && f1 < 1e4
4672 && f2 < 1e4
4673 && f1 > (1.0 / 65536)
4674 && f2 > (1.0 / 65536)
4675 && qAbs(dx) < 1e4
4676 && qAbs(dy) < 1e4;
4677
4679}
4680
4681void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4682{
4683 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4684 if (!d || d->height == 0) {
4685 texture.imageData = nullptr;
4686 texture.width = 0;
4687 texture.height = 0;
4688 texture.x1 = 0;
4689 texture.y1 = 0;
4690 texture.x2 = 0;
4691 texture.y2 = 0;
4692 texture.bytesPerLine = 0;
4694 texture.colorTable = nullptr;
4695 texture.hasAlpha = alpha != 256;
4696 } else {
4697 texture.imageData = d->data;
4698 texture.width = d->width;
4699 texture.height = d->height;
4700
4701 if (sourceRect.isNull()) {
4702 texture.x1 = 0;
4703 texture.y1 = 0;
4704 texture.x2 = texture.width;
4705 texture.y2 = texture.height;
4706 } else {
4707 texture.x1 = sourceRect.x();
4708 texture.y1 = sourceRect.y();
4709 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4710 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4711 }
4712
4713 texture.bytesPerLine = d->bytes_per_line;
4714
4715 texture.format = d->format;
4716 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : nullptr;
4717 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4718 }
4719 texture.const_alpha = alpha;
4720 texture.type = _type;
4721
4723}
4724
4729static inline void drawEllipsePoints(int x, int y, int length,
4730 const QRect &rect,
4731 const QRect &clip,
4732 ProcessSpans pen_func, ProcessSpans brush_func,
4733 QSpanData *pen_data, QSpanData *brush_data)
4734{
4735 if (length == 0)
4736 return;
4737
4738 QT_FT_Span _outline[4];
4739 QT_FT_Span *outline = _outline;
4740 const int midx = rect.x() + (rect.width() + 1) / 2;
4741 const int midy = rect.y() + (rect.height() + 1) / 2;
4742
4743 x = x + midx;
4744 y = midy - y;
4745
4746 // topleft
4747 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4748 outline[0].len = qMin(length, x - outline[0].x);
4749 outline[0].y = y;
4750 outline[0].coverage = 255;
4751
4752 // topright
4753 outline[1].x = x;
4754 outline[1].len = length;
4755 outline[1].y = y;
4756 outline[1].coverage = 255;
4757
4758 // bottomleft
4759 outline[2].x = outline[0].x;
4760 outline[2].len = outline[0].len;
4761 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4762 outline[2].coverage = 255;
4763
4764 // bottomright
4765 outline[3].x = x;
4766 outline[3].len = length;
4767 outline[3].y = outline[2].y;
4768 outline[3].coverage = 255;
4769
4770 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4771 QT_FT_Span _fill[2];
4772 QT_FT_Span *fill = _fill;
4773
4774 // top fill
4775 fill[0].x = outline[0].x + outline[0].len - 1;
4776 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4777 fill[0].y = outline[1].y;
4778 fill[0].coverage = 255;
4779
4780 // bottom fill
4781 fill[1].x = outline[2].x + outline[2].len - 1;
4782 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4783 fill[1].y = outline[3].y;
4784 fill[1].coverage = 255;
4785
4786 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4787 n = qt_intersect_spans(fill, n, clip);
4788 if (n > 0)
4789 brush_func(n, fill, brush_data);
4790 }
4791 if (pen_func) {
4792 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4793 n = qt_intersect_spans(outline, n, clip);
4794 if (n > 0)
4795 pen_func(n, outline, pen_data);
4796 }
4797}
4798
4803static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4804 ProcessSpans pen_func, ProcessSpans brush_func,
4805 QSpanData *pen_data, QSpanData *brush_data)
4806{
4807 const qreal a = qreal(rect.width()) / 2;
4808 const qreal b = qreal(rect.height()) / 2;
4809 qreal d = b*b - (a*a*b) + 0.25*a*a;
4810
4811 int x = 0;
4812 int y = (rect.height() + 1) / 2;
4813 int startx = x;
4814
4815 // region 1
4816 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4817 if (d < 0) { // select E
4818 d += b*b*(2*x + 3);
4819 ++x;
4820 } else { // select SE
4821 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4822 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4823 pen_func, brush_func, pen_data, brush_data);
4824 startx = ++x;
4825 --y;
4826 }
4827 }
4828 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4829 pen_func, brush_func, pen_data, brush_data);
4830
4831 // region 2
4832 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4833 const int miny = rect.height() & 0x1;
4834 while (y > miny) {
4835 if (d < 0) { // select SE
4836 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4837 ++x;
4838 } else { // select S
4839 d += a*a*(-2*y + 3);
4840 }
4841 --y;
4842 drawEllipsePoints(x, y, 1, rect, clip,
4843 pen_func, brush_func, pen_data, brush_data);
4844 }
4845}
4846
4847/*
4848 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4849 \overload
4850 \reimp
4851*/
4852
4853
4854#ifdef QT_DEBUG_DRAW
4855void dumpClip(int width, int height, const QClipData *clip)
4856{
4858 clipImg.fill(0xffff0000);
4859
4860 int x0 = width;
4861 int x1 = 0;
4862 int y0 = height;
4863 int y1 = 0;
4864
4865 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4866
4867 for (int i = 0; i < clip->count; ++i) {
4868 const QT_FT_Span *span = ((QClipData *) clip)->spans() + i;
4869 for (int j = 0; j < span->len; ++j)
4870 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4871 x0 = qMin(x0, int(span->x));
4872 x1 = qMax(x1, int(span->x + span->len - 1));
4873
4874 y0 = qMin(y0, int(span->y));
4875 y1 = qMax(y1, int(span->y));
4876 }
4877
4878 static int counter = 0;
4879
4880 Q_ASSERT(y0 >= 0);
4881 Q_ASSERT(x0 >= 0);
4882 Q_ASSERT(y1 >= 0);
4883 Q_ASSERT(x1 >= 0);
4884
4885 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4886 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
4887}
4888#endif
4889
4890
IOBluetoothDevice * device
\inmodule QtGui
Definition qbrush.h:30
QTransform transform() const
Definition qbrush.h:122
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
QClipData(int height)
void appendSpans(const QT_FT_Span *s, int num)
struct QClipData::ClipLine * m_clipLines
void setClipRegion(const QRegion &region)
void setClipRect(const QRect &rect)
QT_FT_Span * m_spans
TransferFunction transferFunction() const noexcept
Returns the predefined transfer function of the color space or TransferFunction::Custom if it doesn't...
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
@ ExtendedRgb
Definition qcolor.h:35
float alphaF() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1497
static QColor fromRgbF(float r, float g, float b, float a=1.0)
Static convenience function that returns a QColor constructed from the RGB color values,...
Definition qcolor.cpp:2427
\inmodule QtGui
Definition qbrush.h:446
void drawPath(const QVectorPath &path)
void drawPoints(const QPoint *points, int num)
void drawLine(const QPointF &p1, const QPointF &p2)
static int repetitionLimit()
Definition qstroker_p.h:235
void setGlyphCache(const void *key, QFontEngineGlyphCache *data)
virtual bool expectsGammaCorrectedBlending() const
virtual Glyph * glyphData(glyph_t glyph, const QFixedPoint &subPixelPosition, GlyphFormat neededFormat, const QTransform &t)
virtual int glyphMargin(GlyphFormat format)
virtual bool supportsVerticalSubPixelPositions() const
virtual bool supportsTransformation(const QTransform &transform) const
GlyphFormat glyphFormat
virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs)
QFontEngineGlyphCache * glyphCache(const void *key, GlyphFormat format, const QTransform &transform, const QColor &color=QColor()) const
virtual bool hasInternalCaching() const
virtual QFixedPoint subPixelPositionFor(const QFixedPoint &position) const
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 qbrush.h:135
InterpolationMode
Definition qbrush.h:161
@ ColorInterpolation
Definition qbrush.h:162
InterpolationMode interpolationMode() const
Definition qbrush.cpp:1706
@ RepeatSpread
Definition qbrush.h:149
QGradientStops stops() const
Returns the stop points for this gradient.
Definition qbrush.cpp:1631
\inmodule QtGui
Definition qimage.h:37
static QPixelFormat toPixelFormat(QImage::Format format) noexcept
Converts format into a QPixelFormat.
Definition qimage.cpp:6392
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1637
int width() const
Returns the width of the image.
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1222
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_ARGB6666_Premultiplied
Definition qimage.h:52
@ Format_ARGB8555_Premultiplied
Definition qimage.h:54
@ Format_MonoLSB
Definition qimage.h:44
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_ARGB8565_Premultiplied
Definition qimage.h:50
@ Format_Mono
Definition qimage.h:43
@ Format_A2BGR30_Premultiplied
Definition qimage.h:62
@ Format_Indexed8
Definition qimage.h:45
@ NImageFormats
Definition qimage.h:80
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
@ Format_A2RGB30_Premultiplied
Definition qimage.h:64
@ Format_ARGB4444_Premultiplied
Definition qimage.h:57
QRect rect() const
Returns the enclosing rectangle (0, 0, width(), height()) of the image.
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
constexpr QPointF p1() const
Returns the line's start point.
Definition qline.h:317
constexpr QPointF p2() const
Returns the line's end point.
Definition qline.h:322
void setLength(qreal len)
Sets the length of the line to the given finite length.
Definition qline.h:368
\inmodule QtCore\compares equality \compareswith equality QLineF \endcompareswith
Definition qline.h:18
constexpr QPoint p1() const
Returns the line's start point.
Definition qline.h:104
constexpr QPoint p2() const
Returns the line's end point.
Definition qline.h:109
\inmodule QtGui
Definition qbrush.h:394
qsizetype size() const noexcept
Definition qlist.h:397
T & first()
Definition qlist.h:645
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1925
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1918
qsizetype size() const noexcept
Definition qhash.h:1567
iterator erase(const_iterator it)
Definition qhash.h:1965
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:2025
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
virtual int devType() const
int width() const
int height() const
virtual void drawRects(const QRect *rects, int rectCount) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual void stroke(const QVectorPath &path, const QPen &pen)
virtual void drawEllipse(const QRectF &r) override
Reimplement this function to draw the largest ellipse that can be contained within rectangle rect.
virtual void drawLines(const QLine *lines, int lineCount) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual void clip(const QVectorPath &path, Qt::ClipOperation op)=0
virtual bool requiresPretransformedGlyphPositions(QFontEngine *fontEngine, const QTransform &m) const
virtual bool shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const
virtual void setState(QPainterState *s)
virtual void drawPoints(const QPointF *points, int pointCount) override
Draws the first pointCount points in the buffer points.
virtual void drawStaticTextItem(QStaticTextItem *)
QPaintEngine::DirtyFlags state() const
Returns a combination of flags identifying the set of properties that need to be updated when updatin...
void setActive(bool newState)
Sets the active state of the paint engine to state.
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem)
This function draws the text item textItem at position p.
PolygonDrawMode
\value OddEvenMode The polygon should be drawn using OddEven fill rule.
QPainter * painter() const
Returns the paint engine's painter.
void setDirty(DirtyFlags df)
PaintEngineFeatures gccaps
QRegion systemClip() const
\inmodule QtGui
\inmodule QtGui
QTransform matrix
Definition qpainter_p.h:130
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or \nullptr if the painter is n...
@ NonCosmeticBrushPatterns
Definition qpainter.h:57
@ SmoothPixmapTransform
Definition qpainter.h:54
@ Antialiasing
Definition qpainter.h:52
@ VerticalSubpixelPositioning
Definition qpainter.h:55
CompositionMode
Defines the modes supported for digital image compositing.
Definition qpainter.h:97
@ CompositionMode_SourceOver
Definition qpainter.h:98
@ CompositionMode_Source
Definition qpainter.h:101
\inmodule QtGui
Definition qpen.h:28
void setStyle(Qt::PenStyle)
[0]
QList< qreal > dashPattern() const
Returns the dash pattern of this pen.
Definition qpen.cpp:400
bool isCosmetic() const
Returns true if the pen is cosmetic; otherwise returns false.
Definition qpen.cpp:757
Qt::PenCapStyle capStyle() const
Returns the pen's cap style.
Definition qpen.cpp:636
qreal miterLimit() const
Returns the miter limit of the pen.
Definition qpen.cpp:524
QBrush brush() const
Returns the brush used to fill strokes generated with this pen.
Definition qpen.cpp:715
qreal dashOffset() const
Returns the dash offset for the pen.
Definition qpen.cpp:484
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
The QPlatformPixmap class provides an abstraction for native pixmaps.
virtual QImage toImage() const =0
virtual QImage * buffer()
ClassId classId() const
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition qpoint.h:404
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtGui
Definition qbrush.h:412
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
QImage::Format prepare(QImage *image)
QPainter::CompositionMode compositionMode
qsizetype bytesPerLine() const
QImage::Format format
QImage colorizeBitmap(const QImage &image, const QColor &color)
uchar * buffer() const
bool isUnclipped_normalized(const QRect &rect) const
Returns true if the rectangle is completely within the current clip state of the paint engine.
QScopedPointer< QT_FT_Raster > grayRaster
void initializeRasterizer(QSpanData *data)
ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const
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)
QScopedPointer< QClipData > baseClip
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)
QScopedPointer< QRasterBuffer > rasterBuffer
ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const
QScopedPointer< QRasterizer > rasterizer
The QRasterPaintEngine class enables hardware acceleration of painting operations in Qt for Embedded ...
void compositionModeChanged() override
QPainterState * createState(QPainterState *orig) const override
void drawStaticTextItem(QStaticTextItem *textItem) override
\reimp
void fill(const QVectorPath &path, const QBrush &brush) override
~QRasterPaintEngine()
Destroys this paint engine.
void transformChanged() override
void setState(QPainterState *s) override
void drawEllipse(const QRectF &rect) override
\reimp
virtual bool drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine)
void updatePen(const QPen &pen)
void renderHintsChanged() override
void drawPoints(const QPointF *points, int pointCount) override
\reimp
QRasterPaintEngineState * state()
const QClipData * clipData() const
virtual void fillPath(const QPainterPath &path, QSpanData *fillData)
bool end() override
\reimp
void stroke(const QVectorPath &path, const QPen &pen) override
void drawTextItem(const QPointF &p, const QTextItem &textItem) override
\reimp
void updateMatrix(const QTransform &matrix)
void opacityChanged() override
void drawPixmap(const QPointF &p, const QPixmap &pm) override
void updateBrush(const QBrush &brush)
void drawImage(const QPointF &p, const QImage &img) override
bool begin(QPaintDevice *device) override
\reimp
bool requiresPretransformedGlyphPositions(QFontEngine *fontEngine, const QTransform &m) const override
QPoint coordinateOffset() const override
void alphaPenBlt(const void *src, int bpl, int depth, int rx, int ry, int w, int h, bool useGammaCorrection)
void clipEnabledChanged() override
QRasterBuffer * rasterBuffer()
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
\reimp
void drawLines(const QLine *line, int lineCount) override
\reimp
void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr) override
\reimp
bool shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const override
Returns whether glyph caching is supported by the font engine fontEngine with the given transform m a...
void fillRect(const QRectF &rect, const QBrush &brush) override
\reimp
void clip(const QVectorPath &path, Qt::ClipOperation op) override
virtual void fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
QRasterPaintEngine(QPaintDevice *device)
void brushOriginChanged() override
void drawRects(const QRect *rects, int rectCount) override
\reimp
void setAntialiased(bool antialiased)
void initialize(ProcessSpans blend, void *data)
void rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap=false)
void setClipRect(const QRect &clipRect)
void rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:500
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:672
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:669
constexpr QRectF translated(qreal dx, qreal dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:762
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:497
QRectF normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:1522
constexpr QRect toRect() const noexcept
Returns a QRect based on the values of this rectangle.
Definition qrect.h:859
constexpr void translate(qreal dx, qreal dy) noexcept
Moves the rectangle dx along the x-axis and dy along the y-axis, relative to the current position.
Definition qrect.h:738
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:499
void set(const QRectF &r)
QRectVectorPath(const QRect &r)
void set(const QRect &r)
QRectVectorPath(const QRectF &r)
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:167
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
QRect normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:277
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:164
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:415
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:182
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:176
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:173
constexpr void setRect(int x, int y, int w, int h) noexcept
Sets the coordinates of the rectangle's top-left corner to ({x}, {y}), and its size to the given widt...
Definition qrect.h:346
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr void setX(int x) noexcept
Sets the left edge of the rectangle to the given x coordinate.
Definition qrect.h:215
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
constexpr int right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:179
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRect boundingRect() const noexcept
Returns the bounding rectangle of this region.
int rectCount() const noexcept
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
const_iterator begin() const noexcept
static constexpr QRgba64 fromArgb32(uint rgb)
Definition qrgba64.h:56
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
Definition qstringview.h:78
QByteArray toLatin1() const
Returns a Latin-1 representation of the string as a QByteArray.
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
Internal QTextItem.
\inmodule QtGui
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
qreal m11() const
Returns the horizontal scaling factor.
Definition qtransform.h:199
static QTransform fromTranslate(qreal dx, qreal dy)
Creates a matrix which corresponds to a translation of dx along the x axis and dy along the y axis.
QTransform inverted(bool *invertible=nullptr) const
Returns an inverted copy of this matrix.
TransformationType type() const
Returns the transformation type of this matrix.
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
TransformationType
\value TxNone \value TxTranslate \value TxScale \value TxRotate \value TxShear \value TxProject
Definition qtransform.h:22
bool isRect() const
static uint polygonFlags(QPaintEngine::PolygonDrawMode mode)
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
rect
[4]
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
ClipOperation
@ ReplaceClip
@ IntersectClip
@ NoClip
@ transparent
Definition qnamespace.h:47
@ CustomDashLine
@ SolidLine
@ NoPen
BrushStyle
@ DiagCrossPattern
@ HorPattern
@ BDiagPattern
@ SolidPattern
@ Dense5Pattern
@ RadialGradientPattern
@ Dense1Pattern
@ Dense3Pattern
@ TexturePattern
@ LinearGradientPattern
@ Dense4Pattern
@ NoBrush
@ CrossPattern
@ ConicalGradientPattern
@ FDiagPattern
@ Dense6Pattern
@ Dense7Pattern
@ VerPattern
@ Dense2Pattern
@ WindingFill
@ OddEvenFill
@ SquareCap
@ FlatCap
Definition brush.cpp:5
Definition image.cpp:4
static jboolean copy(JNIEnv *, jobject)
SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats]
SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats]
SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats]
Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert)
Definition qbrush.cpp:146
bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush &brush)
Definition qbrush.cpp:202
static const QCssKnownValue positions[NumKnownPositionModes - 1]
static bool initialize()
Definition qctf.cpp:94
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
DrawHelper qDrawHelper[QImage::NImageFormats]
void(* SrcOverBlendFunc)(uchar *destPixels, int dbpl, const uchar *src, int spbl, int w, int h, int const_alpha)
static constexpr int qt_div_255(int x)
QT_FT_SpanFunc ProcessSpans
static QRgba64 interpolate256(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
static QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
#define GRADIENT_STOPTABLE_SIZE
void qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
void(* SrcOverScaleFunc)(uchar *destPixels, int dbpl, const uchar *src, int spbl, int srch, const QRectF &targetRect, const QRectF &sourceRect, const QRect &clipRect, int const_alpha)
void qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
void(* SrcOverTransformFunc)(uchar *destPixels, int dbpl, const uchar *src, int spbl, const QRectF &targetRect, const QRectF &sourceRect, const QRect &clipRect, const QTransform &targetRectTransform, int const_alpha)
#define QT_RETHROW
#define QT_THROW(A)
#define QT_CATCH(A)
#define QT_TRY
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
int q_gray_rendered_spans(TRaster *raster)
const QT_FT_Raster_Funcs qt_ft_grays_raster
#define MINIMUM_POOL_SIZE
@ None
Definition qhash.cpp:531
QImage::Format qt_maybeDataCompatibleOpaqueVersion(QImage::Format format)
Definition qimage_p.h:326
int qt_depthForFormat(QImage::Format format)
Definition qimage_p.h:142
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
int qFloor(T v)
Definition qmath.h:42
int qCeil(T v)
Definition qmath.h:36
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
QRect qt_mapFillRect(const QRectF &rect, const QTransform &xf)
Definition qmath_p.h:27
MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum mode
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum const void GLbitfield GLsizei numGlyphs
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLenum src
GLsizei range
GLint GLsizei width
GLuint color
[2]
GLint left
GLenum type
GLenum GLenum dst
GLint GLint bottom
GLuint GLfloat x0
GLenum target
GLbitfield flags
GLenum GLuint texture
GLboolean GLboolean g
GLint first
GLfloat n
GLint GLsizei GLsizei GLenum format
GLuint GLfloat GLfloat y0
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint counter
GLsizei GLsizei GLchar * source
GLuint GLenum GLenum transform
GLfixed GLfixed GLint GLint GLfixed points
GLdouble s
[6]
Definition qopenglext.h:235
GLenum func
Definition qopenglext.h:663
GLbyte nx
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLuint GLuint * names
GLint void * img
Definition qopenglext.h:233
GLfixed ny
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint GLuint64EXT address
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLenum GLenum GLsizei void GLsizei void void * span
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLenum GLenum GLenum GLenum GLenum scale
GLubyte * pattern
GLboolean invert
Definition qopenglext.h:226
static const QRectF boundingRect(const QPointF *points, int pointCount)
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
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 QImage qt_imageForBrush(int brushStyle, bool invert)
Definition qbrush.cpp:146
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
int q_gray_rendered_spans(QT_FT_Raster raster)
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)
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)
qreal qpen_widthf(const QPen &p)
Definition qpainter_p.h:55
Qt::BrushStyle qbrush_style(const QBrush &b)
Definition qpainter_p.h:63
Qt::PenStyle qpen_style(const QPen &p)
Definition qpainter_p.h:56
const QColor & qbrush_color(const QBrush &b)
Definition qpainter_p.h:64
const void * data_ptr(const QTransform &t)
Definition qpainter_p.h:48
Qt::PenCapStyle qpen_capStyle(const QPen &p)
Definition qpainter_p.h:57
Qt::PenJoinStyle qpen_joinStyle(const QPen &p)
Definition qpainter_p.h:58
QPixelLayout qPixelLayouts[]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QT_FT_RASTER_FLAG_CLIP
#define QT_FT_RASTER_FLAG_DIRECT
#define QT_FT_OUTLINE_NONE
#define QT_FT_RASTER_FLAG_AA
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr QRgb qPremultiply(QRgb x)
Definition qrgb.h:45
constexpr QRgba64 qRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
Definition qrgba64.h:180
QT_BEGIN_NAMESPACE QRgba64 combineAlpha256(QRgba64 rgba64, uint alpha256)
Definition qrgba64_p.h:26
static const struct TessellationWindingOrderTab cw[]
SSL_CTX int void * arg
#define qt_fixed_to_real(fixed)
Definition qstroker_p.h:65
QT_BEGIN_NAMESPACE typedef qreal qfixed
Definition qstroker_p.h:63
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define tr(X)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned int glyph_t
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
size_t quintptr
Definition qtypes.h:167
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
static const uint base
Definition qurlidna.cpp:20
QFuture< QSet< QChar > > set
[10]
Q_CHECK_PTR(a=new int[80])
std::uniform_real_distribution dist(1, 2.5)
[2]
QTextStream out(stdout)
[7]
QObject::connect nullptr
QReadWriteLock lock
[0]
ba fill(true)
p ry()++
p rx()++
QRect r1(100, 200, 11, 16)
[0]
widget render & pixmap
QClipData * oldClip
QClipData * newClip
Qt::ClipOperation operation
ProcessSpans blendColor
QFixed y
Definition qfixed_p.h:163
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
QRgb buffer32[GRADIENT_STOPTABLE_SIZE]
QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE]
CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode)
QGradient::InterpolationMode interpolationMode
QRadialGradientData radial
const QRgb * colorTable32
QLinearGradientData linear
QGradient::Spread spread
QConicalGradientData conical
struct QLinearGradientData::@211 origin
AlphaRGBBlitFunc alphaRGBBlit
QRasterBuffer * rasterBuffer
void initTexture(const QImage *image, int alpha, QTextureData::Type=QTextureData::Plain, const QRect &sourceRect=QRect())
void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode, bool isCosmetic)
BitmapBlitFunc bitmapBlit
std::shared_ptr< const void > cachedGradient
QImage * tempImage
signed int txop
RectFillFunc fillRect
ProcessSpans unclipped_blend
ProcessSpans blend
QColor solidColor
QGradientData gradient
const QClipData * clip
AlphamapBlitFunc alphamapBlit
void setupMatrix(const QTransform &matrix, int bilinear)
QT_FT_Raster_DoneFunc raster_done
QT_FT_Raster_ResetFunc raster_reset
QT_FT_Raster_RenderFunc raster_render
QT_FT_Raster_NewFunc raster_new
QT_FT_Bitmap * target
unsigned char coverage