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