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