1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
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>
51 #include <qtextcodec.h>
53 #include <qvarlengtharray.h>
55 #include <qcoreapplication.h>
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>
75 extern int qt_antialiasing_threshold; // QApplication.cpp
77 /*****************************************************************************
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
87 void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
90 /*****************************************************************************
91 QCoreGraphicsPaintEngine utility functions
92 *****************************************************************************/
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());
101 inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
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())
109 return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
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
116 #ifdef QT_MAC_USE_NATIVE_GRADIENTS
117 static bool drawGradientNatively(const QGradient *gradient)
119 return gradient->spread() == QGradient::PadSpread;
123 static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
125 QBrush *brush = static_cast<QBrush *>(info);
126 Q_ASSERT(brush && brush->gradient());
128 const QGradientStops stops = brush->gradient()->stops();
129 const int n = stops.count();
131 const QGradientStop *begin = stops.constBegin();
132 const QGradientStop *end = begin + n;
135 const QGradientStop *i = begin;
136 while (i != end && i->first < p)
141 c = begin->second.rgba();
142 } else if (i == end) {
143 c = (end - 1)->second.rgba();
145 const QGradientStop &s1 = *(i - 1);
146 const QGradientStop &s2 = *i;
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));
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));
167 void QCoreGraphicsPaintEnginePrivate::resetClip()
169 static bool inReset = false;
174 CGAffineTransform old_xform = CGContextGetCTM(hd);
177 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
178 while (stackCount > 0) {
179 restoreGraphicsState();
184 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
185 CGContextConcatCTM(hd, old_xform);
188 static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
190 return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
193 static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
195 CGMutablePathRef ret = CGPathCreateMutable();
197 for (int i=0; i<p.elementCount(); ++i) {
198 const QPainterPath::Element &elm = p.elementAt(i);
200 case QPainterPath::MoveToElement:
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);
208 case QPainterPath::LineToElement:
209 CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
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);
221 qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
226 && p.elementAt(p.elementCount() - 1).x == startPt.x()
227 && p.elementAt(p.elementCount() - 1).y == startPt.y())
228 CGPathCloseSubpath(ret);
232 CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
233 QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
234 bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
236 CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
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);
246 m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
248 if (!m_postRoutineRegistered) {
249 m_postRoutineRegistered = true;
250 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
253 return m_genericColorSpace;
255 // Just return the main display colorspace for the moment.
256 return macDisplayColorSpace();
260 //pattern handling (tiling)
262 # define QMACPATTERN_MASK_MULTIPLIER 32
264 # define QMACPATTERN_MASK_MULTIPLIER 1
269 QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
270 ~QMacPattern() { CGImageRelease(image); }
273 return CGImageGetWidth(image);
275 return 8*QMACPATTERN_MASK_MULTIPLIER;
276 return data.pixmap.width();
280 return CGImageGetHeight(image);
282 return 8*QMACPATTERN_MASK_MULTIPLIER;
283 return data.pixmap.height();
297 static void qt_mac_draw_pattern(void *info, CGContextRef c)
299 QMacPattern *pat = (QMacPattern*)info;
301 bool isBitmap = (pat->data.pixmap.depth() == 1);
302 if(!pat->image) { //lazy cache
304 Q_ASSERT(pat->data.bytes);
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);
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);
319 const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
320 QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
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);
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;
339 w = pat->data.pixmap.width();
340 h = pat->data.pixmap.height();
342 pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
344 pat->image = (CGImageRef)pat->data.pixmap.macCGHandle();
347 w = CGImageGetWidth(pat->image);
348 h = CGImageGetHeight(pat->image);
352 bool needRestore = false;
353 if (CGImageIsMask(pat->image)) {
354 CGContextSaveGState(c);
355 CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
357 CGRect rect = CGRectMake(0, 0, w, h);
358 qt_mac_drawCGImage(c, &rect, pat->image);
360 CGContextRestoreGState(c);
362 static void qt_mac_dispose_pattern(void *info)
364 QMacPattern *pat = (QMacPattern*)info;
368 /*****************************************************************************
369 QCoreGraphicsPaintEngine member functions
370 *****************************************************************************/
372 inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
374 return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent
375 & ~QPaintEngine::PerspectiveTransform
376 & ~QPaintEngine::ConicalGradientFill
377 & ~QPaintEngine::LinearGradientFill
378 & ~QPaintEngine::RadialGradientFill
379 & ~QPaintEngine::BrushStroke);
382 QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
383 : QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
387 QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
388 : QPaintEngine(dptr, qt_mac_cg_features())
392 QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
397 QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
399 Q_D(QCoreGraphicsPaintEngine);
400 if(isActive()) { // already active painting
401 qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
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);
414 d->saveGraphicsState();
415 d->orig_xform = CGContextGetCTM(d->hd);
417 CGShadingRelease(d->shading);
420 d->setClip(0); //clear the context's clipping
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);
429 if((w->windowType() == Qt::Desktop)) {
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");
436 } else if(d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap
437 QPixmap *pm = (QPixmap*)d->pdev;
439 qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
445 setDirty(QPaintEngine::DirtyPen);
446 setDirty(QPaintEngine::DirtyBrush);
447 setDirty(QPaintEngine::DirtyBackground);
448 setDirty(QPaintEngine::DirtyHints);
453 QCoreGraphicsPaintEngine::end()
455 Q_D(QCoreGraphicsPaintEngine);
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)
462 CGShadingRelease(d->shading);
467 d->restoreGraphicsState();
468 CGContextSynchronize(d->hd);
469 CGContextRelease(d->hd);
476 QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
478 Q_D(QCoreGraphicsPaintEngine);
479 QPaintEngine::DirtyFlags flags = state.state();
481 if (flags & DirtyTransform)
482 updateMatrix(state.transform());
484 if (flags & DirtyClipEnabled) {
485 if (state.isClipEnabled())
486 updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
488 updateClipPath(QPainterPath(), Qt::NoClip);
491 if (flags & DirtyClipPath) {
492 updateClipPath(state.clipPath(), state.clipOperation());
493 } else if (flags & DirtyClipRegion) {
494 updateClipRegion(state.clipRegion(), state.clipOperation());
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))
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());
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;
526 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
527 static const float sqrt2 = sqrt(2);
528 qreal width = d->current.pen.widthF();
531 d->cosmeticPenSize = sqrt(pow(d->pixelSize.y(), 2) + pow(d->pixelSize.x(), 2)) / sqrt2 * width;
537 QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
539 Q_D(QCoreGraphicsPaintEngine);
540 Q_ASSERT(isActive());
541 d->current.pen = pen;
542 d->setStrokePen(pen);
546 QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
548 Q_D(QCoreGraphicsPaintEngine);
549 Q_ASSERT(isActive());
550 d->current.brush = brush;
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;
558 gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
564 CGShadingRelease(d->shading);
567 d->setFillBrush(brushOrigin);
571 QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
573 Q_D(QCoreGraphicsPaintEngine);
574 CGContextSetAlpha(d->hd, opacity);
578 QCoreGraphicsPaintEngine::updateFont(const QFont &)
580 Q_D(QCoreGraphicsPaintEngine);
581 Q_ASSERT(isActive());
582 updatePen(d->current.pen);
586 QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
588 Q_D(QCoreGraphicsPaintEngine);
589 Q_ASSERT(isActive());
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()))
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);
604 QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
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();
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;
623 CGRect rect = CGRectMake(0, 0, 0, 0);
624 CGContextClipToRect(d->hd, rect);
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);
632 CGContextEOClip(d->hd);
635 } else if(op == Qt::IntersectClip) {
636 d->current.clip = d->current.clip.intersected(clipRegion);
637 d->setClip(&d->current.clip);
643 QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
645 Q_D(QCoreGraphicsPaintEngine);
646 Q_ASSERT(isActive());
647 if(op == Qt::NoClip) {
648 d->current.clipEnabled = false;
649 d->current.clip = QRegion();
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);
664 QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
666 Q_D(QCoreGraphicsPaintEngine);
667 Q_ASSERT(isActive());
669 if (state->compositionMode() == QPainter::CompositionMode_Destination)
672 CGMutablePathRef path = qt_mac_compose_path(p);
673 uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
674 if(p.fillRule() == Qt::WindingFill)
675 ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
677 ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
678 CGContextBeginPath(d->hd);
679 d->drawPath(ops, path);
684 QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
686 Q_D(QCoreGraphicsPaintEngine);
687 Q_ASSERT(isActive());
689 if (state->compositionMode() == QPainter::CompositionMode_Destination)
692 for (int i=0; i<rectCount; ++i) {
695 CGMutablePathRef path = CGPathCreateMutable();
696 CGPathAddRect(path, 0, qt_mac_compose_rect(r));
697 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
704 QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
706 Q_D(QCoreGraphicsPaintEngine);
707 Q_ASSERT(isActive());
709 if (state->compositionMode() == QPainter::CompositionMode_Destination)
712 if (d->current.pen.capStyle() == Qt::FlatCap)
713 CGContextSetLineCap(d->hd, kCGLineCapSquare);
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);
722 bool doRestore = false;
723 if(d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
724 //we don't want adjusted pens for point rendering
726 d->saveGraphicsState();
727 CGContextSetLineWidth(d->hd, d->current.pen.widthF());
729 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
731 d->restoreGraphicsState();
733 if (d->current.pen.capStyle() == Qt::FlatCap)
734 CGContextSetLineCap(d->hd, kCGLineCapButt);
738 QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
740 Q_D(QCoreGraphicsPaintEngine);
741 Q_ASSERT(isActive());
743 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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,
756 QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
758 Q_D(QCoreGraphicsPaintEngine);
759 Q_ASSERT(isActive());
761 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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);
779 QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
781 Q_D(QCoreGraphicsPaintEngine);
782 Q_ASSERT(isActive());
784 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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());
793 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
797 void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
799 Q_D(QCoreGraphicsPaintEngine);
800 Q_ASSERT(isActive());
802 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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);
814 d->saveGraphicsState();
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())));
823 image = (CGImageRef)pm.macCGHandle();
825 qt_mac_drawCGImage(d->hd, &rect, image);
827 d->restoreGraphicsState();
830 void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
831 Qt::ImageConversionFlags flags)
833 Q_D(QCoreGraphicsPaintEngine);
835 Q_ASSERT(isActive());
837 if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
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);
849 void QCoreGraphicsPaintEngine::initialize()
853 void QCoreGraphicsPaintEngine::cleanup()
858 QCoreGraphicsPaintEngine::handle() const
864 QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
867 Q_D(QCoreGraphicsPaintEngine);
868 Q_ASSERT(isActive());
870 if (state->compositionMode() == QPainter::CompositionMode_Destination)
874 d->saveGraphicsState();
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;
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);
898 CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
899 CGContextFillRect(d->hd, mac_rect);
902 d->restoreGraphicsState();
904 CGColorSpaceRelease(cs);
905 CGPatternRelease(pat);
908 void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
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
916 QPaintEngine::drawTextItem(pos, item);
920 if (state->compositionMode() == QPainter::CompositionMode_Destination)
923 const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
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));
931 Q_ASSERT(type() == QPaintEngine::CoreGraphics);
933 QFontEngine *fe = ti.fontEngine;
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;
938 CGContextSetShouldAntialias(d->hd, textAA);
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());
945 case QFontEngine::Box:
946 d->drawBoxTextItem(pos, ti);
954 CGContextSetShouldAntialias(d->hd, !textAA);
957 updateBrush(oldBrush, oldBrushOrigin);
960 QPainter::RenderHints
961 QCoreGraphicsPaintEngine::supportedRenderHints() const
963 return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
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))
981 extern void CGContextSetCompositeOperation(CGContextRef, int);
982 } // private function, but is in all versions of OS X.
984 QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
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;
990 case QPainter::CompositionMode_Multiply:
991 cg_mode = kCGBlendModeMultiply;
993 case QPainter::CompositionMode_Screen:
994 cg_mode = kCGBlendModeScreen;
996 case QPainter::CompositionMode_Overlay:
997 cg_mode = kCGBlendModeOverlay;
999 case QPainter::CompositionMode_Darken:
1000 cg_mode = kCGBlendModeDarken;
1002 case QPainter::CompositionMode_Lighten:
1003 cg_mode = kCGBlendModeLighten;
1005 case QPainter::CompositionMode_ColorDodge:
1006 cg_mode = kCGBlendModeColorDodge;
1008 case QPainter::CompositionMode_ColorBurn:
1009 cg_mode = kCGBlendModeColorBurn;
1011 case QPainter::CompositionMode_HardLight:
1012 cg_mode = kCGBlendModeHardLight;
1014 case QPainter::CompositionMode_SoftLight:
1015 cg_mode = kCGBlendModeSoftLight;
1017 case QPainter::CompositionMode_Difference:
1018 cg_mode = kCGBlendModeDifference;
1020 case QPainter::CompositionMode_Exclusion:
1021 cg_mode = kCGBlendModeExclusion;
1023 case QPainter::CompositionMode_Plus:
1024 cg_mode = kCGBlendModePlusLighter;
1026 case QPainter::CompositionMode_SourceOver:
1027 cg_mode = kCGBlendModeNormal;
1029 case QPainter::CompositionMode_DestinationOver:
1030 cg_mode = kCGBlendModeDestinationOver;
1032 case QPainter::CompositionMode_Clear:
1033 cg_mode = kCGBlendModeClear;
1035 case QPainter::CompositionMode_Source:
1036 cg_mode = kCGBlendModeCopy;
1038 case QPainter::CompositionMode_Destination:
1041 case QPainter::CompositionMode_SourceIn:
1042 cg_mode = kCGBlendModeSourceIn;
1044 case QPainter::CompositionMode_DestinationIn:
1045 cg_mode = kCGCompositeModeDestinationIn;
1047 case QPainter::CompositionMode_SourceOut:
1048 cg_mode = kCGBlendModeSourceOut;
1050 case QPainter::CompositionMode_DestinationOut:
1051 cg_mode = kCGBlendModeDestinationOver;
1053 case QPainter::CompositionMode_SourceAtop:
1054 cg_mode = kCGBlendModeSourceAtop;
1056 case QPainter::CompositionMode_DestinationAtop:
1057 cg_mode = kCGBlendModeDestinationAtop;
1059 case QPainter::CompositionMode_Xor:
1060 cg_mode = kCGBlendModeXOR;
1066 CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
1070 // The standard porter duff ops.
1071 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3
1072 && mode <= QPainter::CompositionMode_Xor) {
1073 int cg_mode = kCGCompositeModeCopy;
1075 case QPainter::CompositionMode_SourceOver:
1076 cg_mode = kCGCompositeModeSourceOver;
1078 case QPainter::CompositionMode_DestinationOver:
1079 cg_mode = kCGCompositeModeDestinationOver;
1081 case QPainter::CompositionMode_Clear:
1082 cg_mode = kCGCompositeModeClear;
1085 qWarning("QCoreGraphicsPaintEngine: Unhandled composition mode %d", (int)mode);
1087 case QPainter::CompositionMode_Source:
1088 cg_mode = kCGCompositeModeCopy;
1090 case QPainter::CompositionMode_Destination:
1091 cg_mode = CGCompositeMode(-1);
1093 case QPainter::CompositionMode_SourceIn:
1094 cg_mode = kCGCompositeModeSourceIn;
1096 case QPainter::CompositionMode_DestinationIn:
1097 cg_mode = kCGCompositeModeDestinationIn;
1099 case QPainter::CompositionMode_SourceOut:
1100 cg_mode = kCGCompositeModeSourceOut;
1102 case QPainter::CompositionMode_DestinationOut:
1103 cg_mode = kCGCompositeModeDestinationOut;
1105 case QPainter::CompositionMode_SourceAtop:
1106 cg_mode = kCGCompositeModeSourceAtop;
1108 case QPainter::CompositionMode_DestinationAtop:
1109 cg_mode = kCGCompositeModeDestinationAtop;
1111 case QPainter::CompositionMode_Xor:
1112 cg_mode = kCGCompositeModeXOR;
1116 CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
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;
1123 case QPainter::CompositionMode_Multiply:
1124 cg_mode = kCGBlendModeMultiply;
1126 case QPainter::CompositionMode_Screen:
1127 cg_mode = kCGBlendModeScreen;
1129 case QPainter::CompositionMode_Overlay:
1130 cg_mode = kCGBlendModeOverlay;
1132 case QPainter::CompositionMode_Darken:
1133 cg_mode = kCGBlendModeDarken;
1135 case QPainter::CompositionMode_Lighten:
1136 cg_mode = kCGBlendModeLighten;
1138 case QPainter::CompositionMode_ColorDodge:
1139 cg_mode = kCGBlendModeColorDodge;
1141 case QPainter::CompositionMode_ColorBurn:
1142 cg_mode = kCGBlendModeColorBurn;
1144 case QPainter::CompositionMode_HardLight:
1145 cg_mode = kCGBlendModeHardLight;
1147 case QPainter::CompositionMode_SoftLight:
1148 cg_mode = kCGBlendModeSoftLight;
1150 case QPainter::CompositionMode_Difference:
1151 cg_mode = kCGBlendModeDifference;
1153 case QPainter::CompositionMode_Exclusion:
1154 cg_mode = kCGBlendModeExclusion;
1156 case QPainter::CompositionMode_Plus:
1157 needPrivateAPI = true;
1158 cg_mode = kCGCompositeModePlusLighter;
1163 if (!needPrivateAPI)
1164 CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
1166 CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
1173 QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
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);
1181 CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
1182 kCGInterpolationHigh : kCGInterpolationNone);
1184 bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing;
1185 if (!textAntialiasing || d->disabledSmoothFonts) {
1186 d->disabledSmoothFonts = !textAntialiasing;
1187 CGContextSetShouldSmoothFonts(d->hd, textAntialiasing);
1192 Returns the size of one device pixel in user-space coordinates.
1194 QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
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()));
1202 Adjusts the pen width so we get correct line widths in the
1203 non-transformed, aliased case.
1205 float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
1207 Q_Q(QCoreGraphicsPaintEngine);
1208 float ret = penWidth;
1209 if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
1212 else if (penWidth < 3)
1221 QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
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()));
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());
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);
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)) {
1271 linedashes[i] += cglinewidth/2;
1273 linedashes[i] -= cglinewidth/2;
1276 CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
1279 CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
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)
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];
1307 void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
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);
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 *>(¤t.brush),
1320 1, domain, 4, 0, &callbacks);
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);
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);
1340 CGFunctionRelease(fill_func);
1344 if(bs != Qt::SolidPattern && bs != Qt::NoBrush
1345 #ifndef QT_MAC_USE_NATIVE_GRADIENTS
1346 && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
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();
1364 qpattern->as_mask = true;
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();
1373 int width = qpattern->width(), height = qpattern->height();
1374 qpattern->foreground = current.brush.color();
1376 CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
1377 CGContextSetFillColorSpace(hd, fill_colorspace);
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());
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);
1392 CGPatternRelease(fill_pattern);
1393 CGColorSpaceRelease(fill_colorspace);
1394 } else if(bs != Qt::NoBrush) {
1395 CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
1400 QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
1402 Q_Q(QCoreGraphicsPaintEngine);
1405 QRegion sysClip = q->systemClip();
1406 if(!sysClip.isEmpty())
1407 qt_mac_clip_cg(hd, sysClip, &orig_xform);
1409 qt_mac_clip_cg(hd, *rgn, 0);
1413 struct qt_mac_cg_transform_path {
1414 CGMutablePathRef path;
1415 CGAffineTransform transform;
1418 void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
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);
1426 case kCGPathElementAddLineToPoint:
1427 CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
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);
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);
1438 case kCGPathElementCloseSubpath:
1439 CGPathCloseSubpath(t->path);
1442 qDebug() << "Unhandled path transform type: " << element->type;
1446 void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
1448 Q_Q(QCoreGraphicsPaintEngine);
1449 Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
1450 if((ops & (CGFill | CGEOFill))) {
1453 CGContextBeginPath(hd);
1454 CGContextAddPath(hd, path);
1455 saveGraphicsState();
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));
1467 CGContextDrawShading(hd, shading);
1468 restoreGraphicsState();
1471 } else if (current.brush.style() == Qt::NoBrush) {
1476 if((ops & CGStroke) && current.pen.style() == Qt::NoPen)
1479 if(ops & (CGEOFill | CGFill)) {
1480 CGContextBeginPath(hd);
1481 CGContextAddPath(hd, path);
1482 if (ops & CGEOFill) {
1483 CGContextEOFillPath(hd);
1485 CGContextFillPath(hd);
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);
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)
1507 CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
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);
1517 CGContextSetLineWidth(hd, cosmeticPenSize);
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);
1529 CGContextAddPath(hd, path);
1532 CGContextStrokePath(hd);
1533 if (needContextSave)
1534 restoreGraphicsState();