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 "qmemrotate_p.h"
66#include "qrgba64_p.h"
67
68#include "qpaintengine_raster_p.h"
69// #include "qbezier_p.h"
70#include "qoutlinemapper_p.h"
71
72#include <limits.h>
73#include <algorithm>
74
75#ifdef Q_OS_WIN
76# include <qvarlengtharray.h>
77# include <private/qfontengine_p.h>
78# include <qt_windows.h>
79#ifdef Q_OS_WIN64
80# include <malloc.h>
81# endif
82#endif
83
84QT_BEGIN_NAMESPACE
85
86class QRectVectorPath : public QVectorPath {
87public:
88 inline void set(const QRect &r) {
89 qreal left = r.x();
90 qreal right = r.x() + r.width();
91 qreal top = r.y();
92 qreal bottom = r.y() + r.height();
93 pts[0] = left;
94 pts[1] = top;
95 pts[2] = right;
96 pts[3] = top;
97 pts[4] = right;
98 pts[5] = bottom;
99 pts[6] = left;
100 pts[7] = bottom;
101 }
102
103 inline void set(const QRectF &r) {
104 qreal left = r.x();
105 qreal right = r.x() + r.width();
106 qreal top = r.y();
107 qreal bottom = r.y() + r.height();
108 pts[0] = left;
109 pts[1] = top;
110 pts[2] = right;
111 pts[3] = top;
112 pts[4] = right;
113 pts[5] = bottom;
114 pts[6] = left;
115 pts[7] = bottom;
116 }
117 inline QRectVectorPath(const QRect &r)
118 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
119 {
120 set(r);
121 }
122 inline QRectVectorPath(const QRectF &r)
123 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
124 {
125 set(r);
126 }
127 inline QRectVectorPath()
128 : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
129 { }
130
131 qreal pts[8];
132};
133
134Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
135
136#define qreal_to_fixed_26_6(f) (int(f * 64))
137#define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
138#define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
139
140// #define QT_DEBUG_DRAW
141#ifdef QT_DEBUG_DRAW
142void dumpClip(int width, int height, const QClipData *clip);
143#endif
144
145#define QT_FAST_SPANS
146
147
148// A little helper macro to get a better approximation of dimensions.
149// If we have a rect that starting at 0.5 of width 3.5 it should span
150// 4 pixels.
151#define int_dim(pos, dim) (int(pos+dim) - int(pos))
152
153static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
154
155#ifdef Q_OS_WIN
156
157static inline bool winClearTypeFontsEnabled()
158{
159#ifdef Q_OS_WINRT
160 return false;
161#else // Q_OS_WINRT
162 UINT result = 0;
163#if !defined(SPI_GETFONTSMOOTHINGTYPE) // MinGW
164# define SPI_GETFONTSMOOTHINGTYPE 0x200A
165# define FE_FONTSMOOTHINGCLEARTYPE 0x002
166#endif
167 SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0);
168 return result == FE_FONTSMOOTHINGCLEARTYPE;
169#endif // !Q_OS_WINRT
170}
171
172/*!
173 \internal
174 */
175bool QRasterPaintEngine::clearTypeFontsEnabled()
176{
177 static const bool result = winClearTypeFontsEnabled();
178 return result;
179}
180
181#endif // Q_OS_WIN
182
183
184
185/********************************************************************************
186 * Span functions
187 */
188static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
189static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
190static void qt_span_clip(int count, const QSpan *spans, void *userData);
191
192struct ClipData
193{
194 QClipData *oldClip;
195 QClipData *newClip;
196 Qt::ClipOperation operation;
197};
198
199enum LineDrawMode {
200 LineDrawClipped,
201 LineDrawNormal,
202 LineDrawIncludeLastPixel
203};
204
205static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
206 ProcessSpans pen_func, ProcessSpans brush_func,
207 QSpanData *pen_data, QSpanData *brush_data);
208
209struct QRasterFloatPoint {
210 qreal x;
211 qreal y;
212};
213
214#ifdef QT_DEBUG_DRAW
215static const QRectF boundingRect(const QPointF *points, int pointCount)
216{
217 const QPointF *e = points;
218 const QPointF *last = points + pointCount;
219 qreal minx, maxx, miny, maxy;
220 minx = maxx = e->x();
221 miny = maxy = e->y();
222 while (++e < last) {
223 if (e->x() < minx)
224 minx = e->x();
225 else if (e->x() > maxx)
226 maxx = e->x();
227 if (e->y() < miny)
228 miny = e->y();
229 else if (e->y() > maxy)
230 maxy = e->y();
231 }
232 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
233}
234#endif
235
236static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
237{
238 ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
239}
240
241static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
242{
243 ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
244}
245
246static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
247 qfixed c2x, qfixed c2y,
248 qfixed ex, qfixed ey,
249 void *data)
250{
251 ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
252 QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
253 QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
254}
255
256
257#if !defined(QT_NO_DEBUG) && 0
258static void qt_debug_path(const QPainterPath &path)
259{
260 const char *names[] = {
261 "MoveTo ",
262 "LineTo ",
263 "CurveTo ",
264 "CurveToData"
265 };
266
267 fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
268 for (int i=0; i<path.elementCount(); ++i) {
269 const QPainterPath::Element &e = path.elementAt(i);
270 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
271 fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
272 }
273}
274#endif
275
276// QRect::normalized() will change the width/height of the rectangle due to
277// its incusive-integer definition of left/right vs width. This is not
278// something we want to change in QRect as that would potentially introduce
279// regressions all over the place, so we implement a straightforward
280// normalized here. QRectF already does this, so QRectF::normalized() is ok to
281// use.
282static QRect qrect_normalized(const QRect &rect)
283{
284 int x, y, w, h;
285 if (Q_UNLIKELY(rect.width() < 0)) {
286 x = rect.x() + rect.width();
287 w = -rect.width();
288 } else {
289 x = rect.x();
290 w = rect.width();
291 }
292
293 if (Q_UNLIKELY(rect.height() < 0)) {
294 y = rect.y() + rect.height();
295 h = -rect.height();
296 } else {
297 y = rect.y();
298 h = rect.height();
299 }
300
301 return QRect(x, y, w, h);
302}
303
304
305QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
306 QPaintEngineExPrivate(),
307 cachedLines(0)
308{
309}
310
311
312/*!
313 \class QRasterPaintEngine
314 \preliminary
315 \ingroup qws
316 \inmodule QtGui
317 \since 4.2
318
319 \brief The QRasterPaintEngine class enables hardware acceleration
320 of painting operations in Qt for Embedded Linux.
321
322 Note that this functionality is only available in
323 Qt for Embedded Linux.
324
325 In Qt for Embedded Linux, painting is a pure software
326 implementation. But starting with Qt 4.2, it is
327 possible to add an accelerated graphics driver to take advantage
328 of available hardware resources.
329
330 Hardware acceleration is accomplished by creating a custom screen
331 driver, accelerating the copying from memory to the screen, and
332 implementing a custom paint engine accelerating the various
333 painting operations. Then a custom paint device and a custom
334 window surface must be implemented to make
335 Qt for Embedded Linux aware of the accelerated driver.
336
337 \note The QRasterPaintEngine class does not support 8-bit images.
338 Instead, they need to be converted to a supported format, such as
339 QImage::Format_ARGB32_Premultiplied.
340
341 \sa QPaintEngine
342*/
343
344/*!
345 \fn Type QRasterPaintEngine::type() const
346 \reimp
347*/
348
349/*!
350 \typedef QSpan
351 \relates QRasterPaintEngine
352
353 A struct equivalent to QT_FT_Span, containing a position (x,
354 y), the span's length in pixels and its color/coverage (a value
355 ranging from 0 to 255).
356*/
357
358/*!
359 \since 4.5
360
361 Creates a raster based paint engine for operating on the given
362 \a device, with the complete set of \l
363 {QPaintEngine::PaintEngineFeature}{paint engine features and
364 capabilities}.
365*/
366QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
367 : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
368{
369 d_func()->device = device;
370 init();
371}
372
373/*!
374 \internal
375*/
376QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
377 : QPaintEngineEx(dd)
378{
379 d_func()->device = device;
380 init();
381}
382
383void QRasterPaintEngine::init()
384{
385 Q_D(QRasterPaintEngine);
386
387
388#ifdef Q_OS_WIN
389 d->hdc = 0;
390#endif
391
392 // The antialiasing raster.
393 d->grayRaster.reset(new QT_FT_Raster);
394 Q_CHECK_PTR(d->grayRaster.data());
395 if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
396 QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
397
398
399 d->rasterizer.reset(new QRasterizer);
400 d->rasterBuffer.reset(new QRasterBuffer());
401 d->outlineMapper.reset(new QOutlineMapper);
402 d->outlinemapper_xform_dirty = true;
403
404 d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
405 d->basicStroker.setLineToHook(qt_ft_outline_line_to);
406 d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
407
408 d->baseClip.reset(new QClipData(d->device->height()));
409 d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
410
411 d->image_filler.init(d->rasterBuffer.data(), this);
412 d->image_filler.type = QSpanData::Texture;
413
414 d->image_filler_xform.init(d->rasterBuffer.data(), this);
415 d->image_filler_xform.type = QSpanData::Texture;
416
417 d->solid_color_filler.init(d->rasterBuffer.data(), this);
418 d->solid_color_filler.type = QSpanData::Solid;
419
420 d->deviceDepth = d->device->depth();
421
422 d->mono_surface = false;
423 gccaps &= ~PorterDuff;
424
425 QImage::Format format = QImage::Format_Invalid;
426
427 switch (d->device->devType()) {
428 case QInternal::Pixmap:
429 qWarning("QRasterPaintEngine: unsupported for pixmaps...");
430 break;
431 case QInternal::Image:
432 format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
433 break;
434 default:
435 qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
436 d->device = 0;
437 return;
438 }
439
440 switch (format) {
441 case QImage::Format_MonoLSB:
442 case QImage::Format_Mono:
443 d->mono_surface = true;
444 break;
445 default:
446 if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha)
447 gccaps |= PorterDuff;
448 break;
449 }
450}
451
452
453/*!
454 Destroys this paint engine.
455*/
456QRasterPaintEngine::~QRasterPaintEngine()
457{
458 Q_D(QRasterPaintEngine);
459
460 qt_ft_grays_raster.raster_done(*d->grayRaster.data());
461}
462
463/*!
464 \reimp
465*/
466bool QRasterPaintEngine::begin(QPaintDevice *device)
467{
468 Q_D(QRasterPaintEngine);
469
470 if (device->devType() == QInternal::Pixmap) {
471 QPixmap *pixmap = static_cast<QPixmap *>(device);
472 QPlatformPixmap *pd = pixmap->handle();
473 if (pd->classId() == QPlatformPixmap::RasterClass || pd->classId() == QPlatformPixmap::BlitterClass)
474 d->device = pd->buffer();
475 } else {
476 d->device = device;
477 }
478
479 // Make sure QPaintEngine::paintDevice() returns the proper device.
480 d->pdev = d->device;
481
482 Q_ASSERT(d->device->devType() == QInternal::Image
483 || d->device->devType() == QInternal::CustomRaster);
484
485 d->systemStateChanged();
486
487 QRasterPaintEngineState *s = state();
488 ensureOutlineMapper();
489 d->outlineMapper->m_clip_rect = d->deviceRect;
490
491 if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
492 d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
493 if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
494 d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
495
496 d->rasterizer->setClipRect(d->deviceRect);
497
498 s->penData.init(d->rasterBuffer.data(), this);
499 s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
500 s->stroker = &d->basicStroker;
501 d->basicStroker.setClipRect(d->deviceRect);
502
503 s->brushData.init(d->rasterBuffer.data(), this);
504 s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
505
506 d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
507
508 setDirty(DirtyBrushOrigin);
509
510#ifdef QT_DEBUG_DRAW
511 qDebug() << "QRasterPaintEngine::begin(" << (void *) device
512 << ") devType:" << device->devType()
513 << "devRect:" << d->deviceRect;
514 if (d->baseClip) {
515 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
516 }
517#endif
518
519 if (d->mono_surface)
520 d->glyphCacheFormat = QFontEngine::Format_Mono;
521#if defined(Q_OS_WIN)
522 else if (clearTypeFontsEnabled())
523#else
524 else if (false)
525#endif
526 {
527 QImage::Format format = static_cast<QImage *>(d->device)->format();
528 if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
529 d->glyphCacheFormat = QFontEngine::Format_A32;
530 else
531 d->glyphCacheFormat = QFontEngine::Format_A8;
532 } else
533 d->glyphCacheFormat = QFontEngine::Format_A8;
534
535 setActive(true);
536 return true;
537}
538
539/*!
540 \reimp
541*/
542bool QRasterPaintEngine::end()
543{
544#ifdef QT_DEBUG_DRAW
545 Q_D(QRasterPaintEngine);
546 qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
547 if (d->baseClip) {
548 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
549 }
550#endif
551
552 return true;
553}
554
555/*!
556 \internal
557*/
558void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
559{
560 QRasterPaintEngineState *s = state();
561 // FALCON: get rid of this line, see drawImage call below.
562 s->matrix = matrix;
563 QTransform::TransformationType txop = s->matrix.type();
564
565 switch (txop) {
566
567 case QTransform::TxNone:
568 s->flags.int_xform = true;
569 break;
570
571 case QTransform::TxTranslate:
572 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
573 && qreal(int(s->matrix.dy())) == s->matrix.dy();
574 break;
575
576 case QTransform::TxScale:
577 s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
578 && qreal(int(s->matrix.dy())) == s->matrix.dy()
579 && qreal(int(s->matrix.m11())) == s->matrix.m11()
580 && qreal(int(s->matrix.m22())) == s->matrix.m22();
581 break;
582
583 default: // shear / perspective...
584 s->flags.int_xform = false;
585 break;
586 }
587
588 s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
589
590 ensureOutlineMapper();
591}
592
593
594
595QRasterPaintEngineState::~QRasterPaintEngineState()
596{
597 if (flags.has_clip_ownership)
598 delete clip;
599}
600
601
602QRasterPaintEngineState::QRasterPaintEngineState()
603{
604 stroker = 0;
605
606 fillFlags = 0;
607 strokeFlags = 0;
608 pixmapFlags = 0;
609
610 intOpacity = 256;
611
612 txscale = 1.;
613
614 flags.fast_pen = true;
615 flags.non_complex_pen = false;
616 flags.antialiased = false;
617 flags.bilinear = false;
618 flags.legacy_rounding = false;
619 flags.fast_text = true;
620 flags.int_xform = true;
621 flags.tx_noshear = true;
622 flags.fast_images = true;
623
624 clip = 0;
625 flags.has_clip_ownership = false;
626
627 dirty = 0;
628}
629
630QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
631 : QPainterState(s)
632 , lastPen(s.lastPen)
633 , penData(s.penData)
634 , stroker(s.stroker)
635 , strokeFlags(s.strokeFlags)
636 , lastBrush(s.lastBrush)
637 , brushData(s.brushData)
638 , fillFlags(s.fillFlags)
639 , pixmapFlags(s.pixmapFlags)
640 , intOpacity(s.intOpacity)
641 , txscale(s.txscale)
642 , clip(s.clip)
643 , dirty(s.dirty)
644 , flag_bits(s.flag_bits)
645{
646 brushData.tempImage = 0;
647 penData.tempImage = 0;
648 flags.has_clip_ownership = false;
649}
650
651/*!
652 \internal
653*/
654QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
655{
656 QRasterPaintEngineState *s;
657 if (!orig)
658 s = new QRasterPaintEngineState();
659 else
660 s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
661
662 return s;
663}
664
665/*!
666 \internal
667*/
668void QRasterPaintEngine::setState(QPainterState *s)
669{
670 Q_D(QRasterPaintEngine);
671 QPaintEngineEx::setState(s);
672 QRasterPaintEngineState *t = state();
673 if (t->clip && t->clip->enabled != t->clipEnabled) {
674 // Since we do not "detach" clipdata when changing only enabled state, we need to resync state here
675 t->clip->enabled = t->clipEnabled;
676 }
677 d->rasterBuffer->compositionMode = s->composition_mode;
678}
679
680/*!
681 \fn QRasterPaintEngineState *QRasterPaintEngine::state()
682 \internal
683*/
684
685/*!
686 \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
687 \internal
688*/
689
690/*!
691 \internal
692*/
693void QRasterPaintEngine::penChanged()
694{
695#ifdef QT_DEBUG_DRAW
696 qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
697#endif
698 QRasterPaintEngineState *s = state();
699 Q_ASSERT(s);
700 s->strokeFlags |= DirtyPen;
701 s->dirty |= DirtyPen;
702}
703
704/*!
705 \internal
706*/
707void QRasterPaintEngine::updatePen(const QPen &pen)
708{
709 Q_D(QRasterPaintEngine);
710 QRasterPaintEngineState *s = state();
711#ifdef QT_DEBUG_DRAW
712 qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
713#endif
714
715 Qt::PenStyle pen_style = qpen_style(pen);
716
717 s->lastPen = pen;
718 s->strokeFlags = 0;
719
720 s->penData.clip = d->clip();
721 s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
722
723 if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
724 || pen.brush().transform().type() >= QTransform::TxNone) {
725 d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
726 }
727
728 // Slightly ugly handling of an uncommon case... We need to change
729 // the pen because it is reused in draw_midpoint to decide dashed
730 // or non-dashed.
731 if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
732 pen_style = Qt::SolidLine;
733 s->lastPen.setStyle(Qt::SolidLine);
734 }
735
736 d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
737 d->basicStroker.setCapStyle(qpen_capStyle(pen));
738 d->basicStroker.setMiterLimit(pen.miterLimit());
739
740 qreal penWidth = qpen_widthf(pen);
741 if (penWidth == 0)
742 d->basicStroker.setStrokeWidth(1);
743 else
744 d->basicStroker.setStrokeWidth(penWidth);
745
746 if(pen_style == Qt::SolidLine) {
747 s->stroker = &d->basicStroker;
748 } else if (pen_style != Qt::NoPen) {
749 if (!d->dashStroker)
750 d->dashStroker.reset(new QDashStroker(&d->basicStroker));
751 if (qt_pen_is_cosmetic(pen, s->renderHints)) {
752 d->dashStroker->setClipRect(d->deviceRect);
753 } else {
754 // ### I've seen this inverted devrect multiple places now...
755 QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
756 d->dashStroker->setClipRect(clipRect);
757 }
758 d->dashStroker->setDashPattern(pen.dashPattern());
759 d->dashStroker->setDashOffset(pen.dashOffset());
760 s->stroker = d->dashStroker.data();
761 } else {
762 s->stroker = 0;
763 }
764
765 ensureRasterState(); // needed because of tx_noshear...
766 bool cosmetic = qt_pen_is_cosmetic(pen, s->renderHints);
767 s->flags.fast_pen = pen_style > Qt::NoPen
768 && s->penData.blend
769 && ((cosmetic && penWidth <= 1)
770 || (!cosmetic && (s->flags.tx_noshear || !s->flags.antialiased) && penWidth * s->txscale <= 1));
771
772 s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
773
774 s->strokeFlags = 0;
775}
776
777
778
779/*!
780 \internal
781*/
782void QRasterPaintEngine::brushOriginChanged()
783{
784 QRasterPaintEngineState *s = state();
785#ifdef QT_DEBUG_DRAW
786 qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
787#endif
788
789 s->fillFlags |= DirtyBrushOrigin;
790}
791
792
793/*!
794 \internal
795*/
796void QRasterPaintEngine::brushChanged()
797{
798 QRasterPaintEngineState *s = state();
799#ifdef QT_DEBUG_DRAW
800 qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
801#endif
802 s->fillFlags |= DirtyBrush;
803}
804
805
806
807
808/*!
809 \internal
810*/
811void QRasterPaintEngine::updateBrush(const QBrush &brush)
812{
813#ifdef QT_DEBUG_DRAW
814 qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
815#endif
816 Q_D(QRasterPaintEngine);
817 QRasterPaintEngineState *s = state();
818 // must set clip prior to setup, as setup uses it...
819 s->brushData.clip = d->clip();
820 s->brushData.setup(brush, s->intOpacity, s->composition_mode);
821 if (s->fillFlags & DirtyTransform
822 || brush.transform().type() >= QTransform::TxNone)
823 d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
824 s->lastBrush = brush;
825 s->fillFlags = 0;
826}
827
828void QRasterPaintEngine::updateOutlineMapper()
829{
830 Q_D(QRasterPaintEngine);
831 d->outlineMapper->setMatrix(state()->matrix);
832}
833
834void QRasterPaintEngine::updateRasterState()
835{
836 QRasterPaintEngineState *s = state();
837
838 if (s->dirty & DirtyTransform)
839 updateMatrix(s->matrix);
840
841 if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
842 const QPainter::CompositionMode mode = s->composition_mode;
843 s->flags.fast_text = (s->penData.type == QSpanData::Solid)
844 && s->intOpacity == 256
845 && (mode == QPainter::CompositionMode_Source
846 || (mode == QPainter::CompositionMode_SourceOver
847 && s->penData.solidColor.isOpaque()));
848 }
849
850 s->dirty = 0;
851}
852
853
854/*!
855 \internal
856*/
857void QRasterPaintEngine::opacityChanged()
858{
859 QRasterPaintEngineState *s = state();
860
861#ifdef QT_DEBUG_DRAW
862 qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
863#endif
864
865 s->fillFlags |= DirtyOpacity;
866 s->strokeFlags |= DirtyOpacity;
867 s->pixmapFlags |= DirtyOpacity;
868 s->dirty |= DirtyOpacity;
869 s->intOpacity = (int) (s->opacity * 256);
870}
871
872/*!
873 \internal
874*/
875void QRasterPaintEngine::compositionModeChanged()
876{
877 Q_D(QRasterPaintEngine);
878 QRasterPaintEngineState *s = state();
879
880#ifdef QT_DEBUG_DRAW
881 qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
882#endif
883
884 s->fillFlags |= DirtyCompositionMode;
885 s->dirty |= DirtyCompositionMode;
886
887 s->strokeFlags |= DirtyCompositionMode;
888 d->rasterBuffer->compositionMode = s->composition_mode;
889
890 d->recalculateFastImages();
891}
892
893/*!
894 \internal
895*/
896void QRasterPaintEngine::renderHintsChanged()
897{
898 QRasterPaintEngineState *s = state();
899
900#ifdef QT_DEBUG_DRAW
901 qDebug() << "QRasterPaintEngine::renderHintsChanged()" << Qt::hex << s->renderHints;
902#endif
903
904 bool was_aa = s->flags.antialiased;
905 bool was_bilinear = s->flags.bilinear;
906
907 s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
908#if QT_DEPRECATED_SINCE(5, 14)
909 if (s->renderHints & QPainter::HighQualityAntialiasing)
910 s->flags.antialiased = true;
911#endif
912 s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
913 s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
914
915 if (was_aa != s->flags.antialiased)
916 s->strokeFlags |= DirtyHints;
917
918 if (was_bilinear != s->flags.bilinear) {
919 s->strokeFlags |= DirtyPen;
920 s->fillFlags |= DirtyBrush;
921 }
922
923 Q_D(QRasterPaintEngine);
924 d->recalculateFastImages();
925}
926
927/*!
928 \internal
929*/
930void QRasterPaintEngine::transformChanged()
931{
932 QRasterPaintEngineState *s = state();
933
934#ifdef QT_DEBUG_DRAW
935 qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
936#endif
937
938 s->fillFlags |= DirtyTransform;
939 s->strokeFlags |= DirtyTransform;
940
941 s->dirty |= DirtyTransform;
942
943 Q_D(QRasterPaintEngine);
944 d->recalculateFastImages();
945}
946
947/*!
948 \internal
949*/
950void QRasterPaintEngine::clipEnabledChanged()
951{
952 QRasterPaintEngineState *s = state();
953
954#ifdef QT_DEBUG_DRAW
955 qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
956#endif
957
958 if (s->clip) {
959 s->clip->enabled = s->clipEnabled;
960 s->fillFlags |= DirtyClipEnabled;
961 s->strokeFlags |= DirtyClipEnabled;
962 s->pixmapFlags |= DirtyClipEnabled;
963 }
964}
965
966void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
967 const QImage &img,
968 SrcOverBlendFunc func,
969 const QRect &clip,
970 int alpha,
971 const QRect &sr)
972{
973 if (alpha == 0 || !clip.isValid())
974 return;
975 if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
976 return;
977 if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
978 return;
979
980 Q_ASSERT(img.depth() >= 8);
981
982 qsizetype srcBPL = img.bytesPerLine();
983 const uchar *srcBits = img.bits();
984 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
985 int iw = img.width();
986 int ih = img.height();
987
988 if (!sr.isEmpty()) {
989 iw = sr.width();
990 ih = sr.height();
991 // Adjust the image according to the source offset...
992 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
993 }
994
995 // adapt the x parameters
996 int x = qRound(pt.x());
997 int cx1 = clip.x();
998 int cx2 = clip.x() + clip.width();
999 if (x < cx1) {
1000 int d = cx1 - x;
1001 srcBits += srcSize * d;
1002 iw -= d;
1003 x = cx1;
1004 }
1005 if (x + iw > cx2) {
1006 int d = x + iw - cx2;
1007 iw -= d;
1008 }
1009 if (iw <= 0)
1010 return;
1011
1012 // adapt the y paremeters...
1013 int cy1 = clip.y();
1014 int cy2 = clip.y() + clip.height();
1015 int y = qRound(pt.y());
1016 if (y < cy1) {
1017 int d = cy1 - y;
1018 srcBits += srcBPL * d;
1019 ih -= d;
1020 y = cy1;
1021 }
1022 if (y + ih > cy2) {
1023 int d = y + ih - cy2;
1024 ih -= d;
1025 }
1026 if (ih <= 0)
1027 return;
1028
1029 // call the blend function...
1030 int dstSize = rasterBuffer->bytesPerPixel();
1031 qsizetype dstBPL = rasterBuffer->bytesPerLine();
1032 func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
1033 srcBits, srcBPL,
1034 iw, ih,
1035 alpha);
1036}
1037
1038void QRasterPaintEnginePrivate::blitImage(const QPointF &pt,
1039 const QImage &img,
1040 const QRect &clip,
1041 const QRect &sr)
1042{
1043 if (!clip.isValid())
1044 return;
1045 if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
1046 return;
1047 if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
1048 return;
1049
1050 Q_ASSERT(img.depth() >= 8);
1051
1052 qsizetype srcBPL = img.bytesPerLine();
1053 const uchar *srcBits = img.bits();
1054 int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
1055 int iw = img.width();
1056 int ih = img.height();
1057
1058 if (!sr.isEmpty()) {
1059 iw = sr.width();
1060 ih = sr.height();
1061 // Adjust the image according to the source offset...
1062 srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
1063 }
1064
1065 // adapt the x parameters
1066 int x = qRound(pt.x());
1067 int cx1 = clip.x();
1068 int cx2 = clip.x() + clip.width();
1069 if (x < cx1) {
1070 int d = cx1 - x;
1071 srcBits += srcSize * d;
1072 iw -= d;
1073 x = cx1;
1074 }
1075 if (x + iw > cx2) {
1076 int d = x + iw - cx2;
1077 iw -= d;
1078 }
1079 if (iw <= 0)
1080 return;
1081
1082 // adapt the y paremeters...
1083 int cy1 = clip.y();
1084 int cy2 = clip.y() + clip.height();
1085 int y = qRound(pt.y());
1086 if (y < cy1) {
1087 int d = cy1 - y;
1088 srcBits += srcBPL * d;
1089 ih -= d;
1090 y = cy1;
1091 }
1092 if (y + ih > cy2) {
1093 int d = y + ih - cy2;
1094 ih -= d;
1095 }
1096 if (ih <= 0)
1097 return;
1098
1099 // blit..
1100 int dstSize = rasterBuffer->bytesPerPixel();
1101 qsizetype dstBPL = rasterBuffer->bytesPerLine();
1102 const uint *src = (const uint *) srcBits;
1103 uint *dst = reinterpret_cast<uint *>(rasterBuffer->buffer() + x * dstSize + y * dstBPL);
1104
1105 const int len = iw * (qt_depthForFormat(rasterBuffer->format) >> 3);
1106 for (int y = 0; y < ih; ++y) {
1107 memcpy(dst, src, len);
1108 dst = (quint32 *)(((uchar *) dst) + dstBPL);
1109 src = (const quint32 *)(((const uchar *) src) + srcBPL);
1110 }
1111}
1112
1113
1114void QRasterPaintEnginePrivate::systemStateChanged()
1115{
1116 deviceRectUnclipped = QRect(0, 0,
1117 qMin(QT_RASTER_COORD_LIMIT, device->width()),
1118 qMin(QT_RASTER_COORD_LIMIT, device->height()));
1119
1120 if (!systemClip.isEmpty()) {
1121 QRegion clippedDeviceRgn = systemClip & deviceRectUnclipped;
1122 deviceRect = clippedDeviceRgn.boundingRect();
1123 baseClip->setClipRegion(clippedDeviceRgn);
1124 } else {
1125 deviceRect = deviceRectUnclipped;
1126 baseClip->setClipRect(deviceRect);
1127 }
1128#ifdef QT_DEBUG_DRAW
1129 qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << deviceRectUnclipped << systemClip;
1130#endif
1131
1132 exDeviceRect = deviceRect;
1133
1134 Q_Q(QRasterPaintEngine);
1135 if (q->state()) {
1136 q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1137 q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1138 q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1139 }
1140}
1141
1142void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1143{
1144 if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1145 return;
1146
1147 Q_Q(QRasterPaintEngine);
1148 bool bilinear = q->state()->flags.bilinear;
1149
1150 if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
1151 spanData->setupMatrix(b.transform() * m, bilinear);
1152 } else {
1153 if (m.type() <= QTransform::TxTranslate) {
1154 // specialize setupMatrix for translation matrices
1155 // to avoid needless matrix inversion
1156 spanData->m11 = 1;
1157 spanData->m12 = 0;
1158 spanData->m13 = 0;
1159 spanData->m21 = 0;
1160 spanData->m22 = 1;
1161 spanData->m23 = 0;
1162 spanData->m33 = 1;
1163 spanData->dx = -m.dx();
1164 spanData->dy = -m.dy();
1165 spanData->txop = m.type();
1166 spanData->bilinear = bilinear;
1167 spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
1168 spanData->adjustSpanMethods();
1169 } else {
1170 spanData->setupMatrix(m, bilinear);
1171 }
1172 }
1173}
1174
1175// #define QT_CLIPPING_RATIOS
1176
1177#ifdef QT_CLIPPING_RATIOS
1178int rectClips;
1179int regionClips;
1180int totalClips;
1181
1182static void checkClipRatios(QRasterPaintEnginePrivate *d)
1183{
1184 if (d->clip()->hasRectClip)
1185 rectClips++;
1186 if (d->clip()->hasRegionClip)
1187 regionClips++;
1188 totalClips++;
1189
1190 if ((totalClips % 5000) == 0) {
1191 printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
1192 rectClips * 100.0 / (qreal) totalClips,
1193 regionClips * 100.0 / (qreal) totalClips,
1194 (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
1195 totalClips = 0;
1196 rectClips = 0;
1197 regionClips = 0;
1198 }
1199
1200}
1201#endif
1202
1203static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
1204{
1205 if (s->flags.has_clip_ownership)
1206 delete s->clip;
1207 s->clip = 0;
1208 s->flags.has_clip_ownership = false;
1209}
1210
1211static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
1212{
1213 s->fillFlags |= QPaintEngine::DirtyClipPath;
1214 s->strokeFlags |= QPaintEngine::DirtyClipPath;
1215 s->pixmapFlags |= QPaintEngine::DirtyClipPath;
1216
1217 d->solid_color_filler.clip = d->clip();
1218 d->solid_color_filler.adjustSpanMethods();
1219
1220#ifdef QT_DEBUG_DRAW
1221 dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
1222#endif
1223
1224}
1225
1226
1227/*!
1228 \internal
1229*/
1230void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1231{
1232#ifdef QT_DEBUG_DRAW
1233 qDebug() << "QRasterPaintEngine::clip(): " << path << op;
1234
1235 if (path.elements()) {
1236 for (int i=0; i<path.elementCount(); ++i) {
1237 qDebug() << " - " << path.elements()[i]
1238 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1239 }
1240 } else {
1241 for (int i=0; i<path.elementCount(); ++i) {
1242 qDebug() << " ---- "
1243 << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
1244 }
1245 }
1246#endif
1247
1248 Q_D(QRasterPaintEngine);
1249 QRasterPaintEngineState *s = state();
1250
1251 // There are some cases that are not supported by clip(QRect)
1252 if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) {
1253 if (s->matrix.type() <= QTransform::TxScale
1254 && path.isRect()) {
1255#ifdef QT_DEBUG_DRAW
1256 qDebug(" --- optimizing vector clip to rect clip...");
1257#endif
1258 const qreal *points = path.points();
1259 QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
1260 if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
1261 return;
1262 }
1263 }
1264
1265 if (op == Qt::NoClip) {
1266 qrasterpaintengine_state_setNoClip(s);
1267
1268 } else {
1269 QClipData *base = d->baseClip.data();
1270
1271 // Intersect with current clip when available...
1272 if (op == Qt::IntersectClip && s->clip)
1273 base = s->clip;
1274
1275 // We always intersect, except when there is nothing to
1276 // intersect with, in which case we simplify the operation to
1277 // a replace...
1278 Qt::ClipOperation isectOp = Qt::IntersectClip;
1279 if (base == 0)
1280 isectOp = Qt::ReplaceClip;
1281
1282 QClipData *newClip = new QClipData(d->rasterBuffer->height());
1283 newClip->initialize();
1284 ClipData clipData = { base, newClip, isectOp };
1285 ensureOutlineMapper();
1286 d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
1287
1288 newClip->fixup();
1289
1290 if (s->flags.has_clip_ownership)
1291 delete s->clip;
1292
1293 s->clip = newClip;
1294 s->flags.has_clip_ownership = true;
1295 }
1296 qrasterpaintengine_dirty_clip(d, s);
1297}
1298
1299
1300
1301/*!
1302 \internal
1303*/
1304void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
1305{
1306#ifdef QT_DEBUG_DRAW
1307 qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
1308#endif
1309
1310 QRasterPaintEngineState *s = state();
1311
1312 if (op == Qt::NoClip) {
1313 qrasterpaintengine_state_setNoClip(s);
1314
1315 } else if (s->matrix.type() > QTransform::TxScale) {
1316 QPaintEngineEx::clip(rect, op);
1317 return;
1318
1319 } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
1320 QPaintEngineEx::clip(rect, op);
1321 return;
1322 }
1323}
1324
1325
1326bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
1327{
1328 Q_D(QRasterPaintEngine);
1329 // normalize before using the & operator which uses QRect::normalize()
1330 // internally which will give us the wrong values.
1331 QRect clipRect = qrect_normalized(r) & d->deviceRect;
1332 QRasterPaintEngineState *s = state();
1333
1334 if (op == Qt::ReplaceClip || s->clip == 0) {
1335
1336 // No current clip, hence we intersect with sysclip and be
1337 // done with it...
1338 QRegion clipRegion = systemClip();
1339 QClipData *clip = new QClipData(d->rasterBuffer->height());
1340
1341 if (clipRegion.isEmpty())
1342 clip->setClipRect(clipRect);
1343 else
1344 clip->setClipRegion(clipRegion & clipRect);
1345
1346 if (s->flags.has_clip_ownership)
1347 delete s->clip;
1348
1349 s->clip = clip;
1350 s->clip->enabled = true;
1351 s->flags.has_clip_ownership = true;
1352
1353 } else if (op == Qt::IntersectClip){ // intersect clip with current clip
1354 QClipData *base = s->clip;
1355
1356 Q_ASSERT(base);
1357 if (base->hasRectClip || base->hasRegionClip) {
1358 if (!s->flags.has_clip_ownership) {
1359 s->clip = new QClipData(d->rasterBuffer->height());
1360 s->flags.has_clip_ownership = true;
1361 }
1362 if (base->hasRectClip)
1363 s->clip->setClipRect(base->clipRect & clipRect);
1364 else
1365 s->clip->setClipRegion(base->clipRegion & clipRect);
1366 s->clip->enabled = true;
1367 } else {
1368 return false;
1369 }
1370 } else {
1371 return false;
1372 }
1373
1374 qrasterpaintengine_dirty_clip(d, s);
1375 return true;
1376}
1377
1378
1379/*!
1380 \internal
1381*/
1382void QRasterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
1383{
1384#ifdef QT_DEBUG_DRAW
1385 qDebug() << "QRasterPaintEngine::clip(): " << region << op;
1386#endif
1387
1388 Q_D(QRasterPaintEngine);
1389
1390 if (region.rectCount() == 1) {
1391 clip(region.boundingRect(), op);
1392 return;
1393 }
1394
1395 QRasterPaintEngineState *s = state();
1396 const QClipData *clip = d->clip();
1397 const QClipData *baseClip = d->baseClip.data();
1398
1399 if (op == Qt::NoClip) {
1400 qrasterpaintengine_state_setNoClip(s);
1401 } else if (s->matrix.type() > QTransform::TxScale
1402 || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
1403 || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
1404 QPaintEngineEx::clip(region, op);
1405 } else {
1406 const QClipData *curClip;
1407 QClipData *newClip;
1408
1409 if (op == Qt::IntersectClip)
1410 curClip = clip;
1411 else
1412 curClip = baseClip;
1413
1414 if (s->flags.has_clip_ownership) {
1415 newClip = s->clip;
1416 Q_ASSERT(newClip);
1417 } else {
1418 newClip = new QClipData(d->rasterBuffer->height());
1419 s->clip = newClip;
1420 s->flags.has_clip_ownership = true;
1421 }
1422
1423 QRegion r = s->matrix.map(region);
1424 if (curClip->hasRectClip)
1425 newClip->setClipRegion(r & curClip->clipRect);
1426 else if (curClip->hasRegionClip)
1427 newClip->setClipRegion(r & curClip->clipRegion);
1428
1429 qrasterpaintengine_dirty_clip(d, s);
1430 }
1431}
1432
1433/*!
1434 \fn const QClipData *QRasterPaintEngine::clipData() const
1435
1436 \internal
1437*/
1438
1439
1440/*!
1441 \internal
1442*/
1443void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
1444{
1445#ifdef QT_DEBUG_DRAW
1446 qDebug() << " --- fillPath, bounds=" << path.boundingRect();
1447#endif
1448
1449 if (!fillData->blend)
1450 return;
1451
1452 Q_D(QRasterPaintEngine);
1453
1454 const QRectF controlPointRect = path.controlPointRect();
1455
1456 QRasterPaintEngineState *s = state();
1457 const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
1458 ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
1459 const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1460 || deviceRect.right() > QT_RASTER_COORD_LIMIT
1461 || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1462 || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1463
1464 if (!s->flags.antialiased && !do_clip) {
1465 d->initializeRasterizer(fillData);
1466 d->rasterizer->rasterize(path * s->matrix, path.fillRule());
1467 return;
1468 }
1469
1470 ensureOutlineMapper();
1471 d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
1472}
1473
1474static void fillRect_normalized(const QRect &r, QSpanData *data,
1475 QRasterPaintEnginePrivate *pe)
1476{
1477 int x1, x2, y1, y2;
1478
1479 bool rectClipped = true;
1480
1481 if (data->clip) {
1482 x1 = qMax(r.x(), data->clip->xmin);
1483 x2 = qMin(r.x() + r.width(), data->clip->xmax);
1484 y1 = qMax(r.y(), data->clip->ymin);
1485 y2 = qMin(r.y() + r.height(), data->clip->ymax);
1486 rectClipped = data->clip->hasRectClip;
1487
1488 } else if (pe) {
1489 x1 = qMax(r.x(), pe->deviceRect.x());
1490 x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
1491 y1 = qMax(r.y(), pe->deviceRect.y());
1492 y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
1493 } else {
1494 x1 = qMax(r.x(), 0);
1495 x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
1496 y1 = qMax(r.y(), 0);
1497 y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
1498 }
1499
1500 if (x2 <= x1 || y2 <= y1)
1501 return;
1502
1503 const int width = x2 - x1;
1504 const int height = y2 - y1;
1505
1506 bool isUnclipped = rectClipped
1507 || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
1508
1509 if (pe && isUnclipped) {
1510 const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
1511
1512 if (data->fillRect && (mode == QPainter::CompositionMode_Source
1513 || (mode == QPainter::CompositionMode_SourceOver
1514 && data->solidColor.isOpaque())))
1515 {
1516 data->fillRect(data->rasterBuffer, x1, y1, width, height, data->solidColor);
1517 return;
1518 }
1519 }
1520
1521 ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
1522
1523 const int nspans = 256;
1524 QT_FT_Span spans[nspans];
1525
1526 Q_ASSERT(data->blend);
1527 int y = y1;
1528 while (y < y2) {
1529 int n = qMin(nspans, y2 - y);
1530 int i = 0;
1531 while (i < n) {
1532 spans[i].x = x1;
1533 spans[i].len = width;
1534 spans[i].y = y + i;
1535 spans[i].coverage = 255;
1536 ++i;
1537 }
1538
1539 blend(n, spans, data);
1540 y += n;
1541 }
1542}
1543
1544/*!
1545 \reimp
1546*/
1547void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
1548{
1549#ifdef QT_DEBUG_DRAW
1550 qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
1551#endif
1552 Q_D(QRasterPaintEngine);
1553 ensureRasterState();
1554 QRasterPaintEngineState *s = state();
1555
1556 // Fill
1557 ensureBrush();
1558 if (s->brushData.blend) {
1559 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
1560 const QRect *r = rects;
1561 const QRect *lastRect = rects + rectCount;
1562
1563 int offset_x = int(s->matrix.dx());
1564 int offset_y = int(s->matrix.dy());
1565 while (r < lastRect) {
1566 QRect rect = qrect_normalized(*r);
1567 QRect rr = rect.translated(offset_x, offset_y);
1568 fillRect_normalized(rr, &s->brushData, d);
1569 ++r;
1570 }
1571 } else {
1572 QRectVectorPath path;
1573 for (int i=0; i<rectCount; ++i) {
1574 path.set(rects[i]);
1575 fill(path, s->brush);
1576 }
1577 }
1578 }
1579
1580 ensurePen();
1581 if (s->penData.blend) {
1582 QRectVectorPath path;
1583 if (s->flags.fast_pen) {
1584 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1585 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1586 for (int i = 0; i < rectCount; ++i) {
1587 path.set(rects[i]);
1588 stroker.drawPath(path);
1589 }
1590 } else {
1591 for (int i = 0; i < rectCount; ++i) {
1592 path.set(rects[i]);
1593 stroke(path, s->pen);
1594 }
1595 }
1596 }
1597}
1598
1599/*!
1600 \reimp
1601*/
1602void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
1603{
1604#ifdef QT_DEBUG_DRAW
1605 qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1606#endif
1607#ifdef QT_FAST_SPANS
1608 Q_D(QRasterPaintEngine);
1609 ensureRasterState();
1610 QRasterPaintEngineState *s = state();
1611
1612
1613 if (s->flags.tx_noshear) {
1614 ensureBrush();
1615 if (s->brushData.blend) {
1616 d->initializeRasterizer(&s->brushData);
1617 for (int i = 0; i < rectCount; ++i) {
1618 const QRectF &rect = rects[i].normalized();
1619 if (rect.isEmpty())
1620 continue;
1621 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
1622 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
1623 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
1624 }
1625 }
1626
1627 ensurePen();
1628 if (s->penData.blend) {
1629 QRectVectorPath path;
1630 if (s->flags.fast_pen) {
1631 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1632 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1633 for (int i = 0; i < rectCount; ++i) {
1634 path.set(rects[i]);
1635 stroker.drawPath(path);
1636 }
1637 } else {
1638 for (int i = 0; i < rectCount; ++i) {
1639 path.set(rects[i]);
1640 QPaintEngineEx::stroke(path, s->lastPen);
1641 }
1642 }
1643 }
1644
1645 return;
1646 }
1647#endif // QT_FAST_SPANS
1648 QPaintEngineEx::drawRects(rects, rectCount);
1649}
1650
1651
1652/*!
1653 \internal
1654*/
1655void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1656{
1657 Q_D(QRasterPaintEngine);
1658 QRasterPaintEngineState *s = state();
1659
1660 ensurePen(pen);
1661 if (!s->penData.blend)
1662 return;
1663
1664 if (s->flags.fast_pen) {
1665 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1666 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
1667 stroker.drawPath(path);
1668 } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
1669 qreal width = qt_pen_is_cosmetic(s->lastPen, s->renderHints)
1670 ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
1671 : qpen_widthf(s->lastPen) * s->txscale;
1672 int dashIndex = 0;
1673 qreal dashOffset = s->lastPen.dashOffset();
1674 bool inDash = true;
1675 qreal patternLength = 0;
1676 const QVector<qreal> pattern = s->lastPen.dashPattern();
1677 for (int i = 0; i < pattern.size(); ++i)
1678 patternLength += pattern.at(i);
1679
1680 if (patternLength > 0) {
1681 int n = qFloor(dashOffset / patternLength);
1682 dashOffset -= n * patternLength;
1683 while (dashOffset >= pattern.at(dashIndex)) {
1684 dashOffset -= pattern.at(dashIndex);
1685 if (++dashIndex >= pattern.size())
1686 dashIndex = 0;
1687 inDash = !inDash;
1688 }
1689 }
1690
1691 Q_D(QRasterPaintEngine);
1692 d->initializeRasterizer(&s->penData);
1693 int lineCount = path.elementCount() / 2;
1694 const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
1695
1696 for (int i = 0; i < lineCount; ++i) {
1697 if (lines[i].p1() == lines[i].p2()) {
1698 if (s->lastPen.capStyle() != Qt::FlatCap) {
1699 QPointF p = lines[i].p1();
1700 QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
1701 QPointF(p.x() + width*0.5, p.y())));
1702 d->rasterizer->rasterizeLine(line.p1(), line.p2(), width / line.length());
1703 }
1704 continue;
1705 }
1706
1707 const QLineF line = s->matrix.map(lines[i]);
1708 if (qpen_style(s->lastPen) == Qt::SolidLine) {
1709 d->rasterizer->rasterizeLine(line.p1(), line.p2(),
1710 width / line.length(),
1711 s->lastPen.capStyle() == Qt::SquareCap);
1712 } else {
1713 d->rasterizeLine_dashed(line, width,
1714 &dashIndex, &dashOffset, &inDash);
1715 }
1716 }
1717 }
1718 else
1719 QPaintEngineEx::stroke(path, pen);
1720}
1721
1722QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
1723{
1724 QRasterPaintEngineState *s = state();
1725
1726 qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
1727
1728 int x1 = qRound(rect.x() + delta);
1729 int y1 = qRound(rect.y() + delta);
1730 int x2 = qRound(rect.right() + delta);
1731 int y2 = qRound(rect.bottom() + delta);
1732
1733 if (x2 < x1)
1734 qSwap(x1, x2);
1735 if (y2 < y1)
1736 qSwap(y1, y2);
1737
1738 return QRect(x1, y1, x2 - x1, y2 - y1);
1739}
1740
1741/*!
1742 \internal
1743*/
1744void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1745{
1746 if (path.isEmpty())
1747 return;
1748#ifdef QT_DEBUG_DRAW
1749 QRectF rf = path.controlPointRect();
1750 qDebug() << "QRasterPaintEngine::fill(): "
1751 << "size=" << path.elementCount()
1752 << ", hints=" << Qt::hex << path.hints()
1753 << rf << brush;
1754#endif
1755
1756 Q_D(QRasterPaintEngine);
1757 QRasterPaintEngineState *s = state();
1758
1759 ensureBrush(brush);
1760 if (!s->brushData.blend)
1761 return;
1762
1763 if (path.shape() == QVectorPath::RectangleHint) {
1764 if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
1765 const qreal *p = path.points();
1766 QPointF tl = QPointF(p[0], p[1]) * s->matrix;
1767 QPointF br = QPointF(p[4], p[5]) * s->matrix;
1768 fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
1769 return;
1770 }
1771 ensureRasterState();
1772 if (s->flags.tx_noshear) {
1773 d->initializeRasterizer(&s->brushData);
1774 // ### Is normalizing really necessary here?
1775 const qreal *p = path.points();
1776 QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
1777 if (!r.isEmpty()) {
1778 const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
1779 const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
1780 d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
1781 }
1782 return;
1783 }
1784 }
1785
1786 // ### Optimize for non transformed ellipses and rectangles...
1787 QRectF cpRect = path.controlPointRect();
1788 const QRect pathDeviceRect = s->matrix.mapRect(cpRect).toRect();
1789 // Skip paths that by conservative estimates are completely outside the paint device.
1790 if (!pathDeviceRect.intersects(d->deviceRect))
1791 return;
1792
1793 ProcessSpans blend = d->getBrushFunc(pathDeviceRect, &s->brushData);
1794
1795 // ### Falcon
1796// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
1797// || deviceRect.right() > QT_RASTER_COORD_LIMIT
1798// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
1799// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
1800
1801 // ### Falonc: implement....
1802// if (!s->flags.antialiased && !do_clip) {
1803// d->initializeRasterizer(&s->brushData);
1804// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
1805// return;
1806// }
1807
1808 ensureOutlineMapper();
1809 d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
1810}
1811
1812void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
1813{
1814 Q_D(QRasterPaintEngine);
1815 QRasterPaintEngineState *s = state();
1816
1817 if (!s->flags.antialiased) {
1818 uint txop = s->matrix.type();
1819 if (txop == QTransform::TxNone) {
1820 fillRect_normalized(toNormalizedFillRect(r), data, d);
1821 return;
1822 } else if (txop == QTransform::TxTranslate) {
1823 const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
1824 fillRect_normalized(rr, data, d);
1825 return;
1826 } else if (txop == QTransform::TxScale) {
1827 const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
1828 fillRect_normalized(rr, data, d);
1829 return;
1830 }
1831 }
1832 ensureRasterState();
1833 if (s->flags.tx_noshear) {
1834 d->initializeRasterizer(data);
1835 QRectF nr = r.normalized();
1836 if (!nr.isEmpty()) {
1837 const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1838 const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1839 d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1840 }
1841 return;
1842 }
1843
1844 QPainterPath path;
1845 path.addRect(r);
1846 ensureOutlineMapper();
1847 fillPath(path, data);
1848}
1849
1850/*!
1851 \reimp
1852*/
1853void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1854{
1855#ifdef QT_DEBUG_DRAW
1856 qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1857#endif
1858 QRasterPaintEngineState *s = state();
1859
1860 ensureBrush(brush);
1861 if (!s->brushData.blend)
1862 return;
1863
1864 fillRect(r, &s->brushData);
1865}
1866
1867/*!
1868 \reimp
1869*/
1870void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1871{
1872#ifdef QT_DEBUG_DRAW
1873 qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1874#endif
1875 Q_D(QRasterPaintEngine);
1876 QRasterPaintEngineState *s = state();
1877
1878 d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(color.rgba64(), s->intOpacity));
1879
1880 if (d->solid_color_filler.solidColor.isTransparent()
1881 && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1882 return;
1883 }
1884 d->solid_color_filler.clip = d->clip();
1885 d->solid_color_filler.adjustSpanMethods();
1886 fillRect(r, &d->solid_color_filler);
1887}
1888
1889static inline bool isAbove(const QPointF *a, const QPointF *b)
1890{
1891 return a->y() < b->y();
1892}
1893
1894static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
1895{
1896 Q_ASSERT(upper);
1897 Q_ASSERT(lower);
1898
1899 Q_ASSERT(pointCount >= 2);
1900
1901 QVector<const QPointF *> sorted;
1902 sorted.reserve(pointCount);
1903
1904 upper->reserve(pointCount * 3 / 4);
1905 lower->reserve(pointCount * 3 / 4);
1906
1907 for (int i = 0; i < pointCount; ++i)
1908 sorted << points + i;
1909
1910 std::sort(sorted.begin(), sorted.end(), isAbove);
1911
1912 qreal splitY = sorted.at(sorted.size() / 2)->y();
1913
1914 const QPointF *end = points + pointCount;
1915 const QPointF *last = end - 1;
1916
1917 QVector<QPointF> *bin[2] = { upper, lower };
1918
1919 for (const QPointF *p = points; p < end; ++p) {
1920 int side = p->y() < splitY;
1921 int lastSide = last->y() < splitY;
1922
1923 if (side != lastSide) {
1924 if (qFuzzyCompare(p->y(), splitY)) {
1925 bin[!side]->append(*p);
1926 } else if (qFuzzyCompare(last->y(), splitY)) {
1927 bin[side]->append(*last);
1928 } else {
1929 QPointF delta = *p - *last;
1930 QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1931
1932 bin[0]->append(intersection);
1933 bin[1]->append(intersection);
1934 }
1935 }
1936
1937 bin[side]->append(*p);
1938
1939 last = p;
1940 }
1941
1942 // give up if we couldn't reduce the point count
1943 return upper->size() < pointCount && lower->size() < pointCount;
1944}
1945
1946/*!
1947 \internal
1948 */
1949void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1950{
1951 Q_D(QRasterPaintEngine);
1952 QRasterPaintEngineState *s = state();
1953
1954 const int maxPoints = 0xffff;
1955
1956 // max amount of points that raster engine can reliably handle
1957 if (pointCount > maxPoints) {
1958 QVector<QPointF> upper, lower;
1959
1960 if (splitPolygon(points, pointCount, &upper, &lower)) {
1961 fillPolygon(upper.constData(), upper.size(), mode);
1962 fillPolygon(lower.constData(), lower.size(), mode);
1963 } else
1964 qWarning("Polygon too complex for filling.");
1965
1966 return;
1967 }
1968
1969 // Compose polygon fill..,
1970 QVectorPath vp((const qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
1971 ensureOutlineMapper();
1972 QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1973
1974 // scanconvert.
1975 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1976 &s->brushData);
1977 d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1978}
1979
1980/*!
1981 \reimp
1982*/
1983void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1984{
1985 Q_D(QRasterPaintEngine);
1986 QRasterPaintEngineState *s = state();
1987
1988#ifdef QT_DEBUG_DRAW
1989 qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1990 for (int i=0; i<pointCount; ++i)
1991 qDebug() << " - " << points[i];
1992#endif
1993 Q_ASSERT(pointCount >= 2);
1994
1995 if (mode != PolylineMode && QVectorPath::isRect((const qreal *) points, pointCount)) {
1996 QRectF r(points[0], points[2]);
1997 drawRects(&r, 1);
1998 return;
1999 }
2000
2001 ensurePen();
2002 if (mode != PolylineMode) {
2003 // Do the fill...
2004 ensureBrush();
2005 if (s->brushData.blend)
2006 fillPolygon(points, pointCount, mode);
2007 }
2008
2009 // Do the outline...
2010 if (s->penData.blend) {
2011 QVectorPath vp((const qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
2012 if (s->flags.fast_pen) {
2013 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
2014 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2015 stroker.drawPath(vp);
2016 } else {
2017 QPaintEngineEx::stroke(vp, s->lastPen);
2018 }
2019 }
2020}
2021
2022/*!
2023 \reimp
2024*/
2025void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
2026{
2027 Q_D(QRasterPaintEngine);
2028 QRasterPaintEngineState *s = state();
2029
2030#ifdef QT_DEBUG_DRAW
2031 qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
2032 for (int i=0; i<pointCount; ++i)
2033 qDebug() << " - " << points[i];
2034#endif
2035 Q_ASSERT(pointCount >= 2);
2036 if (mode != PolylineMode && QVectorPath::isRect((const int *) points, pointCount)) {
2037 QRect r(points[0].x(),
2038 points[0].y(),
2039 points[2].x() - points[0].x(),
2040 points[2].y() - points[0].y());
2041 drawRects(&r, 1);
2042 return;
2043 }
2044
2045 ensurePen();
2046
2047 // Do the fill
2048 if (mode != PolylineMode) {
2049 ensureBrush();
2050 if (s->brushData.blend) {
2051 // Compose polygon fill..,
2052 ensureOutlineMapper();
2053 d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
2054 d->outlineMapper->moveTo(*points);
2055 const QPoint *p = points;
2056 const QPoint *ep = points + pointCount - 1;
2057 do {
2058 d->outlineMapper->lineTo(*(++p));
2059 } while (p < ep);
2060 d->outlineMapper->endOutline();
2061
2062 // scanconvert.
2063 ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
2064 &s->brushData);
2065 d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
2066 }
2067 }
2068
2069 // Do the outline...
2070 if (s->penData.blend) {
2071 int count = pointCount * 2;
2072 QVarLengthArray<qreal> fpoints(count);
2073 for (int i=0; i<count; ++i)
2074 fpoints[i] = ((const int *) points)[i];
2075 QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
2076
2077 if (s->flags.fast_pen) {
2078 QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
2079 stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
2080 stroker.drawPath(vp);
2081 } else {
2082 QPaintEngineEx::stroke(vp, s->lastPen);
2083 }
2084 }
2085}
2086
2087/*!
2088 \internal
2089*/
2090void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
2091{
2092#ifdef QT_DEBUG_DRAW
2093 qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2094#endif
2095
2096 QPlatformPixmap *pd = pixmap.handle();
2097 if (pd->classId() == QPlatformPixmap::RasterClass) {
2098 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2099 if (image.depth() == 1) {
2100 Q_D(QRasterPaintEngine);
2101 QRasterPaintEngineState *s = state();
2102 if (s->matrix.type() <= QTransform::TxTranslate) {
2103 ensurePen();
2104 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2105 } else {
2106 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2107 }
2108 } else {
2109 QRasterPaintEngine::drawImage(pos, image);
2110 }
2111 } else {
2112 const QImage image = pixmap.toImage();
2113 if (pixmap.depth() == 1) {
2114 Q_D(QRasterPaintEngine);
2115 QRasterPaintEngineState *s = state();
2116 if (s->matrix.type() <= QTransform::TxTranslate) {
2117 ensurePen();
2118 drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2119 } else {
2120 drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2121 }
2122 } else {
2123 QRasterPaintEngine::drawImage(pos, image);
2124 }
2125 }
2126}
2127
2128/*!
2129 \reimp
2130*/
2131void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2132{
2133#ifdef QT_DEBUG_DRAW
2134 qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2135#endif
2136
2137 QPlatformPixmap* pd = pixmap.handle();
2138 if (pd->classId() == QPlatformPixmap::RasterClass) {
2139 const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2140 if (image.depth() == 1) {
2141 Q_D(QRasterPaintEngine);
2142 QRasterPaintEngineState *s = state();
2143 if (s->matrix.type() <= QTransform::TxTranslate
2144 && r.size() == sr.size()
2145 && r.size() == pixmap.size()) {
2146 ensurePen();
2147 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2148 return;
2149 } else {
2150 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2151 }
2152 } else {
2153 drawImage(r, image, sr);
2154 }
2155 } else {
2156 QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2157 const QImage image = pd->toImage(clippedSource);
2158 QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2159 if (image.depth() == 1) {
2160 Q_D(QRasterPaintEngine);
2161 QRasterPaintEngineState *s = state();
2162 if (s->matrix.type() <= QTransform::TxTranslate
2163 && r.size() == sr.size()
2164 && r.size() == pixmap.size()) {
2165 ensurePen();
2166 drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2167 return;
2168 } else {
2169 drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2170 }
2171 } else {
2172 drawImage(r, image, translatedSource);
2173 }
2174 }
2175}
2176
2177static inline int fast_ceil_positive(const qreal &v)
2178{
2179 const int iv = int(v);
2180 if (v - iv == 0)
2181 return iv;
2182 else
2183 return iv + 1;
2184}
2185
2186static inline const QRect toAlignedRect_positive(const QRectF &rect)
2187{
2188 const int xmin = int(rect.x());
2189 const int xmax = int(fast_ceil_positive(rect.right()));
2190 const int ymin = int(rect.y());
2191 const int ymax = int(fast_ceil_positive(rect.bottom()));
2192 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2193}
2194
2195/*!
2196 \internal
2197*/
2198void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2199{
2200#ifdef QT_DEBUG_DRAW
2201 qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
2202#endif
2203
2204 Q_D(QRasterPaintEngine);
2205 QRasterPaintEngineState *s = state();
2206 qreal scale = img.devicePixelRatio();
2207
2208 if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
2209 drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2210 img,
2211 QRectF(0, 0, img.width(), img.height()));
2212 } else {
2213
2214 const QClipData *clip = d->clip();
2215 QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2216
2217 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img)) {
2218 if (!clip) {
2219 d->blitImage(pt, img, d->deviceRect);
2220 return;
2221 } else if (clip->hasRectClip) {
2222 d->blitImage(pt, img, clip->clipRect);
2223 return;
2224 }
2225 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2226 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2227 if (func) {
2228 if (!clip) {
2229 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2230 return;
2231 } else if (clip->hasRectClip) {
2232 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2233 return;
2234 }
2235 }
2236 }
2237
2238
2239
2240 d->image_filler.clip = clip;
2241 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2242 if (!d->image_filler.blend)
2243 return;
2244 d->image_filler.dx = -pt.x();
2245 d->image_filler.dy = -pt.y();
2246 QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2247
2248 fillRect_normalized(rr, &d->image_filler, d);
2249 }
2250
2251}
2252
2253QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2254{
2255 return QRectF(r.topLeft() * t, r.bottomRight() * t);
2256}
2257
2258namespace {
2259 enum RotationType {
2260 Rotation90,
2261 Rotation180,
2262 Rotation270,
2263 NoRotation
2264 };
2265
2266 inline RotationType qRotationType(const QTransform &transform)
2267 {
2268 QTransform::TransformationType type = transform.type();
2269
2270 if (type > QTransform::TxRotate)
2271 return NoRotation;
2272
2273 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2274 && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2275 return Rotation90;
2276
2277 if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2278 && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2279 return Rotation180;
2280
2281 if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2282 && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2283 return Rotation270;
2284
2285 return NoRotation;
2286 }
2287
2288 inline bool isPixelAligned(const QRectF &rect) {
2289 return QRectF(rect.toRect()) == rect;
2290 }
2291}
2292
2293/*!
2294 \reimp
2295*/
2296void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2297 Qt::ImageConversionFlags)
2298{
2299#ifdef QT_DEBUG_DRAW
2300 qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2301#endif
2302
2303 if (r.isEmpty())
2304 return;
2305
2306 Q_D(QRasterPaintEngine);
2307 QRasterPaintEngineState *s = state();
2308 Q_ASSERT(s);
2309 int sr_l = qFloor(sr.left());
2310 int sr_r = qCeil(sr.right()) - 1;
2311 int sr_t = qFloor(sr.top());
2312 int sr_b = qCeil(sr.bottom()) - 1;
2313
2314 if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2315 // as fillRect will apply the aliased coordinate delta we need to
2316 // subtract it here as we don't use it for image drawing
2317 QTransform old = s->matrix;
2318
2319 if (s->flags.legacy_rounding)
2320 s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
2321
2322 // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2323 QRgb color = img.pixel(sr_l, sr_t);
2324 switch (img.format()) {
2325 case QImage::Format_ARGB32_Premultiplied:
2326 case QImage::Format_ARGB8565_Premultiplied:
2327 case QImage::Format_ARGB6666_Premultiplied:
2328 case QImage::Format_ARGB8555_Premultiplied:
2329 case QImage::Format_ARGB4444_Premultiplied:
2330 case QImage::Format_RGBA8888_Premultiplied:
2331 case QImage::Format_A2BGR30_Premultiplied:
2332 case QImage::Format_A2RGB30_Premultiplied:
2333 // Combine premultiplied color with the opacity set on the painter.
2334 d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity);
2335 break;
2336 default:
2337 d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity));
2338 break;
2339 }
2340
2341 if (d->solid_color_filler.solidColor.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver)
2342 return;
2343
2344 d->solid_color_filler.clip = d->clip();
2345 d->solid_color_filler.adjustSpanMethods();
2346 fillRect(r, &d->solid_color_filler);
2347
2348 s->matrix = old;
2349 return;
2350 }
2351
2352 bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2353
2354 const QClipData *clip = d->clip();
2355
2356 if (s->matrix.type() > QTransform::TxTranslate
2357 && !stretch_sr
2358 && (!clip || clip->hasRectClip)
2359 && s->intOpacity == 256
2360 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2361 || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
2362 && d->rasterBuffer->format == img.format()
2363 && (d->rasterBuffer->format == QImage::Format_RGB16
2364 || d->rasterBuffer->format == QImage::Format_RGB32
2365 || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
2366 && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
2367 {
2368 RotationType rotationType = qRotationType(s->matrix);
2369 const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2370
2371 if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2372 QRectF transformedTargetRect = s->matrix.mapRect(r);
2373
2374 if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
2375 || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
2376 {
2377 QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2378 if (clippedTransformedTargetRect.isNull())
2379 return;
2380
2381 QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2382
2383 QRect clippedSourceRect
2384 = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2385 clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2386
2387 clippedSourceRect = clippedSourceRect.intersected(img.rect());
2388
2389 const qsizetype dbpl = d->rasterBuffer->bytesPerLine();
2390 const qsizetype sbpl = img.bytesPerLine();
2391
2392 uchar *dst = d->rasterBuffer->buffer();
2393 uint bpp = img.depth() >> 3;
2394
2395 const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2396 uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2397
2398 uint cw = clippedSourceRect.width();
2399 uint ch = clippedSourceRect.height();
2400
2401 qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2402
2403 return;
2404 }
2405 }
2406 }
2407
2408 if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2409
2410 QRectF targetBounds = s->matrix.mapRect(r);
2411 bool exceedsPrecision = r.width() > 0x7fff
2412 || r.height() > 0x7fff
2413 || targetBounds.width() > 0x7fff
2414 || targetBounds.height() > 0x7fff
2415 || s->matrix.m11() >= 512
2416 || s->matrix.m22() >= 512;
2417
2418 if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2419 if (s->matrix.type() > QTransform::TxScale) {
2420 SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2421 if (func && (!clip || clip->hasRectClip)) {
2422 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2423 img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2424 s->matrix, s->intOpacity);
2425 return;
2426 }
2427 } else {
2428 // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2429 bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2430 bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2431 if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2432 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2433 if (func) {
2434 QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2435 if (!clip) {
2436 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2437 return;
2438 } else if (clip->hasRectClip) {
2439 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2440 return;
2441 }
2442 }
2443 }
2444 SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2445 if (func && (!clip || clip->hasRectClip)) {
2446 func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2447 img.bits(), img.bytesPerLine(), img.height(),
2448 qt_mapRect_non_normalizing(r, s->matrix), sr,
2449 !clip ? d->deviceRect : clip->clipRect,
2450 s->intOpacity);
2451 return;
2452 }
2453 }
2454 }
2455
2456 QTransform copy = s->matrix;
2457 copy.translate(r.x(), r.y());
2458 if (stretch_sr)
2459 copy.scale(r.width() / sr.width(), r.height() / sr.height());
2460 copy.translate(-sr.x(), -sr.y());
2461
2462 d->image_filler_xform.clip = clip;
2463 d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2464 if (!d->image_filler_xform.blend)
2465 return;
2466 d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2467
2468 if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2469 QRectF rr = s->matrix.mapRect(r);
2470
2471 const int x1 = qRound(rr.x());
2472 const int y1 = qRound(rr.y());
2473 const int x2 = qRound(rr.right());
2474 const int y2 = qRound(rr.bottom());
2475
2476 fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2477 return;
2478 }
2479
2480#ifdef QT_FAST_SPANS
2481 ensureRasterState();
2482 if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2483 d->initializeRasterizer(&d->image_filler_xform);
2484 d->rasterizer->setAntialiased(s->flags.antialiased);
2485 d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
2486
2487 const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
2488
2489 const QRectF &rect = r.normalized();
2490 const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
2491 const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f) - offs;
2492
2493 if (s->flags.tx_noshear)
2494 d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2495 else
2496 d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2497 return;
2498 }
2499#endif
2500 const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
2501 QPainterPath path;
2502 path.addRect(r);
2503 QTransform m = s->matrix;
2504 s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2505 m.m21(), m.m22(), m.m23(),
2506 m.m31() - offs, m.m32() - offs, m.m33());
2507 fillPath(path, &d->image_filler_xform);
2508 s->matrix = m;
2509 } else {
2510 if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img)) {
2511 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2512 if (!clip) {
2513 d->blitImage(pt, img, d->deviceRect, sr.toRect());
2514 return;
2515 } else if (clip->hasRectClip) {
2516 d->blitImage(pt, img, clip->clipRect, sr.toRect());
2517 return;
2518 }
2519 } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2520 SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2521 if (func) {
2522 QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2523 if (!clip) {
2524 d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2525 return;
2526 } else if (clip->hasRectClip) {
2527 d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2528 return;
2529 }
2530 }
2531 }
2532
2533 d->image_filler.clip = clip;
2534 d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2535 if (!d->image_filler.blend)
2536 return;
2537 d->image_filler.dx = -(r.x() + s->matrix.