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