Coverage Report

Created: 2026-11-12 06:50

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
0
    inline void set(const QRectF &r) {
70
0
        qreal left = r.x();
71
0
        qreal right = r.x() + r.width();
72
0
        qreal top = r.y();
73
0
        qreal bottom = r.y() + r.height();
74
0
        pts[0] = left;
75
0
        pts[1] = top;
76
0
        pts[2] = right;
77
0
        pts[3] = top;
78
0
        pts[4] = right;
79
0
        pts[5] = bottom;
80
0
        pts[6] = left;
81
0
        pts[7] = bottom;
82
0
    }
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
0
        : QVectorPath(pts, 4, nullptr, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
100
0
    { }
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
0
    QPaintEngineExPrivate(),
243
0
    cachedLines(0)
244
0
{
245
0
}
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
0
    : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
294
0
{
295
0
    d_func()->device = device;
296
0
    init();
297
0
}
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
0
{
311
0
    Q_D(QRasterPaintEngine);
312
313
314
#ifdef Q_OS_WIN
315
    d->hdc = 0;
316
#endif
317
318
    // The antialiasing raster.
319
0
    d->grayRaster.reset(new QT_FT_Raster);
320
0
    Q_CHECK_PTR(d->grayRaster.data());
321
0
    if (QT_MANGLE_NAMESPACE(qt_ft_grays_raster).raster_new(d->grayRaster.data()))
322
0
        QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
323
324
325
0
    d->rasterizer.reset(new QRasterizer);
326
0
    d->rasterBuffer.reset(new QRasterBuffer());
327
0
    d->outlineMapper.reset(new QOutlineMapper);
328
0
    d->outlinemapper_xform_dirty = true;
329
330
0
    d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
331
0
    d->basicStroker.setLineToHook(qt_ft_outline_line_to);
332
0
    d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
333
334
0
    d->baseClip.reset(new QClipData(d->device->height()));
335
0
    d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
336
337
0
    d->image_filler.init(d->rasterBuffer.data(), this);
338
0
    d->image_filler.type = QSpanData::Texture;
339
340
0
    d->image_filler_xform.init(d->rasterBuffer.data(), this);
341
0
    d->image_filler_xform.type = QSpanData::Texture;
342
343
0
    d->solid_color_filler.init(d->rasterBuffer.data(), this);
344
0
    d->solid_color_filler.type = QSpanData::Solid;
345
346
0
    d->deviceDepth = d->device->depth();
347
348
0
    d->mono_surface = false;
349
0
    gccaps &= ~PorterDuff;
350
351
0
    QImage::Format format = QImage::Format_Invalid;
352
353
0
    switch (d->device->devType()) {
354
0
    case QInternal::Pixmap:
355
0
        qWarning("QRasterPaintEngine: unsupported for pixmaps...");
356
0
        break;
357
0
    case QInternal::Image:
358
0
        format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
359
0
        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
0
    }
365
366
0
    switch (format) {
367
0
    case QImage::Format_MonoLSB:
368
0
    case QImage::Format_Mono:
369
0
        d->mono_surface = true;
370
0
        break;
371
0
    default:
372
0
        if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha)
373
0
            gccaps |= PorterDuff;
374
0
        break;
375
0
    }
376
0
}
377
378
379
/*!
380
    Destroys this paint engine.
381
*/
382
QRasterPaintEngine::~QRasterPaintEngine()
383
0
{
384
0
    Q_D(QRasterPaintEngine);
385
386
0
    QT_MANGLE_NAMESPACE(qt_ft_grays_raster).raster_done(*d->grayRaster.data());
387
0
}
388
389
/*!
390
    \reimp
391
*/
392
bool QRasterPaintEngine::begin(QPaintDevice *device)
393
0
{
394
0
    Q_D(QRasterPaintEngine);
395
396
0
    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
0
    } else {
402
0
        d->device = device;
403
0
    }
404
405
    // Make sure QPaintEngine::paintDevice() returns the proper device.
406
0
    d->pdev = d->device;
407
408
0
    Q_ASSERT(d->device->devType() == QInternal::Image
409
0
             || d->device->devType() == QInternal::CustomRaster);
410
411
0
    d->systemStateChanged();
412
413
0
    QRasterPaintEngineState *s = state();
414
0
    ensureOutlineMapper();
415
0
    d->outlineMapper->setClipRect(d->deviceRect);
416
0
    d->rasterizer->setClipRect(d->deviceRect);
417
418
0
    s->penData.init(d->rasterBuffer.data(), this);
419
0
    s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);
420
0
    s->stroker = &d->basicStroker;
421
0
    d->basicStroker.setClipRect(d->deviceRect);
422
423
0
    s->brushData.init(d->rasterBuffer.data(), this);
424
0
    s->brushData.setup(s->brush, s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);
425
426
0
    d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
427
428
0
    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
0
    if (d->mono_surface)
440
0
        d->glyphCacheFormat = QFontEngine::Format_Mono;
441
#if defined(Q_OS_WIN)
442
    else if (clearTypeFontsEnabled())
443
#else
444
0
    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
0
        d->glyphCacheFormat = QFontEngine::Format_A8;
454
455
0
    setActive(true);
456
0
    return true;
457
0
}
458
459
/*!
460
    \reimp
461
*/
462
bool QRasterPaintEngine::end()
463
0
{
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
0
    return true;
473
0
}
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
0
{
492
0
    if (flags.has_clip_ownership)
493
0
        delete clip;
494
0
}
495
496
497
QRasterPaintEngineState::QRasterPaintEngineState()
498
0
{
499
0
    stroker = nullptr;
500
501
0
    fillFlags = 0;
502
0
    strokeFlags = 0;
503
0
    pixmapFlags = 0;
504
505
0
    intOpacity = 256;
506
507
0
    txscale = 1.;
508
509
0
    flag_bits = 0;
510
0
    flags.fast_pen = true;
511
0
    flags.non_complex_pen = false;
512
0
    flags.antialiased = false;
513
0
    flags.bilinear = false;
514
0
    flags.fast_text = true;
515
0
    flags.tx_noshear = true;
516
0
    flags.fast_images = true;
517
0
    flags.cosmetic_brush = true;
518
519
0
    clip = nullptr;
520
0
    flags.has_clip_ownership = false;
521
522
0
    dirty = 0;
523
0
}
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
0
{
551
0
    QRasterPaintEngineState *s;
552
0
    if (!orig)
553
0
        s = new QRasterPaintEngineState();
554
0
    else
555
0
        s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
556
557
0
    return s;
558
0
}
559
560
/*!
561
    \internal
562
*/
563
void QRasterPaintEngine::setState(QPainterState *s)
564
0
{
565
0
    Q_D(QRasterPaintEngine);
566
0
    QPaintEngineEx::setState(s);
567
0
    QRasterPaintEngineState *t = state();
568
0
    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
0
    d->rasterBuffer->compositionMode = s->composition_mode;
573
0
}
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
0
{
604
0
    Q_D(QRasterPaintEngine);
605
0
    QRasterPaintEngineState *s = state();
606
#ifdef QT_DEBUG_DRAW
607
    qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
608
#endif
609
610
0
    Qt::PenStyle pen_style = qpen_style(pen);
611
612
0
    s->lastPen = pen;
613
0
    s->strokeFlags = 0;
614
615
0
    s->penData.clip = d->clip();
616
0
    s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity,
617
0
                     s->composition_mode, s->flags.cosmetic_brush);
618
619
0
    if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
620
0
        || pen.brush().transform().type() >= QTransform::TxNone) {
621
0
        d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
622
0
    }
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
0
    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
0
    d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
633
0
    d->basicStroker.setCapStyle(qpen_capStyle(pen));
634
0
    d->basicStroker.setMiterLimit(pen.miterLimit());
635
636
0
    qreal penWidth = qpen_widthf(pen);
637
0
    if (penWidth == 0)
638
0
        d->basicStroker.setStrokeWidth(1);
639
0
    else
640
0
        d->basicStroker.setStrokeWidth(penWidth);
641
642
0
    if (pen_style == Qt::SolidLine) {
643
0
        s->stroker = &d->basicStroker;
644
0
    } 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
0
    ensureRasterState(); // needed because of tx_noshear...
662
0
    bool cosmetic = pen.isCosmetic();
663
0
    s->flags.fast_pen = pen_style > Qt::NoPen
664
0
            && s->penData.blend
665
0
            && ((cosmetic && penWidth <= 1)
666
0
                || (!cosmetic && (s->flags.tx_noshear || !s->flags.antialiased) && penWidth * s->txscale <= 1));
667
668
0
    s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
669
670
0
    s->strokeFlags = 0;
671
0
}
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
0
{
709
#ifdef QT_DEBUG_DRAW
710
    qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
711
#endif
712
0
    Q_D(QRasterPaintEngine);
713
0
    QRasterPaintEngineState *s = state();
714
    // must set clip prior to setup, as setup uses it...
715
0
    s->brushData.clip = d->clip();
716
0
    s->brushData.setup(brush, s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);
717
0
    if (s->fillFlags & DirtyTransform
718
0
        || brush.transform().type() >= QTransform::TxNone)
719
0
        d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
720
0
    s->lastBrush = brush;
721
0
    s->fillFlags = 0;
722
0
}
723
724
void QRasterPaintEngine::updateOutlineMapper()
725
0
{
726
0
    Q_D(QRasterPaintEngine);
727
0
    d->outlineMapper->setMatrix(state()->matrix);
728
0
}
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
0
{
1013
0
    deviceRectUnclipped = QRect(0, 0,
1014
0
            qMin(QT_RASTER_COORD_LIMIT, device->width()),
1015
0
            qMin(QT_RASTER_COORD_LIMIT, device->height()));
1016
1017
0
    if (!systemClip.isEmpty()) {
1018
0
        QRegion clippedDeviceRgn = systemClip & deviceRectUnclipped;
1019
0
        deviceRect = clippedDeviceRgn.boundingRect();
1020
0
        baseClip->setClipRegion(clippedDeviceRgn);
1021
0
    } else {
1022
0
        deviceRect = deviceRectUnclipped;
1023
0
        baseClip->setClipRect(deviceRect);
1024
0
    }
1025
#ifdef QT_DEBUG_DRAW
1026
    qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << deviceRectUnclipped << systemClip;
1027
#endif
1028
1029
0
    exDeviceRect = deviceRect;
1030
1031
0
    Q_Q(QRasterPaintEngine);
1032
0
    if (q->state()) {
1033
0
        q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
1034
0
        q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
1035
0
        q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
1036
0
    }
1037
0
}
1038
1039
void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
1040
0
{
1041
0
    if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
1042
0
        return;
1043
1044
0
    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
0
{
1499
#ifdef QT_DEBUG_DRAW
1500
    qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount);
1501
#endif
1502
0
#ifdef QT_FAST_SPANS
1503
0
    Q_D(QRasterPaintEngine);
1504
0
    ensureRasterState();
1505
0
    QRasterPaintEngineState *s = state();
1506
1507
1508
0
    if (s->flags.tx_noshear) {
1509
0
        ensureBrush();
1510
0
        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
0
        ensurePen();
1523
0
        if (s->penData.blend) {
1524
0
            QRectVectorPath path;
1525
0
            if (s->flags.fast_pen) {
1526
0
                QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1527
0
                for (int i = 0; i < rectCount; ++i) {
1528
0
                    path.set(rects[i]);
1529
0
                    stroker.drawPath(path);
1530
0
                }
1531
0
            } 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
0
        }
1538
1539
0
        return;
1540
0
    }
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
            // Map the corners rather than position and size separately, so
1719
            // that rects sharing an edge round that edge to the same pixel.
1720
0
            const QRect rr = toNormalizedFillRect(QRectF(s->matrix.map(r.topLeft()),
1721
0
                                                         s->matrix.map(r.bottomRight())));
1722
0
            fillRect_normalized(rr, data, d);
1723
0
            return;
1724
0
        }
1725
0
    }
1726
0
    ensureRasterState();
1727
0
    if (s->flags.tx_noshear) {
1728
0
        d->initializeRasterizer(data);
1729
0
        QRectF nr = r.normalized();
1730
0
        if (!nr.isEmpty()) {
1731
0
            const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
1732
0
            const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
1733
0
            d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
1734
0
        }
1735
0
        return;
1736
0
    }
1737
1738
0
    QPainterPath path;
1739
0
    path.addRect(r);
1740
0
    ensureOutlineMapper();
1741
0
    fillPath(path, data);
1742
0
}
1743
1744
/*!
1745
    \reimp
1746
*/
1747
void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
1748
0
{
1749
#ifdef QT_DEBUG_DRAW
1750
    qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
1751
#endif
1752
0
    QRasterPaintEngineState *s = state();
1753
1754
0
    ensureBrush(brush);
1755
0
    if (!s->brushData.blend)
1756
0
        return;
1757
1758
0
    fillRect(r, &s->brushData);
1759
0
}
1760
1761
static QColor qPremultiplyWithExtraAlpha(const QColor &c, int alpha)
1762
0
{
1763
0
    if (alpha == 0)
1764
0
        return Qt::transparent;
1765
0
    if (c.spec() == QColor::ExtendedRgb) {
1766
0
        float r, g, b, a;
1767
0
        c.getRgbF(&r, &g, &b, &a);
1768
0
        a = a * alpha * (1.f / 256.f);
1769
0
        return QColor::fromRgbF(r * a, g * a, b * a, a);
1770
0
    }
1771
0
    return qPremultiply(combineAlpha256(c.rgba64(), alpha));
1772
0
}
1773
1774
/*!
1775
    \reimp
1776
*/
1777
void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
1778
0
{
1779
#ifdef QT_DEBUG_DRAW
1780
    qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
1781
#endif
1782
0
    Q_D(QRasterPaintEngine);
1783
0
    QRasterPaintEngineState *s = state();
1784
1785
0
    d->solid_color_filler.solidColor = qPremultiplyWithExtraAlpha(color, s->intOpacity);
1786
1787
0
    if (d->solid_color_filler.solidColor.alphaF() <= 0.0f
1788
0
        && s->composition_mode == QPainter::CompositionMode_SourceOver) {
1789
0
        return;
1790
0
    }
1791
0
    d->solid_color_filler.clip = d->clip();
1792
0
    d->solid_color_filler.adjustSpanMethods();
1793
0
    fillRect(r, &d->solid_color_filler);
1794
0
}
1795
1796
static inline bool isAbove(const QPointF *a, const QPointF *b)
1797
0
{
1798
0
    return a->y() < b->y();
1799
0
}
1800
1801
static bool splitPolygon(const QPointF *points, int pointCount, QList<QPointF> *upper, QList<QPointF> *lower)
1802
0
{
1803
0
    Q_ASSERT(upper);
1804
0
    Q_ASSERT(lower);
1805
1806
0
    Q_ASSERT(pointCount >= 2);
1807
1808
0
    QList<const QPointF *> sorted;
1809
0
    sorted.reserve(pointCount);
1810
1811
0
    upper->reserve(pointCount * 3 / 4);
1812
0
    lower->reserve(pointCount * 3 / 4);
1813
1814
0
    for (int i = 0; i < pointCount; ++i)
1815
0
        sorted << points + i;
1816
1817
0
    std::sort(sorted.begin(), sorted.end(), isAbove);
1818
1819
0
    qreal splitY = sorted.at(sorted.size() / 2)->y();
1820
1821
0
    const QPointF *end = points + pointCount;
1822
0
    const QPointF *last = end - 1;
1823
1824
0
    QList<QPointF> *bin[2] = { upper, lower };
1825
1826
0
    for (const QPointF *p = points; p < end; ++p) {
1827
0
        int side = p->y() < splitY;
1828
0
        int lastSide = last->y() < splitY;
1829
1830
0
        if (side != lastSide) {
1831
0
            if (qFuzzyCompare(p->y(), splitY)) {
1832
0
                bin[!side]->append(*p);
1833
0
            } else if (qFuzzyCompare(last->y(), splitY)) {
1834
0
                bin[side]->append(*last);
1835
0
            } else {
1836
0
                QPointF delta = *p - *last;
1837
0
                QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
1838
1839
0
                bin[0]->append(intersection);
1840
0
                bin[1]->append(intersection);
1841
0
            }
1842
0
        }
1843
1844
0
        bin[side]->append(*p);
1845
1846
0
        last = p;
1847
0
    }
1848
1849
    // give up if we couldn't reduce the point count
1850
0
    return upper->size() < pointCount && lower->size() < pointCount;
1851
0
}
1852
1853
/*!
1854
  \internal
1855
 */
1856
void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1857
0
{
1858
0
    Q_D(QRasterPaintEngine);
1859
0
    QRasterPaintEngineState *s = state();
1860
1861
0
    const int maxPoints = 0xffff;
1862
1863
    // max amount of points that raster engine can reliably handle
1864
0
    if (pointCount > maxPoints) {
1865
0
        QList<QPointF> upper, lower;
1866
1867
0
        if (splitPolygon(points, pointCount, &upper, &lower)) {
1868
0
            fillPolygon(upper.constData(), upper.size(), mode);
1869
0
            fillPolygon(lower.constData(), lower.size(), mode);
1870
0
        } else
1871
0
            qWarning("Polygon too complex for filling.");
1872
1873
0
        return;
1874
0
    }
1875
1876
    // Compose polygon fill..,
1877
0
    QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1878
0
    ensureOutlineMapper();
1879
0
    QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
1880
1881
    // scanconvert.
1882
0
    ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1883
0
                                              &s->brushData);
1884
0
    d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
1885
0
}
1886
1887
/*!
1888
    \reimp
1889
*/
1890
void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1891
0
{
1892
0
    Q_D(QRasterPaintEngine);
1893
0
    QRasterPaintEngineState *s = state();
1894
1895
#ifdef QT_DEBUG_DRAW
1896
    qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
1897
    for (int i=0; i<pointCount; ++i)
1898
        qDebug() << "   - " << points[i];
1899
#endif
1900
0
    Q_ASSERT(pointCount >= 2);
1901
1902
0
    if (mode != PolylineMode && QVectorPath::isRect((const qreal *) points, pointCount)) {
1903
0
        QRectF r(points[0], points[2]);
1904
0
        drawRects(&r, 1);
1905
0
        return;
1906
0
    }
1907
1908
0
    ensurePen();
1909
0
    if (mode != PolylineMode) {
1910
        // Do the fill...
1911
0
        ensureBrush();
1912
0
        if (s->brushData.blend)
1913
0
            fillPolygon(points, pointCount, mode);
1914
0
    }
1915
1916
    // Do the outline...
1917
0
    if (s->penData.blend) {
1918
0
        QVectorPath vp((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
1919
0
        if (s->flags.fast_pen) {
1920
0
            QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1921
0
            stroker.drawPath(vp);
1922
0
        } else {
1923
0
            QPaintEngineEx::stroke(vp, s->lastPen);
1924
0
        }
1925
0
    }
1926
0
}
1927
1928
/*!
1929
    \reimp
1930
*/
1931
void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
1932
0
{
1933
0
    Q_D(QRasterPaintEngine);
1934
0
    QRasterPaintEngineState *s = state();
1935
1936
#ifdef QT_DEBUG_DRAW
1937
    qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
1938
    for (int i=0; i<pointCount; ++i)
1939
        qDebug() << "   - " << points[i];
1940
#endif
1941
0
    Q_ASSERT(pointCount >= 2);
1942
0
    if (mode != PolylineMode && QVectorPath::isRect((const int *) points, pointCount)) {
1943
0
        QRect r(points[0].x(),
1944
0
                points[0].y(),
1945
0
                points[2].x() - points[0].x(),
1946
0
                points[2].y() - points[0].y());
1947
0
        drawRects(&r, 1);
1948
0
        return;
1949
0
    }
1950
1951
0
    ensurePen();
1952
1953
    // Do the fill
1954
0
    if (mode != PolylineMode) {
1955
0
        ensureBrush();
1956
0
        if (s->brushData.blend) {
1957
            // Compose polygon fill..,
1958
0
            ensureOutlineMapper();
1959
0
            d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
1960
0
            d->outlineMapper->moveTo(*points);
1961
0
            const QPoint *p = points;
1962
0
            const QPoint *ep = points + pointCount - 1;
1963
0
            do {
1964
0
                d->outlineMapper->lineTo(*(++p));
1965
0
            } while (p < ep);
1966
0
            d->outlineMapper->endOutline();
1967
1968
            // scanconvert.
1969
0
            ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
1970
0
                                                      &s->brushData);
1971
0
            d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
1972
0
        }
1973
0
    }
1974
1975
    // Do the outline...
1976
0
    if (s->penData.blend) {
1977
0
        int count = pointCount * 2;
1978
0
        QVarLengthArray<qreal> fpoints(count);
1979
0
        for (int i=0; i<count; ++i)
1980
0
            fpoints[i] = ((const int *) points)[i];
1981
0
        QVectorPath vp((qreal *) fpoints.data(), pointCount, nullptr, QVectorPath::polygonFlags(mode));
1982
1983
0
        if (s->flags.fast_pen) {
1984
0
            QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
1985
0
            stroker.drawPath(vp);
1986
0
        } else {
1987
0
            QPaintEngineEx::stroke(vp, s->lastPen);
1988
0
        }
1989
0
    }
1990
0
}
1991
1992
/*!
1993
    \internal
1994
*/
1995
void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
1996
0
{
1997
#ifdef QT_DEBUG_DRAW
1998
    qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
1999
#endif
2000
2001
0
    QPlatformPixmap *pd = pixmap.handle();
2002
0
    if (pd->classId() == QPlatformPixmap::RasterClass) {
2003
0
        const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2004
0
        if (image.depth() == 1) {
2005
0
            Q_D(QRasterPaintEngine);
2006
0
            QRasterPaintEngineState *s = state();
2007
0
            if (s->matrix.type() <= QTransform::TxTranslate) {
2008
0
                ensurePen();
2009
0
                drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2010
0
            } else {
2011
0
                drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2012
0
            }
2013
0
        } else {
2014
0
            QRasterPaintEngine::drawImage(pos, image);
2015
0
        }
2016
0
    } else {
2017
0
        const QImage image = pixmap.toImage();
2018
0
        if (pixmap.depth() == 1) {
2019
0
            Q_D(QRasterPaintEngine);
2020
0
            QRasterPaintEngineState *s = state();
2021
0
            if (s->matrix.type() <= QTransform::TxTranslate) {
2022
0
                ensurePen();
2023
0
                drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2024
0
            } else {
2025
0
                drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
2026
0
            }
2027
0
        } else {
2028
0
            QRasterPaintEngine::drawImage(pos, image);
2029
0
        }
2030
0
    }
2031
0
}
2032
2033
/*!
2034
    \reimp
2035
*/
2036
void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
2037
0
{
2038
#ifdef QT_DEBUG_DRAW
2039
    qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
2040
#endif
2041
2042
0
    QPlatformPixmap* pd = pixmap.handle();
2043
0
    if (pd->classId() == QPlatformPixmap::RasterClass) {
2044
0
        const QImage &image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2045
0
        if (image.depth() == 1) {
2046
0
            Q_D(QRasterPaintEngine);
2047
0
            QRasterPaintEngineState *s = state();
2048
0
            if (s->matrix.type() <= QTransform::TxTranslate
2049
0
                && r.size() == sr.size()
2050
0
                && r.size() == pixmap.size()) {
2051
0
                ensurePen();
2052
0
                drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2053
0
                return;
2054
0
            } else {
2055
0
                drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
2056
0
            }
2057
0
        } else {
2058
0
            drawImage(r, image, sr);
2059
0
        }
2060
0
    } else {
2061
0
        QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
2062
0
        const QImage image = pd->toImage(clippedSource);
2063
0
        QRectF translatedSource = sr.translated(-clippedSource.topLeft());
2064
0
        if (image.depth() == 1) {
2065
0
            Q_D(QRasterPaintEngine);
2066
0
            QRasterPaintEngineState *s = state();
2067
0
            if (s->matrix.type() <= QTransform::TxTranslate
2068
0
                && r.size() == sr.size()
2069
0
                && r.size() == pixmap.size()) {
2070
0
                ensurePen();
2071
0
                drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
2072
0
                return;
2073
0
            } else {
2074
0
                drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
2075
0
            }
2076
0
        } else {
2077
0
            drawImage(r, image, translatedSource);
2078
0
        }
2079
0
    }
2080
0
}
2081
2082
static inline int fast_ceil_positive(const qreal &v)
2083
0
{
2084
0
    const int iv = int(v);
2085
0
    if (v - iv == 0)
2086
0
        return iv;
2087
0
    else
2088
0
        return iv + 1;
2089
0
}
2090
2091
static inline const QRect toAlignedRect_positive(const QRectF &rect)
2092
0
{
2093
0
    const int xmin = int(rect.x());
2094
0
    const int xmax = int(fast_ceil_positive(rect.right()));
2095
0
    const int ymin = int(rect.y());
2096
0
    const int ymax = int(fast_ceil_positive(rect.bottom()));
2097
0
    return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
2098
0
}
2099
2100
/*!
2101
    \internal
2102
*/
2103
void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
2104
0
{
2105
#ifdef QT_DEBUG_DRAW
2106
    qDebug() << " - QRasterPaintEngine::drawImage(), p=" <<  p << " image=" << img.size() << "depth=" << img.depth();
2107
#endif
2108
2109
0
    Q_D(QRasterPaintEngine);
2110
0
    QRasterPaintEngineState *s = state();
2111
0
    qreal scale = img.devicePixelRatio();
2112
2113
0
    if (scale > 1.0 ||  s->matrix.type() > QTransform::TxTranslate) {
2114
0
        drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
2115
0
                  img,
2116
0
                  QRectF(0, 0, img.width(), img.height()));
2117
0
    } else {
2118
2119
0
        const QClipData *clip = d->clip();
2120
0
        QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
2121
2122
0
        if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, img.rect())) {
2123
0
            if (!clip) {
2124
0
                d->blitImage(pt, img, d->deviceRect);
2125
0
                return;
2126
0
            } else if (clip->hasRectClip) {
2127
0
                d->blitImage(pt, img, clip->clipRect);
2128
0
                return;
2129
0
            }
2130
0
        } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2131
0
            SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2132
0
            if (func) {
2133
0
                if (!clip) {
2134
0
                    d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
2135
0
                    return;
2136
0
                } else if (clip->hasRectClip) {
2137
0
                    d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
2138
0
                    return;
2139
0
                }
2140
0
            }
2141
0
        }
2142
2143
2144
2145
0
        d->image_filler.clip = clip;
2146
0
        d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
2147
0
        if (!d->image_filler.blend)
2148
0
            return;
2149
0
        d->image_filler.dx = -pt.x();
2150
0
        d->image_filler.dy = -pt.y();
2151
0
        QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
2152
2153
0
        fillRect_normalized(rr, &d->image_filler, d);
2154
0
    }
2155
2156
0
}
2157
2158
QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
2159
0
{
2160
0
    return QRectF(r.topLeft() * t, r.bottomRight() * t);
2161
0
}
2162
2163
namespace {
2164
    enum RotationType {
2165
        Rotation90,
2166
        Rotation180,
2167
        Rotation270,
2168
        NoRotation
2169
    };
2170
2171
    inline RotationType qRotationType(const QTransform &transform)
2172
0
    {
2173
0
        QTransform::TransformationType type = transform.type();
2174
2175
0
        if (type > QTransform::TxRotate)
2176
0
            return NoRotation;
2177
2178
0
        if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
2179
0
            && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
2180
0
            return Rotation90;
2181
2182
0
        if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
2183
0
            && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
2184
0
            return Rotation180;
2185
2186
0
        if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
2187
0
            && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
2188
0
            return Rotation270;
2189
2190
0
        return NoRotation;
2191
0
    }
2192
2193
    inline bool isPixelAligned(const QPointF &pt)
2194
0
    {
2195
0
        return QPointF(pt.toPoint()) == pt;
2196
0
    }
2197
    inline bool isPixelAligned(const QRectF &rect)
2198
0
    {
2199
0
        return QRectF(rect.toRect()) == rect;
2200
0
    }
2201
}
2202
2203
/*!
2204
    \reimp
2205
*/
2206
void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
2207
                                   Qt::ImageConversionFlags)
2208
0
{
2209
#ifdef QT_DEBUG_DRAW
2210
    qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
2211
#endif
2212
2213
0
    if (r.isEmpty())
2214
0
        return;
2215
2216
0
    Q_D(QRasterPaintEngine);
2217
0
    QRasterPaintEngineState *s = state();
2218
0
    Q_ASSERT(s);
2219
0
    int sr_l = qFloor(sr.left());
2220
0
    int sr_r = qCeil(sr.right()) - 1;
2221
0
    int sr_t = qFloor(sr.top());
2222
0
    int sr_b = qCeil(sr.bottom()) - 1;
2223
2224
0
    if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
2225
        // as fillRect will apply the aliased coordinate delta we need to
2226
        // subtract it here as we don't use it for image drawing
2227
0
        QTransform old = s->matrix;
2228
2229
        // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
2230
0
        QRgb color = img.pixel(sr_l, sr_t);
2231
0
        if (img.pixelFormat().premultiplied() == QPixelFormat::Premultiplied) {
2232
            // Combine premultiplied color with the opacity set on the painter.
2233
0
            d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity);
2234
0
        } else {
2235
0
            d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity));
2236
0
        }
2237
2238
0
        if (d->solid_color_filler.solidColor.alphaF() <= 0.0f && s->composition_mode == QPainter::CompositionMode_SourceOver)
2239
0
            return;
2240
2241
0
        d->solid_color_filler.clip = d->clip();
2242
0
        d->solid_color_filler.adjustSpanMethods();
2243
0
        fillRect(r, &d->solid_color_filler);
2244
2245
0
        s->matrix = old;
2246
0
        return;
2247
0
    }
2248
2249
0
    bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
2250
2251
0
    const QClipData *clip = d->clip();
2252
2253
0
    if (s->matrix.type() == QTransform::TxRotate
2254
0
        && !stretch_sr
2255
0
        && (!clip || clip->hasRectClip)
2256
0
        && s->intOpacity == 256
2257
0
        && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
2258
0
            || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))
2259
0
    {
2260
0
        RotationType rotationType = qRotationType(s->matrix);
2261
0
        Q_ASSERT(d->rasterBuffer->format < QImage::NImageFormats);
2262
0
        const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
2263
2264
0
        if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) {
2265
0
            QRectF transformedTargetRect = s->matrix.mapRect(r);
2266
2267
0
            if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, transformedTargetRect.topRight(), sr)) {
2268
0
                QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
2269
0
                if (clippedTransformedTargetRect.isNull())
2270
0
                    return;
2271
2272
0
                QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
2273
2274
0
                QRect clippedSourceRect
2275
0
                    = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
2276
0
                            clippedTargetRect.width(), clippedTargetRect.height()).toRect();
2277
2278
0
                clippedSourceRect = clippedSourceRect.intersected(img.rect());
2279
2280
0
                const qsizetype dbpl = d->rasterBuffer->bytesPerLine();
2281
0
                const qsizetype sbpl = img.bytesPerLine();
2282
2283
0
                uchar *dst = d->rasterBuffer->buffer();
2284
0
                uint bpp = img.depth() >> 3;
2285
2286
0
                const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
2287
0
                uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
2288
2289
0
                uint cw = clippedSourceRect.width();
2290
0
                uint ch = clippedSourceRect.height();
2291
2292
0
                qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
2293
2294
0
                return;
2295
0
            }
2296
0
        }
2297
0
    }
2298
2299
0
    if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
2300
2301
0
        QRectF targetBounds = s->matrix.mapRect(r);
2302
0
        bool exceedsPrecision = r.width() > 0x7fff
2303
0
                             || r.height() > 0x7fff
2304
0
                             || targetBounds.left() < -0x7fff
2305
0
                             || targetBounds.top() < -0x7fff
2306
0
                             || targetBounds.right() > 0x7fff
2307
0
                             || targetBounds.bottom() > 0x7fff
2308
0
                             || targetBounds.width() > 0x7fff
2309
0
                             || targetBounds.height() > 0x7fff
2310
0
                             || s->matrix.m11() >= 512
2311
0
                             || s->matrix.m22() >= 512;
2312
0
        if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2313
0
            if (s->matrix.type() > QTransform::TxScale) {
2314
0
                SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
2315
                // The fast transform methods doesn't really work on small targets, see QTBUG-93475
2316
                // And it can't antialias the edges
2317
0
                if (func && (!clip || clip->hasRectClip) && !s->flags.antialiased && targetBounds.width() >= 16 && targetBounds.height() >= 16) {
2318
0
                    func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
2319
0
                         img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
2320
0
                         s->matrix, s->intOpacity);
2321
0
                    return;
2322
0
                }
2323
0
            } else {
2324
                // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
2325
0
                bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
2326
0
                bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
2327
0
                if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
2328
0
                    SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2329
0
                    if (func) {
2330
0
                        QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
2331
0
                        if (!clip) {
2332
0
                            d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2333
0
                            return;
2334
0
                        } else if (clip->hasRectClip) {
2335
0
                            d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2336
0
                            return;
2337
0
                        }
2338
0
                    }
2339
0
                }
2340
0
                SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
2341
0
                if (func && (!clip || clip->hasRectClip)) {
2342
0
                    QRectF tr = qt_mapRect_non_normalizing(r, s->matrix);
2343
0
                    if (!s->flags.antialiased) {
2344
0
                        tr.setX(qRound(tr.x()));
2345
0
                        tr.setY(qRound(tr.y()));
2346
0
                        tr.setWidth(qRound(tr.width()));
2347
0
                        tr.setHeight(qRound(tr.height()));
2348
0
                    }
2349
0
                    func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
2350
0
                         img.bits(), img.bytesPerLine(), img.height(),
2351
0
                         tr, sr,
2352
0
                         !clip ? d->deviceRect : clip->clipRect,
2353
0
                         s->intOpacity);
2354
0
                    return;
2355
0
                }
2356
0
            }
2357
0
        }
2358
2359
0
        QTransform copy = s->matrix;
2360
0
        copy.translate(r.x(), r.y());
2361
0
        if (stretch_sr)
2362
0
            copy.scale(r.width() / sr.width(), r.height() / sr.height());
2363
0
        copy.translate(-sr.x(), -sr.y());
2364
2365
0
        d->image_filler_xform.clip = clip;
2366
0
        d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2367
0
        if (!d->image_filler_xform.blend)
2368
0
            return;
2369
0
        d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2370
2371
0
        if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
2372
0
            QRectF rr = s->matrix.mapRect(r);
2373
2374
0
            const int x1 = qRound(rr.x());
2375
0
            const int y1 = qRound(rr.y());
2376
0
            const int x2 = qRound(rr.right());
2377
0
            const int y2 = qRound(rr.bottom());
2378
2379
0
            fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
2380
0
            return;
2381
0
        }
2382
2383
0
#ifdef QT_FAST_SPANS
2384
0
        ensureRasterState();
2385
0
        if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2386
0
            d->initializeRasterizer(&d->image_filler_xform);
2387
0
            d->rasterizer->setAntialiased(s->flags.antialiased);
2388
2389
0
            const QRectF &rect = r.normalized();
2390
0
            const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2391
0
            const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2392
2393
0
            if (s->flags.tx_noshear)
2394
0
                d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2395
0
            else
2396
0
                d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2397
0
            return;
2398
0
        }
2399
0
#endif
2400
0
        QPainterPath path;
2401
0
        path.addRect(r);
2402
0
        QTransform m = s->matrix;
2403
0
        s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
2404
0
                               m.m21(), m.m22(), m.m23(),
2405
0
                               m.m31(), m.m32(), m.m33());
2406
0
        fillPath(path, &d->image_filler_xform);
2407
0
        s->matrix = m;
2408
0
    } else {
2409
0
        QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
2410
0
        if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) {
2411
0
            if (!clip) {
2412
0
                d->blitImage(pt, img, d->deviceRect, sr.toRect());
2413
0
                return;
2414
0
            } else if (clip->hasRectClip) {
2415
0
                d->blitImage(pt, img, clip->clipRect, sr.toRect());
2416
0
                return;
2417
0
            }
2418
0
        } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
2419
0
            SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
2420
0
            if (func) {
2421
0
                if (!clip) {
2422
0
                    d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
2423
0
                    return;
2424
0
                } else if (clip->hasRectClip) {
2425
0
                    d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
2426
0
                    return;
2427
0
                }
2428
0
            }
2429
0
        }
2430
2431
0
        d->image_filler.clip = clip;
2432
0
        d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
2433
0
        if (!d->image_filler.blend)
2434
0
            return;
2435
0
        d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2436
0
        d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2437
2438
0
        QRectF rr = r;
2439
0
        rr.translate(s->matrix.dx(), s->matrix.dy());
2440
2441
0
        const int x1 = qRound(rr.x());
2442
0
        const int y1 = qRound(rr.y());
2443
0
        const int x2 = qRound(rr.right());
2444
0
        const int y2 = qRound(rr.bottom());
2445
2446
0
        fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
2447
0
    }
2448
0
}
2449
2450
/*!
2451
    \reimp
2452
*/
2453
void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
2454
0
{
2455
#ifdef QT_DEBUG_DRAW
2456
    qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
2457
#endif
2458
0
    Q_D(QRasterPaintEngine);
2459
0
    QRasterPaintEngineState *s = state();
2460
0
    Q_ASSERT(s);
2461
2462
0
    QImage image;
2463
2464
0
    QPlatformPixmap *pd = pixmap.handle();
2465
0
    if (pd->classId() == QPlatformPixmap::RasterClass) {
2466
0
        image = static_cast<QRasterPlatformPixmap *>(pd)->image;
2467
0
    } else {
2468
0
        image = pixmap.toImage();
2469
0
    }
2470
2471
0
    if (image.depth() == 1)
2472
0
        image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
2473
2474
0
    const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
2475
0
    if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
2476
0
        QTransform copy = s->matrix;
2477
0
        copy.translate(r.x(), r.y());
2478
0
        copy.translate(-sr.x(), -sr.y());
2479
0
        const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
2480
0
        copy.scale(inverseDpr, inverseDpr);
2481
0
        d->image_filler_xform.clip = d->clip();
2482
0
        d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2483
0
        if (!d->image_filler_xform.blend)
2484
0
            return;
2485
0
        d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
2486
2487
0
#ifdef QT_FAST_SPANS
2488
0
        ensureRasterState();
2489
0
        if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
2490
0
            d->initializeRasterizer(&d->image_filler_xform);
2491
0
            d->rasterizer->setAntialiased(s->flags.antialiased);
2492
2493
0
            const QRectF &rect = r.normalized();
2494
0
            const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
2495
0
            const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
2496
0
            if (s->flags.tx_noshear)
2497
0
                d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
2498
0
            else
2499
0
                d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
2500
0
            return;
2501
0
        }
2502
0
#endif
2503
0
        QPainterPath path;
2504
0
        path.addRect(r);
2505
0
        fillPath(path, &d->image_filler_xform);
2506
0
    } else {
2507
0
        d->image_filler.clip = d->clip();
2508
2509
0
        d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
2510
0
        if (!d->image_filler.blend)
2511
0
            return;
2512
0
        d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
2513
0
        d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
2514
2515
0
        QRectF rr = r;
2516
0
        rr.translate(s->matrix.dx(), s->matrix.dy());
2517
0
        fillRect_normalized(rr.normalized().toRect(), &d->image_filler, d);
2518
0
    }
2519
0
}
2520
2521
2522
//QWS hack
2523
static inline bool monoVal(const uchar* s, int x)
2524
0
{
2525
0
    return  (s[x>>3] << (x&7)) & 0x80;
2526
0
}
2527
2528
/*!
2529
    \internal
2530
 */
2531
QRasterBuffer *QRasterPaintEngine::rasterBuffer()
2532
0
{
2533
0
    Q_D(QRasterPaintEngine);
2534
0
    return d->rasterBuffer.data();
2535
0
}
2536
2537
/*!
2538
    \internal
2539
*/
2540
void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h, bool useGammaCorrection)
2541
0
{
2542
0
    Q_D(QRasterPaintEngine);
2543
0
    QRasterPaintEngineState *s = state();
2544
2545
0
    if (!s->penData.blend)
2546
0
        return;
2547
2548
0
    QRasterBuffer *rb = d->rasterBuffer.data();
2549
0
    if (rb->colorSpace.transferFunction() == QColorSpace::TransferFunction::Linear)
2550
0
        useGammaCorrection = false;
2551
2552
0
    const QRect rect(rx, ry, w, h);
2553
0
    const QClipData *clip = d->clip();
2554
0
    bool unclipped = false;
2555
0
    if (clip) {
2556
        // inlined QRect::intersects
2557
0
        const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
2558
0
                                && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
2559
2560
0
        if (clip->hasRectClip) {
2561
0
            unclipped = rx > clip->xmin
2562
0
                        && rx + w < clip->xmax
2563
0
                        && ry > clip->ymin
2564
0
                        && ry + h < clip->ymax;
2565
0
        }
2566
2567
0
        if (!intersects)
2568
0
            return;
2569
0
    } else {
2570
        // inlined QRect::intersects
2571
0
        const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
2572
0
                                && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
2573
0
        if (!intersects)
2574
0
            return;
2575
2576
        // inlined QRect::contains
2577
0
        const bool contains = rect.left() >= 0 && rect.right() < rb->width()
2578
0
                              && rect.top() >= 0 && rect.bottom() < rb->height();
2579
2580
0
        unclipped = contains && d->isUnclipped_normalized(rect);
2581
0
    }
2582
2583
0
    ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
2584
0
    const uchar * scanline = static_cast<const uchar *>(src);
2585
2586
0
    if (s->flags.fast_text) {
2587
0
        if (unclipped) {
2588
0
            if (depth == 1) {
2589
0
                if (s->penData.bitmapBlit) {
2590
0
                    s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2591
0
                                          scanline, w, h, bpl);
2592
0
                    return;
2593
0
                }
2594
0
            } else if (depth == 8) {
2595
0
                if (s->penData.alphamapBlit) {
2596
0
                    s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2597
0
                                            scanline, w, h, bpl, nullptr, useGammaCorrection);
2598
0
                    return;
2599
0
                }
2600
0
            } else if (depth == 32) {
2601
                // (A)RGB Alpha mask where the alpha component is not used.
2602
0
                if (s->penData.alphaRGBBlit) {
2603
0
                    s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2604
0
                                            (const uint *) scanline, w, h, bpl / 4, nullptr, useGammaCorrection);
2605
0
                    return;
2606
0
                }
2607
0
            }
2608
0
        } else if ((depth == 8 && s->penData.alphamapBlit) || (depth == 32 && s->penData.alphaRGBBlit)) {
2609
            // (A)RGB Alpha mask where the alpha component is not used.
2610
0
            if (!clip) {
2611
0
                int nx = qMax(0, rx);
2612
0
                int ny = qMax(0, ry);
2613
2614
                // Move scanline pointer to compensate for moved x and y
2615
0
                int xdiff = nx - rx;
2616
0
                int ydiff = ny - ry;
2617
0
                scanline += ydiff * bpl;
2618
0
                scanline += xdiff * (depth == 32 ? 4 : 1);
2619
2620
0
                w -= xdiff;
2621
0
                h -= ydiff;
2622
2623
0
                if (nx + w > d->rasterBuffer->width())
2624
0
                    w = d->rasterBuffer->width() - nx;
2625
0
                if (ny + h > d->rasterBuffer->height())
2626
0
                    h = d->rasterBuffer->height() - ny;
2627
2628
0
                rx = nx;
2629
0
                ry = ny;
2630
0
            }
2631
0
            if (depth == 8)
2632
0
                s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2633
0
                                        scanline, w, h, bpl, clip, useGammaCorrection);
2634
0
            else if (depth == 32)
2635
0
                s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor.rgba64(),
2636
0
                                        (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection);
2637
0
            return;
2638
0
        }
2639
0
    }
2640
2641
0
    int x0 = 0;
2642
0
    if (rx < 0) {
2643
0
        x0 = -rx;
2644
0
        w -= x0;
2645
0
    }
2646
2647
0
    int y0 = 0;
2648
0
    if (ry < 0) {
2649
0
        y0 = -ry;
2650
0
        scanline += bpl * y0;
2651
0
        h -= y0;
2652
0
    }
2653
2654
0
    w = qMin(w, rb->width() - qMax(0, rx));
2655
0
    h = qMin(h, rb->height() - qMax(0, ry));
2656
2657
0
    if (w <= 0 || h <= 0)
2658
0
        return;
2659
2660
0
    const int NSPANS = 512;
2661
0
    QT_FT_Span spans[NSPANS];
2662
0
    int current = 0;
2663
2664
0
    const int x1 = x0 + w;
2665
0
    const int y1 = y0 + h;
2666
2667
0
    if (depth == 1) {
2668
0
        for (int y = y0; y < y1; ++y) {
2669
0
            for (int x = x0; x < x1; ) {
2670
0
                if (!monoVal(scanline, x)) {
2671
0
                    ++x;
2672
0
                    continue;
2673
0
                }
2674
2675
0
                if (current == NSPANS) {
2676
0
                    blend(current, spans, &s->penData);
2677
0
                    current = 0;
2678
0
                }
2679
0
                spans[current].x = x + rx;
2680
0
                spans[current].y = y + ry;
2681
0
                spans[current].coverage = 255;
2682
0
                int len = 1;
2683
0
                ++x;
2684
                // extend span until we find a different one.
2685
0
                while (x < x1 && monoVal(scanline, x)) {
2686
0
                    ++x;
2687
0
                    ++len;
2688
0
                }
2689
0
                spans[current].len = len;
2690
0
                ++current;
2691
0
            }
2692
0
            scanline += bpl;
2693
0
        }
2694
0
    } else if (depth == 8) {
2695
0
        for (int y = y0; y < y1; ++y) {
2696
0
            for (int x = x0; x < x1; ) {
2697
                // Skip those with 0 coverage
2698
0
                if (scanline[x] == 0) {
2699
0
                    ++x;
2700
0
                    continue;
2701
0
                }
2702
2703
0
                if (current == NSPANS) {
2704
0
                    blend(current, spans, &s->penData);
2705
0
                    current = 0;
2706
0
                }
2707
0
                int coverage = scanline[x];
2708
0
                spans[current].x = x + rx;
2709
0
                spans[current].y = y + ry;
2710
0
                spans[current].coverage = coverage;
2711
0
                int len = 1;
2712
0
                ++x;
2713
2714
                // extend span until we find a different one.
2715
0
                while (x < x1 && scanline[x] == coverage) {
2716
0
                    ++x;
2717
0
                    ++len;
2718
0
                }
2719
0
                spans[current].len = len;
2720
0
                ++current;
2721
0
            }
2722
0
            scanline += bpl;
2723
0
        }
2724
0
    } else { // 32-bit alpha...
2725
0
        const uint *sl = (const uint *) scanline;
2726
0
        for (int y = y0; y < y1; ++y) {
2727
0
            for (int x = x0; x < x1; ) {
2728
                // Skip those with 0 coverage
2729
0
                if ((sl[x] & 0x00ffffff) == 0) {
2730
0
                    ++x;
2731
0
                    continue;
2732
0
                }
2733
2734
0
                if (current == NSPANS) {
2735
0
                    blend(current, spans, &s->penData);
2736
0
                    current = 0;
2737
0
                }
2738
0
                uint rgbCoverage = sl[x];
2739
0
                int coverage = qGreen(rgbCoverage);
2740
0
                spans[current].x = x + rx;
2741
0
                spans[current].y = y + ry;
2742
0
                spans[current].coverage = coverage;
2743
0
                int len = 1;
2744
0
                ++x;
2745
2746
                // extend span until we find a different one.
2747
0
                while (x < x1 && sl[x] == rgbCoverage) {
2748
0
                    ++x;
2749
0
                    ++len;
2750
0
                }
2751
0
                spans[current].len = len;
2752
0
                ++current;
2753
0
            }
2754
0
            sl += bpl / sizeof(uint);
2755
0
        }
2756
0
    }
2757
//     qDebug() << "alphaPenBlt: num spans=" << current
2758
//              << "span:" << spans->x << spans->y << spans->len << spans->coverage;
2759
        // Call span func for current set of spans.
2760
0
    if (current != 0)
2761
0
        blend(current, spans, &s->penData);
2762
0
}
2763
2764
/*!
2765
    \internal
2766
*/
2767
bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
2768
                                          const QFixedPoint *positions, QFontEngine *fontEngine)
2769
0
{
2770
0
    Q_D(QRasterPaintEngine);
2771
0
    QRasterPaintEngineState *s = state();
2772
2773
0
    bool verticalSubPixelPositions = fontEngine->supportsVerticalSubPixelPositions()
2774
0
            && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
2775
2776
0
    if (fontEngine->hasInternalCaching()) {
2777
0
        QFontEngine::GlyphFormat neededFormat =
2778
0
            painter()->device()->devType() == QInternal::Widget
2779
0
            ? QFontEngine::Format_None
2780
0
            : QFontEngine::Format_A8;
2781
2782
0
        if (d_func()->mono_surface) // alphaPenBlt can handle mono, too
2783
0
            neededFormat = QFontEngine::Format_Mono;
2784
2785
0
        for (int i = 0; i < numGlyphs; i++) {
2786
0
            QFixedPoint spp = fontEngine->subPixelPositionFor(positions[i]);
2787
0
            if (!verticalSubPixelPositions)
2788
0
                spp.y = 0;
2789
2790
0
            const QFontEngine::Glyph *alphaMap = fontEngine->glyphData(glyphs[i], spp, neededFormat, s->matrix);
2791
0
            if (!alphaMap)
2792
0
                continue;
2793
2794
0
            int depth;
2795
0
            int bytesPerLine;
2796
0
            switch (alphaMap->format) {
2797
0
            case QFontEngine::Format_Mono:
2798
0
                depth = 1;
2799
0
                bytesPerLine = ((alphaMap->width + 31) & ~31) >> 3;
2800
0
                break;
2801
0
            case QFontEngine::Format_A8:
2802
0
                depth = 8;
2803
0
                bytesPerLine = (alphaMap->width + 3) & ~3;
2804
0
                break;
2805
0
            case QFontEngine::Format_A32:
2806
0
                depth = 32;
2807
0
                bytesPerLine = alphaMap->width * 4;
2808
0
                break;
2809
0
            default:
2810
0
                Q_UNREACHABLE();
2811
0
            };
2812
2813
0
            QFixed y = verticalSubPixelPositions
2814
0
                    ? qFloor(positions[i].y)
2815
0
                    : qRound(positions[i].y);
2816
2817
0
            alphaPenBlt(alphaMap->data, bytesPerLine, depth,
2818
0
                        qFloor(positions[i].x) + alphaMap->x,
2819
0
                        qFloor(y) - alphaMap->y,
2820
0
                        alphaMap->width, alphaMap->height,
2821
0
                        fontEngine->expectsGammaCorrectedBlending(QFontEngine::GlyphFormat(alphaMap->format)));
2822
0
        }
2823
2824
0
    } else {
2825
0
        QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat;
2826
2827
0
        QImageTextureGlyphCache *cache =
2828
0
            static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(nullptr, glyphFormat, s->matrix, s->penData.solidColor));
2829
0
        if (!cache) {
2830
0
            cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, s->penData.solidColor);
2831
0
            fontEngine->setGlyphCache(nullptr, cache);
2832
0
        }
2833
2834
0
        cache->populate(fontEngine, numGlyphs, glyphs, positions, s->renderHints);
2835
0
        cache->fillInPendingGlyphs();
2836
2837
0
        const QImage &image = cache->image();
2838
0
        qsizetype bpl = image.bytesPerLine();
2839
2840
0
        int depth = image.depth();
2841
0
        int rightShift = 0;
2842
0
        int leftShift = 0;
2843
0
        if (depth == 32)
2844
0
            leftShift = 2; // multiply by 4
2845
0
        else if (depth == 1)
2846
0
            rightShift = 3; // divide by 8
2847
2848
0
        int margin = fontEngine->glyphMargin(glyphFormat);
2849
0
        const uchar *bits = image.bits();
2850
0
        for (int i=0; i<numGlyphs; ++i) {
2851
0
            QFixedPoint subPixelPosition = fontEngine->subPixelPositionFor(positions[i]);
2852
0
            if (!verticalSubPixelPositions)
2853
0
                subPixelPosition.y = 0;
2854
2855
0
            QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
2856
0
            const QTextureGlyphCache::Coord &c = cache->coords[glyph];
2857
0
            if (c.isNull())
2858
0
                continue;
2859
2860
0
            int x = qFloor(positions[i].x) + c.baseLineX - margin;
2861
0
            int y = (verticalSubPixelPositions
2862
0
                        ? qFloor(positions[i].y)
2863
0
                        : qRound(positions[i].y));
2864
0
            y -= c.baseLineY + margin;
2865
2866
            // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
2867
            //        c.x, c.y,
2868
            //        c.w, c.h,
2869
            //        c.baseLineX, c.baseLineY,
2870
            //        glyphs[i],
2871
            //        x, y,
2872
            //        positions[i].x.toInt(), positions[i].y.toInt());
2873
2874
0
            const uchar *glyphBits = bits + ((c.x << leftShift) >> rightShift) + c.y * bpl;
2875
2876
0
            if (glyphFormat == QFontEngine::Format_ARGB) {
2877
                // The current state transform has already been applied to the positions,
2878
                // so we prevent drawImage() from re-applying the transform by clearing
2879
                // the state for the duration of the call.
2880
0
                QTransform originalTransform = s->matrix;
2881
0
                s->matrix = QTransform();
2882
0
                drawImage(QPoint(x, y), QImage(glyphBits, c.w, c.h, bpl, image.format()));
2883
0
                s->matrix = originalTransform;
2884
0
            } else {
2885
0
                alphaPenBlt(glyphBits,
2886
0
                            bpl,
2887
0
                            depth,
2888
0
                            x,
2889
0
                            y,
2890
0
                            c.w,
2891
0
                            c.h,
2892
0
                            fontEngine->expectsGammaCorrectedBlending(glyphFormat));
2893
0
            }
2894
0
        }
2895
0
    }
2896
0
    return true;
2897
0
}
2898
2899
2900
/*!
2901
 * \internal
2902
 * Returns \c true if the rectangle is completely within the current clip
2903
 * state of the paint engine.
2904
 */
2905
bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
2906
0
{
2907
0
    const QClipData *cl = clip();
2908
0
    if (!cl) {
2909
        // inline contains() for performance (we know the rects are normalized)
2910
0
        const QRect &r1 = deviceRect;
2911
0
        return (r.left() >= r1.left() && r.right() <= r1.right()
2912
0
                && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2913
0
    }
2914
2915
2916
0
    if (cl->hasRectClip) {
2917
        // currently all painting functions clips to deviceRect internally
2918
0
        if (cl->clipRect == deviceRect)
2919
0
            return true;
2920
2921
        // inline contains() for performance (we know the rects are normalized)
2922
0
        const QRect &r1 = cl->clipRect;
2923
0
        return (r.left() >= r1.left() && r.right() <= r1.right()
2924
0
                && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2925
0
    } else {
2926
0
        return qt_region_strictContains(cl->clipRegion, r);
2927
0
    }
2928
0
}
2929
2930
bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
2931
                                            int penWidth) const
2932
0
{
2933
0
    Q_Q(const QRasterPaintEngine);
2934
0
    const QRasterPaintEngineState *s = q->state();
2935
0
    const QClipData *cl = clip();
2936
0
    QRect r = rect.normalized();
2937
0
    if (!cl) {
2938
        // inline contains() for performance (we know the rects are normalized)
2939
0
        const QRect &r1 = deviceRect;
2940
0
        return (r.left() >= r1.left() && r.right() <= r1.right()
2941
0
                && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2942
0
    }
2943
2944
2945
    // currently all painting functions that call this function clip to deviceRect internally
2946
0
    if (cl->hasRectClip && cl->clipRect == deviceRect)
2947
0
        return true;
2948
2949
0
    if (s->flags.antialiased)
2950
0
        ++penWidth;
2951
2952
0
    if (penWidth > 0) {
2953
0
        r.setX(r.x() - penWidth);
2954
0
        r.setY(r.y() - penWidth);
2955
0
        r.setWidth(r.width() + 2 * penWidth);
2956
0
        r.setHeight(r.height() + 2 * penWidth);
2957
0
    }
2958
2959
0
    if (cl->hasRectClip) {
2960
        // inline contains() for performance (we know the rects are normalized)
2961
0
        const QRect &r1 = cl->clipRect;
2962
0
        return (r.left() >= r1.left() && r.right() <= r1.right()
2963
0
                && r.top() >= r1.top() && r.bottom() <= r1.bottom());
2964
0
    } else {
2965
0
        return qt_region_strictContains(cl->clipRegion, r);
2966
0
    }
2967
0
}
2968
2969
inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
2970
                                                   int penWidth) const
2971
0
{
2972
0
    const QRectF norm = rect.normalized();
2973
0
    if (norm.left() <= qreal(INT_MIN) || norm.top() <= qreal(INT_MIN)
2974
0
            || norm.right() > qreal(INT_MAX) || norm.bottom() > qreal(INT_MAX)
2975
0
            || norm.width() > qreal(INT_MAX) || norm.height() > qreal(INT_MAX))
2976
0
        return false;
2977
0
    return isUnclipped(norm.toAlignedRect(), penWidth);
2978
0
}
2979
2980
inline ProcessSpans
2981
QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
2982
                                        const QSpanData *data) const
2983
0
{
2984
0
    return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2985
0
}
2986
2987
inline ProcessSpans
2988
QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
2989
                                        const QSpanData *data) const
2990
0
{
2991
0
    return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
2992
0
}
2993
2994
inline ProcessSpans
2995
QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
2996
                                      const QSpanData *data) const
2997
0
{
2998
0
    Q_Q(const QRasterPaintEngine);
2999
0
    const QRasterPaintEngineState *s = q->state();
3000
3001
0
    if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
3002
0
        return data->blend;
3003
0
    const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
3004
0
    return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
3005
0
}
3006
3007
struct VisibleGlyphRange
3008
{
3009
    int begin;
3010
    int end;
3011
};
3012
3013
static VisibleGlyphRange visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
3014
                                           glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
3015
0
{
3016
0
    QFixed clipLeft = QFixed::fromReal(clip.left() - 1);
3017
0
    QFixed clipRight = QFixed::fromReal(clip.right() + 1);
3018
0
    QFixed clipTop = QFixed::fromReal(clip.top() - 1);
3019
0
    QFixed clipBottom = QFixed::fromReal(clip.bottom() + 1);
3020
3021
0
    int first = 0;
3022
0
    while (first < numGlyphs) {
3023
0
        glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[first]);
3024
0
        QFixed left = metrics.x + positions[first].x;
3025
0
        QFixed top = metrics.y + positions[first].y;
3026
0
        QFixed right = left + metrics.width;
3027
0
        QFixed bottom = top + metrics.height;
3028
0
        if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3029
0
            break;
3030
0
        ++first;
3031
0
    }
3032
0
    int last = numGlyphs - 1;
3033
0
    while (last > first) {
3034
0
        glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[last]);
3035
0
        QFixed left = metrics.x + positions[last].x;
3036
0
        QFixed top = metrics.y + positions[last].y;
3037
0
        QFixed right = left + metrics.width;
3038
0
        QFixed bottom = top + metrics.height;
3039
0
        if (left < clipRight && right > clipLeft && top < clipBottom && bottom > clipTop)
3040
0
            break;
3041
0
        --last;
3042
0
    }
3043
0
    return {first, last + 1};
3044
0
}
3045
3046
/*!
3047
   \reimp
3048
*/
3049
void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
3050
0
{
3051
0
    if (textItem->numGlyphs == 0)
3052
0
        return;
3053
3054
0
    ensurePen();
3055
0
    ensureRasterState();
3056
3057
0
    QTransform matrix = state()->matrix;
3058
3059
0
    QFontEngine *fontEngine = textItem->fontEngine();
3060
0
    if (shouldDrawCachedGlyphs(fontEngine, matrix)) {
3061
0
        drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
3062
0
                         fontEngine);
3063
0
    } else if (matrix.type() < QTransform::TxProject) {
3064
0
        bool invertible;
3065
0
        QTransform invMat = matrix.inverted(&invertible);
3066
0
        if (!invertible)
3067
0
            return;
3068
3069
0
        const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3070
0
                                             textItem->fontEngine(), textItem->glyphs,
3071
0
                                             textItem->glyphPositions, textItem->numGlyphs);
3072
0
        QStaticTextItem copy = *textItem;
3073
0
        copy.glyphs += range.begin;
3074
0
        copy.glyphPositions += range.begin;
3075
0
        copy.numGlyphs = range.end - range.begin;
3076
0
        QPaintEngineEx::drawStaticTextItem(&copy);
3077
0
    } else {
3078
0
        QPaintEngineEx::drawStaticTextItem(textItem);
3079
0
    }
3080
0
}
3081
3082
/*!
3083
    \reimp
3084
*/
3085
void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
3086
0
{
3087
0
    const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
3088
3089
#ifdef QT_DEBUG_DRAW
3090
    Q_D(QRasterPaintEngine);
3091
    fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
3092
           p.x(), p.y(), QStringView(ti.chars, ti.num_chars).toLatin1().data(),
3093
           d->glyphCacheFormat);
3094
#endif
3095
3096
0
    if (ti.glyphs.numGlyphs == 0)
3097
0
        return;
3098
0
    ensurePen();
3099
0
    ensureRasterState();
3100
3101
0
    QRasterPaintEngineState *s = state();
3102
0
    QTransform matrix = s->matrix;
3103
3104
0
    if (shouldDrawCachedGlyphs(ti.fontEngine, matrix)) {
3105
0
        QVarLengthArray<QFixedPoint> positions;
3106
0
        QVarLengthArray<glyph_t> glyphs;
3107
3108
0
        matrix.translate(p.x(), p.y());
3109
0
        ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
3110
3111
0
        drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
3112
0
    } else if (matrix.type() < QTransform::TxProject
3113
0
               && ti.fontEngine->supportsTransformation(matrix)) {
3114
0
        bool invertible;
3115
0
        QTransform invMat = matrix.inverted(&invertible);
3116
0
        if (!invertible)
3117
0
            return;
3118
3119
0
        QVarLengthArray<QFixedPoint> positions;
3120
0
        QVarLengthArray<glyph_t> glyphs;
3121
3122
0
        ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
3123
0
                                         ti.flags, glyphs, positions);
3124
0
        const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
3125
0
                                             ti.fontEngine, glyphs.data(), positions.data(),
3126
0
                                             glyphs.size());
3127
3128
0
        if (range.begin >= range.end)
3129
0
            return;
3130
3131
0
        QStaticTextItem staticTextItem;
3132
0
        staticTextItem.color = s->pen.color();
3133
0
        staticTextItem.font = s->font;
3134
0
        staticTextItem.setFontEngine(ti.fontEngine);
3135
0
        staticTextItem.numGlyphs = range.end - range.begin;
3136
0
        staticTextItem.glyphs = glyphs.data() + range.begin;
3137
0
        staticTextItem.glyphPositions = positions.data() + range.begin;
3138
0
        QPaintEngineEx::drawStaticTextItem(&staticTextItem);
3139
0
    } else {
3140
0
        QPaintEngineEx::drawTextItem(p, ti);
3141
0
    }
3142
0
}
3143
3144
/*!
3145
    \reimp
3146
*/
3147
void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
3148
0
{
3149
0
    Q_D(QRasterPaintEngine);
3150
0
    QRasterPaintEngineState *s = state();
3151
3152
0
    ensurePen();
3153
0
    if (!s->penData.blend)
3154
0
        return;
3155
3156
0
    if (!s->flags.fast_pen) {
3157
0
        QPaintEngineEx::drawPoints(points, pointCount);
3158
0
        return;
3159
0
    }
3160
3161
0
    QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3162
0
    stroker.drawPoints(points, pointCount);
3163
0
}
3164
3165
3166
void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
3167
0
{
3168
0
    Q_D(QRasterPaintEngine);
3169
0
    QRasterPaintEngineState *s = state();
3170
3171
0
    ensurePen();
3172
0
    if (!s->penData.blend)
3173
0
        return;
3174
3175
0
    if (!s->flags.fast_pen) {
3176
0
        QPaintEngineEx::drawPoints(points, pointCount);
3177
0
        return;
3178
0
    }
3179
3180
0
    QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3181
0
    stroker.drawPoints(points, pointCount);
3182
0
}
3183
3184
/*!
3185
    \reimp
3186
*/
3187
void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
3188
0
{
3189
#ifdef QT_DEBUG_DRAW
3190
    qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
3191
#endif
3192
0
    Q_D(QRasterPaintEngine);
3193
0
    QRasterPaintEngineState *s = state();
3194
3195
0
    ensurePen();
3196
0
    if (!s->penData.blend)
3197
0
        return;
3198
3199
0
    if (s->flags.fast_pen) {
3200
0
        QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3201
0
        for (int i=0; i<lineCount; ++i) {
3202
0
            const QLine &l = lines[i];
3203
0
            stroker.drawLine(l.p1(), l.p2());
3204
0
        }
3205
0
    } else {
3206
0
        QPaintEngineEx::drawLines(lines, lineCount);
3207
0
    }
3208
0
}
3209
3210
void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
3211
                                                     qreal width,
3212
                                                     int *dashIndex,
3213
                                                     qreal *dashOffset,
3214
                                                     bool *inDash)
3215
0
{
3216
0
    Q_Q(QRasterPaintEngine);
3217
0
    QRasterPaintEngineState *s = q->state();
3218
3219
0
    const QPen &pen = s->lastPen;
3220
0
    const bool squareCap = (pen.capStyle() == Qt::SquareCap);
3221
0
    const QList<qreal> pattern = pen.dashPattern();
3222
3223
0
    qreal patternLength = 0;
3224
0
    for (int i = 0; i < pattern.size(); ++i)
3225
0
        patternLength += pattern.at(i);
3226
3227
0
    if (patternLength <= 0)
3228
0
        return;
3229
3230
0
    qreal length = line.length();
3231
0
    Q_ASSERT(length > 0);
3232
0
    if (length / (patternLength * width) > QDashStroker::repetitionLimit()) {
3233
0
        rasterizer->rasterizeLine(line.p1(), line.p2(), width / length, squareCap);
3234
0
        return;
3235
0
    }
3236
3237
0
    while (length > 0) {
3238
0
        const bool rasterize = *inDash;
3239
0
        qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
3240
0
        QLineF l = line;
3241
3242
0
        if (dash >= length) {
3243
0
            dash = line.length();  // Avoid accumulated precision error in 'length'
3244
0
            *dashOffset += dash / width;
3245
0
            length = 0;
3246
0
        } else {
3247
0
            *dashOffset = 0;
3248
0
            *inDash = !(*inDash);
3249
0
            if (++*dashIndex >= pattern.size())
3250
0
                *dashIndex = 0;
3251
0
            length -= dash;
3252
0
            l.setLength(dash);
3253
0
            line.setP1(l.p2());
3254
0
        }
3255
3256
0
        if (rasterize && dash > 0)
3257
0
            rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
3258
0
    }
3259
0
}
3260
3261
/*!
3262
    \reimp
3263
*/
3264
void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
3265
0
{
3266
#ifdef QT_DEBUG_DRAW
3267
    qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount;
3268
#endif
3269
0
    Q_D(QRasterPaintEngine);
3270
0
    QRasterPaintEngineState *s = state();
3271
3272
0
    ensurePen();
3273
0
    if (!s->penData.blend)
3274
0
        return;
3275
0
    if (s->flags.fast_pen) {
3276
0
        QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped);
3277
0
        for (int i=0; i<lineCount; ++i) {
3278
0
            QLineF line = lines[i];
3279
0
            stroker.drawLine(line.p1(), line.p2());
3280
0
        }
3281
0
    } else {
3282
0
        QPaintEngineEx::drawLines(lines, lineCount);
3283
0
    }
3284
0
}
3285
3286
3287
/*!
3288
    \reimp
3289
*/
3290
void QRasterPaintEngine::drawEllipse(const QRectF &rect)
3291
0
{
3292
0
    Q_D(QRasterPaintEngine);
3293
0
    QRasterPaintEngineState *s = state();
3294
3295
0
    ensurePen();
3296
0
    if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
3297
0
           || (qpen_style(s->lastPen) == Qt::NoPen))
3298
0
        && !s->flags.antialiased
3299
0
        && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
3300
0
        && !rect.isEmpty()
3301
0
        && s->matrix.type() <= QTransform::TxScale) // no shear
3302
0
    {
3303
0
        ensureBrush();
3304
0
        const QRectF r = s->matrix.mapRect(rect);
3305
0
        ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
3306
0
        ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
3307
0
        const QRect brect = QRect(int(r.x()), int(r.y()),
3308
0
                                  int_dim(r.x(), r.width()),
3309
0
                                  int_dim(r.y(), r.height()));
3310
0
        if (brect == r) {
3311
0
            drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
3312
0
                                   &s->penData, &s->brushData);
3313
0
            return;
3314
0
        }
3315
0
    }
3316
0
    QPaintEngineEx::drawEllipse(rect);
3317
0
}
3318
3319
3320
#ifdef Q_OS_WIN
3321
/*!
3322
    \internal
3323
*/
3324
void QRasterPaintEngine::setDC(HDC hdc) {
3325
    Q_D(QRasterPaintEngine);
3326
    d->hdc = hdc;
3327
}
3328
3329
/*!
3330
    \internal
3331
*/
3332
HDC QRasterPaintEngine::getDC() const
3333
{
3334
    Q_D(const QRasterPaintEngine);
3335
    return d->hdc;
3336
}
3337
3338
/*!
3339
    \internal
3340
*/
3341
void QRasterPaintEngine::releaseDC(HDC) const
3342
{
3343
}
3344
3345
#endif
3346
3347
/*!
3348
    \internal
3349
*/
3350
bool QRasterPaintEngine::requiresPretransformedGlyphPositions(QFontEngine *fontEngine, const QTransform &m) const
3351
0
{
3352
    // Cached glyphs always require pretransformed positions
3353
0
    if (shouldDrawCachedGlyphs(fontEngine, m))
3354
0
        return true;
3355
3356
    // Otherwise let the base-class decide based on the transform
3357
0
    return QPaintEngineEx::requiresPretransformedGlyphPositions(fontEngine, m);
3358
0
}
3359
3360
/*!
3361
   Returns whether glyph caching is supported by the font engine
3362
   \a fontEngine with the given transform \a m applied.
3363
*/
3364
bool QRasterPaintEngine::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const
3365
0
{
3366
    // The raster engine does not support projected cached glyph drawing
3367
0
    if (m.type() >= QTransform::TxProject)
3368
0
        return false;
3369
3370
    // The font engine might not support filling the glyph cache
3371
    // with the given transform applied, in which case we need to
3372
    // fall back to the QPainterPath code-path. This does not apply
3373
    // for engines with internal caching, as we don't use the engine
3374
    // to fill up our cache in that case.
3375
0
    if (!fontEngine->hasInternalCaching() && !fontEngine->supportsTransformation(m))
3376
0
        return false;
3377
3378
0
    if (fontEngine->supportsTransformation(m) && !fontEngine->isSmoothlyScalable)
3379
0
        return true;
3380
3381
0
    return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, m);
3382
0
}
3383
3384
/*!
3385
    \internal
3386
*/
3387
QPoint QRasterPaintEngine::coordinateOffset() const
3388
0
{
3389
0
    return QPoint(0, 0);
3390
0
}
3391
3392
void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
3393
0
{
3394
0
    Q_ASSERT(fg);
3395
0
    if (!fg->blend)
3396
0
        return;
3397
0
    Q_D(QRasterPaintEngine);
3398
3399
0
    Q_ASSERT(image.depth() == 1);
3400
3401
0
    const int spanCount = 512;
3402
0
    QT_FT_Span spans[spanCount];
3403
0
    int n = 0;
3404
3405
    // Boundaries
3406
0
    int w = image.width();
3407
0
    int h = image.height();
3408
0
    int px = qRound(pos.x());
3409
0
    int py = qRound(pos.y());
3410
0
    int ymax = qMin(py + h, d->rasterBuffer->height());
3411
0
    int ymin = qMax(py, 0);
3412
0
    int xmax = qMin(px + w, d->rasterBuffer->width());
3413
0
    int xmin = qMax(px, 0);
3414
3415
0
    int x_offset = xmin - px;
3416
3417
0
    QImage::Format format = image.format();
3418
0
    for (int y = ymin; y < ymax; ++y) {
3419
0
        const uchar *src = image.scanLine(y - py);
3420
0
        if (format == QImage::Format_MonoLSB) {
3421
0
            for (int x = 0; x < xmax - xmin; ++x) {
3422
0
                int src_x = x + x_offset;
3423
0
                uchar pixel = src[src_x >> 3];
3424
0
                if (!pixel) {
3425
0
                    x += 7 - (src_x%8);
3426
0
                    continue;
3427
0
                }
3428
0
                if (pixel & (0x1 << (src_x & 7))) {
3429
0
                    spans[n].x = xmin + x;
3430
0
                    spans[n].y = y;
3431
0
                    spans[n].coverage = 255;
3432
0
                    int len = 1;
3433
0
                    while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
3434
0
                        ++src_x;
3435
0
                        ++len;
3436
0
                    }
3437
0
                    spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3438
0
                    x += len;
3439
0
                    ++n;
3440
0
                    if (n == spanCount) {
3441
0
                        fg->blend(n, spans, fg);
3442
0
                        n = 0;
3443
0
                    }
3444
0
                }
3445
0
            }
3446
0
        } else {
3447
0
            for (int x = 0; x < xmax - xmin; ++x) {
3448
0
                int src_x = x + x_offset;
3449
0
                uchar pixel = src[src_x >> 3];
3450
0
                if (!pixel) {
3451
0
                    x += 7 - (src_x%8);
3452
0
                    continue;
3453
0
                }
3454
0
                if (pixel & (0x80 >> (x & 7))) {
3455
0
                    spans[n].x = xmin + x;
3456
0
                    spans[n].y = y;
3457
0
                    spans[n].coverage = 255;
3458
0
                    int len = 1;
3459
0
                    while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
3460
0
                        ++src_x;
3461
0
                        ++len;
3462
0
                    }
3463
0
                    spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
3464
0
                    x += len;
3465
0
                    ++n;
3466
0
                    if (n == spanCount) {
3467
0
                        fg->blend(n, spans, fg);
3468
0
                        n = 0;
3469
0
                    }
3470
0
                }
3471
0
            }
3472
0
        }
3473
0
    }
3474
0
    if (n) {
3475
0
        fg->blend(n, spans, fg);
3476
0
        n = 0;
3477
0
    }
3478
0
}
3479
3480
/*!
3481
    \enum QRasterPaintEngine::ClipType
3482
    \internal
3483
3484
    \value RectClip Indicates that the currently set clip is a single rectangle.
3485
    \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
3486
*/
3487
3488
/*!
3489
    \internal
3490
    Returns the type of the clip currently set.
3491
*/
3492
QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
3493
0
{
3494
0
    Q_D(const QRasterPaintEngine);
3495
3496
0
    const QClipData *clip = d->clip();
3497
0
    if (!clip || clip->hasRectClip)
3498
0
        return RectClip;
3499
0
    else
3500
0
        return ComplexClip;
3501
0
}
3502
3503
/*!
3504
    \internal
3505
    Returns the bounding rect of the currently set clip.
3506
*/
3507
QRectF QRasterPaintEngine::clipBoundingRect() const
3508
0
{
3509
0
    Q_D(const QRasterPaintEngine);
3510
3511
0
    const QClipData *clip = d->clip();
3512
3513
0
    if (!clip)
3514
0
        return d->deviceRect;
3515
3516
0
    if (clip->hasRectClip)
3517
0
        return clip->clipRect;
3518
3519
0
    return QRectF(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
3520
0
}
3521
3522
void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
3523
0
{
3524
0
    Q_Q(QRasterPaintEngine);
3525
0
    QRasterPaintEngineState *s = q->state();
3526
3527
0
    rasterizer->setAntialiased(s->flags.antialiased);
3528
3529
0
    QRect clipRect(deviceRect);
3530
0
    ProcessSpans blend;
3531
    // ### get from optimized rectbased QClipData
3532
3533
0
    const QClipData *c = clip();
3534
0
    if (c) {
3535
0
        const QRect r(QPoint(c->xmin, c->ymin),
3536
0
                      QSize(c->xmax - c->xmin, c->ymax - c->ymin));
3537
0
        clipRect = clipRect.intersected(r);
3538
0
        blend = data->blend;
3539
0
    } else {
3540
0
        blend = data->unclipped_blend;
3541
0
    }
3542
3543
0
    rasterizer->setClipRect(clipRect);
3544
0
    rasterizer->initialize(blend, data);
3545
0
}
3546
3547
void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3548
                                          ProcessSpans callback,
3549
                                          QSpanData *spanData, QRasterBuffer *rasterBuffer)
3550
0
{
3551
0
    if (!callback || !outline)
3552
0
        return;
3553
3554
0
    Q_Q(QRasterPaintEngine);
3555
0
    QRasterPaintEngineState *s = q->state();
3556
3557
0
    if (!s->flags.antialiased) {
3558
0
        initializeRasterizer(spanData);
3559
3560
0
        const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3561
0
                                      ? Qt::WindingFill
3562
0
                                      : Qt::OddEvenFill;
3563
3564
0
        rasterizer->rasterize(outline, fillRule);
3565
0
        return;
3566
0
    }
3567
3568
0
    rasterize(outline, callback, (void *)spanData, rasterBuffer);
3569
0
}
3570
3571
void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
3572
                                          ProcessSpans callback,
3573
                                          void *userData, QRasterBuffer *)
3574
0
{
3575
0
    if (!callback || !outline)
3576
0
        return;
3577
3578
0
    Q_Q(QRasterPaintEngine);
3579
0
    QRasterPaintEngineState *s = q->state();
3580
3581
0
    if (!s->flags.antialiased) {
3582
0
        rasterizer->setAntialiased(s->flags.antialiased);
3583
0
        rasterizer->setClipRect(deviceRect);
3584
0
        rasterizer->initialize(callback, userData);
3585
3586
0
        const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
3587
0
                                      ? Qt::WindingFill
3588
0
                                      : Qt::OddEvenFill;
3589
3590
0
        rasterizer->rasterize(outline, fillRule);
3591
0
        return;
3592
0
    }
3593
3594
0
    QT_FT_BBox clip_box = { deviceRect.x(),
3595
0
                            deviceRect.y(),
3596
0
                            deviceRect.x() + deviceRect.width(),
3597
0
                            deviceRect.y() + deviceRect.height() };
3598
3599
0
    QT_FT_Raster_Params rasterParams;
3600
0
    rasterParams.target = nullptr;
3601
0
    rasterParams.source = outline;
3602
0
    rasterParams.flags = QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT | QT_FT_RASTER_FLAG_CLIP;
3603
0
    rasterParams.gray_spans = callback;
3604
0
    rasterParams.black_spans = nullptr;
3605
0
    rasterParams.bit_test = nullptr;
3606
0
    rasterParams.bit_set = nullptr;
3607
0
    rasterParams.user = userData;
3608
0
    rasterParams.clip_box = clip_box;
3609
3610
0
    if (QT_MANGLE_NAMESPACE(qt_ft_grays_raster).raster_render(*grayRaster.data(), &rasterParams))
3611
0
        qWarning("QPainter: Rasterization of primitive failed");
3612
0
}
3613
3614
void QRasterPaintEnginePrivate::updateClipping()
3615
0
{
3616
0
    Q_Q(QRasterPaintEngine);
3617
0
    QRasterPaintEngineState *s = q->state();
3618
3619
0
    if (!s->clipEnabled)
3620
0
        return;
3621
3622
0
    qrasterpaintengine_state_setNoClip(s);
3623
0
    replayClipOperations();
3624
0
}
3625
3626
void QRasterPaintEnginePrivate::recalculateFastImages()
3627
0
{
3628
0
    Q_Q(QRasterPaintEngine);
3629
0
    QRasterPaintEngineState *s = q->state();
3630
3631
0
    s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
3632
0
                           && s->matrix.type() <= QTransform::TxShear;
3633
0
}
3634
3635
bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
3636
0
{
3637
0
    Q_Q(const QRasterPaintEngine);
3638
0
    const QRasterPaintEngineState *s = q->state();
3639
3640
0
    return s->flags.fast_images
3641
0
           && (mode == QPainter::CompositionMode_SourceOver
3642
0
               || (mode == QPainter::CompositionMode_Source
3643
0
                   && !image.hasAlphaChannel()));
3644
0
}
3645
3646
bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image, const QPointF &pt, const QRectF &sr) const
3647
0
{
3648
0
    Q_Q(const QRasterPaintEngine);
3649
3650
0
    if (!(mode == QPainter::CompositionMode_Source
3651
0
          || (mode == QPainter::CompositionMode_SourceOver
3652
0
              && !image.hasAlphaChannel())))
3653
0
        return false;
3654
3655
0
    const QRasterPaintEngineState *s = q->state();
3656
0
    Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate);
3657
3658
0
    if (s->intOpacity != 256
3659
0
        || image.depth() < 8
3660
0
        || ((s->renderHints & (QPainter::SmoothPixmapTransform | QPainter::Antialiasing))
3661
0
            && (!isPixelAligned(pt) || !isPixelAligned(sr))))
3662
0
        return false;
3663
3664
0
    QImage::Format dFormat = rasterBuffer->format;
3665
0
    QImage::Format sFormat = image.format();
3666
    // Formats must match or source format must be an opaque version of destination format
3667
0
    if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha)
3668
0
        dFormat = qt_maybeDataCompatibleOpaqueVersion(dFormat);
3669
0
    return (dFormat == sFormat);
3670
0
}
3671
3672
QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
3673
0
{
3674
0
    Q_ASSERT(image.depth() == 1);
3675
3676
0
    const QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
3677
0
    QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
3678
0
    if (sourceImage.isNull() || dest.isNull())
3679
0
        return image; // we must have run out of memory
3680
3681
0
    QRgb fg = qPremultiply(color.rgba());
3682
0
    QRgb bg = 0;
3683
3684
0
    int height = sourceImage.height();
3685
0
    int width = sourceImage.width();
3686
0
    for (int y=0; y<height; ++y) {
3687
0
        const uchar *source = sourceImage.constScanLine(y);
3688
0
        QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
3689
0
        for (int x=0; x < width; ++x)
3690
0
            target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
3691
0
    }
3692
0
    return dest;
3693
0
}
3694
3695
QRasterBuffer::~QRasterBuffer()
3696
0
{
3697
0
}
3698
3699
void QRasterBuffer::init()
3700
0
{
3701
0
    compositionMode = QPainter::CompositionMode_SourceOver;
3702
0
    monoDestinationWithClut = false;
3703
0
    destColor0 = 0;
3704
0
    destColor1 = 0;
3705
0
}
3706
3707
QImage::Format QRasterBuffer::prepare(QImage *image)
3708
0
{
3709
0
    m_buffer = (uchar *)image->bits();
3710
0
    m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
3711
0
    m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
3712
0
    bytes_per_pixel = image->depth()/8;
3713
0
    bytes_per_line = image->bytesPerLine();
3714
3715
0
    format = image->format();
3716
0
    colorSpace = image->colorSpace();
3717
0
    if (image->depth() == 1 && image->colorTable().size() == 2) {
3718
0
        monoDestinationWithClut = true;
3719
0
        const QList<QRgb> colorTable = image->colorTable();
3720
0
        destColor0 = qPremultiply(colorTable[0]);
3721
0
        destColor1 = qPremultiply(colorTable[1]);
3722
0
    }
3723
3724
0
    return format;
3725
0
}
3726
3727
QClipData::QClipData(int height)
3728
0
{
3729
0
    clipSpanHeight = height;
3730
0
    m_clipLines = nullptr;
3731
3732
0
    allocated = 0;
3733
0
    m_spans = nullptr;
3734
0
    xmin = xmax = ymin = ymax = 0;
3735
0
    count = 0;
3736
3737
0
    enabled = true;
3738
0
    hasRectClip = hasRegionClip = false;
3739
0
}
3740
3741
QClipData::~QClipData()
3742
0
{
3743
0
    if (m_clipLines)
3744
0
        free(m_clipLines);
3745
0
    if (m_spans)
3746
0
        free(m_spans);
3747
0
}
3748
3749
void QClipData::initialize()
3750
0
{
3751
0
    if (m_spans)
3752
0
        return;
3753
3754
0
    if (!m_clipLines)
3755
0
        m_clipLines = (ClipLine *)calloc(clipSpanHeight, sizeof(ClipLine));
3756
3757
0
    Q_CHECK_PTR(m_clipLines);
3758
0
    QT_TRY {
3759
0
        allocated = clipSpanHeight;
3760
0
        count = 0;
3761
0
        QT_TRY {
3762
0
            if (hasRegionClip) {
3763
0
                const auto rects = clipRegion.begin();
3764
0
                const int numRects = clipRegion.rectCount();
3765
0
                const int maxSpans = (ymax - ymin) * numRects;
3766
0
                allocated = qMax(allocated, maxSpans);
3767
0
                m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3768
0
                Q_CHECK_PTR(m_spans);
3769
3770
0
                int y = 0;
3771
0
                int firstInBand = 0;
3772
0
                while (firstInBand < numRects) {
3773
0
                    const int currMinY = rects[firstInBand].y();
3774
0
                    const int currMaxY = currMinY + rects[firstInBand].height();
3775
3776
0
                    while (y < currMinY) {
3777
0
                        m_clipLines[y].spans = nullptr;
3778
0
                        m_clipLines[y].count = 0;
3779
0
                        ++y;
3780
0
                    }
3781
3782
0
                    int lastInBand = firstInBand;
3783
0
                    while (lastInBand + 1 < numRects && rects[lastInBand+1].top() == y)
3784
0
                        ++lastInBand;
3785
3786
0
                    while (y < currMaxY) {
3787
3788
0
                        m_clipLines[y].spans = m_spans + count;
3789
0
                        m_clipLines[y].count = lastInBand - firstInBand + 1;
3790
3791
0
                        for (int r = firstInBand; r <= lastInBand; ++r) {
3792
0
                            const QRect &currRect = rects[r];
3793
0
                            QT_FT_Span *span = m_spans + count;
3794
0
                            span->x = currRect.x();
3795
0
                            span->len = currRect.width();
3796
0
                            span->y = y;
3797
0
                            span->coverage = 255;
3798
0
                            ++count;
3799
0
                        }
3800
0
                        ++y;
3801
0
                    }
3802
3803
0
                    firstInBand = lastInBand + 1;
3804
0
                }
3805
3806
0
                Q_ASSERT(count <= allocated);
3807
3808
0
                while (y < clipSpanHeight) {
3809
0
                    m_clipLines[y].spans = nullptr;
3810
0
                    m_clipLines[y].count = 0;
3811
0
                    ++y;
3812
0
                }
3813
3814
0
                return;
3815
0
            }
3816
3817
0
            m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
3818
0
            Q_CHECK_PTR(m_spans);
3819
3820
0
            if (hasRectClip) {
3821
0
                int y = 0;
3822
0
                while (y < ymin) {
3823
0
                    m_clipLines[y].spans = nullptr;
3824
0
                    m_clipLines[y].count = 0;
3825
0
                    ++y;
3826
0
                }
3827
3828
0
                const int len = clipRect.width();
3829
0
                while (y < ymax) {
3830
0
                    QT_FT_Span *span = m_spans + count;
3831
0
                    span->x = xmin;
3832
0
                    span->len = len;
3833
0
                    span->y = y;
3834
0
                    span->coverage = 255;
3835
0
                    ++count;
3836
3837
0
                    m_clipLines[y].spans = span;
3838
0
                    m_clipLines[y].count = 1;
3839
0
                    ++y;
3840
0
                }
3841
3842
0
                while (y < clipSpanHeight) {
3843
0
                    m_clipLines[y].spans = nullptr;
3844
0
                    m_clipLines[y].count = 0;
3845
0
                    ++y;
3846
0
                }
3847
0
            }
3848
0
        } QT_CATCH(...) {
3849
0
            free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
3850
0
            m_spans = nullptr;
3851
0
            QT_RETHROW;
3852
0
        }
3853
0
    } QT_CATCH(...) {
3854
0
        free(m_clipLines); // same for clipLines
3855
0
        m_clipLines = nullptr;
3856
0
        QT_RETHROW;
3857
0
    }
3858
0
}
3859
3860
void QClipData::fixup()
3861
0
{
3862
0
    Q_ASSERT(m_spans);
3863
3864
0
    if (count == 0) {
3865
0
        ymin = ymax = xmin = xmax = 0;
3866
0
        return;
3867
0
    }
3868
3869
0
    int y = -1;
3870
0
    ymin = m_spans[0].y;
3871
0
    ymax = m_spans[count-1].y + 1;
3872
0
    xmin = INT_MAX;
3873
0
    xmax = 0;
3874
3875
0
    const int firstLeft = m_spans[0].x;
3876
0
    const int firstRight = m_spans[0].x + m_spans[0].len;
3877
0
    bool isRect = true;
3878
3879
0
    for (int i = 0; i < count; ++i) {
3880
0
        QT_FT_Span_& span = m_spans[i];
3881
3882
0
        if (span.y != y) {
3883
0
            if (span.y != y + 1 && y != -1)
3884
0
                isRect = false;
3885
0
            y = span.y;
3886
0
            m_clipLines[y].spans = &span;
3887
0
            m_clipLines[y].count = 1;
3888
0
        } else
3889
0
            ++m_clipLines[y].count;
3890
3891
0
        const int spanLeft = span.x;
3892
0
        const int spanRight = spanLeft + span.len;
3893
3894
0
        if (spanLeft < xmin)
3895
0
            xmin = spanLeft;
3896
3897
0
        if (spanRight > xmax)
3898
0
            xmax = spanRight;
3899
3900
0
        if (spanLeft != firstLeft || spanRight != firstRight)
3901
0
            isRect = false;
3902
0
    }
3903
3904
0
    if (isRect) {
3905
0
        hasRectClip = true;
3906
0
        clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
3907
0
    }
3908
0
}
3909
3910
/*
3911
    Convert \a rect to clip spans.
3912
 */
3913
void QClipData::setClipRect(const QRect &rect)
3914
0
{
3915
0
    if (hasRectClip && rect == clipRect)
3916
0
        return;
3917
3918
//    qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
3919
0
    hasRectClip = true;
3920
0
    hasRegionClip = false;
3921
0
    clipRect = rect;
3922
3923
0
    xmin = rect.x();
3924
0
    xmax = rect.x() + rect.width();
3925
0
    ymin = qMin(rect.y(), clipSpanHeight);
3926
0
    ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
3927
3928
0
    if (m_spans) {
3929
0
        free(m_spans);
3930
0
        m_spans = nullptr;
3931
0
    }
3932
3933
//    qDebug() << xmin << xmax << ymin << ymax;
3934
0
}
3935
3936
/*
3937
    Convert \a region to clip spans.
3938
 */
3939
void QClipData::setClipRegion(const QRegion &region)
3940
0
{
3941
0
    if (region.rectCount() == 1) {
3942
0
        setClipRect(region.boundingRect());
3943
0
        return;
3944
0
    }
3945
3946
0
    hasRegionClip = true;
3947
0
    hasRectClip = false;
3948
0
    clipRegion = region;
3949
3950
0
    { // set bounding rect
3951
0
        const QRect rect = region.boundingRect();
3952
0
        xmin = rect.x();
3953
0
        xmax = rect.x() + rect.width();
3954
0
        ymin = rect.y();
3955
0
        ymax = rect.y() + rect.height();
3956
0
    }
3957
3958
0
    if (m_spans) {
3959
0
        free(m_spans);
3960
0
        m_spans = nullptr;
3961
0
    }
3962
3963
0
}
3964
3965
/*!
3966
    \internal
3967
    spans must be sorted on y
3968
*/
3969
static const QT_FT_Span *qt_intersect_spans(const QClipData *clip, int *currentClip,
3970
                                            const QT_FT_Span *spans, const QT_FT_Span *end,
3971
                                            QT_FT_Span **outSpans, int available)
3972
0
{
3973
0
    const_cast<QClipData *>(clip)->initialize();
3974
3975
0
    QT_FT_Span *out = *outSpans;
3976
3977
0
    const QT_FT_Span *clipSpans = clip->m_spans + *currentClip;
3978
0
    const QT_FT_Span *clipEnd = clip->m_spans + clip->count;
3979
3980
0
    while (available && spans < end ) {
3981
0
        if (clipSpans >= clipEnd) {
3982
0
            spans = end;
3983
0
            break;
3984
0
        }
3985
0
        if (clipSpans->y > spans->y) {
3986
0
            ++spans;
3987
0
            continue;
3988
0
        }
3989
0
        if (spans->y != clipSpans->y) {
3990
0
            if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
3991
0
                clipSpans = clip->m_clipLines[spans->y].spans;
3992
0
            else
3993
0
                ++clipSpans;
3994
0
            continue;
3995
0
        }
3996
0
        Q_ASSERT(spans->y == clipSpans->y);
3997
3998
0
        int sx1 = spans->x;
3999
0
        int sx2 = sx1 + spans->len;
4000
0
        int cx1 = clipSpans->x;
4001
0
        int cx2 = cx1 + clipSpans->len;
4002
4003
0
        if (cx1 < sx1 && cx2 < sx1) {
4004
0
            ++clipSpans;
4005
0
            continue;
4006
0
        } else if (sx1 < cx1 && sx2 < cx1) {
4007
0
            ++spans;
4008
0
            continue;
4009
0
        }
4010
0
        int x = qMax(sx1, cx1);
4011
0
        int len = qMin(sx2, cx2) - x;
4012
0
        if (len) {
4013
0
            out->x = qMax(sx1, cx1);
4014
0
            out->len = qMin(sx2, cx2) - out->x;
4015
0
            out->y = spans->y;
4016
0
            out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
4017
0
            ++out;
4018
0
            --available;
4019
0
        }
4020
0
        if (sx2 < cx2) {
4021
0
            ++spans;
4022
0
        } else {
4023
0
            ++clipSpans;
4024
0
        }
4025
0
    }
4026
4027
0
    *outSpans = out;
4028
0
    *currentClip = clipSpans - clip->m_spans;
4029
0
    return spans;
4030
0
}
4031
4032
static void qt_span_fill_clipped(int spanCount, const QT_FT_Span *spans, void *userData)
4033
0
{
4034
//     qDebug() << "qt_span_fill_clipped" << spanCount;
4035
0
    QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4036
4037
0
    Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4038
4039
0
    const int NSPANS = 512;
4040
0
    Q_DECL_UNINITIALIZED QT_FT_Span cspans[NSPANS];
4041
0
    int currentClip = 0;
4042
0
    const QT_FT_Span *end = spans + spanCount;
4043
0
    while (spans < end) {
4044
0
        QT_FT_Span *clipped = cspans;
4045
0
        spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
4046
//         qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
4047
//                  << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
4048
4049
0
        if (clipped - cspans)
4050
0
            fillData->unclipped_blend(clipped - cspans, cspans, fillData);
4051
0
    }
4052
0
}
4053
4054
/*
4055
    \internal
4056
    Clip spans to \a{clip}-rectangle.
4057
    Returns number of unclipped spans
4058
*/
4059
static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
4060
                              const QRect &clip)
4061
0
{
4062
0
    const int minx = clip.left();
4063
0
    const int miny = clip.top();
4064
0
    const int maxx = clip.right();
4065
0
    const int maxy = clip.bottom();
4066
4067
0
    QT_FT_Span *end = spans + numSpans;
4068
0
    while (spans < end) {
4069
0
        if (spans->y >= miny)
4070
0
            break;
4071
0
        ++spans;
4072
0
    }
4073
4074
0
    QT_FT_Span *s = spans;
4075
0
    while (s < end) {
4076
0
        if (s->y > maxy)
4077
0
            break;
4078
0
        if (s->x > maxx || s->x + s->len <= minx) {
4079
0
            s->len = 0;
4080
0
            ++s;
4081
0
            continue;
4082
0
        }
4083
0
        if (s->x < minx) {
4084
0
            s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
4085
0
            s->x = minx;
4086
0
        } else {
4087
0
            s->len = qMin(s->len, (maxx - s->x + 1));
4088
0
        }
4089
0
        ++s;
4090
0
    }
4091
4092
0
    return s - spans;
4093
0
}
4094
4095
4096
static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans,
4097
                                  void *userData)
4098
0
{
4099
0
    QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
4100
0
    Q_ASSERT(fillData->blend && fillData->unclipped_blend);
4101
4102
0
    Q_ASSERT(fillData->clip);
4103
0
    Q_ASSERT(!fillData->clip->clipRect.isEmpty());
4104
4105
0
    QT_FT_Span *s = const_cast<QT_FT_Span *>(spans);
4106
    // hw: check if this const_cast<> is safe!!!
4107
0
    count = qt_intersect_spans(s, count,
4108
0
                               fillData->clip->clipRect);
4109
0
    if (count > 0)
4110
0
        fillData->unclipped_blend(count, s, fillData);
4111
0
}
4112
4113
static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData)
4114
0
{
4115
0
    ClipData *clipData = reinterpret_cast<ClipData *>(userData);
4116
4117
//     qDebug() << " qt_span_clip: " << count << clipData->operation;
4118
//     for (int i = 0; i < qMin(count, 10); ++i) {
4119
//         qDebug() << "    " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
4120
//     }
4121
4122
0
    switch (clipData->operation) {
4123
4124
0
    case Qt::IntersectClip:
4125
0
        {
4126
0
            QClipData *newClip = clipData->newClip;
4127
0
            newClip->initialize();
4128
4129
0
            int currentClip = 0;
4130
0
            const QT_FT_Span *end = spans + count;
4131
0
            while (spans < end) {
4132
0
                QT_FT_Span *newspans = newClip->m_spans + newClip->count;
4133
0
                spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
4134
0
                                           &newspans, newClip->allocated - newClip->count);
4135
0
                newClip->count = newspans - newClip->m_spans;
4136
0
                if (spans < end) {
4137
0
                    newClip->m_spans = q_check_ptr((QT_FT_Span *)realloc(newClip->m_spans, newClip->allocated * 2 * sizeof(QT_FT_Span)));
4138
0
                    newClip->allocated *= 2;
4139
0
                }
4140
0
            }
4141
0
        }
4142
0
        break;
4143
4144
0
    case Qt::ReplaceClip:
4145
0
        clipData->newClip->appendSpans(spans, count);
4146
0
        break;
4147
0
    case Qt::NoClip:
4148
0
        break;
4149
0
    }
4150
0
}
4151
4152
class QGradientCache
4153
{
4154
public:
4155
    struct CacheInfo
4156
    {
4157
        inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
4158
0
            stops(std::move(s)), opacity(op), interpolationMode(mode) {}
4159
        QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE];
4160
        QRgb buffer32[GRADIENT_STOPTABLE_SIZE];
4161
        QGradientStops stops;
4162
        int opacity;
4163
        QGradient::InterpolationMode interpolationMode;
4164
    };
4165
4166
    using QGradientColorTableHash = QMultiHash<quint64, std::shared_ptr<const CacheInfo>>;
4167
4168
0
    std::shared_ptr<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
4169
0
        quint64 hash_val = 0;
4170
4171
0
        const QGradientStops stops = gradient.stops();
4172
0
        for (int i = 0; i < stops.size() && i <= 2; i++)
4173
0
            hash_val += stops[i].second.rgba64();
4174
4175
0
        QMutexLocker lock(&mutex);
4176
0
        QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
4177
4178
0
        if (it == cache.constEnd())
4179
0
            return addCacheElement(hash_val, gradient, opacity);
4180
0
        else {
4181
0
            do {
4182
0
                const auto &cache_info = it.value();
4183
0
                if (cache_info->stops == stops && cache_info->opacity == opacity && cache_info->interpolationMode == gradient.interpolationMode())
4184
0
                    return cache_info;
4185
0
                ++it;
4186
0
            } while (it != cache.constEnd() && it.key() == hash_val);
4187
            // an exact match for these stops and opacity was not found, create new cache
4188
0
            return addCacheElement(hash_val, gradient, opacity);
4189
0
        }
4190
0
    }
4191
4192
0
    inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
4193
protected:
4194
0
    inline int maxCacheSize() const { return 60; }
4195
    inline void generateGradientColorTable(const QGradient& g,
4196
                                           QRgba64 *colorTable,
4197
                                           int size, int opacity) const;
4198
0
    std::shared_ptr<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
4199
0
        if (cache.size() == maxCacheSize()) {
4200
            // may remove more than 1, but OK
4201
0
            cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize())));
4202
0
        }
4203
0
        auto cache_entry = std::make_shared<CacheInfo>(gradient.stops(), opacity, gradient.interpolationMode());
4204
0
        generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
4205
0
        for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
4206
0
            cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
4207
0
        return cache.insert(hash_val, std::move(cache_entry)).value();
4208
0
    }
4209
4210
    QGradientColorTableHash cache;
4211
    QMutex mutex;
4212
};
4213
4214
void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
4215
0
{
4216
0
    const QGradientStops stops = gradient.stops();
4217
0
    int stopCount = stops.size();
4218
0
    Q_ASSERT(stopCount > 0);
4219
4220
0
    bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
4221
4222
0
    if (stopCount == 2) {
4223
0
        QRgba64 first_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4224
0
        QRgba64 second_color = combineAlpha256(stops[1].second.rgba64(), opacity);
4225
4226
0
        qreal first_stop = stops[0].first;
4227
0
        qreal second_stop = stops[1].first;
4228
4229
0
        if (second_stop < first_stop) {
4230
0
            quint64 tmp = first_color;
4231
0
            first_color = second_color;
4232
0
            second_color = tmp;
4233
0
            qSwap(first_stop, second_stop);
4234
0
        }
4235
4236
0
        if (colorInterpolation) {
4237
0
            first_color = qPremultiply(first_color);
4238
0
            second_color = qPremultiply(second_color);
4239
0
        }
4240
4241
0
        int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
4242
0
        int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
4243
4244
0
        uint red_first = uint(first_color.red()) << 16;
4245
0
        uint green_first = uint(first_color.green()) << 16;
4246
0
        uint blue_first = uint(first_color.blue()) << 16;
4247
0
        uint alpha_first = uint(first_color.alpha()) << 16;
4248
4249
0
        uint red_second = uint(second_color.red()) << 16;
4250
0
        uint green_second = uint(second_color.green()) << 16;
4251
0
        uint blue_second = uint(second_color.blue()) << 16;
4252
0
        uint alpha_second = uint(second_color.alpha()) << 16;
4253
4254
0
        int i = 0;
4255
0
        for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
4256
0
            if (colorInterpolation)
4257
0
                colorTable[i] = first_color;
4258
0
            else
4259
0
                colorTable[i] = qPremultiply(first_color);
4260
0
        }
4261
4262
0
        if (i < second_index) {
4263
0
            qreal reciprocal = qreal(1) / (second_index - first_index);
4264
4265
0
            int red_delta = qRound((qreal(red_second) - red_first) * reciprocal);
4266
0
            int green_delta = qRound((qreal(green_second) - green_first) * reciprocal);
4267
0
            int blue_delta = qRound((qreal(blue_second) - blue_first) * reciprocal);
4268
0
            int alpha_delta = qRound((qreal(alpha_second) - alpha_first) * reciprocal);
4269
4270
            // rounding
4271
0
            red_first += 1 << 15;
4272
0
            green_first += 1 << 15;
4273
0
            blue_first += 1 << 15;
4274
0
            alpha_first += 1 << 15;
4275
4276
0
            for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
4277
0
                red_first += red_delta;
4278
0
                green_first += green_delta;
4279
0
                blue_first += blue_delta;
4280
0
                alpha_first += alpha_delta;
4281
4282
0
                const QRgba64 color = qRgba64(red_first >> 16, green_first >> 16, blue_first >> 16, alpha_first >> 16);
4283
4284
0
                if (colorInterpolation)
4285
0
                    colorTable[i] = color;
4286
0
                else
4287
0
                    colorTable[i] = qPremultiply(color);
4288
0
            }
4289
0
        }
4290
4291
0
        for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
4292
0
            if (colorInterpolation)
4293
0
                colorTable[i] = second_color;
4294
0
            else
4295
0
                colorTable[i] = qPremultiply(second_color);
4296
0
        }
4297
4298
0
        return;
4299
0
    }
4300
4301
0
    QRgba64 current_color = combineAlpha256(stops[0].second.rgba64(), opacity);
4302
0
    if (stopCount == 1) {
4303
0
        current_color = qPremultiply(current_color);
4304
0
        for (int i = 0; i < size; ++i)
4305
0
            colorTable[i] = current_color;
4306
0
        return;
4307
0
    }
4308
4309
    // The position where the gradient begins and ends
4310
0
    qreal begin_pos = stops[0].first;
4311
0
    qreal end_pos = stops[stopCount-1].first;
4312
4313
0
    int pos = 0; // The position in the color table.
4314
0
    QRgba64 next_color;
4315
4316
0
    qreal incr = 1 / qreal(size); // the double increment.
4317
0
    qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
4318
4319
     // Up to first point
4320
0
    colorTable[pos++] = qPremultiply(current_color);
4321
0
    while (dpos <= begin_pos) {
4322
0
        colorTable[pos] = colorTable[pos - 1];
4323
0
        ++pos;
4324
0
        dpos += incr;
4325
0
    }
4326
4327
0
    int current_stop = 0; // We always interpolate between current and current + 1.
4328
4329
0
    qreal t; // position between current left and right stops
4330
0
    qreal t_delta; // the t increment per entry in the color table
4331
4332
0
    if (dpos < end_pos) {
4333
        // Gradient area
4334
0
        while (dpos > stops[current_stop+1].first)
4335
0
            ++current_stop;
4336
4337
0
        if (current_stop != 0)
4338
0
            current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4339
0
        next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4340
4341
0
        if (colorInterpolation) {
4342
0
            current_color = qPremultiply(current_color);
4343
0
            next_color = qPremultiply(next_color);
4344
0
        }
4345
4346
0
        qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4347
0
        qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4348
0
        t = (dpos - stops[current_stop].first) * c;
4349
0
        t_delta = incr * c;
4350
4351
0
        while (true) {
4352
0
            Q_ASSERT(current_stop < stopCount);
4353
4354
0
            int dist = qRound(t);
4355
0
            int idist = 256 - dist;
4356
4357
0
            if (colorInterpolation)
4358
0
                colorTable[pos] = interpolate256(current_color, idist, next_color, dist);
4359
0
            else
4360
0
                colorTable[pos] = qPremultiply(interpolate256(current_color, idist, next_color, dist));
4361
4362
0
            ++pos;
4363
0
            dpos += incr;
4364
4365
0
            if (dpos >= end_pos)
4366
0
                break;
4367
4368
0
            t += t_delta;
4369
4370
0
            int skip = 0;
4371
0
            while (dpos > stops[current_stop+skip+1].first)
4372
0
                ++skip;
4373
4374
0
            if (skip != 0) {
4375
0
                current_stop += skip;
4376
0
                if (skip == 1)
4377
0
                    current_color = next_color;
4378
0
                else
4379
0
                    current_color = combineAlpha256(stops[current_stop].second.rgba64(), opacity);
4380
0
                next_color = combineAlpha256(stops[current_stop+1].second.rgba64(), opacity);
4381
4382
0
                if (colorInterpolation) {
4383
0
                    if (skip != 1)
4384
0
                        current_color = qPremultiply(current_color);
4385
0
                    next_color = qPremultiply(next_color);
4386
0
                }
4387
4388
0
                qreal diff = stops[current_stop+1].first - stops[current_stop].first;
4389
0
                qreal c = (diff == 0) ? qreal(0) : 256 / diff;
4390
0
                t = (dpos - stops[current_stop].first) * c;
4391
0
                t_delta = incr * c;
4392
0
            }
4393
0
        }
4394
0
    }
4395
4396
    // After last point
4397
0
    current_color = qPremultiply(combineAlpha256(stops[stopCount - 1].second.rgba64(), opacity));
4398
0
    while (pos < size - 1) {
4399
0
        colorTable[pos] = current_color;
4400
0
        ++pos;
4401
0
    }
4402
4403
    // Make sure the last color stop is represented at the end of the table
4404
0
    colorTable[size - 1] = current_color;
4405
0
}
4406
4407
Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
4408
4409
4410
void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
4411
0
{
4412
0
    rasterBuffer = rb;
4413
0
    type = None;
4414
0
    txop = 0;
4415
0
    bilinear = false;
4416
0
    m11 = m22 = m33 = 1.;
4417
0
    m12 = m13 = m21 = m23 = dx = dy = 0.0;
4418
0
    clip = pe ? pe->d_func()->clip() : nullptr;
4419
0
}
4420
4421
Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
4422
4423
void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode,
4424
                      bool isCosmetic)
4425
0
{
4426
0
    Qt::BrushStyle brushStyle = qbrush_style(brush);
4427
0
    cachedGradient.reset();
4428
0
    switch (brushStyle) {
4429
0
    case Qt::SolidPattern: {
4430
0
        type = Solid;
4431
0
        QColor c = qbrush_color(brush);
4432
0
        solidColor = qPremultiplyWithExtraAlpha(c, alpha);
4433
0
        if (solidColor.alphaF() <= 0.0f && compositionMode == QPainter::CompositionMode_SourceOver)
4434
0
            type = None;
4435
0
        break;
4436
0
    }
4437
4438
0
    case Qt::LinearGradientPattern:
4439
0
        {
4440
0
            type = LinearGradient;
4441
0
            const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
4442
0
            gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4443
4444
0
            auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4445
0
            gradient.colorTable32 = cacheInfo->buffer32;
4446
0
#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4447
0
            gradient.colorTable64 = cacheInfo->buffer64;
4448
0
#endif
4449
0
            cachedGradient = std::move(cacheInfo);
4450
4451
0
            gradient.spread = g->spread();
4452
4453
0
            QLinearGradientData &linearData = gradient.linear;
4454
4455
0
            linearData.origin.x = g->start().x();
4456
0
            linearData.origin.y = g->start().y();
4457
0
            linearData.end.x = g->finalStop().x();
4458
0
            linearData.end.y = g->finalStop().y();
4459
0
            break;
4460
0
        }
4461
4462
0
    case Qt::RadialGradientPattern:
4463
0
        {
4464
0
            type = RadialGradient;
4465
0
            const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
4466
0
            gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4467
4468
0
            auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4469
0
            gradient.colorTable32 = cacheInfo->buffer32;
4470
0
#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4471
0
            gradient.colorTable64 = cacheInfo->buffer64;
4472
0
#endif
4473
0
            cachedGradient = std::move(cacheInfo);
4474
4475
0
            gradient.spread = g->spread();
4476
4477
0
            QRadialGradientData &radialData = gradient.radial;
4478
4479
0
            QPointF center = g->center();
4480
0
            radialData.center.x = center.x();
4481
0
            radialData.center.y = center.y();
4482
0
            radialData.center.radius = g->centerRadius();
4483
0
            QPointF focal = g->focalPoint();
4484
0
            radialData.focal.x = focal.x();
4485
0
            radialData.focal.y = focal.y();
4486
0
            radialData.focal.radius = g->focalRadius();
4487
0
        }
4488
0
        break;
4489
4490
0
    case Qt::ConicalGradientPattern:
4491
0
        {
4492
0
            type = ConicalGradient;
4493
0
            const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
4494
0
            gradient.alphaColor = !brush.isOpaque() || alpha != 256;
4495
4496
0
            auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
4497
0
            gradient.colorTable32 = cacheInfo->buffer32;
4498
0
#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
4499
0
            gradient.colorTable64 = cacheInfo->buffer64;
4500
0
#endif
4501
0
            cachedGradient = std::move(cacheInfo);
4502
4503
0
            gradient.spread = QGradient::RepeatSpread;
4504
4505
0
            QConicalGradientData &conicalData = gradient.conical;
4506
4507
0
            QPointF center = g->center();
4508
0
            conicalData.center.x = center.x();
4509
0
            conicalData.center.y = center.y();
4510
0
            conicalData.angle = qDegreesToRadians(g->angle());
4511
0
        }
4512
0
        break;
4513
4514
0
    case Qt::Dense1Pattern:
4515
0
    case Qt::Dense2Pattern:
4516
0
    case Qt::Dense3Pattern:
4517
0
    case Qt::Dense4Pattern:
4518
0
    case Qt::Dense5Pattern:
4519
0
    case Qt::Dense6Pattern:
4520
0
    case Qt::Dense7Pattern:
4521
0
    case Qt::HorPattern:
4522
0
    case Qt::VerPattern:
4523
0
    case Qt::CrossPattern:
4524
0
    case Qt::BDiagPattern:
4525
0
    case Qt::FDiagPattern:
4526
0
    case Qt::DiagCrossPattern:
4527
0
        type = Texture;
4528
0
        if (!tempImage)
4529
0
            tempImage = new QImage();
4530
0
        *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
4531
0
        initTexture(tempImage, alpha, isCosmetic ? QTextureData::Pattern : QTextureData::Tiled);
4532
0
        break;
4533
0
    case Qt::TexturePattern:
4534
0
        type = Texture;
4535
0
        if (!tempImage)
4536
0
            tempImage = new QImage();
4537
4538
0
        if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
4539
0
            *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
4540
0
        else
4541
0
            *tempImage = brush.textureImage();
4542
0
        initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
4543
0
        break;
4544
4545
0
    case Qt::NoBrush:
4546
0
    default:
4547
0
        type = None;
4548
0
        break;
4549
0
    }
4550
0
    adjustSpanMethods();
4551
0
}
4552
4553
void QSpanData::adjustSpanMethods()
4554
0
{
4555
0
    bitmapBlit = nullptr;
4556
0
    alphamapBlit = nullptr;
4557
0
    alphaRGBBlit = nullptr;
4558
4559
0
    fillRect = nullptr;
4560
4561
0
    switch(type) {
4562
0
    case None:
4563
0
        unclipped_blend = nullptr;
4564
0
        break;
4565
0
    case Solid: {
4566
0
        const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format];
4567
0
        unclipped_blend = drawHelper.blendColor;
4568
0
        bitmapBlit = drawHelper.bitmapBlit;
4569
0
        alphamapBlit = drawHelper.alphamapBlit;
4570
0
        alphaRGBBlit = drawHelper.alphaRGBBlit;
4571
0
        fillRect = drawHelper.fillRect;
4572
0
        break;
4573
0
    }
4574
0
    case LinearGradient:
4575
0
    case RadialGradient:
4576
0
    case ConicalGradient:
4577
0
        unclipped_blend = qBlendGradient;
4578
0
        break;
4579
0
    case Texture:
4580
0
        unclipped_blend = qBlendTexture;
4581
0
        if (!texture.imageData)
4582
0
            unclipped_blend = nullptr;
4583
4584
0
        break;
4585
0
    }
4586
    // setup clipping
4587
0
    if (!unclipped_blend) {
4588
0
        blend = nullptr;
4589
0
    } else if (!clip) {
4590
0
        blend = unclipped_blend;
4591
0
    } else if (clip->hasRectClip) {
4592
0
        blend = clip->clipRect.isEmpty() ? nullptr : qt_span_fill_clipRect;
4593
0
    } else {
4594
0
        blend = qt_span_fill_clipped;
4595
0
    }
4596
0
}
4597
4598
void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
4599
0
{
4600
0
    QTransform delta;
4601
    // make sure we round off correctly in qdrawhelper.cpp
4602
0
    delta.translate(1.0 / 65536, 1.0 / 65536);
4603
4604
0
    QTransform inv = (delta * matrix).inverted();
4605
0
    m11 = inv.m11();
4606
0
    m12 = inv.m12();
4607
0
    m13 = inv.m13();
4608
0
    m21 = inv.m21();
4609
0
    m22 = inv.m22();
4610
0
    m23 = inv.m23();
4611
0
    m33 = inv.m33();
4612
0
    dx = inv.dx();
4613
0
    dy = inv.dy();
4614
0
    txop = inv.type();
4615
0
    bilinear = bilin;
4616
4617
0
    const bool affine = inv.isAffine();
4618
0
    const qreal f1 = m11 * m11 + m21 * m21;
4619
0
    const qreal f2 = m12 * m12 + m22 * m22;
4620
0
    fast_matrix = affine
4621
0
        && f1 < 1e4
4622
0
        && f2 < 1e4
4623
0
        && f1 > (1.0 / 65536)
4624
0
        && f2 > (1.0 / 65536)
4625
0
        && qAbs(dx) < 1e4
4626
0
        && qAbs(dy) < 1e4;
4627
4628
0
    adjustSpanMethods();
4629
0
}
4630
4631
void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
4632
0
{
4633
0
    const QImageData *d = const_cast<QImage *>(image)->data_ptr();
4634
0
    if (!d || d->height == 0) {
4635
0
        texture.imageData = nullptr;
4636
0
        texture.width = 0;
4637
0
        texture.height = 0;
4638
0
        texture.x1 = 0;
4639
0
        texture.y1 = 0;
4640
0
        texture.x2 = 0;
4641
0
        texture.y2 = 0;
4642
0
        texture.bytesPerLine = 0;
4643
0
        texture.format = QImage::Format_Invalid;
4644
0
        texture.colorTable = nullptr;
4645
0
        texture.hasAlpha = alpha != 256;
4646
0
    } else {
4647
0
        texture.imageData = d->data;
4648
0
        texture.width = d->width;
4649
0
        texture.height = d->height;
4650
4651
0
        if (sourceRect.isNull()) {
4652
0
            texture.x1 = 0;
4653
0
            texture.y1 = 0;
4654
0
            texture.x2 = texture.width;
4655
0
            texture.y2 = texture.height;
4656
0
        } else {
4657
0
            texture.x1 = sourceRect.x();
4658
0
            texture.y1 = sourceRect.y();
4659
0
            texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
4660
0
            texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
4661
0
        }
4662
4663
0
        texture.bytesPerLine = d->bytes_per_line;
4664
4665
0
        texture.format = d->format;
4666
0
        texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : nullptr;
4667
0
        texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
4668
0
    }
4669
0
    texture.const_alpha = alpha;
4670
0
    texture.type = _type;
4671
4672
0
    adjustSpanMethods();
4673
0
}
4674
4675
/*!
4676
    \internal
4677
    \a x and \a y is relative to the midpoint of \a rect.
4678
*/
4679
static inline void drawEllipsePoints(int x, int y, int length,
4680
                                     const QRect &rect,
4681
                                     const QRect &clip,
4682
                                     ProcessSpans pen_func, ProcessSpans brush_func,
4683
                                     QSpanData *pen_data, QSpanData *brush_data)
4684
0
{
4685
0
    if (length == 0)
4686
0
        return;
4687
4688
0
    QT_FT_Span _outline[4];
4689
0
    QT_FT_Span *outline = _outline;
4690
0
    const int midx = rect.x() + (rect.width() + 1) / 2;
4691
0
    const int midy = rect.y() + (rect.height() + 1) / 2;
4692
4693
0
    x = x + midx;
4694
0
    y = midy - y;
4695
4696
    // topleft
4697
0
    outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
4698
0
    outline[0].len = qMin(length, x - outline[0].x);
4699
0
    outline[0].y = y;
4700
0
    outline[0].coverage = 255;
4701
4702
    // topright
4703
0
    outline[1].x = x;
4704
0
    outline[1].len = length;
4705
0
    outline[1].y = y;
4706
0
    outline[1].coverage = 255;
4707
4708
    // bottomleft
4709
0
    outline[2].x = outline[0].x;
4710
0
    outline[2].len = outline[0].len;
4711
0
    outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
4712
0
    outline[2].coverage = 255;
4713
4714
    // bottomright
4715
0
    outline[3].x = x;
4716
0
    outline[3].len = length;
4717
0
    outline[3].y = outline[2].y;
4718
0
    outline[3].coverage = 255;
4719
4720
0
    if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
4721
0
        QT_FT_Span _fill[2];
4722
0
        QT_FT_Span *fill = _fill;
4723
4724
        // top fill
4725
0
        fill[0].x = outline[0].x + outline[0].len - 1;
4726
0
        fill[0].len = qMax(0, outline[1].x - fill[0].x);
4727
0
        fill[0].y = outline[1].y;
4728
0
        fill[0].coverage = 255;
4729
4730
        // bottom fill
4731
0
        fill[1].x = outline[2].x + outline[2].len - 1;
4732
0
        fill[1].len = qMax(0, outline[3].x - fill[1].x);
4733
0
        fill[1].y = outline[3].y;
4734
0
        fill[1].coverage = 255;
4735
4736
0
        int n = (fill[0].y >= fill[1].y ? 1 : 2);
4737
0
        n = qt_intersect_spans(fill, n, clip);
4738
0
        if (n > 0)
4739
0
            brush_func(n, fill, brush_data);
4740
0
    }
4741
0
    if (pen_func) {
4742
0
        int n = (outline[1].y >= outline[2].y ? 2 : 4);
4743
0
        n = qt_intersect_spans(outline, n, clip);
4744
0
        if (n > 0)
4745
0
            pen_func(n, outline, pen_data);
4746
0
    }
4747
0
}
4748
4749
/*!
4750
    \internal
4751
    Draws an ellipse using the integer point midpoint algorithm.
4752
*/
4753
static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
4754
                                   ProcessSpans pen_func, ProcessSpans brush_func,
4755
                                   QSpanData *pen_data, QSpanData *brush_data)
4756
0
{
4757
0
    const qreal a = qreal(rect.width()) / 2;
4758
0
    const qreal b = qreal(rect.height()) / 2;
4759
0
    qreal d = b*b - (a*a*b) + 0.25*a*a;
4760
4761
0
    int x = 0;
4762
0
    int y = (rect.height() + 1) / 2;
4763
0
    int startx = x;
4764
4765
    // region 1
4766
0
    while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
4767
0
        if (d < 0) { // select E
4768
0
            d += b*b*(2*x + 3);
4769
0
            ++x;
4770
0
        } else {     // select SE
4771
0
            d += b*b*(2*x + 3) + a*a*(-2*y + 2);
4772
0
            drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4773
0
                              pen_func, brush_func, pen_data, brush_data);
4774
0
            startx = ++x;
4775
0
            --y;
4776
0
        }
4777
0
    }
4778
0
    drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
4779
0
                      pen_func, brush_func, pen_data, brush_data);
4780
4781
    // region 2
4782
0
    d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
4783
0
    const int miny = rect.height() & 0x1;
4784
0
    while (y > miny) {
4785
0
        if (d < 0) { // select SE
4786
0
            d += b*b*(2*x + 2) + a*a*(-2*y + 3);
4787
0
            ++x;
4788
0
        } else {     // select S
4789
0
            d += a*a*(-2*y + 3);
4790
0
        }
4791
0
        --y;
4792
0
        drawEllipsePoints(x, y, 1, rect, clip,
4793
0
                          pen_func, brush_func, pen_data, brush_data);
4794
0
    }
4795
0
}
4796
4797
/*
4798
    \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
4799
    \overload
4800
    \reimp
4801
*/
4802
4803
4804
#ifdef QT_DEBUG_DRAW
4805
void dumpClip(int width, int height, const QClipData *clip)
4806
{
4807
    QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
4808
    clipImg.fill(0xffff0000);
4809
4810
    int x0 = width;
4811
    int x1 = 0;
4812
    int y0 = height;
4813
    int y1 = 0;
4814
4815
    ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
4816
4817
    for (int i = 0; i < clip->count; ++i) {
4818
        const QT_FT_Span *span = ((QClipData *) clip)->spans() + i;
4819
        for (int j = 0; j < span->len; ++j)
4820
            clipImg.setPixel(span->x + j, span->y, 0xffffff00);
4821
        x0 = qMin(x0, int(span->x));
4822
        x1 = qMax(x1, int(span->x + span->len - 1));
4823
4824
        y0 = qMin(y0, int(span->y));
4825
        y1 = qMax(y1, int(span->y));
4826
    }
4827
4828
    static int counter = 0;
4829
4830
    Q_ASSERT(y0 >= 0);
4831
    Q_ASSERT(x0 >= 0);
4832
    Q_ASSERT(y1 >= 0);
4833
    Q_ASSERT(x1 >= 0);
4834
4835
    fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
4836
    clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
4837
}
4838
#endif
4839
4840
4841
QT_END_NAMESPACE