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