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