1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <AppKit/AppKit.h>
41#include <CoreGraphics/CoreGraphics.h>
42
43#include "qpaintengine_mac_p.h"
44#if defined(QT_PRINTSUPPORT_LIB)
45#include "qprintengine_mac_p.h"
46#endif
47
48#include <qbitmap.h>
49#include <qpaintdevice.h>
50#include <qpainterpath.h>
51#include <qpixmapcache.h>
52#include <private/qpaintengine_raster_p.h>
53#if defined(QT_PRINTSUPPORT_LIB)
54#include <qprinter.h>
55#endif
56#include <qstack.h>
57#include <qwidget.h>
58#include <qvarlengtharray.h>
59#include <qdebug.h>
60#include <qcoreapplication.h>
61#include <qmath.h>
62
63#include <qpa/qplatformpixmap.h>
64
65#include <private/qfont_p.h>
66#include <private/qfontengine_p.h>
67#include <private/qfontengine_coretext_p.h>
68#include <private/qnumeric_p.h>
69#include <private/qpainter_p.h>
70#include <private/qpainterpath_p.h>
71#include <private/qtextengine_p.h>
72#include <private/qcoregraphics_p.h>
73
74#include "qcocoahelpers.h"
75
76#include <string.h>
77
78QT_BEGIN_NAMESPACE
79
80/*****************************************************************************
81  QCoreGraphicsPaintEngine utility functions
82 *****************************************************************************/
83
84void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t)
85{
86    free(const_cast<void *>(memoryToFree));
87}
88
89CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
90{
91    QImage image = pixmap.toImage();
92    if (image.format() != QImage::Format_ARGB32_Premultiplied)
93        image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
94
95    const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
96    const int sbpr = image.bytesPerLine();
97    const uint nbytes = sw * sh;
98    //  alpha is always 255 for bitmaps, ignore it in this case.
99    const quint32 mask = pixmap.depth() == 1 ? 0x00ffffff : 0xffffffff;
100    quint8 *dptr = static_cast<quint8 *>(malloc(nbytes));
101    quint32 *sptr = reinterpret_cast<quint32 *>(image.scanLine(0)), *srow;
102    for (int y = sy, offset=0; y < sh; ++y) {
103        srow = sptr + (y * (sbpr / 4));
104        for (int x = sx; x < sw; ++x)
105            *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0;
106    }
107    QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(nullptr, dptr, nbytes, qt_mac_cgimage_data_free);
108    return CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, nullptr, false);
109}
110
111//conversion
112inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; }
113CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
114    return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(),  t.dy());
115}
116
117inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col)
118{
119    CGFloat components[] = {
120        qt_mac_convert_color_to_cg(col.red()),
121        qt_mac_convert_color_to_cg(col.green()),
122        qt_mac_convert_color_to_cg(col.blue()),
123        qt_mac_convert_color_to_cg(col.alpha())
124    };
125    QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
126    return CGColorCreate(colorSpace, components);
127}
128
129// There's architectural problems with using native gradients
130// on the Mac at the moment, so disable them.
131// #define QT_MAC_USE_NATIVE_GRADIENTS
132
133#ifdef QT_MAC_USE_NATIVE_GRADIENTS
134static bool drawGradientNatively(const QGradient *gradient)
135{
136    return gradient->spread() == QGradient::PadSpread;
137}
138
139// gradiant callback
140static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
141{
142    QBrush *brush = static_cast<QBrush *>(info);
143    Q_ASSERT(brush && brush->gradient());
144
145    const QGradientStops stops = brush->gradient()->stops();
146    const int n = stops.count();
147    Q_ASSERT(n >= 1);
148    const QGradientStop *begin = stops.constBegin();
149    const QGradientStop *end = begin + n;
150
151    qreal p = in[0];
152    const QGradientStop *i = begin;
153    while (i != end && i->first < p)
154        ++i;
155
156    QRgb c;
157    if (i == begin) {
158        c = begin->second.rgba();
159    } else if (i == end) {
160        c = (end - 1)->second.rgba();
161    } else {
162        const QGradientStop &s1 = *(i - 1);
163        const QGradientStop &s2 = *i;
164        qreal p1 = s1.first;
165        qreal p2 = s2.first;
166        QRgb c1 = s1.second.rgba();
167        QRgb c2 = s2.second.rgba();
168        int idist = 256 * (p - p1) / (p2 - p1);
169        int dist = 256 - idist;
170        c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist),
171                  INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist),
172                  INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist),
173                  INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist));
174    }
175
176    out[0] = qt_mac_convert_color_to_cg(qRed(c));
177    out[1] = qt_mac_convert_color_to_cg(qGreen(c));
178    out[2] = qt_mac_convert_color_to_cg(qBlue(c));
179    out[3] = qt_mac_convert_color_to_cg(qAlpha(c));
180}
181#endif
182
183//clipping handling
184void QCoreGraphicsPaintEnginePrivate::resetClip()
185{
186    static bool inReset = false;
187    if (inReset)
188        return;
189    inReset = true;
190
191    CGAffineTransform old_xform = CGContextGetCTM(hd);
192
193    //setup xforms
194    CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
195    while (stackCount > 0) {
196        restoreGraphicsState();
197    }
198    saveGraphicsState();
199    inReset = false;
200    //reset xforms
201    CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
202    CGContextConcatCTM(hd, old_xform);
203}
204
205static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
206{
207    return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
208}
209
210static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
211{
212    CGMutablePathRef ret = CGPathCreateMutable();
213    QPointF startPt;
214    for (int i=0; i<p.elementCount(); ++i) {
215        const QPainterPath::Element &elm = p.elementAt(i);
216        switch (elm.type) {
217            case QPainterPath::MoveToElement:
218                if (i > 0
219                        && p.elementAt(i - 1).x == startPt.x()
220                        && p.elementAt(i - 1).y == startPt.y())
221                    CGPathCloseSubpath(ret);
222                startPt = QPointF(elm.x, elm.y);
223                CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off);
224                break;
225            case QPainterPath::LineToElement:
226                CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
227                break;
228            case QPainterPath::CurveToElement:
229                Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement);
230                Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement);
231                CGPathAddCurveToPoint(ret, 0,
232                        elm.x+off, elm.y+off,
233                        p.elementAt(i+1).x+off, p.elementAt(i+1).y+off,
234                        p.elementAt(i+2).x+off, p.elementAt(i+2).y+off);
235                i+=2;
236                break;
237            default:
238                qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
239                break;
240        }
241    }
242    if (!p.isEmpty()
243            && p.elementAt(p.elementCount() - 1).x == startPt.x()
244            && p.elementAt(p.elementCount() - 1).y == startPt.y())
245        CGPathCloseSubpath(ret);
246    return ret;
247}
248
249//pattern handling (tiling)
250#if 1
251#  define QMACPATTERN_MASK_MULTIPLIER 32
252#else
253#  define QMACPATTERN_MASK_MULTIPLIER 1
254#endif
255class QMacPattern
256{
257public:
258    QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
259    ~QMacPattern() { CGImageRelease(image); }
260    int width() {
261        if (image)
262            return CGImageGetWidth(image);
263        if (data.bytes)
264            return 8*QMACPATTERN_MASK_MULTIPLIER;
265        return data.pixmap.width();
266    }
267    int height() {
268        if (image)
269            return CGImageGetHeight(image);
270        if (data.bytes)
271            return 8*QMACPATTERN_MASK_MULTIPLIER;
272        return data.pixmap.height();
273    }
274
275    //input
276    QColor foreground;
277    bool as_mask;
278    struct {
279        QPixmap pixmap;
280        const uchar *bytes;
281    } data;
282    QPaintDevice *pdev;
283    //output
284    CGImageRef image;
285};
286static void qt_mac_draw_pattern(void *info, CGContextRef c)
287{
288    QMacPattern *pat = (QMacPattern*)info;
289    int w = 0, h = 0;
290    bool isBitmap = (pat->data.pixmap.depth() == 1);
291    if (!pat->image) { //lazy cache
292        if (pat->as_mask) {
293            Q_ASSERT(pat->data.bytes);
294            w = h = 8;
295#if (QMACPATTERN_MASK_MULTIPLIER == 1)
296            CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, pat->data.bytes, w*h, nullptr);
297            pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false);
298            CGDataProviderRelease(provider);
299#else
300            const int numBytes = (w*h)/sizeof(uchar);
301            uchar xor_bytes[numBytes];
302            for (int i = 0; i < numBytes; ++i)
303                xor_bytes[i] = pat->data.bytes[i] ^ 0xFF;
304            CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, xor_bytes, w*h, nullptr);
305            CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false);
306            CGDataProviderRelease(provider);
307
308            const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
309            QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
310            pm.fill(c0);
311            QMacCGContext pm_ctx(&pm);
312            CGContextSetFillColorWithColor(c, cgColorForQColor(c1));
313            CGRect rect = CGRectMake(0, 0, w, h);
314            for (int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) {
315                rect.origin.x = x * w;
316                for (int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) {
317                    rect.origin.y = y * h;
318                    qt_mac_drawCGImage(pm_ctx, &rect, swatch);
319                }
320            }
321            pat->image = qt_mac_create_imagemask(pm, pm.rect());
322            CGImageRelease(swatch);
323            w *= QMACPATTERN_MASK_MULTIPLIER;
324            h *= QMACPATTERN_MASK_MULTIPLIER;
325#endif
326        } else {
327            w = pat->data.pixmap.width();
328            h = pat->data.pixmap.height();
329            if (isBitmap)
330                pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
331            else
332                pat->image = qt_mac_toCGImage(pat->data.pixmap.toImage());
333        }
334    } else {
335        w = CGImageGetWidth(pat->image);
336        h = CGImageGetHeight(pat->image);
337    }
338
339    //draw
340    bool needRestore = false;
341    if (CGImageIsMask(pat->image)) {
342        CGContextSaveGState(c);
343        CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground));
344    }
345    CGRect rect = CGRectMake(0, 0, w, h);
346    qt_mac_drawCGImage(c, &rect, pat->image);
347    if (needRestore)
348        CGContextRestoreGState(c);
349}
350static void qt_mac_dispose_pattern(void *info)
351{
352    QMacPattern *pat = (QMacPattern*)info;
353    delete pat;
354}
355
356/*****************************************************************************
357  QCoreGraphicsPaintEngine member functions
358 *****************************************************************************/
359
360inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
361{
362    return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent
363                                              & ~QPaintEngine::PerspectiveTransform
364                                              & ~QPaintEngine::ConicalGradientFill
365                                              & ~QPaintEngine::LinearGradientFill
366                                              & ~QPaintEngine::RadialGradientFill
367                                              & ~QPaintEngine::BrushStroke);
368}
369
370QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
371: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
372{
373}
374
375QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
376: QPaintEngine(dptr, qt_mac_cg_features())
377{
378}
379
380QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
381{
382}
383
384bool
385QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
386{
387    Q_D(QCoreGraphicsPaintEngine);
388    if (isActive()) {                         // already active painting
389        qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
390        return false;
391    }
392
393    //initialization
394    d->pdev = pdev;
395    d->complexXForm = false;
396    d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
397    d->cosmeticPenSize = 1;
398    d->current.clipEnabled = false;
399    d->pixelSize = QPoint(1,1);
400
401    if (pdev->devType() != QInternal::Printer) {
402        QMacCGContext ctx(pdev);
403        d->hd = CGContextRetain(ctx);
404        if (d->hd) {
405            d->saveGraphicsState();
406            d->orig_xform = CGContextGetCTM(d->hd);
407            if (d->shading) {
408                CGShadingRelease(d->shading);
409                d->shading = nullptr;
410            }
411            d->setClip(nullptr);  //clear the context's clipping
412        }
413    }
414
415    setActive(true);
416
417    if (d->pdev->devType() == QInternal::Widget) {                    // device is a widget
418        QWidget *w = (QWidget*)d->pdev;
419        bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped);
420
421        if ((w->windowType() == Qt::Desktop)) {
422            if (!unclipped)
423                qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on OS X");
424            // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file)
425        } else if (unclipped) {
426            qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting");
427        }
428    } else if (d->pdev->devType() == QInternal::Pixmap) {             // device is a pixmap
429        QPixmap *pm = (QPixmap*)d->pdev;
430        if (pm->isNull()) {
431            qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
432            end();
433            return false;
434        }
435    }
436
437    setDirty(QPaintEngine::DirtyPen);
438    setDirty(QPaintEngine::DirtyBrush);
439    setDirty(QPaintEngine::DirtyBackground);
440    setDirty(QPaintEngine::DirtyHints);
441    return true;
442}
443
444bool
445QCoreGraphicsPaintEngine::end()
446{
447    Q_D(QCoreGraphicsPaintEngine);
448    setActive(false);
449    if (d->pdev->devType() == QInternal::Widget && static_cast<QWidget*>(d->pdev)->windowType() == Qt::Desktop) {
450        // ### need to do [qt_mac_window_for(static_cast<QWidget *>(d->pdev)) orderOut]; (need to rename)
451    }
452    if (d->shading) {
453        CGShadingRelease(d->shading);
454        d->shading = 0;
455    }
456    d->pdev = nullptr;
457    if (d->hd) {
458        d->restoreGraphicsState();
459        CGContextSynchronize(d->hd);
460        CGContextRelease(d->hd);
461        d->hd = nullptr;
462    }
463    return true;
464}
465
466void
467QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
468{
469    Q_D(QCoreGraphicsPaintEngine);
470    QPaintEngine::DirtyFlags flags = state.state();
471
472    if (flags & DirtyTransform)
473        updateMatrix(state.transform());
474
475    if (flags & DirtyClipEnabled) {
476        if (state.isClipEnabled())
477            updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
478        else
479            updateClipPath(QPainterPath(), Qt::NoClip);
480    }
481
482    if (flags & DirtyClipPath) {
483        updateClipPath(state.clipPath(), state.clipOperation());
484    } else if (flags & DirtyClipRegion) {
485        updateClipRegion(state.clipRegion(), state.clipOperation());
486    }
487
488    // If the clip has changed we need to update all other states
489    // too, since they are included in the system context on OSX,
490    // and changing the clip resets that context back to scratch.
491    if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled))
492        flags |= AllDirty;
493
494    if (flags & DirtyPen)
495        updatePen(state.pen());
496    if (flags & (DirtyBrush|DirtyBrushOrigin))
497        updateBrush(state.brush(), state.brushOrigin());
498    if (flags & DirtyFont)
499        updateFont(state.font());
500    if (flags & DirtyOpacity)
501        updateOpacity(state.opacity());
502    if (flags & DirtyHints)
503        updateRenderHints(state.renderHints());
504    if (flags & DirtyCompositionMode)
505        updateCompositionMode(state.compositionMode());
506
507    if (flags & (DirtyPen | DirtyTransform | DirtyHints)) {
508        if (!qt_pen_is_cosmetic(d->current.pen, state.renderHints())) {
509            d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone;
510        } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 ||
511                  d->current.transform.m11() > d->current.transform.m22()+1.0) {
512            d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath;
513            d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF());
514            if (!d->cosmeticPenSize)
515                d->cosmeticPenSize = 1.0;
516        } else {
517            d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
518            static const float sqrt2 = std::sqrt(2.0f);
519            qreal width = d->current.pen.widthF();
520            if (!width)
521                width = 1;
522            d->cosmeticPenSize = std::sqrt(std::pow(d->pixelSize.y(), 2) + std::pow(d->pixelSize.x(), 2)) / sqrt2 * width;
523        }
524    }
525}
526
527void
528QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
529{
530    Q_D(QCoreGraphicsPaintEngine);
531    Q_ASSERT(isActive());
532    d->current.pen = pen;
533    d->setStrokePen(pen);
534}
535
536void
537QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
538{
539    Q_D(QCoreGraphicsPaintEngine);
540    Q_ASSERT(isActive());
541    d->current.brush = brush;
542
543#ifdef QT_MAC_USE_NATIVE_GRADIENTS
544    // Quartz supports only pad spread
545    if (const QGradient *gradient = brush.gradient()) {
546        if (drawGradientNatively(gradient)) {
547            gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill;
548        } else {
549            gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
550        }
551    }
552#endif
553
554    if (d->shading) {
555        CGShadingRelease(d->shading);
556        d->shading = nullptr;
557    }
558    d->setFillBrush(brushOrigin);
559}
560
561void
562QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
563{
564    Q_D(QCoreGraphicsPaintEngine);
565    CGContextSetAlpha(d->hd, opacity);
566}
567
568void
569QCoreGraphicsPaintEngine::updateFont(const QFont &)
570{
571    Q_D(QCoreGraphicsPaintEngine);
572    Q_ASSERT(isActive());
573    updatePen(d->current.pen);
574}
575
576void
577QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
578{
579    Q_D(QCoreGraphicsPaintEngine);
580    Q_ASSERT(isActive());
581
582    if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13())
583        || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23())
584        || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33()))
585        return;
586
587    d->current.transform = transform;
588    d->setTransform(transform.isIdentity() ? 0 : &transform);
589    d->complexXForm = (transform.m11() != 1 || transform.m22() != 1
590            || transform.m12() != 0 || transform.m21() != 0);
591    d->pixelSize = d->devicePixelSize(d->hd);
592}
593
594void
595QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
596{
597    Q_D(QCoreGraphicsPaintEngine);
598    Q_ASSERT(isActive());
599    if (op == Qt::NoClip) {
600        if (d->current.clipEnabled) {
601            d->current.clipEnabled = false;
602            d->current.clip = QRegion();
603            d->setClip(nullptr);
604        }
605    } else {
606        if (!d->current.clipEnabled)
607            op = Qt::ReplaceClip;
608        d->current.clipEnabled = true;
609        QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule());
610        if (op == Qt::ReplaceClip) {
611            d->current.clip = clipRegion;
612            d->setClip(nullptr);
613            if (p.isEmpty()) {
614                CGRect rect = CGRectMake(0, 0, 0, 0);
615                CGContextClipToRect(d->hd, rect);
616            } else {
617                CGMutablePathRef path = qt_mac_compose_path(p);
618                CGContextBeginPath(d->hd);
619                CGContextAddPath(d->hd, path);
620                if (p.fillRule() == Qt::WindingFill)
621                    CGContextClip(d->hd);
622                else
623                    CGContextEOClip(d->hd);
624                CGPathRelease(path);
625            }
626        } else if (op == Qt::IntersectClip) {
627            d->current.clip = d->current.clip.intersected(clipRegion);
628            d->setClip(&d->current.clip);
629        }
630    }
631}
632
633void
634QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
635{
636    Q_D(QCoreGraphicsPaintEngine);
637    Q_ASSERT(isActive());
638    if (op == Qt::NoClip) {
639        d->current.clipEnabled = false;
640        d->current.clip = QRegion();
641        d->setClip(nullptr);
642    } else {
643        if (!d->current.clipEnabled)
644            op = Qt::ReplaceClip;
645        d->current.clipEnabled = true;
646        if (op == Qt::IntersectClip)
647            d->current.clip = d->current.clip.intersected(clipRegion);
648        else if (op == Qt::ReplaceClip)
649            d->current.clip = clipRegion;
650        d->setClip(&d->current.clip);
651    }
652}
653
654void
655QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
656{
657    Q_D(QCoreGraphicsPaintEngine);
658    Q_ASSERT(isActive());
659
660    if (state->compositionMode() == QPainter::CompositionMode_Destination)
661        return;
662
663    CGMutablePathRef path = qt_mac_compose_path(p);
664    uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
665    if (p.fillRule() == Qt::WindingFill)
666        ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
667    else
668        ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
669    CGContextBeginPath(d->hd);
670    d->drawPath(ops, path);
671    CGPathRelease(path);
672}
673
674void
675QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
676{
677    Q_D(QCoreGraphicsPaintEngine);
678    Q_ASSERT(isActive());
679
680    if (state->compositionMode() == QPainter::CompositionMode_Destination)
681        return;
682
683    for (int i=0; i<rectCount; ++i) {
684        QRectF r = rects[i];
685
686        CGMutablePathRef path = CGPathCreateMutable();
687        CGPathAddRect(path, nullptr, qt_mac_compose_rect(r));
688        d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
689                path);
690        CGPathRelease(path);
691    }
692}
693
694void
695QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
696{
697    Q_D(QCoreGraphicsPaintEngine);
698    Q_ASSERT(isActive());
699
700    if (state->compositionMode() == QPainter::CompositionMode_Destination)
701        return;
702
703    if (d->current.pen.capStyle() == Qt::FlatCap)
704        CGContextSetLineCap(d->hd, kCGLineCapSquare);
705
706    CGMutablePathRef path = CGPathCreateMutable();
707    for (int i=0; i < pointCount; i++) {
708        float x = points[i].x(), y = points[i].y();
709        CGPathMoveToPoint(path, nullptr, x, y);
710        CGPathAddLineToPoint(path, nullptr, x+0.001, y);
711    }
712
713    bool doRestore = false;
714    if (d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
715        //we don't want adjusted pens for point rendering
716        doRestore = true;
717        d->saveGraphicsState();
718        CGContextSetLineWidth(d->hd, d->current.pen.widthF());
719    }
720    d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
721    if (doRestore)
722        d->restoreGraphicsState();
723    CGPathRelease(path);
724    if (d->current.pen.capStyle() == Qt::FlatCap)
725        CGContextSetLineCap(d->hd, kCGLineCapButt);
726}
727
728void
729QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
730{
731    Q_D(QCoreGraphicsPaintEngine);
732    Q_ASSERT(isActive());
733
734    if (state->compositionMode() == QPainter::CompositionMode_Destination)
735        return;
736
737    CGMutablePathRef path = CGPathCreateMutable();
738    CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1);
739    CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()),
740            r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false);
741    d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke,
742            path);
743    CGPathRelease(path);
744}
745
746void
747QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
748{
749    Q_D(QCoreGraphicsPaintEngine);
750    Q_ASSERT(isActive());
751
752    if (state->compositionMode() == QPainter::CompositionMode_Destination)
753        return;
754
755    CGMutablePathRef path = CGPathCreateMutable();
756    CGPathMoveToPoint(path, nullptr, points[0].x(), points[0].y());
757    for (int x = 1; x < pointCount; ++x)
758        CGPathAddLineToPoint(path, nullptr, points[x].x(), points[x].y());
759    if (mode != PolylineMode && points[0] != points[pointCount-1])
760        CGPathAddLineToPoint(path, nullptr, points[0].x(), points[0].y());
761    uint op = QCoreGraphicsPaintEnginePrivate::CGStroke;
762    if (mode != PolylineMode)
763        op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill
764            : QCoreGraphicsPaintEnginePrivate::CGFill;
765    d->drawPath(op, path);
766    CGPathRelease(path);
767}
768
769void
770QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
771{
772    Q_D(QCoreGraphicsPaintEngine);
773    Q_ASSERT(isActive());
774
775    if (state->compositionMode() == QPainter::CompositionMode_Destination)
776        return;
777
778    CGMutablePathRef path = CGPathCreateMutable();
779    for (int i = 0; i < lineCount; i++) {
780        const QPointF start = lines[i].p1(), end = lines[i].p2();
781        CGPathMoveToPoint(path, nullptr, start.x(), start.y());
782        CGPathAddLineToPoint(path, nullptr, end.x(), end.y());
783    }
784    d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
785    CGPathRelease(path);
786}
787
788void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
789{
790    Q_D(QCoreGraphicsPaintEngine);
791    Q_ASSERT(isActive());
792
793    if (state->compositionMode() == QPainter::CompositionMode_Destination)
794        return;
795
796    if (pm.isNull())
797        return;
798
799    bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false;
800    CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
801    QCFType<CGImageRef> image;
802    bool isBitmap = (pm.depth() == 1);
803    if (isBitmap) {
804        doRestore = true;
805        d->saveGraphicsState();
806
807        const QColor &col = d->current.pen.color();
808        CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col));
809        image = qt_mac_create_imagemask(pm, sr);
810    } else if (differentSize) {
811        QCFType<CGImageRef> img = qt_mac_toCGImage(pm.toImage());
812        if (img)
813            image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
814    } else {
815        image = qt_mac_toCGImage(pm.toImage());
816    }
817    qt_mac_drawCGImage(d->hd, &rect, image);
818    if (doRestore)
819        d->restoreGraphicsState();
820}
821
822void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
823                                         Qt::ImageConversionFlags flags)
824{
825    Q_D(QCoreGraphicsPaintEngine);
826    Q_UNUSED(flags);
827    Q_ASSERT(isActive());
828
829    if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
830        return;
831
832    QCFType<CGImageRef> cgimage = qt_mac_toCGImage(img);
833    CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
834    if (QRectF(0, 0, img.width(), img.height()) != sr)
835        cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(),
836                                               sr.width(), sr.height()));
837    qt_mac_drawCGImage(d->hd, &rect, cgimage);
838}
839
840void QCoreGraphicsPaintEngine::initialize()
841{
842}
843
844void QCoreGraphicsPaintEngine::cleanup()
845{
846}
847
848CGContextRef
849QCoreGraphicsPaintEngine::handle() const
850{
851    return d_func()->hd;
852}
853
854void
855QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
856        const QPointF &p)
857{
858    Q_D(QCoreGraphicsPaintEngine);
859    Q_ASSERT(isActive());
860
861    if (state->compositionMode() == QPainter::CompositionMode_Destination)
862        return;
863
864    //save the old state
865    d->saveGraphicsState();
866
867    //setup the pattern
868    QMacPattern *qpattern = new QMacPattern;
869    qpattern->data.pixmap = pixmap;
870    qpattern->foreground = d->current.pen.color();
871    qpattern->pdev = d->pdev;
872    CGPatternCallbacks callbks;
873    callbks.version = 0;
874    callbks.drawPattern = qt_mac_draw_pattern;
875    callbks.releaseInfo = qt_mac_dispose_pattern;
876    const int width = qpattern->width(), height = qpattern->height();
877    CGAffineTransform trans = CGContextGetCTM(d->hd);
878    CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
879            trans, width, height,
880            kCGPatternTilingNoDistortion, true, &callbks);
881    CGColorSpaceRef cs = CGColorSpaceCreatePattern(nullptr);
882    CGContextSetFillColorSpace(d->hd, cs);
883    CGFloat component = 1.0; //just one
884    CGContextSetFillPattern(d->hd, pat, &component);
885    CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans);
886    CGContextSetPatternPhase(d->hd, phase);
887
888    //fill the rectangle
889    CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
890    CGContextFillRect(d->hd, mac_rect);
891
892    //restore the state
893    d->restoreGraphicsState();
894    //cleanup
895    CGColorSpaceRelease(cs);
896    CGPatternRelease(pat);
897}
898
899void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
900{
901    Q_D(QCoreGraphicsPaintEngine);
902    if (d->current.transform.type() == QTransform::TxProject
903#ifndef QMAC_NATIVE_GRADIENTS
904        || painter()->pen().brush().gradient()  //Just let the base engine "emulate" the gradient
905#endif
906        ) {
907        QPaintEngine::drawTextItem(pos, item);
908        return;
909    }
910
911    if (state->compositionMode() == QPainter::CompositionMode_Destination)
912        return;
913
914    const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
915
916    QPen oldPen = painter()->pen();
917    QBrush oldBrush = painter()->brush();
918    QPointF oldBrushOrigin = painter()->brushOrigin();
919    updatePen(Qt::NoPen);
920    updateBrush(oldPen.brush(), QPointF(0, 0));
921
922    Q_ASSERT(type() == QPaintEngine::CoreGraphics);
923
924    QFontEngine *fe = ti.fontEngine;
925
926    const bool textAA = ((state->renderHints() & QPainter::TextAntialiasing)
927                         && !(fe->fontDef.styleStrategy & QFont::NoAntialias));
928    const bool lineAA = state->renderHints() & QPainter::Antialiasing;
929    if (textAA != lineAA)
930        CGContextSetShouldAntialias(d->hd, textAA);
931
932    const bool smoothing = textAA && !(fe->fontDef.styleStrategy & QFont::NoSubpixelAntialias);
933    if (d->disabledSmoothFonts == smoothing)
934        CGContextSetShouldSmoothFonts(d->hd, smoothing);
935
936    if (ti.glyphs.numGlyphs) {
937        switch (fe->type()) {
938        case QFontEngine::Mac:
939            static_cast<QCoreTextFontEngine *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
940            break;
941        case QFontEngine::Box:
942            d->drawBoxTextItem(pos, ti);
943            break;
944        default:
945            break;
946        }
947    }
948
949    if (textAA != lineAA)
950        CGContextSetShouldAntialias(d->hd, !textAA);
951
952    if (smoothing == d->disabledSmoothFonts)
953        CGContextSetShouldSmoothFonts(d->hd, !d->disabledSmoothFonts);
954
955    updatePen(oldPen);
956    updateBrush(oldBrush, oldBrushOrigin);
957}
958
959QPainter::RenderHints
960QCoreGraphicsPaintEngine::supportedRenderHints() const
961{
962    return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
963}
964enum CGCompositeMode {
965        kCGCompositeModeClear            = 0,
966        kCGCompositeModeCopy             = 1,
967        kCGCompositeModeSourceOver       = 2,
968        kCGCompositeModeSourceIn         = 3,
969        kCGCompositeModeSourceOut        = 4,
970        kCGCompositeModeSourceAtop       = 5,
971        kCGCompositeModeDestinationOver  = 6,
972        kCGCompositeModeDestinationIn    = 7,
973        kCGCompositeModeDestinationOut   = 8,
974        kCGCompositeModeDestinationAtop  = 9,
975        kCGCompositeModeXOR              = 10,
976        kCGCompositeModePlusDarker       = 11, // (max (0, (1-d) + (1-s)))
977        kCGCompositeModePlusLighter      = 12, // (min (1, s + d))
978    };
979extern "C" {
980    extern void CGContextSetCompositeOperation(CGContextRef, int);
981} // private function, but is in all versions of OS X.
982void
983QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
984{
985    int cg_mode = kCGBlendModeNormal;
986    switch (mode) {
987    case QPainter::CompositionMode_Multiply:
988        cg_mode = kCGBlendModeMultiply;
989        break;
990    case QPainter::CompositionMode_Screen:
991        cg_mode = kCGBlendModeScreen;
992        break;
993    case QPainter::CompositionMode_Overlay:
994        cg_mode = kCGBlendModeOverlay;
995        break;
996    case QPainter::CompositionMode_Darken:
997        cg_mode = kCGBlendModeDarken;
998        break;
999    case QPainter::CompositionMode_Lighten:
1000        cg_mode = kCGBlendModeLighten;
1001        break;
1002    case QPainter::CompositionMode_ColorDodge:
1003        cg_mode = kCGBlendModeColorDodge;
1004        break;
1005    case QPainter::CompositionMode_ColorBurn:
1006        cg_mode = kCGBlendModeColorBurn;
1007        break;
1008    case QPainter::CompositionMode_HardLight:
1009        cg_mode = kCGBlendModeHardLight;
1010        break;
1011    case QPainter::CompositionMode_SoftLight:
1012        cg_mode = kCGBlendModeSoftLight;
1013        break;
1014    case QPainter::CompositionMode_Difference:
1015        cg_mode = kCGBlendModeDifference;
1016        break;
1017    case QPainter::CompositionMode_Exclusion:
1018        cg_mode = kCGBlendModeExclusion;
1019        break;
1020    case QPainter::CompositionMode_Plus:
1021        cg_mode = kCGBlendModePlusLighter;
1022        break;
1023    case QPainter::CompositionMode_SourceOver:
1024        cg_mode = kCGBlendModeNormal;
1025        break;
1026    case QPainter::CompositionMode_DestinationOver:
1027        cg_mode = kCGBlendModeDestinationOver;
1028        break;
1029    case QPainter::CompositionMode_Clear:
1030        cg_mode = kCGBlendModeClear;
1031        break;
1032    case QPainter::CompositionMode_Source:
1033        cg_mode = kCGBlendModeCopy;
1034        break;
1035    case QPainter::CompositionMode_Destination:
1036        cg_mode = -1;
1037        break;
1038    case QPainter::CompositionMode_SourceIn:
1039        cg_mode = kCGBlendModeSourceIn;
1040        break;
1041    case QPainter::CompositionMode_DestinationIn:
1042        cg_mode = kCGCompositeModeDestinationIn;
1043        break;
1044    case QPainter::CompositionMode_SourceOut:
1045        cg_mode = kCGBlendModeSourceOut;
1046        break;
1047    case QPainter::CompositionMode_DestinationOut:
1048        cg_mode = kCGBlendModeDestinationOver;
1049        break;
1050    case QPainter::CompositionMode_SourceAtop:
1051        cg_mode = kCGBlendModeSourceAtop;
1052        break;
1053    case QPainter::CompositionMode_DestinationAtop:
1054        cg_mode = kCGBlendModeDestinationAtop;
1055        break;
1056    case QPainter::CompositionMode_Xor:
1057        cg_mode = kCGBlendModeXOR;
1058        break;
1059    default:
1060        break;
1061    }
1062    if (cg_mode > -1) {
1063        CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
1064    }
1065}
1066
1067void
1068QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
1069{
1070    Q_D(QCoreGraphicsPaintEngine);
1071    CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing);
1072    CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
1073                                     kCGInterpolationHigh : kCGInterpolationNone);
1074    bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing;
1075    if (!textAntialiasing || d->disabledSmoothFonts) {
1076        d->disabledSmoothFonts = !textAntialiasing;
1077        CGContextSetShouldSmoothFonts(d->hd, textAntialiasing);
1078    }
1079}
1080
1081/*
1082    Returns the size of one device pixel in user-space coordinates.
1083*/
1084QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
1085{
1086    QPointF p1 = current.transform.inverted().map(QPointF(0, 0));
1087    QPointF p2 = current.transform.inverted().map(QPointF(1, 1));
1088    return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y()));
1089}
1090
1091/*
1092    Adjusts the pen width so we get correct line widths in the
1093    non-transformed, aliased case.
1094*/
1095float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
1096{
1097    Q_Q(QCoreGraphicsPaintEngine);
1098    float ret = penWidth;
1099    if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
1100        if (penWidth < 2)
1101            ret = 1;
1102        else if (penWidth < 3)
1103            ret = 1.5;
1104        else
1105            ret = penWidth -1;
1106    }
1107    return ret;
1108}
1109
1110void
1111QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
1112{
1113    //pencap
1114    CGLineCap cglinecap = kCGLineCapButt;
1115    if (pen.capStyle() == Qt::SquareCap)
1116        cglinecap = kCGLineCapSquare;
1117    else if (pen.capStyle() == Qt::RoundCap)
1118        cglinecap = kCGLineCapRound;
1119    CGContextSetLineCap(hd, cglinecap);
1120    CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF()));
1121
1122    //join
1123    CGLineJoin cglinejoin = kCGLineJoinMiter;
1124    if (pen.joinStyle() == Qt::BevelJoin)
1125        cglinejoin = kCGLineJoinBevel;
1126    else if (pen.joinStyle() == Qt::RoundJoin)
1127        cglinejoin = kCGLineJoinRound;
1128    CGContextSetLineJoin(hd, cglinejoin);
1129//    CGContextSetMiterLimit(hd, pen.miterLimit());
1130
1131    //pen style
1132    QVector<CGFloat> linedashes;
1133    if (pen.style() == Qt::CustomDashLine) {
1134        QVector<qreal> customs = pen.dashPattern();
1135        for (int i = 0; i < customs.size(); ++i)
1136            linedashes.append(customs.at(i));
1137    } else if (pen.style() == Qt::DashLine) {
1138        linedashes.append(4);
1139        linedashes.append(2);
1140    } else if (pen.style() == Qt::DotLine) {
1141        linedashes.append(1);
1142        linedashes.append(2);
1143    } else if (pen.style() == Qt::DashDotLine) {
1144        linedashes.append(4);
1145        linedashes.append(2);
1146        linedashes.append(1);
1147        linedashes.append(2);
1148    } else if (pen.style() == Qt::DashDotDotLine) {
1149        linedashes.append(4);
1150        linedashes.append(2);
1151        linedashes.append(1);
1152        linedashes.append(2);
1153        linedashes.append(1);
1154        linedashes.append(2);
1155    }
1156    const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF());
1157    for (int i = 0; i < linedashes.size(); ++i) {
1158        linedashes[i] *= cglinewidth;
1159        if (cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) {
1160            if ((i%2))
1161                linedashes[i] += cglinewidth/2;
1162            else
1163                linedashes[i] -= cglinewidth/2;
1164        }
1165    }
1166    CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
1167
1168    // color
1169    CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color()));
1170}
1171
1172// Add our own patterns here to deal with the fact that the coordinate system
1173// is flipped vertically with Quartz2D.
1174static const uchar *qt_mac_patternForBrush(int brushStyle)
1175{
1176    Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
1177    static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 };
1178    static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 };
1179    static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa };
1180    static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
1181    static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 };
1182    static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 };
1183    static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff };
1184    static const uchar hor_pat[]    = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff };
1185    static const uchar ver_pat[]    = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
1186    static const uchar cross_pat[]  = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef };
1187    static const uchar fdiag_pat[]  = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
1188    static const uchar bdiag_pat[]  = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
1189    static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
1190    static const uchar *const pat_tbl[] = {
1191        dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
1192        dense6_pat, dense7_pat,
1193        hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
1194    return pat_tbl[brushStyle - Qt::Dense1Pattern];
1195}
1196
1197void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
1198{
1199    // pattern
1200    Qt::BrushStyle bs = current.brush.style();
1201#ifdef QT_MAC_USE_NATIVE_GRADIENTS
1202    if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) {
1203        const QGradient *grad = static_cast<const QGradient*>(current.brush.gradient());
1204        if (drawGradientNatively(grad)) {
1205            Q_ASSERT(grad->spread() == QGradient::PadSpread);
1206
1207            static const CGFloat domain[] = { 0.0f, +1.0f };
1208            static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, nullptr };
1209            CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(&current.brush),
1210                    1, domain, 4, nullptr, &callbacks);
1211
1212            CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)
1213            if (bs == Qt::LinearGradientPattern) {
1214                const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad);
1215                const QPointF start(linearGrad->start());
1216                const QPointF stop(linearGrad->finalStop());
1217                shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()),
1218                                               CGPointMake(stop.x(), stop.y()), fill_func, true, true);
1219            } else {
1220                Q_ASSERT(bs == Qt::RadialGradientPattern);
1221                const QRadialGradient *radialGrad = static_cast<const QRadialGradient *>(grad);
1222                QPointF center(radialGrad->center());
1223                QPointF focal(radialGrad->focalPoint());
1224                qreal radius = radialGrad->radius();
1225                qreal focalRadius = radialGrad->focalRadius();
1226                shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
1227                                                focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
1228            }
1229
1230            CGFunctionRelease(fill_func);
1231        }
1232    } else
1233#endif
1234    if (bs != Qt::SolidPattern && bs != Qt::NoBrush
1235#ifndef QT_MAC_USE_NATIVE_GRADIENTS
1236       && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
1237#endif
1238        )
1239    {
1240        QMacPattern *qpattern = new QMacPattern;
1241        qpattern->pdev = pdev;
1242        CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 };
1243        CGColorSpaceRef base_colorspace = nullptr;
1244        if (bs == Qt::TexturePattern) {
1245            qpattern->data.pixmap = current.brush.texture();
1246            if (qpattern->data.pixmap.isQBitmap()) {
1247                const QColor &col = current.brush.color();
1248                components[0] = qt_mac_convert_color_to_cg(col.red());
1249                components[1] = qt_mac_convert_color_to_cg(col.green());
1250                components[2] = qt_mac_convert_color_to_cg(col.blue());
1251                base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
1252            }
1253        } else {
1254            qpattern->as_mask = true;
1255
1256            qpattern->data.bytes = qt_mac_patternForBrush(bs);
1257            const QColor &col = current.brush.color();
1258            components[0] = qt_mac_convert_color_to_cg(col.red());
1259            components[1] = qt_mac_convert_color_to_cg(col.green());
1260            components[2] = qt_mac_convert_color_to_cg(col.blue());
1261            base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
1262        }
1263        int width = qpattern->width(), height = qpattern->height();
1264        qpattern->foreground = current.brush.color();
1265
1266        CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
1267        CGContextSetFillColorSpace(hd, fill_colorspace);
1268
1269        CGAffineTransform xform = CGContextGetCTM(hd);
1270        xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform);
1271        xform = CGAffineTransformTranslate(xform, offset.x(), offset.y());
1272
1273        CGPatternCallbacks callbks;
1274        callbks.version = 0;
1275        callbks.drawPattern = qt_mac_draw_pattern;
1276        callbks.releaseInfo = qt_mac_dispose_pattern;
1277        CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
1278                xform, width, height, kCGPatternTilingNoDistortion,
1279                !base_colorspace, &callbks);
1280        CGContextSetFillPattern(hd, fill_pattern, components);
1281
1282
1283        CGPatternRelease(fill_pattern);
1284        CGColorSpaceRelease(base_colorspace);
1285        CGColorSpaceRelease(fill_colorspace);
1286    } else if (bs != Qt::NoBrush) {
1287        CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color()));
1288    }
1289}
1290
1291void
1292QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
1293{
1294    Q_Q(QCoreGraphicsPaintEngine);
1295    if (hd) {
1296        resetClip();
1297        QRegion sysClip = q->systemClip();
1298        if (!sysClip.isEmpty())
1299            qt_mac_clip_cg(hd, sysClip, &orig_xform);
1300        if (rgn)
1301            qt_mac_clip_cg(hd, *rgn, nullptr);
1302    }
1303}
1304
1305struct qt_mac_cg_transform_path {
1306    CGMutablePathRef path;
1307    CGAffineTransform transform;
1308};
1309
1310void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
1311{
1312    Q_ASSERT(info && element);
1313    qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info;
1314    switch (element->type) {
1315    case kCGPathElementMoveToPoint:
1316        CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
1317        break;
1318    case kCGPathElementAddLineToPoint:
1319        CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
1320        break;
1321    case kCGPathElementAddQuadCurveToPoint:
1322        CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
1323                                  element->points[1].x, element->points[1].y);
1324        break;
1325    case kCGPathElementAddCurveToPoint:
1326        CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
1327                              element->points[1].x, element->points[1].y,
1328                              element->points[2].x, element->points[2].y);
1329        break;
1330    case kCGPathElementCloseSubpath:
1331        CGPathCloseSubpath(t->path);
1332        break;
1333    default:
1334        qDebug() << "Unhandled path transform type: " << element->type;
1335    }
1336}
1337
1338void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
1339{
1340    Q_Q(QCoreGraphicsPaintEngine);
1341    Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
1342    if ((ops & (CGFill | CGEOFill))) {
1343        if (shading) {
1344            Q_ASSERT(path);
1345            CGContextBeginPath(hd);
1346            CGContextAddPath(hd, path);
1347            saveGraphicsState();
1348            if (ops & CGFill)
1349                CGContextClip(hd);
1350            else if (ops & CGEOFill)
1351                CGContextEOClip(hd);
1352            if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) {
1353                CGRect boundingBox = CGPathGetBoundingBox(path);
1354                CGContextConcatCTM(hd,
1355                    CGAffineTransformMake(boundingBox.size.width, 0,
1356                                          0, boundingBox.size.height,
1357                                          boundingBox.origin.x, boundingBox.origin.y));
1358            }
1359            CGContextDrawShading(hd, shading);
1360            restoreGraphicsState();
1361            ops &= ~CGFill;
1362            ops &= ~CGEOFill;
1363        } else if (current.brush.style() == Qt::NoBrush) {
1364            ops &= ~CGFill;
1365            ops &= ~CGEOFill;
1366        }
1367    }
1368    if ((ops & CGStroke) && current.pen.style() == Qt::NoPen)
1369        ops &= ~CGStroke;
1370
1371    if (ops & (CGEOFill | CGFill)) {
1372        CGContextBeginPath(hd);
1373        CGContextAddPath(hd, path);
1374        if (ops & CGEOFill) {
1375            CGContextEOFillPath(hd);
1376        } else {
1377            CGContextFillPath(hd);
1378        }
1379    }
1380
1381    // Avoid saving and restoring the context if we can.
1382    const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone ||
1383                                  !(q->state->renderHints() & QPainter::Antialiasing));
1384    if (ops & CGStroke) {
1385        if (needContextSave)
1386            saveGraphicsState();
1387        CGContextBeginPath(hd);
1388
1389        // Translate a fraction of a pixel size in the y direction
1390        // to make sure that primitives painted at pixel borders
1391        // fills the right pixel. This is needed since the y xais
1392        // in the Quartz coordinate system is inverted compared to Qt.
1393        if (!(q->state->renderHints() & QPainter::Antialiasing)) {
1394            if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3)
1395                CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25);
1396            else
1397                CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
1398        }
1399
1400        if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) {
1401            // If antialiazing is enabled, use the cosmetic pen size directly.
1402            if (q->state->renderHints() & QPainter::Antialiasing)
1403                CGContextSetLineWidth(hd,  cosmeticPenSize);
1404            else if (current.pen.widthF() <= 1)
1405                CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f);
1406            else
1407                CGContextSetLineWidth(hd, cosmeticPenSize);
1408        }
1409        if (cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) {
1410            qt_mac_cg_transform_path t;
1411            t.transform = qt_mac_convert_transform_to_cg(current.transform);
1412            t.path = CGPathCreateMutable();
1413            CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path
1414            setTransform(nullptr); //unset the context transform
1415            CGContextSetLineWidth(hd,  cosmeticPenSize);
1416            CGContextAddPath(hd, t.path);
1417            CGPathRelease(t.path);
1418        } else {
1419            CGContextAddPath(hd, path);
1420        }
1421
1422        CGContextStrokePath(hd);
1423        if (needContextSave)
1424            restoreGraphicsState();
1425    }
1426}
1427
1428QT_END_NAMESPACE
1429