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