Qt 6.x
The Qt SDK
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 }
82 inline QRectVectorPath(const QRect &r)
84 {
85 set(r);
86 }
87 inline QRectVectorPath(const QRectF &r)
89 {
90 set(r);
91 }
94 { }
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
151{
155};
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
170};
171
172#ifdef QT_DEBUG_DRAW
173static const QRectF boundingRect(const QPointF *points, int pointCount)
174{
175 const QPointF *e = points;
176 const QPointF *last = points + pointCount;
177 qreal minx, maxx, miny, maxy;
178 minx = maxx = e->x();
179 miny = maxy = e->y();
180 while (++e < last) {
181 if (e->x() < minx)
182 minx = e->x();
183 else if (e->x() > maxx)
184 maxx = e->x();
185 if (e->y() < miny)
186 miny = e->y();
187 else if (e->y() > maxy)
188 maxy = e->y();
189 }
190 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
191}
192#endif
193
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);
228 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
229 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
230 }
231}
232#endif
233
236 cachedLines(0)
237{
238}
239
240
272/*
273 \fn QPaintEngine::Type QRasterPaintEngine::type() const
274 \reimp
275*/
276
287{
288 d_func()->device = device;
289 init();
290}
291
296 : QPaintEngineEx(dd)
297{
298 d_func()->device = device;
299 init();
300}
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
376{
378
379 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
380}
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) {
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
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;
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_ASSUME(d->rasterBuffer->format < QImage::NImageFormats);
2265 const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2266
2267 if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(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)) {
2345 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2346 img.bits(), img.bytesPerLine(), img.height(),
2347 qt_mapRect_non_normalizing(r, s->matrix), sr,
2348 !clip ? d->deviceRect : clip->clipRect,
2349 s->intOpacity);
2350 return;
2351 }
2352 }
2353 }
2354
2355 QTransform copy = s->matrix;
2356 copy.translate(r.x(), r.y());
2357 if (stretch_sr)
2358 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2359 copy.translate(-sr.x(), -sr.y());
2360
2361 d->image_filler_xform.clip = clip;
2362 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2363 if (!d->image_filler_xform.blend)
2364 return;
2365 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2366
2367 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2368 QRectF rr = s->matrix.mapRect(r);
2369
2370 const int x1 = qRound(rr.x());
2371 const int y1 = qRound(rr.y());
2372 const int x2 = qRound(rr.right());
2373 const int y2 = qRound(rr.bottom());
2374
2375 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2376 return;
2377 }
2378
2379#ifdef QT_FAST_SPANS
2380 ensureRasterState();
2381 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2382 d->initializeRasterizer(&d->image_filler_xform);
2383 d->rasterizer->setAntialiased(s->flags.antialiased);
2384
2385 const QRectF &rect = r.normalized();
2386 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2387 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2388
2389 if (s->flags.tx_noshear)
2390 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2391 else
2392 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2393 return;
2394 }
2395#endif
2397 path.addRect(r);
2398 QTransform m = s->matrix;
2399 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2400 m.m21(), m.m22(), m.m23(),
2401 m.m31(), m.m32(), m.m33());
2402 fillPath(path, &d->image_filler_xform);
2403 s->matrix = m;
2404 } else {
2405 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2406 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) {
2407 if (!clip) {
2408 d->blitImage(pt, img, d->deviceRect, sr.toRect());
2409 return;
2410 } else if (clip->hasRectClip) {
2411 d->blitImage(pt, img, clip->clipRect, sr.toRect());
2412 return;
2413 }
2414 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2415 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2416 if (func) {
2417 if (!clip) {
2418 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2419 return;
2420 } else if (clip->hasRectClip) {
2421 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2422 return;
2423 }
2424 }
2425 }
2426
2427 d->image_filler.clip = clip;
2428 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2429 if (!d->image_filler.blend)
2430 return;
2431 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2432 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2433
2434 QRectF rr = r;
2435 rr.translate(s->matrix.dx(), s->matrix.dy());
2436
2437 const int x1 = qRound(rr.x());
2438 const int y1 = qRound(rr.y());
2439 const int x2 = qRound(rr.right());
2440 const int y2 = qRound(rr.bottom());
2441
2442 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2443 }
2444}
2445
2450{
2451#ifdef QT_DEBUG_DRAW
2452 qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2453#endif
2454 Q_D(QRasterPaintEngine);
2456 Q_ASSERT(s);
2457
2458 QImage image;
2459
2460 QPlatformPixmap *pd = pixmap.handle();
2462 image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2463 } else {
2464 image = pixmap.toImage();
2465 }
2466
2467 if (image.depth() == 1)
2468 image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2469
2470 const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
2471 if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
2472 QTransform copy = s->matrix;
2473 copy.translate(r.x(), r.y());
2474 copy.translate(-sr.x(), -sr.y());
2475 const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
2476 copy.scale(inverseDpr, inverseDpr);
2477 d->image_filler_xform.clip = d->clip();
2478 d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2479 if (!d->image_filler_xform.blend)
2480 return;
2481 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2482
2483#ifdef QT_FAST_SPANS
2484 ensureRasterState();
2485 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2486 d->initializeRasterizer(&d->image_filler_xform);
2487 d->rasterizer->setAntialiased(s->flags.antialiased);
2488
2489 const QRectF &rect = r.normalized();
2490 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2491 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2492 if (s->flags.tx_noshear)
2493 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2494 else
2495 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2496 return;
2497 }
2498#endif
2500 path.addRect(r);
2501 fillPath(path, &d->image_filler_xform);
2502 } else {
2503 d->image_filler.clip = d->clip();
2504
2505 d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2506 if (!d->image_filler.blend)
2507 return;
2508 d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2509 d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2510
2511 QRectF rr = r;
2512 rr.translate(s->matrix.dx(), s->matrix.dy());
2513 fillRect_normalized(rr.normalized().toRect(), &d->image_filler, d);
2514 }
2515}
2516
2517
2518//QWS hack
2519static inline bool monoVal(const uchar* s, int x)
2520{
2521 return (s[x>>3] << (x&7)) & 0x80;
2522}
2523
2528{
2529 Q_D(QRasterPaintEngine);
2530 return d->rasterBuffer.data();
2531}
2532
2536void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
2537{
2538 Q_D(QRasterPaintEngine);
2540
2541 if (!s->penData.blend)
2542 return;
2543
2544 QRasterBuffer *rb = d->rasterBuffer.data();
2546 useGammaCorrection = false;
2547
2548 const QRect rect(rx, ry, w, h);
2549 const QClipData *clip = d->clip();
2550 bool unclipped = false;
2551 if (clip) {
2552 // inlined QRect::intersects
2553 const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2554 && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2555
2556 if (clip->hasRectClip) {
2557 unclipped = rx > clip->xmin
2558 && rx + w < clip->xmax
2559 && ry > clip->ymin
2560 && ry + h < clip->ymax;
2561 }
2562
2563 if (!intersects)
2564 return;
2565 } else {
2566 // inlined QRect::intersects
2567 const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2568 && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2569 if (!intersects)
2570 return;
2571
2572 // inlined QRect::contains
2573 const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2574 && rect.top() >= 0 && rect.bottom() < rb->height();
2575
2576 unclipped = contains && d->isUnclipped_normalized(rect);
2577 }
2578
2579 ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2580 const uchar * scanline = static_cast<const uchar *>(src);
2581
2582 if (s->flags.fast_text) {
2583 if (unclipped) {
2584 if (depth == 1) {
2585 if (s->penData.bitmapBlit) {
2586 s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2587 scanline, w, h, bpl);
2588 return;
2589 }
2590 } else if (depth == 8) {
2591 if (s->penData.alphamapBlit) {
2592 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2593 scanline, w, h, bpl, nullptr, useGammaCorrection);
2594 return;
2595 }
2596 } else if (depth == 32) {
2597 // (A)RGB Alpha mask where the alpha component is not used.
2598 if (s->penData.alphaRGBBlit) {
2599 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2600 (const uint *) scanline, w, h, bpl / 4, nullptr, useGammaCorrection);
2601 return;
2602 }
2603 }
2604 } else if ((depth == 8 && s->penData.alphamapBlit) || (depth == 32 && s->penData.alphaRGBBlit)) {
2605 // (A)RGB Alpha mask where the alpha component is not used.
2606 if (!clip) {
2607 int nx = qMax(0, rx);
2608 int ny = qMax(0, ry);
2609
2610 // Move scanline pointer to compensate for moved x and y
2611 int xdiff = nx - rx;
2612 int ydiff = ny - ry;
2613 scanline += ydiff * bpl;
2614 scanline += xdiff * (depth == 32 ? 4 : 1);
2615
2616 w -= xdiff;
2617 h -= ydiff;
2618
2619 if (nx + w > d->rasterBuffer->width())
2620 w = d->rasterBuffer->width() - nx;
2621 if (ny + h > d->rasterBuffer->height())
2622 h = d->rasterBuffer->height() - ny;
2623
2624 rx = nx;
2625 ry = ny;
2626 }
2627 if (depth == 8)
2628 s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2629 scanline, w, h, bpl, clip, useGammaCorrection);
2630 else if (depth == 32)
2631 s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2632 (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
2633 return;
2634 }
2635 }
2636
2637 int x0 = 0;
2638 if (rx < 0) {
2639 x0 = -rx;
2640 w -= x0;
2641 }
2642
2643 int y0 = 0;
2644 if (ry < 0) {
2645 y0 = -ry;
2646 scanline += bpl * y0;
2647 h -= y0;
2648 }
2649
2650 w = qMin(w, rb->width() - qMax(0, rx));
2651 h = qMin(h, rb->height() - qMax(0, ry));
2652
2653 if (w <= 0 || h <= 0)
2654 return;
2655
2656 const int NSPANS = 512;
2657 QT_FT_Span spans[NSPANS];
2658 int current = 0;
2659
2660 const int x1 = x0 + w;
2661 const int y1 = y0 + h;
2662
2663 if (depth == 1) {
2664 for (int y = y0; y < y1; ++y) {
2665 for (int x = x0; x < x1; ) {
2666 if (!monoVal(scanline, x)) {
2667 ++x;
2668 continue;
2669 }
2670
2671 if (current == NSPANS) {
2672 blend(current, spans, &s->penData);
2673 current = 0;
2674 }
2675 spans[current].x = x + rx;
2676 spans[current].y = y + ry;
2677 spans[current].coverage = 255;
2678 int len = 1;
2679 ++x;
2680 // extend span until we find a different one.
2681 while (x < x1 && monoVal(scanline, x)) {
2682 ++x;
2683 ++len;
2684 }
2685 spans[current].len = len;
2686 ++current;
2687 }
2688 scanline += bpl;
2689 }
2690 } else if (depth == 8) {
2691 for (int y = y0; y < y1; ++y) {
2692 for (int x = x0; x < x1; ) {
2693 // Skip those with 0 coverage
2694 if (scanline[x] == 0) {
2695 ++x;
2696 continue;
2697 }
2698
2699 if (current == NSPANS) {
2700 blend(current, spans, &s->penData);
2701 current = 0;
2702 }
2703 int coverage = scanline[x];
2704 spans[current].x = x + rx;
2705 spans[current].y = y + ry;
2706 spans[current].coverage = coverage;
2707 int len = 1;
2708 ++x;
2709
2710 // extend span until we find a different one.
2711 while (x < x1 && scanline[x] == coverage) {
2712 ++x;
2713 ++len;
2714 }
2715 spans[current].len = len;
2716 ++current;
2717 }
2718 scanline += bpl;
2719 }
2720 } else { // 32-bit alpha...
2721 const uint *sl = (const uint *) scanline;
2722 for (int y = y0; y < y1; ++y) {
2723 for (int x = x0; x < x1; ) {
2724 // Skip those with 0 coverage
2725 if ((sl[x] & 0x00ffffff) == 0) {
2726 ++x;
2727 continue;
2728 }
2729
2730 if (current == NSPANS) {
2731 blend(current, spans, &s->penData);
2732 current = 0;
2733 }
2734 uint rgbCoverage = sl[x];
2735 int coverage = qGreen(rgbCoverage);
2736 spans[current].x = x + rx;
2737 spans[current].y = y + ry;
2738 spans[current].coverage = coverage;
2739 int len = 1;
2740 ++x;
2741
2742 // extend span until we find a different one.
2743 while (x < x1 && sl[x] == rgbCoverage) {
2744 ++x;
2745 ++len;
2746 }
2747 spans[current].len = len;
2748 ++current;
2749 }
2750 sl += bpl / sizeof(uint);
2751 }
2752 }
2753// qDebug() << "alphaPenBlt: num spans=" << current
2754// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2755 // Call span func for current set of spans.
2756 if (current != 0)
2757 blend(current, spans, &s->penData);
2758}
2759
2764 const QFixedPoint *positions, QFontEngine *fontEngine)
2765{
2766 Q_D(QRasterPaintEngine);
2768
2769 bool verticalSubPixelPositions = fontEngine->supportsVerticalSubPixelPositions()
2770 && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
2771
2772 if (fontEngine->hasInternalCaching()) {
2773 QFontEngine::GlyphFormat neededFormat =
2777
2778 if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2779 neededFormat = QFontEngine::Format_Mono;
2780
2781 for (int i = 0; i < numGlyphs; i++) {
2782 QFixedPoint spp = fontEngine->subPixelPositionFor(positions[i]);
2783 if (!verticalSubPixelPositions)
2784 spp.y = 0;
2785
2786 const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyphs[i], spp, neededFormat, s->matrix);
2787 if (!alphaMap)
2788 continue;
2789
2790 int depth;
2791 int bytesPerLine;
2792 switch (alphaMap->format) {
2794 depth = 1;
2795 bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
2796 break;
2798 depth = 8;
2799 bytesPerLine = (alphaMap->width + 3) & ~3;
2800 break;
2802 depth = 32;
2803 bytesPerLine = alphaMap->width * 4;
2804 break;
2805 default:
2806 Q_UNREACHABLE();
2807 };
2808
2809 QFixed y = verticalSubPixelPositions
2810 ? qFloor(positions[i].y)
2811 : qRound(positions[i].y);
2812
2813 alphaPenBlt(alphaMap->data, bytesPerLine, depth,
2814 qFloor(positions[i].x) + alphaMap->x,
2815 qFloor(y) - alphaMap->y,
2816 alphaMap->width, alphaMap->height,
2817 fontEngine->expectsGammaCorrectedBlending());
2818 }
2819
2820 } else {
2821 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
2822
2824 static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(nullptr, glyphFormat, s->matrix, s->penData.solidColor));
2825 if (!cache) {
2826 cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, s->penData.solidColor);
2827 fontEngine->setGlyphCache(nullptr, cache);
2828 }
2829
2830 cache->populate(fontEngine, numGlyphs, glyphs, positions, s->renderHints);
2831 cache->fillInPendingGlyphs();
2832
2833 const QImage &image = cache->image();
2834 qsizetype bpl = image.bytesPerLine();
2835
2836 int depth = image.depth();
2837 int rightShift = 0;
2838 int leftShift = 0;
2839 if (depth == 32)
2840 leftShift = 2; // multiply by 4
2841 else if (depth == 1)
2842 rightShift = 3; // divide by 8
2843
2844 int margin = fontEngine->glyphMargin(glyphFormat);
2845 const uchar *bits = image.bits();
2846 for (int i=0; i<numGlyphs; ++i) {
2847 QFixedPoint subPixelPosition = fontEngine->subPixelPositionFor(positions[i]);
2848 if (!verticalSubPixelPositions)
2849 subPixelPosition.y = 0;
2850
2851 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2852 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2853 if (c.isNull())
2854 continue;
2855
2856 int x = qFloor(positions[i].x) + c.baseLineX - margin;
2857 int y = (verticalSubPixelPositions
2858 ? qFloor(positions[i].y)
2859 : qRound(positions[i].y));
2860 y -= c.baseLineY + margin;
2861
2862 // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2863 // c.x, c.y,
2864 // c.w, c.h,
2865 // c.baseLineX, c.baseLineY,
2866 // glyphs[i],
2867 // x, y,
2868 // positions[i].x.toInt(), positions[i].y.toInt());
2869
2870 const uchar *glyphBits = bits + ((c.x << leftShift) >> rightShift) + c.y * bpl;
2871
2872 if (glyphFormat == QFontEngine::Format_ARGB) {
2873 // The current state transform has already been applied to the positions,
2874 // so we prevent drawImage() from re-applying the transform by clearing
2875 // the state for the duration of the call.
2876 QTransform originalTransform = s->matrix;
2877 s->matrix = QTransform();
2878 drawImage(QPoint(x, y), QImage(glyphBits, c.w, c.h, bpl, image.format()));
2879 s->matrix = originalTransform;
2880 } else {
2881 alphaPenBlt(glyphBits, bpl, depth, x, y, c.w, c.h, fontEngine->expectsGammaCorrectedBlending());
2882 }
2883 }
2884 }
2885 return true;
2886}
2887
2888
2894{
2895 const QClipData *cl = clip();
2896 if (!cl) {
2897 // inline contains() for performance (we know the rects are normalized)
2898 const QRect &r1 = deviceRect;
2899 return (r.left() >= r1.left() && r.right() <= r1.right()
2900 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2901 }
2902
2903
2904 if (cl->hasRectClip) {
2905 // currently all painting functions clips to deviceRect internally
2906 if (cl->clipRect == deviceRect)
2907 return true;
2908
2909 // inline contains() for performance (we know the rects are normalized)
2910 const QRect &r1 = cl->clipRect;
2911 return (r.left() >= r1.left() && r.right() <= r1.right()
2912 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2913 } else {
2914 return qt_region_strictContains(cl->clipRegion, r);
2915 }
2916}
2917
2919 int penWidth) const
2920{
2921 Q_Q(const QRasterPaintEngine);
2922 const QRasterPaintEngineState *s = q->state();
2923 const QClipData *cl = clip();
2924 QRect r = rect.normalized();
2925 if (!cl) {
2926 // inline contains() for performance (we know the rects are normalized)
2927 const QRect &r1 = deviceRect;
2928 return (r.left() >= r1.left() && r.right() <= r1.right()
2929 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2930 }
2931
2932
2933 // currently all painting functions that call this function clip to deviceRect internally
2934 if (cl->hasRectClip && cl->clipRect == deviceRect)
2935 return true;
2936
2937 if (s->flags.antialiased)
2938 ++penWidth;
2939
2940 if (penWidth > 0) {
2941 r.setX(r.x() - penWidth);
2942 r.setY(r.y() - penWidth);
2943 r.setWidth(r.width() + 2 * penWidth);
2944 r.setHeight(r.height() + 2 * penWidth);
2945 }
2946
2947 if (cl->hasRectClip) {
2948 // inline contains() for performance (we know the rects are normalized)
2949 const QRect &r1 = cl->clipRect;
2950 return (r.left() >= r1.left() && r.right() <= r1.right()
2951 && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2952 } else {
2953 return qt_region_strictContains(cl->clipRegion, r);
2954 }
2955}
2956
2958 int penWidth) const
2959{
2960 const QRectF norm = rect.normalized();
2961 if (norm.left() <= INT_MIN || norm.top() <= INT_MIN
2962 || norm.right() > INT_MAX || norm.bottom() > INT_MAX
2963 || norm.width() > INT_MAX || norm.height() > INT_MAX)
2964 return false;
2965 return isUnclipped(norm.toAlignedRect(), penWidth);
2966}
2967
2968inline ProcessSpans
2970 const QSpanData *data) const
2971{
2972 return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
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 Q_Q(const QRasterPaintEngine);
2987 const QRasterPaintEngineState *s = q->state();
2988
2989 if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
2990 return data->blend;
2991 const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
2992 return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
2993}
2994
2996{
2998 int end;
2999};
3000
3002 glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
3003{
3004 QFixed clipLeft = QFixed::fromReal(clip.left() - 1);
3005 QFixed clipRight = QFixed::fromReal(clip.right() + 1);
3006 QFixed clipTop = QFixed::fromReal(clip.top() - 1);
3007 QFixed clipBottom = QFixed::fromReal(clip.bottom() + 1);
3008
3009 int first = 0;
3010 while (first < numGlyphs) {
3011 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
3013 QFixed top = metrics.y + positions[first].y;
3014 QFixed right = left + metrics.width;
3015 QFixed bottom = top + metrics.height;
3016 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3017 break;
3018 ++first;
3019 }
3020 int last = numGlyphs - 1;
3021 while (last > first) {
3022 glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
3023 QFixed left = metrics.x + positions[last].x;
3024 QFixed top = metrics.y + positions[last].y;
3025 QFixed right = left + metrics.width;
3026 QFixed bottom = top + metrics.height;
3027 if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3028 break;
3029 --last;
3030 }
3031 return {first, last + 1};
3032}
3033
3038{
3039 if (textItem->numGlyphs == 0)
3040 return;
3041
3042 ensurePen();
3043 ensureRasterState();
3044
3046
3047 QFontEngine *fontEngine = textItem->fontEngine();
3048 if (shouldDrawCachedGlyphs(fontEngine, matrix)) {
3049 drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3050 fontEngine);
3051 } else if (matrix.type() < QTransform::TxProject) {
3052 bool invertible;
3053 QTransform invMat = matrix.inverted(&invertible);
3054 if (!invertible)
3055 return;
3056
3057 const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3058 textItem->fontEngine(), textItem->glyphs,
3059 textItem->glyphPositions, textItem->numGlyphs);
3060 QStaticTextItem copy = *textItem;
3061 copy.glyphs += range.begin;
3062 copy.glyphPositions += range.begin;
3063 copy.numGlyphs = range.end - range.begin;
3065 } else {
3067 }
3068}
3069
3074{
3075 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3076
3077#ifdef QT_DEBUG_DRAW
3078 Q_D(QRasterPaintEngine);
3079 fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3080 p.x(), p.y(), QStringView(ti.chars, ti.num_chars).toLatin1().data(),
3081 d->glyphCacheFormat);
3082#endif
3083
3084 if (ti.glyphs.numGlyphs == 0)
3085 return;
3086 ensurePen();
3087 ensureRasterState();
3088
3090 QTransform matrix = s->matrix;
3091
3095
3096 matrix.translate(p.x(), p.y());
3098
3099 drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3100 } else if (matrix.type() < QTransform::TxProject
3102 bool invertible;
3103 QTransform invMat = matrix.inverted(&invertible);
3104 if (!invertible)
3105 return;
3106
3109
3111 ti.flags, glyphs, positions);
3112 const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3113 ti.fontEngine, glyphs.data(), positions.data(),
3114 glyphs.size());
3115
3116 if (range.begin >= range.end)
3117 return;
3118
3119 QStaticTextItem staticTextItem;
3120 staticTextItem.color = s->pen.color();
3121 staticTextItem.font = s->font;
3122 staticTextItem.setFontEngine(ti.fontEngine);
3123 staticTextItem.numGlyphs = range.end - range.begin;
3124 staticTextItem.glyphs = glyphs.data() + range.begin;
3125 staticTextItem.glyphPositions = positions.data() + range.begin;
3126 QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3127 } else {
3129 }
3130}
3131
3136{
3137 Q_D(QRasterPaintEngine);
3139
3140 ensurePen();
3141 if (!s->penData.blend)
3142 return;
3143
3144 if (!s->flags.fast_pen) {
3146 return;
3147 }
3148
3149 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3150 stroker.drawPoints(points, pointCount);
3151}
3152
3153
3155{
3156 Q_D(QRasterPaintEngine);
3158
3159 ensurePen();
3160 if (!s->penData.blend)
3161 return;
3162
3163 if (!s->flags.fast_pen) {
3165 return;
3166 }
3167
3168 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3169 stroker.drawPoints(points, pointCount);
3170}
3171
3175void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3176{
3177#ifdef QT_DEBUG_DRAW
3178 qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3179#endif
3180 Q_D(QRasterPaintEngine);
3182
3183 ensurePen();
3184 if (!s->penData.blend)
3185 return;
3186
3187 if (s->flags.fast_pen) {
3188 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3189 for (int i=0; i<lineCount; ++i) {
3190 const QLine &l = lines[i];
3191 stroker.drawLine(l.p1(), l.p2());
3192 }
3193 } else {
3194 QPaintEngineEx::drawLines(lines, lineCount);
3195 }
3196}
3197
3199 qreal width,
3200 int *dashIndex,
3201 qreal *dashOffset,
3202 bool *inDash)
3203{
3204 Q_Q(QRasterPaintEngine);
3206
3207 const QPen &pen = s->lastPen;
3208 const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3209 const QList<qreal> pattern = pen.dashPattern();
3210
3211 qreal patternLength = 0;
3212 for (int i = 0; i < pattern.size(); ++i)
3213 patternLength += pattern.at(i);
3214
3215 if (patternLength <= 0)
3216 return;
3217
3218 qreal length = line.length();
3219 Q_ASSERT(length > 0);
3220 if (length / (patternLength * width) > QDashStroker::repetitionLimit()) {
3221 rasterizer->rasterizeLine(line.p1(), line.p2(), width / length, squareCap);
3222 return;
3223 }
3224
3225 while (length > 0) {
3226 const bool rasterize = *inDash;
3227 qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3228 QLineF l = line;
3229
3230 if (dash >= length) {
3231 dash = line.length(); // Avoid accumulated precision error in 'length'
3232 *dashOffset += dash / width;
3233 length = 0;
3234 } else {
3235 *dashOffset = 0;
3236 *inDash = !(*inDash);
3237 if (++*dashIndex >= pattern.size())
3238 *dashIndex = 0;
3239 length -= dash;
3240 l.setLength(dash);
3241 line.setP1(l.p2());
3242 }
3243
3244 if (rasterize && dash > 0)
3245 rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3246 }
3247}
3248
3252void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3253{
3254#ifdef QT_DEBUG_DRAW
3255 qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3256#endif
3257 Q_D(QRasterPaintEngine);
3259
3260 ensurePen();
3261 if (!s->penData.blend)
3262 return;
3263 if (s->flags.fast_pen) {
3264 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3265 for (int i=0; i<lineCount; ++i) {
3266 QLineF line = lines[i];
3267 stroker.drawLine(line.p1(), line.p2());
3268 }
3269 } else {
3270 QPaintEngineEx::drawLines(lines, lineCount);
3271 }
3272}
3273
3274
3279{
3280 Q_D(QRasterPaintEngine);
3282
3283 ensurePen();
3284 if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3285 || (qpen_style(s->lastPen) == Qt::NoPen))
3286 && !s->flags.antialiased
3287 && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3288 && !rect.isEmpty()
3289 && s->matrix.type() <= QTransform::TxScale) // no shear
3290 {
3291 ensureBrush();
3292 const QRectF r = s->matrix.mapRect(rect);
3293 ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3294 ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3295 const QRect brect = QRect(int(r.x()), int(r.y()),
3296 int_dim(r.x(), r.width()),
3297 int_dim(r.y(), r.height()));
3298 if (brect == r) {
3299 drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3300 &s->penData, &s->brushData);
3301 return;
3302 }
3303 }
3305}
3306
3307
3308#ifdef Q_OS_WIN
3312void QRasterPaintEngine::setDC(HDC hdc) {
3313 Q_D(QRasterPaintEngine);
3314 d->hdc = hdc;
3315}
3316
3320HDC QRasterPaintEngine::getDC() const
3321{
3322 Q_D(const QRasterPaintEngine);
3323 return d->hdc;
3324}
3325
3329void QRasterPaintEngine::releaseDC(HDC) const
3330{
3331}
3332
3333#endif
3334
3339{
3340 // Cached glyphs always require pretransformed positions
3341 if (shouldDrawCachedGlyphs(fontEngine, m))
3342 return true;
3343
3344 // Otherwise let the base-class decide based on the transform
3346}
3347
3353{
3354 // The raster engine does not support projected cached glyph drawing
3355 if (m.type() >= QTransform::TxProject)
3356 return false;
3357
3358 // The font engine might not support filling the glyph cache
3359 // with the given transform applied, in which case we need to
3360 // fall back to the QPainterPath code-path. This does not apply
3361 // for engines with internal caching, as we don't use the engine
3362 // to fill up our cache in that case.
3363 if (!fontEngine->hasInternalCaching() && !fontEngine->supportsTransformation(m))
3364 return false;
3365
3366 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, m);
3367}
3368
3373{
3374 return QPoint(0, 0);
3375}
3376
3377void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3378{
3379 Q_ASSERT(fg);
3380 if (!fg->blend)
3381 return;
3382 Q_D(QRasterPaintEngine);
3383
3384 Q_ASSERT(image.depth() == 1);
3385
3386 const int spanCount = 512;
3387 QT_FT_Span spans[spanCount];
3388 int n = 0;
3389
3390 // Boundaries
3391 int w = image.width();
3392 int h = image.height();
3393 int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
3394 int ymin = qMax(qRound(pos.y()), 0);
3395 int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
3396 int xmin = qMax(qRound(pos.x()), 0);
3397
3398 int x_offset = xmin - qRound(pos.x());
3399
3400 QImage::Format format = image.format();
3401 for (int y = ymin; y < ymax; ++y) {
3402 const uchar *src = image.scanLine(y - qRound(pos.y()));
3404 for (int x = 0; x < xmax - xmin; ++x) {
3405 int src_x = x + x_offset;
3406 uchar pixel = src[src_x >> 3];
3407 if (!pixel) {
3408 x += 7 - (src_x%8);
3409 continue;
3410 }
3411 if (pixel & (0x1 << (src_x & 7))) {
3412 spans[n].x = xmin + x;
3413 spans[n].y = y;
3414 spans[n].coverage = 255;
3415 int len = 1;
3416 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3417 ++src_x;
3418 ++len;
3419 }
3420 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3421 x += len;
3422 ++n;
3423 if (n == spanCount) {
3424 fg->blend(n, spans, fg);
3425 n = 0;
3426 }
3427 }
3428 }
3429 } else {
3430 for (int x = 0; x < xmax - xmin; ++x) {
3431 int src_x = x + x_offset;
3432 uchar pixel = src[src_x >> 3];
3433 if (!pixel) {
3434 x += 7 - (src_x%8);
3435 continue;
3436 }
3437 if (pixel & (0x80 >> (x & 7))) {
3438 spans[n].x = xmin + x;
3439 spans[n].y = y;
3440 spans[n].coverage = 255;
3441 int len = 1;
3442 while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3443 ++src_x;
3444 ++len;
3445 }
3446 spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3447 x += len;
3448 ++n;
3449 if (n == spanCount) {
3450 fg->blend(n, spans, fg);
3451 n = 0;
3452 }
3453 }
3454 }
3455 }
3456 }
3457 if (n) {
3458 fg->blend(n, spans, fg);
3459 n = 0;
3460 }
3461}
3462
3476{
3477 Q_D(const QRasterPaintEngine);
3478
3479 const QClipData *clip = d->clip();
3480 if (!clip || clip->hasRectClip)
3481 return RectClip;
3482 else
3483 return ComplexClip;
3484}
3485
3491{
3492 Q_D(const QRasterPaintEngine);
3493
3494 const QClipData *clip = d->clip();
3495
3496 if (!clip)
3497 return d->deviceRect;
3498
3499 if (clip->hasRectClip)
3500 return clip->clipRect;
3501
3502 return QRectF(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3503}
3504
3506{
3507 Q_Q(QRasterPaintEngine);
3509
3510 rasterizer->setAntialiased(s->flags.antialiased);
3511
3512 QRect clipRect(deviceRect);
3513 ProcessSpans blend;
3514 // ### get from optimized rectbased QClipData
3515
3516 const QClipData *c = clip();
3517 if (c) {
3518 const QRect r(QPoint(c->xmin, c->ymin),
3519 QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3520 clipRect = clipRect.intersected(r);
3521 blend = data->blend;
3522 } else {
3523 blend = data->unclipped_blend;
3524 }
3525
3526 rasterizer->setClipRect(clipRect);
3527 rasterizer->initialize(blend, data);
3528}
3529
3531 ProcessSpans callback,
3532 QSpanData *spanData, QRasterBuffer *rasterBuffer)
3533{
3534 if (!callback || !outline)
3535 return;
3536
3537 Q_Q(QRasterPaintEngine);
3539
3540 if (!s->flags.antialiased) {
3541 initializeRasterizer(spanData);
3542
3543 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3546
3547 rasterizer->rasterize(outline, fillRule);
3548 return;
3549 }
3550
3551 rasterize(outline, callback, (void *)spanData, rasterBuffer);
3552}
3553
3554extern "C" {
3556}
3557
3558static inline uchar *alignAddress(uchar *address, quintptr alignmentMask)
3559{
3560 return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask);
3561}
3562
3564 ProcessSpans callback,
3565 void *userData, QRasterBuffer *)
3566{
3567 if (!callback || !outline)
3568 return;
3569
3570 Q_Q(QRasterPaintEngine);
3572
3573 if (!s->flags.antialiased) {
3574 rasterizer->setAntialiased(s->flags.antialiased);
3576 rasterizer->initialize(callback, userData);
3577
3578 const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3581
3582 rasterizer->rasterize(outline, fillRule);
3583 return;
3584 }
3585
3586 // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
3587 // minimize memory reallocations. However if initial size for
3588 // raster pool is changed for lower value, reallocations will
3589 // occur normally.
3590 int rasterPoolSize = MINIMUM_POOL_SIZE;
3591 uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf];
3592 uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf);
3593 uchar *rasterPoolOnHeap = nullptr;
3594
3595 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3596
3597 void *data = userData;
3598
3599 QT_FT_BBox clip_box = { deviceRect.x(),
3600 deviceRect.y(),
3602 deviceRect.y() + deviceRect.height() };
3603
3604 QT_FT_Raster_Params rasterParams;
3605 rasterParams.target = nullptr;
3606 rasterParams.source = outline;
3607 rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
3608 rasterParams.gray_spans = nullptr;
3609 rasterParams.black_spans = nullptr;
3610 rasterParams.bit_test = nullptr;
3611 rasterParams.bit_set = nullptr;
3612 rasterParams.user = data;
3613 rasterParams.clip_box = clip_box;
3614
3615 bool done = false;
3616 int error;
3617
3618 int rendered_spans = 0;
3619
3620 while (!done) {
3621
3623 rasterParams.gray_spans = callback;
3624 rasterParams.skip_spans = rendered_spans;
3625 error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
3626
3627 // Out of memory, reallocate some more and try again...
3628 if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
3629 rasterPoolSize *= 2;
3630 if (rasterPoolSize > 1024 * 1024) {
3631 qWarning("QPainter: Rasterization of primitive failed");
3632 break;
3633 }
3634
3635 rendered_spans += q_gray_rendered_spans(*grayRaster.data());
3636
3637 free(rasterPoolOnHeap);
3638 rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf);
3639
3640 Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
3641
3642 rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf);
3643
3646 qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
3647 } else {
3648 done = true;
3649 }
3650 }
3651
3652 free(rasterPoolOnHeap);
3653}
3654
3656{
3657 Q_Q(QRasterPaintEngine);
3659
3660 if (!s->clipEnabled)
3661 return;
3662
3665}
3666
3668{
3669 Q_Q(QRasterPaintEngine);
3671
3672 s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3673 && s->matrix.type() <= QTransform::TxShear;
3674}
3675
3677{
3678 Q_Q(const QRasterPaintEngine);
3679 const QRasterPaintEngineState *s = q->state();
3680
3681 return s->flags.fast_images
3684 && !image.hasAlphaChannel()));
3685}
3686
3688{
3689 Q_Q(const QRasterPaintEngine);
3690
3693 && !image.hasAlphaChannel())))
3694 return false;
3695
3696 const QRasterPaintEngineState *s = q->state();
3697 Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate);
3698
3699 if (s->intOpacity != 256
3700 || image.depth() < 8
3702 && (!isPixelAligned(pt) || !isPixelAligned(sr))))
3703 return false;
3704
3706 QImage::Format sFormat = image.format();
3707 // Formats must match or source format must be a subset of destination format
3708 if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha) {
3709 if ((sFormat == QImage::Format_RGB32 && dFormat == QImage::Format_ARGB32)
3710 || (sFormat == QImage::Format_RGBX8888 && dFormat == QImage::Format_RGBA8888)
3711 || (sFormat == QImage::Format_RGBX64 && dFormat == QImage::Format_RGBA64))
3712 sFormat = dFormat;
3713 else
3714 sFormat = qt_maybeAlphaVersionWithSameDepth(sFormat); // this returns premul formats
3715 }
3716 return (dFormat == sFormat);
3717}
3718
3720{
3721 Q_ASSERT(image.depth() == 1);
3722
3723 const QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3725 if (sourceImage.isNull() || dest.isNull())
3726 return image; // we must have run out of memory
3727
3728 QRgb fg = qPremultiply(color.rgba());
3729 QRgb bg = 0;
3730
3731 int height = sourceImage.height();
3732 int width = sourceImage.width();
3733 for (int y=0; y<height; ++y) {
3734 const uchar *source = sourceImage.constScanLine(y);
3735 QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3736 for (int x=0; x < width; ++x)
3737 target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3738 }
3739 return dest;
3740}
3741
3743{
3744}
3745
3747{
3750 destColor0 = 0;
3751 destColor1 = 0;
3752}
3753
3755{
3756 m_buffer = (uchar *)image->bits();
3757 m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3758 m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3759 bytes_per_pixel = image->depth()/8;
3760 bytes_per_line = image->bytesPerLine();
3761
3762 format = image->format();
3763 colorSpace = image->colorSpace();
3764 if (image->depth() == 1 && image->colorTable().size() == 2) {
3766 const QList<QRgb> colorTable = image->colorTable();
3767 destColor0 = qPremultiply(colorTable[0]);
3768 destColor1 = qPremultiply(colorTable[1]);
3769 }
3770
3771 return format;
3772}
3773
3775{
3777 m_clipLines = nullptr;
3778
3779 allocated = 0;
3780 m_spans = nullptr;
3781 xmin = xmax = ymin = ymax = 0;
3782 count = 0;
3783
3784 enabled = true;
3785 hasRectClip = hasRegionClip = false;
3786}
3787
3789{
3790 if (m_clipLines)
3791 free(m_clipLines);
3792 if (m_spans)
3793 free(m_spans);
3794}
3795
3797{
3798 if (m_spans)
3799 return;
3800
3801 if (!m_clipLines)
3802 m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
3803
3805 QT_TRY {
3807 count = 0;
3808 QT_TRY {
3809 if (hasRegionClip) {
3810 const auto rects = clipRegion.begin();
3811 const int numRects = clipRegion.rectCount();
3812 const int maxSpans = (ymax - ymin) * numRects;
3813 allocated = qMax(allocated, maxSpans);
3814 m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3816
3817 int y = 0;
3818 int firstInBand = 0;
3819 while (firstInBand < numRects) {
3820 const int currMinY = rects[firstInBand].y();
3821 const int currMaxY = currMinY + rects[firstInBand].height();
3822
3823 while (y < currMinY) {
3824 m_clipLines[y].spans = nullptr;
3825 m_clipLines[y].count = 0;
3826 ++y;
3827 }
3828
3829 int lastInBand = firstInBand;
3830 while (lastInBand + 1 < numRects && rects[lastInBand+1].top() == y)
3831 ++lastInBand;
3832
3833 while (y < currMaxY) {
3834
3836 m_clipLines[y].count = lastInBand - firstInBand + 1;
3837
3838 for (int r = firstInBand; r <= lastInBand; ++r) {
3839 const QRect &currRect = rects[r];
3841 span->x = currRect.x();
3842 span->len = currRect.width();
3843 span->y = y;
3844 span->coverage = 255;
3845 ++count;
3846 }
3847 ++y;
3848 }
3849
3850 firstInBand = lastInBand + 1;
3851 }
3852
3854
3855 while (y < clipSpanHeight) {
3856 m_clipLines[y].spans = nullptr;
3857 m_clipLines[y].count = 0;
3858 ++y;
3859 }
3860
3861 return;
3862 }
3863
3864 m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3866
3867 if (hasRectClip) {
3868 int y = 0;
3869 while (y < ymin) {
3870 m_clipLines[y].spans = nullptr;
3871 m_clipLines[y].count = 0;
3872 ++y;
3873 }
3874
3875 const int len = clipRect.width();
3876 while (y < ymax) {
3878 span->x = xmin;
3879 span->len = len;
3880 span->y = y;
3881 span->coverage = 255;
3882 ++count;
3883
3885 m_clipLines[y].count = 1;
3886 ++y;
3887 }
3888
3889 while (y < clipSpanHeight) {
3890 m_clipLines[y].spans = nullptr;
3891 m_clipLines[y].count = 0;
3892 ++y;
3893 }
3894 }
3895 } QT_CATCH(...) {
3896 free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3897 m_spans = nullptr;
3898 QT_RETHROW;
3899 }
3900 } QT_CATCH(...) {
3901 free(m_clipLines); // same for clipLines
3902 m_clipLines = nullptr;
3903 QT_RETHROW;
3904 }
3905}
3906
3908{
3910
3911 if (count == 0) {
3912 ymin = ymax = xmin = xmax = 0;
3913 return;
3914 }
3915
3916 int y = -1;
3917 ymin = m_spans[0].y;
3918 ymax = m_spans[count-1].y + 1;
3919 xmin = INT_MAX;
3920 xmax = 0;
3921
3922 const int firstLeft = m_spans[0].x;
3923 const int firstRight = m_spans[0].x + m_spans[0].len;
3924 bool isRect = true;
3925
3926 for (int i = 0; i < count; ++i) {
3928
3929 if (span.y != y) {
3930 if (span.y != y + 1 && y != -1)
3931 isRect = false;
3932 y = span.y;
3933 m_clipLines[y].spans = &span;
3934 m_clipLines[y].count = 1;
3935 } else
3936 ++m_clipLines[y].count;
3937
3938 const int spanLeft = span.x;
3939 const int spanRight = spanLeft + span.len;
3940
3941 if (spanLeft < xmin)
3942 xmin = spanLeft;
3943
3944 if (spanRight > xmax)
3945 xmax = spanRight;
3946
3947 if (spanLeft != firstLeft || spanRight != firstRight)
3948 isRect = false;
3949 }
3950
3951 if (isRect) {
3952 hasRectClip = true;
3954 }
3955}
3956
3957/*
3958 Convert \a rect to clip spans.
3959 */
3961{
3962 if (hasRectClip && rect == clipRect)
3963 return;
3964
3965// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3966 hasRectClip = true;
3967 hasRegionClip = false;
3968 clipRect = rect;
3969
3970 xmin = rect.x();
3971 xmax = rect.x() + rect.width();
3972 ymin = qMin(rect.y(), clipSpanHeight);
3973 ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3974
3975 if (m_spans) {
3976 free(m_spans);
3977 m_spans = nullptr;
3978 }
3979
3980// qDebug() << xmin << xmax << ymin << ymax;
3981}
3982
3983/*
3984 Convert \a region to clip spans.
3985 */
3987{
3988 if (region.rectCount() == 1) {
3989 setClipRect(region.boundingRect());
3990 return;
3991 }
3992
3993 hasRegionClip = true;
3994 hasRectClip = false;
3995 clipRegion = region;
3996
3997 { // set bounding rect
3998 const QRect rect = region.boundingRect();
3999 xmin = rect.x();
4000 xmax = rect.x() + rect.width();
4001 ymin = rect.y();
4002 ymax = rect.y() + rect.height();
4003 }
4004
4005 if (m_spans) {
4006 free(m_spans);
4007 m_spans = nullptr;
4008 }
4009
4010}
4011
4016static const QT_FT_Span *qt_intersect_spans(const QClipData *clip, int *currentClip,
4017 const QT_FT_Span *spans, const QT_FT_Span *end,
4018 QT_FT_Span **outSpans, int available)
4019{
4020 const_cast<QClipData *>(clip)->initialize();
4021
4022 QT_FT_Span *out = *outSpans;
4023
4024 const QT_FT_Span *clipSpans = clip->m_spans + *currentClip;
4025 const QT_FT_Span *clipEnd = clip->m_spans + clip->count;
4026
4027 while (available && spans < end ) {
4028 if (clipSpans >= clipEnd) {
4029 spans = end;
4030 break;
4031 }
4032 if (clipSpans->y > spans->y) {
4033 ++spans;
4034 continue;
4035 }
4036 if (spans->y != clipSpans->y) {
4037 if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
4038 clipSpans = clip->m_clipLines[spans->y].spans;
4039 else
4040 ++clipSpans;
4041 continue;
4042 }
4043 Q_ASSERT(spans->y == clipSpans->y);
4044
4045 int sx1 = spans->x;
4046 int sx2 = sx1 + spans->len;
4047 int cx1 = clipSpans->x;
4048 int cx2 = cx1 + clipSpans->len;
4049
4050 if (cx1 < sx1 && cx2 < sx1) {
4051 ++clipSpans;
4052 continue;
4053 } else if (sx1 < cx1 && sx2 < cx1) {
4054 ++spans;
4055 continue;
4056 }
4057 int x = qMax(sx1, cx1);
4058 int len = qMin(sx2, cx2) - x;
4059 if (len) {
4060 out->x = qMax(sx1, cx1);
4061 out->len = qMin(sx2, cx2) - out->x;
4062 out->y = spans->y;
4063 out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4064 ++out;
4065 --available;
4066 }
4067 if (sx2 < cx2) {
4068 ++spans;
4069 } else {
4070 ++clipSpans;
4071 }
4072 }
4073
4074 *outSpans = out;
4075 *currentClip = clipSpans - clip->m_spans;
4076 return spans;
4077}
4078
4079static void qt_span_fill_clipped(int spanCount, const QT_FT_Span *spans, void *userData)
4080{
4081// qDebug() << "qt_span_fill_clipped" << spanCount;
4082 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4083
4084 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4085
4086 const int NSPANS = 512;
4087 QT_FT_Span cspans[NSPANS];
4088 int currentClip = 0;
4089 const QT_FT_Span *end = spans + spanCount;
4090 while (spans < end) {
4091 QT_FT_Span *clipped = cspans;
4092 spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
4093// qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4094// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4095
4096 if (clipped - cspans)
4097 fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4098 }
4099}
4100
4101/*
4102 \internal
4103 Clip spans to \a{clip}-rectangle.
4104 Returns number of unclipped spans
4105*/
4106static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
4107 const QRect &clip)
4108{
4109 const int minx = clip.left();
4110 const int miny = clip.top();
4111 const int maxx = clip.right();
4112 const int maxy = clip.bottom();
4113
4114 QT_FT_Span *end = spans + numSpans;
4115 while (spans < end) {
4116 if (spans->y >= miny)
4117 break;
4118 ++spans;
4119 }
4120
4121 QT_FT_Span *s = spans;
4122 while (s < end) {
4123 if (s->y > maxy)
4124 break;
4125 if (s->x > maxx || s->x + s->len <= minx) {
4126 s->len = 0;
4127 ++s;
4128 continue;
4129 }
4130 if (s->x < minx) {
4131 s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
4132 s->x = minx;
4133 } else {
4134 s->len = qMin(s->len, (maxx - s->x + 1));
4135 }
4136 ++s;
4137 }
4138
4139 return s - spans;
4140}
4141
4142
4143static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans,
4144 void *userData)
4145{
4146 QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4147 Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4148
4149 Q_ASSERT(fillData->clip);
4150 Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4151
4152 QT_FT_Span *s = const_cast<QT_FT_Span *>(spans);
4153 // hw: check if this const_cast<> is safe!!!
4155 fillData->clip->clipRect);
4156 if (count > 0)
4157 fillData->unclipped_blend(count, s, fillData);
4158}
4159
4160static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData)
4161{
4162 ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4163
4164// qDebug() << " qt_span_clip: " << count << clipData->operation;
4165// for (int i = 0; i < qMin(count, 10); ++i) {
4166// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4167// }
4168
4169 switch (clipData->operation) {
4170
4171 case Qt::IntersectClip:
4172 {
4173 QClipData *newClip = clipData->newClip;
4174 newClip->initialize();
4175
4176 int currentClip = 0;
4177 const QT_FT_Span *end = spans + count;
4178 while (spans < end) {
4179 QT_FT_Span *newspans = newClip->m_spans + newClip->count;
4180 spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
4181 &newspans, newClip->allocated - newClip->count);
4182 newClip->count = newspans - newClip->m_spans;
4183 if (spans < end) {
4184 newClip->m_spans = q_check_ptr((QT_FT_Span *)realloc(newClip->m_spans, newClip->allocated * 2 * sizeof(QT_FT_Span)));
4185 newClip->allocated *= 2;
4186 }
4187 }
4188 }
4189 break;
4190
4191 case Qt::ReplaceClip:
4192 clipData->newClip->appendSpans(spans, count);
4193 break;
4194 case Qt::NoClip:
4195 break;
4196 }
4197}
4198
4200{
4201public:
4203 {
4205 stops(std::move(s)), opacity(op), interpolationMode(mode) {}
4211 };
4212
4214
4215 std::shared_ptr<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
4216 quint64 hash_val = 0;
4217
4218 const QGradientStops stops = gradient.stops();
4219 for (int i = 0; i < stops.size() && i <= 2; i++)
4220 hash_val += stops[i].second.rgba64();
4221
4224
4225 if (it == cache.constEnd())
4226 return addCacheElement(hash_val, gradient, opacity);
4227 else {
4228 do {
4229 const auto &cache_info = it.value();
4230 if (cache_info->stops == stops && cache_info->opacity == opacity && cache_info->interpolationMode == gradient.interpolationMode())
4231 return cache_info;
4232 ++it;
4233 } while (it != cache.constEnd() && it.key() == hash_val);
4234 // an exact match for these stops and opacity was not found, create new cache
4235 return addCacheElement(hash_val, gradient, opacity);
4236 }
4237 }
4238
4239 inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4240protected:
4241 inline int maxCacheSize() const { return 60; }
4242 inline void generateGradientColorTable(const QGradient& g,
4243 QRgba64 *colorTable,
4244 int size, int opacity) const;
4245 std::shared_ptr<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4246 if (cache.size() == maxCacheSize()) {
4247 // may remove more than 1, but OK
4249 }
4250 auto cache_entry = std::make_shared<CacheInfo>(gradient.stops(), opacity, gradient.interpolationMode());
4251 generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
4252 for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
4253 cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
4254 return cache.insert(hash_val, std::move(cache_entry)).value();
4255 }
4256
4259};
4260
4261void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
4262{
4263 const QGradientStops stops = gradient.stops();
4264 int stopCount = stops.size();
4265 Q_ASSERT(stopCount > 0);
4266
4267 bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4268
4269 if (stopCount == 2) {
4270 QRgba64 first_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4271 QRgba64 second_color = combineAlpha256(stops[1].second.rgba64(), opacity);
4272
4273 qreal first_stop = stops[0].first;
4274 qreal second_stop = stops[1].first;
4275
4276 if (second_stop < first_stop) {
4277 quint64 tmp = first_color;
4278 first_color = second_color;
4279 second_color = tmp;
4280 qSwap(first_stop, second_stop);
4281 }
4282
4283 if (colorInterpolation) {
4284 first_color = qPremultiply(first_color);
4285 second_color = qPremultiply(second_color);
4286 }
4287
4288 int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4289 int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4290
4291 uint red_first = uint(first_color.red()) << 16;
4292 uint green_first = uint(first_color.green()) << 16;
4293 uint blue_first = uint(first_color.blue()) << 16;
4294 uint alpha_first = uint(first_color.alpha()) << 16;
4295
4296 uint red_second = uint(second_color.red()) << 16;
4297 uint green_second = uint(second_color.green()) << 16;
4298 uint blue_second = uint(second_color.blue()) << 16;
4299 uint alpha_second = uint(second_color.alpha()) << 16;
4300
4301 int i = 0;
4302 for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4303 if (colorInterpolation)
4304 colorTable[i] = first_color;
4305 else
4306 colorTable[i] = qPremultiply(first_color);
4307 }
4308
4309 if (i < second_index) {
4310 qreal reciprocal = qreal(1) / (second_index - first_index);
4311
4312 int red_delta = qRound((qreal(red_second) - red_first) * reciprocal);
4313 int green_delta = qRound((qreal(green_second) - green_first) * reciprocal);
4314 int blue_delta = qRound((qreal(blue_second) - blue_first) * reciprocal);
4315 int alpha_delta = qRound((qreal(alpha_second) - alpha_first) * reciprocal);
4316
4317 // rounding
4318 red_first += 1 << 15;
4319 green_first += 1 << 15;
4320 blue_first += 1 << 15;
4321 alpha_first += 1 << 15;
4322
4323 for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4324 red_first += red_delta;
4325 green_first += green_delta;
4326 blue_first += blue_delta;
4327 alpha_first += alpha_delta;
4328
4329 const QRgba64 color = qRgba64(red_first >> 16, green_first >> 16, blue_first >> 16, alpha_first >> 16);
4330
4331 if (colorInterpolation)
4332 colorTable[i] = color;
4333 else
4334 colorTable[i] = qPremultiply(color);
4335 }
4336 }
4337
4338 for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4339 if (colorInterpolation)
4340 colorTable[i] = second_color;
4341 else
4342 colorTable[i] = qPremultiply(second_color);
4343 }
4344
4345 return;
4346 }
4347
4348 QRgba64 current_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4349 if (stopCount == 1) {
4350 current_color = qPremultiply(current_color);
4351 for (int i = 0; i < size; ++i)
4352 colorTable[i] = current_color;
4353 return;
4354 }
4355
4356 // The position where the gradient begins and ends
4357 qreal begin_pos = stops[0].first;
4358 qreal end_pos = stops[stopCount-1].first;
4359
4360 int pos = 0; // The position in the color table.
4361 QRgba64 next_color;
4362
4363 qreal incr = 1 / qreal(size); // the double increment.
4364 qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4365
4366 // Up to first point
4367 colorTable[pos++] = qPremultiply(current_color);
4368 while (dpos <= begin_pos) {
4369 colorTable[pos] = colorTable[pos - 1];
4370 ++pos;
4371 dpos += incr;
4372 }
4373
4374 int current_stop = 0; // We always interpolate between current and current + 1.
4375
4376 qreal t; // position between current left and right stops
4377 qreal t_delta; // the t increment per entry in the color table
4378
4379 if (dpos < end_pos) {
4380 // Gradient area
4381 while (dpos > stops[current_stop+1].first)
4382 ++current_stop;
4383
4384 if (current_stop != 0)
4385 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4386 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4387
4388 if (colorInterpolation) {
4389 current_color = qPremultiply(current_color);
4390 next_color = qPremultiply(next_color);
4391 }
4392
4393 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4394 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4395 t = (dpos - stops[current_stop].first) * c;
4396 t_delta = incr * c;
4397
4398 while (true) {
4399 Q_ASSERT(current_stop < stopCount);
4400
4401 int dist = qRound(t);
4402 int idist = 256 - dist;
4403
4404 if (colorInterpolation)
4405 colorTable[pos] = interpolate256(current_color, idist, next_color, dist);
4406 else
4407 colorTable[pos] = qPremultiply(interpolate256(current_color, idist, next_color, dist));
4408
4409 ++pos;
4410 dpos += incr;
4411
4412 if (dpos >= end_pos)
4413 break;
4414
4415 t += t_delta;
4416
4417 int skip = 0;
4418 while (dpos > stops[current_stop+skip+1].first)
4419 ++skip;
4420
4421 if (skip != 0) {
4422 current_stop += skip;
4423 if (skip == 1)
4424 current_color = next_color;
4425 else
4426 current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4427 next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4428
4429 if (colorInterpolation) {
4430 if (skip != 1)
4431 current_color = qPremultiply(current_color);
4432 next_color = qPremultiply(next_color);
4433 }
4434
4435 qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4436 qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4437 t = (dpos - stops[current_stop].first) * c;
4438 t_delta = incr * c;
4439 }
4440 }
4441 }
4442
4443 // After last point
4444 current_color = qPremultiply(combineAlpha256(stops[stopCount - 1].second.rgba64(), opacity));
4445 while (pos < size - 1) {
4446 colorTable[pos] = current_color;
4447 ++pos;
4448 }
4449
4450 // Make sure the last color stop is represented at the end of the table
4451 colorTable[size - 1] = current_color;
4452}
4453
4454Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4455
4456
4458{
4459 rasterBuffer = rb;
4460 type = None;
4461 txop = 0;
4462 bilinear = false;
4463 m11 = m22 = m33 = 1.;
4464 m12 = m13 = m21 = m23 = dx = dy = 0.0;
4465 clip = pe ? pe->d_func()->clip() : nullptr;
4466}
4467
4468Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4469
4471 bool isCosmetic)
4472{
4473 Qt::BrushStyle brushStyle = qbrush_style(brush);
4474 cachedGradient.reset();
4475 switch (brushStyle) {
4476 case Qt::SolidPattern: {
4477 type = Solid;
4480 if (solidColor.alphaF() <= 0.0f && compositionMode == QPainter::CompositionMode_SourceOver)
4481 type = None;
4482 break;
4483 }
4484
4486 {
4488 const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4489 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4490
4491 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4492 gradient.colorTable32 = cacheInfo->buffer32;
4493#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4494 gradient.colorTable64 = cacheInfo->buffer64;
4495#endif
4496 cachedGradient = std::move(cacheInfo);
4497
4498 gradient.spread = g->spread();
4499
4500 QLinearGradientData &linearData = gradient.linear;
4501
4502 linearData.origin.x = g->start().x();
4503 linearData.origin.y = g->start().y();
4504 linearData.end.x = g->finalStop().x();
4505 linearData.end.y = g->finalStop().y();
4506 break;
4507 }
4508
4510 {
4512 const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4513 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4514
4515 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4516 gradient.colorTable32 = cacheInfo->buffer32;
4517#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4518 gradient.colorTable64 = cacheInfo->buffer64;
4519#endif
4520 cachedGradient = std::move(cacheInfo);
4521
4522 gradient.spread = g->spread();
4523
4524 QRadialGradientData &radialData = gradient.radial;
4525
4526 QPointF center = g->center();
4527 radialData.center.x = center.x();
4528 radialData.center.y = center.y();
4529 radialData.center.radius = g->centerRadius();
4530 QPointF focal = g->focalPoint();
4531 radialData.focal.x = focal.x();
4532 radialData.focal.y = focal.y();
4533 radialData.focal.radius = g->focalRadius();
4534 }
4535 break;
4536
4538 {
4540 const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4541 gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4542
4543 auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4544 gradient.colorTable32 = cacheInfo->buffer32;
4545#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4546 gradient.colorTable64 = cacheInfo->buffer64;
4547#endif
4548 cachedGradient = std::move(cacheInfo);
4549
4551
4552 QConicalGradientData &conicalData = gradient.conical;
4553
4554 QPointF center = g->center();
4555 conicalData.center.x = center.x();
4556 conicalData.center.y = center.y();
4557 conicalData.angle = qDegreesToRadians(g->angle());
4558 }
4559 break;
4560
4561 case Qt::Dense1Pattern:
4562 case Qt::Dense2Pattern:
4563 case Qt::Dense3Pattern:
4564 case Qt::Dense4Pattern:
4565 case Qt::Dense5Pattern:
4566 case Qt::Dense6Pattern:
4567 case Qt::Dense7Pattern:
4568 case Qt::HorPattern:
4569 case Qt::VerPattern:
4570 case Qt::CrossPattern:
4571 case Qt::BDiagPattern:
4572 case Qt::FDiagPattern:
4574 type = Texture;
4575 if (!tempImage)
4576 tempImage = new QImage();
4577 *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4579 break;
4580 case Qt::TexturePattern:
4581 type = Texture;
4582 if (!tempImage)
4583 tempImage = new QImage();
4584
4585 if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4586 *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4587 else
4588 *tempImage = brush.textureImage();
4590 break;
4591
4592 case Qt::NoBrush:
4593 default:
4594 type = None;
4595 break;
4596 }
4598}
4599
4601{
4602 bitmapBlit = nullptr;
4603 alphamapBlit = nullptr;
4604 alphaRGBBlit = nullptr;
4605
4606 fillRect = nullptr;
4607
4608 switch(type) {
4609 case None:
4610 unclipped_blend = nullptr;
4611 break;
4612 case Solid: {
4613 const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format];
4614 unclipped_blend = drawHelper.blendColor;
4615 bitmapBlit = drawHelper.bitmapBlit;
4616 alphamapBlit = drawHelper.alphamapBlit;
4617 alphaRGBBlit = drawHelper.alphaRGBBlit;
4618 fillRect = drawHelper.fillRect;
4619 break;
4620 }
4621 case LinearGradient:
4622 case RadialGradient:
4623 case ConicalGradient:
4625 break;
4626 case Texture:
4628 if (!texture.imageData)
4629 unclipped_blend = nullptr;
4630
4631 break;
4632 }
4633 // setup clipping
4634 if (!unclipped_blend) {
4635 blend = nullptr;
4636 } else if (!clip) {
4638 } else if (clip->hasRectClip) {
4640 } else {
4642 }
4643}
4644
4646{
4647 QTransform delta;
4648 // make sure we round off correctly in qdrawhelper.cpp
4649 delta.translate(1.0 / 65536, 1.0 / 65536);
4650
4651 QTransform inv = (delta * matrix).inverted();
4652 m11 = inv.m11();
4653 m12 = inv.m12();
4654 m13 = inv.m13();
4655 m21 = inv.m21();
4656 m22 = inv.m22();
4657 m23 = inv.m23();
4658 m33 = inv.m33();
4659 dx = inv.dx();
4660 dy = inv.dy();
4661 txop = inv.type();
4662 bilinear = bilin;
4663
4664 const bool affine = inv.isAffine();
4665 const qreal f1 = m11 * m11 + m21 * m21;
4666 const qreal f2 = m12 * m12 + m22 * m22;
4667 fast_matrix = affine
4668 && f1 < 1e4
4669 && f2 < 1e4
4670 && f1 > (1.0 / 65536)
4671 && f2 > (1.0 / 65536)
4672 && qAbs(dx) < 1e4
4673 && qAbs(dy) < 1e4;
4674
4676}
4677
4678void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4679{
4680 const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4681 if (!d || d->height == 0) {
4682 texture.imageData = nullptr;
4683 texture.width = 0;
4684 texture.height = 0;
4685 texture.x1 = 0;
4686 texture.y1 = 0;
4687 texture.x2 = 0;
4688 texture.y2 = 0;
4689 texture.bytesPerLine = 0;
4691 texture.colorTable = nullptr;
4692 texture.hasAlpha = alpha != 256;
4693 } else {
4694 texture.imageData = d->data;
4695 texture.width = d->width;
4696 texture.height = d->height;
4697
4698 if (sourceRect.isNull()) {
4699 texture.x1 = 0;
4700 texture.y1 = 0;
4701 texture.x2 = texture.width;
4702 texture.y2 = texture.height;
4703 } else {
4704 texture.x1 = sourceRect.x();
4705 texture.y1 = sourceRect.y();
4706 texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4707 texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4708 }
4709
4710 texture.bytesPerLine = d->bytes_per_line;
4711
4712 texture.format = d->format;
4713 texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : nullptr;
4714 texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4715 }
4716 texture.const_alpha = alpha;
4717 texture.type = _type;
4718
4720}
4721
4726static inline void drawEllipsePoints(int x, int y, int length,
4727 const QRect &rect,
4728 const QRect &clip,
4729 ProcessSpans pen_func, ProcessSpans brush_func,
4730 QSpanData *pen_data, QSpanData *brush_data)
4731{
4732 if (length == 0)
4733 return;
4734
4735 QT_FT_Span _outline[4];
4736 QT_FT_Span *outline = _outline;
4737 const int midx = rect.x() + (rect.width() + 1) / 2;
4738 const int midy = rect.y() + (rect.height() + 1) / 2;
4739
4740 x = x + midx;
4741 y = midy - y;
4742
4743 // topleft
4744 outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4745 outline[0].len = qMin(length, x - outline[0].x);
4746 outline[0].y = y;
4747 outline[0].coverage = 255;
4748
4749 // topright
4750 outline[1].x = x;
4751 outline[1].len = length;
4752 outline[1].y = y;
4753 outline[1].coverage = 255;
4754
4755 // bottomleft
4756 outline[2].x = outline[0].x;
4757 outline[2].len = outline[0].len;
4758 outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4759 outline[2].coverage = 255;
4760
4761 // bottomright
4762 outline[3].x = x;
4763 outline[3].len = length;
4764 outline[3].y = outline[2].y;
4765 outline[3].coverage = 255;
4766
4767 if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4768 QT_FT_Span _fill[2];
4769 QT_FT_Span *fill = _fill;
4770
4771 // top fill
4772 fill[0].x = outline[0].x + outline[0].len - 1;
4773 fill[0].len = qMax(0, outline[1].x - fill[0].x);
4774 fill[0].y = outline[1].y;
4775 fill[0].coverage = 255;
4776
4777 // bottom fill
4778 fill[1].x = outline[2].x + outline[2].len - 1;
4779 fill[1].len = qMax(0, outline[3].x - fill[1].x);
4780 fill[1].y = outline[3].y;
4781 fill[1].coverage = 255;
4782
4783 int n = (fill[0].y >= fill[1].y ? 1 : 2);
4784 n = qt_intersect_spans(fill, n, clip);
4785 if (n > 0)
4786 brush_func(n, fill, brush_data);
4787 }
4788 if (pen_func) {
4789 int n = (outline[1].y >= outline[2].y ? 2 : 4);
4790 n = qt_intersect_spans(outline, n, clip);
4791 if (n > 0)
4792 pen_func(n, outline, pen_data);
4793 }
4794}
4795
4800static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4801 ProcessSpans pen_func, ProcessSpans brush_func,
4802 QSpanData *pen_data, QSpanData *brush_data)
4803{
4804 const qreal a = qreal(rect.width()) / 2;
4805 const qreal b = qreal(rect.height()) / 2;
4806 qreal d = b*b - (a*a*b) + 0.25*a*a;
4807
4808 int x = 0;
4809 int y = (rect.height() + 1) / 2;
4810 int startx = x;
4811
4812 // region 1
4813 while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4814 if (d < 0) { // select E
4815 d += b*b*(2*x + 3);
4816 ++x;
4817 } else { // select SE
4818 d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4819 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4820 pen_func, brush_func, pen_data, brush_data);
4821 startx = ++x;
4822 --y;
4823 }
4824 }
4825 drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4826 pen_func, brush_func, pen_data, brush_data);
4827
4828 // region 2
4829 d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4830 const int miny = rect.height() & 0x1;
4831 while (y > miny) {
4832 if (d < 0) { // select SE
4833 d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4834 ++x;
4835 } else { // select S
4836 d += a*a*(-2*y + 3);
4837 }
4838 --y;
4839 drawEllipsePoints(x, y, 1, rect, clip,
4840 pen_func, brush_func, pen_data, brush_data);
4841 }
4842}
4843
4844/*
4845 \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4846 \overload
4847 \reimp
4848*/
4849
4850
4851#ifdef QT_DEBUG_DRAW
4852void dumpClip(int width, int height, const QClipData *clip)
4853{
4855 clipImg.fill(0xffff0000);
4856
4857 int x0 = width;
4858 int x1 = 0;
4859 int y0 = height;
4860 int y1 = 0;
4861
4862 ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4863
4864 for (int i = 0; i < clip->count; ++i) {
4865 const QT_FT_Span *span = ((QClipData *) clip)->spans() + i;
4866 for (int j = 0; j < span->len; ++j)
4867 clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4868 x0 = qMin(x0, int(span->x));
4869 x1 = qMax(x1, int(span->x + span->len - 1));
4870
4871 y0 = qMin(y0, int(span->y));
4872 y1 = qMax(y1, int(span->y));
4873 }
4874
4875 static int counter = 0;
4876
4877 Q_ASSERT(y0 >= 0);
4878 Q_ASSERT(x0 >= 0);
4879 Q_ASSERT(y1 >= 0);
4880 Q_ASSERT(x1 >= 0);
4881
4882 fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4883 clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
4884}
4885#endif
4886
4887
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:534
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
void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, QVarLengthArray< glyph_t > &glyphs_out, QVarLengthArray< QFixedPoint > &positions)
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:5721
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1615
QSize size() const
Returns the size of the image, i.e.
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:1197
int height() const
Returns the height of the image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGBA8888
Definition qimage.h:59
@ 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_RGBA64
Definition qimage.h:68
@ Format_Mono
Definition qimage.h:43
@ Format_RGBX64
Definition qimage.h:67
@ Format_A2BGR30_Premultiplied
Definition qimage.h:62
@ Format_Indexed8
Definition qimage.h:45
@ NImageFormats
Definition qimage.h:79
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
@ Format_A2RGB30_Premultiplied
Definition qimage.h:64
@ Format_ARGB4444_Premultiplied
Definition qimage.h:57
@ Format_ARGB32
Definition qimage.h:47
@ Format_RGBX8888
Definition qimage.h:58
const uchar * constScanLine(int) const
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1657
QRect rect() const
Returns the enclosing rectangle (0, 0, width(), height()) of the image.
\inmodule QtCore
Definition qline.h:182
constexpr QPointF p1() const
Returns the line's start point.
Definition qline.h:289
qreal length() const
Returns the length of the line.
Definition qline.cpp:542
constexpr QPointF p2() const
Returns the line's end point.
Definition qline.h:294
void setLength(qreal len)
Sets the length of the line to the given finite length.
Definition qline.h:340
\inmodule QtCore
Definition qline.h:18
constexpr QPoint p1() const
Returns the line's start point.
Definition qline.h:96
constexpr QPoint p2() const
Returns the line's end point.
Definition qline.h:101
\inmodule QtGui
Definition qbrush.h:394
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
T & first()
Definition qlist.h:628
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator begin()
Definition qlist.h:608
void reserve(qsizetype size)
Definition qlist.h:746
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:1833
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1930
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1826
qsizetype size() const noexcept
Definition qhash.h:1490
iterator erase(const_iterator it)
Definition qhash.h:1873
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1921
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
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:25
QList< qreal > dashPattern() const
Returns the dash pattern of this pen.
Definition qpen.cpp:420
bool isCosmetic() const
Returns true if the pen is cosmetic; otherwise returns false.
Definition qpen.cpp:783
Qt::PenCapStyle capStyle() const
Returns the pen's cap style.
Definition qpen.cpp:662
qreal miterLimit() const
Returns the miter limit of the pen.
Definition qpen.cpp:548
QBrush brush() const
Returns the brush used to fill strokes generated with this pen.
Definition qpen.cpp:741
qreal dashOffset() const
Returns the dash offset for the pen.
Definition qpen.cpp:506
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:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
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:394
\inmodule QtCore\reentrant
Definition qpoint.h:23
\inmodule QtGui
Definition qbrush.h:412
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
double bounded(double highest)
Generates one random double in the range between 0 (inclusive) and highest (exclusive).
Definition qrandom.h:72
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:483
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:647
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:499
QRect toAlignedRect() const noexcept
Definition qrect.cpp:2330
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:658
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:655
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:748
constexpr QPointF bottomLeft() const noexcept
Returns the position of the rectangle's bottom-left corner.
Definition qrect.h:513
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:496
bool intersects(const QRectF &r) const noexcept
Returns true if this rectangle intersects with the given rectangle (i.e.
Definition qrect.cpp:2263
QRectF normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:1514
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
constexpr QPointF bottomRight() const noexcept
Returns the position of the rectangle's bottom-right corner.
Definition qrect.h:511
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
constexpr QRect toRect() const noexcept
Returns a QRect based on the values of this rectangle.
Definition qrect.h:845
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:724
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:497
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:652
constexpr QPointF topRight() const noexcept
Returns the position of the rectangle's top-right corner.
Definition qrect.h:512
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:498
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:166
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:238
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:163
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:414
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:181
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:220
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:175
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:172
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:345
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:184
constexpr void setX(int x) noexcept
Sets the left edge of the rectangle to the given x coordinate.
Definition qrect.h:214
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
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:260
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:187
constexpr int right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:178
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
constexpr quint16 red() const
Definition qrgba64.h:70
constexpr quint16 alpha() const
Definition qrgba64.h:73
constexpr quint16 green() const
Definition qrgba64.h:71
constexpr quint16 blue() const
Definition qrgba64.h:72
static constexpr QRgba64 fromArgb32(uint rgb)
Definition qrgba64.h:56
\inmodule QtCore
Definition qsize.h:25
QFontEngine * fontEngine() const
QFixedPoint * glyphPositions
void setFontEngine(QFontEngine *fe)
\inmodule QtCore
Definition qstringview.h:76
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:5710
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
Internal QTextItem.
RenderFlags flags
const QChar * chars
QGlyphLayout glyphs
QFontEngine * fontEngine
\inmodule QtGui
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
qreal m21() const
Returns the horizontal shearing factor.
Definition qtransform.h:211
qreal m23() const
Returns the vertical projection factor.
Definition qtransform.h:219
qreal m12() const
Returns the vertical shearing factor.
Definition qtransform.h:203
qreal m33() const
Returns the division factor.
Definition qtransform.h:231
qreal dx() const
Returns the horizontal translation factor.
Definition qtransform.h:235
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.
bool isAffine() const
Returns true if the matrix represent an affine transformation, otherwise returns false.
Definition qtransform.h:165
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...
qreal m13() const
Returns the horizontal projection factor.
Definition qtransform.h:207
qreal m22() const
Returns the vertical scaling factor.
Definition qtransform.h:215
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
TransformationType
\value TxNone \value TxTranslate \value TxScale \value TxRotate \value TxShear \value TxProject
Definition qtransform.h:22
qreal dy() const
Returns the vertical translation factor.
Definition qtransform.h:239
constexpr size_type size() const noexcept
const T * constData() const
T * data() noexcept
bool isRect() const
static uint polygonFlags(QPaintEngine::PolygonDrawMode mode)
qSwap(pi, e)
double e
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:46
@ 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:67
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:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#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
int qt_depthForFormat(QImage::Format format)
Definition qimage_p.h:140
QImage::Format qt_maybeAlphaVersionWithSameDepth(QImage::Format format)
Definition qimage_p.h:362
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
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
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
GLint left
GLenum type
GLenum GLenum dst
GLint GLint bottom
GLuint GLfloat x0
GLenum target
GLbitfield flags
GLenum GLuint texture
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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
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
GLenum GLsizei len
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]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
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[QImage::NImageFormats]
#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
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned int glyph_t
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
size_t quintptr
Definition qtypes.h:72
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
double qreal
Definition qtypes.h:92
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
RectFillFunc fillRect
AlphaRGBBlitFunc alphaRGBBlit
ProcessSpans blendColor
AlphamapBlitFunc alphamapBlit
BitmapBlitFunc bitmapBlit
struct QConicalGradientData::@218 center
QFixed y
Definition qfixed_p.h:163
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
unsigned short height
unsigned short width
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::@214 origin
struct QLinearGradientData::@215 end
struct QRadialGradientData::@217 focal
struct QRadialGradientData::@216 center
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
QT_FT_Raster_BitTest_Func bit_test
QT_FT_SpanFunc gray_spans
QT_FT_Raster_BitSet_Func bit_set
QT_FT_SpanFunc black_spans
unsigned char coverage