Coverage Report

Created: 2026-05-16 07:21

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