Coverage Report

Created: 2025-07-12 07:23

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