1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qpaintengine_mac_p.h"
43 #include "qprintengine_mac_p.h"
46 #include <qpaintdevice.h>
47 #include <qpainterpath.h>
48 #include <qpixmapcache.h>
49 #include <private/qpaintengine_raster_p.h>
52 #include <qtextcodec.h>
54 #include <qvarlengtharray.h>
56 #include <qcoreapplication.h>
59 #include <qpa/qplatformpixmap.h>
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>
69 #include "qcocoahelpers.h"
75 /*****************************************************************************
76 QCoreGraphicsPaintEngine utility functions
77 *****************************************************************************/
79 void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
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);
89 CGContextBeginPath(hd);
91 CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
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);
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);
113 if (orig_xform) {//reset xforms
114 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
115 CGContextConcatCTM(hd, old_xform);
119 CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
121 bool isWidget = (paintDevice->devType() == QInternal::Widget);
122 return QCoreGraphicsPaintEngine::macDisplayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)
126 // Implemented for qt_mac_p.h
127 QMacCGContext::QMacCGContext(QPainter *p)
129 QPaintEngine *pe = p->paintEngine();
130 if (pe->type() == QPaintEngine::MacPrinter)
131 pe = static_cast<QMacPrintEngine*>(pe)->paintEngine();
134 if (pe->type() == QPaintEngine::CoreGraphics)
135 context = static_cast<QCoreGraphicsPaintEngine*>(pe)->handle();
137 int devType = p->device()->devType();
138 if (pe->type() == QPaintEngine::Raster
139 && (devType == QInternal::Widget || devType == QInternal::Pixmap || devType == QInternal::Image)) {
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;
146 const QImage *image = (const QImage *) pe->paintDevice();
148 context = CGBitmapContextCreate((void *) image->bits(), image->width(), image->height(),
149 8, image->bytesPerLine(), colorspace, flags);
151 CGContextTranslateCTM(context, 0, image->height());
152 CGContextScaleCTM(context, 1, -1);
154 if (devType == QInternal::Widget) {
155 QRegion clip = p->paintEngine()->systemClip();
156 QTransform native = p->deviceTransform();
158 if (p->hasClipping()) {
159 QRegion r = p->clipRegion();
160 r.translate(native.dx(), native.dy());
166 qt_mac_clip_cg(context, clip, 0);
168 CGContextTranslateCTM(context, native.dx(), native.dy());
171 CGContextRetain(context);
175 void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t)
177 free(const_cast<void *>(memoryToFree));
180 CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
182 QImage image = pixmap.toImage();
183 if (image.format() != QImage::Format_ARGB32_Premultiplied)
184 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
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;
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);
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());
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.
215 \warning This function is only available on Mac OS X.
216 \warning This function is duplicated in qmacstyle_mac.mm
218 CGContextRef qt_mac_cg_context(const QPaintDevice *pdev)
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;
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);
233 qDebug() << "qt_mac_cg_context: Unsupported pixmap class";
236 CGContextTranslateCTM(ret, 0, pm->height());
237 CGContextScaleCTM(ret, 1, -1);
239 } else if (pdev->devType() == QInternal::Widget) {
240 //CGContextRef ret = static_cast<CGContextRef>(static_cast<const QWidget *>(pdev)->macCGHandle());
241 ///CGContextRetain(ret);
243 qDebug() << "qt_mac_cg_context: not implemented: Widget class";
249 inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
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())
257 return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
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
264 #ifdef QT_MAC_USE_NATIVE_GRADIENTS
265 static bool drawGradientNatively(const QGradient *gradient)
267 return gradient->spread() == QGradient::PadSpread;
271 static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
273 QBrush *brush = static_cast<QBrush *>(info);
274 Q_ASSERT(brush && brush->gradient());
276 const QGradientStops stops = brush->gradient()->stops();
277 const int n = stops.count();
279 const QGradientStop *begin = stops.constBegin();
280 const QGradientStop *end = begin + n;
283 const QGradientStop *i = begin;
284 while (i != end && i->first < p)
289 c = begin->second.rgba();
290 } else if (i == end) {
291 c = (end - 1)->second.rgba();
293 const QGradientStop &s1 = *(i - 1);
294 const QGradientStop &s2 = *i;
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));
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));
315 void QCoreGraphicsPaintEnginePrivate::resetClip()
317 static bool inReset = false;
322 CGAffineTransform old_xform = CGContextGetCTM(hd);
325 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
326 while (stackCount > 0) {
327 restoreGraphicsState();
332 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
333 CGContextConcatCTM(hd, old_xform);
336 static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
338 return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
341 static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
343 CGMutablePathRef ret = CGPathCreateMutable();
345 for (int i=0; i<p.elementCount(); ++i) {
346 const QPainterPath::Element &elm = p.elementAt(i);
348 case QPainterPath::MoveToElement:
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);
356 case QPainterPath::LineToElement:
357 CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
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);
369 qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
374 && p.elementAt(p.elementCount() - 1).x == startPt.x()
375 && p.elementAt(p.elementCount() - 1).y == startPt.y())
376 CGPathCloseSubpath(ret);
380 CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
381 QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
382 bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
384 CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
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);
394 m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
396 if (!m_postRoutineRegistered) {
397 m_postRoutineRegistered = true;
398 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
401 return m_genericColorSpace;
403 // Just return the main display colorspace for the moment.
404 return macDisplayColorSpace();
409 Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
410 to support multiple displays correctly.
412 CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
414 CGColorSpaceRef colorSpace;
416 CGDirectDisplayID displayID;
417 CMProfileRef displayProfile = 0;
419 displayID = CGMainDisplayID();
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
428 if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
431 CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
433 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
435 return macDisplayColorSpace(0); // fall back on main display
439 colorSpace = CGColorSpaceCreateDeviceRGB();
441 m_displayColorSpaceHash.insert(displayID, colorSpace);
442 CMCloseProfile(displayProfile);
443 if (!m_postRoutineRegistered) {
444 m_postRoutineRegistered = true;
445 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
450 void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
452 if (m_genericColorSpace) {
453 CFRelease(m_genericColorSpace);
454 m_genericColorSpace = 0;
456 QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
457 while (it != m_displayColorSpaceHash.constEnd()) {
459 CFRelease(it.value());
462 m_displayColorSpaceHash.clear();
465 //pattern handling (tiling)
467 # define QMACPATTERN_MASK_MULTIPLIER 32
469 # define QMACPATTERN_MASK_MULTIPLIER 1
474 QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
475 ~QMacPattern() { CGImageRelease(image); }
478 return CGImageGetWidth(image);
480 return 8*QMACPATTERN_MASK_MULTIPLIER;
481 return data.pixmap.width();
485 return CGImageGetHeight(image);
487 return 8*QMACPATTERN_MASK_MULTIPLIER;
488 return data.pixmap.height();
502 static void qt_mac_draw_pattern(void *info, CGContextRef c)
504 QMacPattern *pat = (QMacPattern*)info;
506 bool isBitmap = (pat->data.pixmap.depth() == 1);
507 if (!pat->image) { //lazy cache
509 Q_ASSERT(pat->data.bytes);
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);
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);
524 const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
525 QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
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);
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;
544 w = pat->data.pixmap.width();
545 h = pat->data.pixmap.height();
547 pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
549 pat->image = qt_mac_image_to_cgimage(pat->data.pixmap.toImage());
552 w = CGImageGetWidth(pat->image);
553 h = CGImageGetHeight(pat->image);
557 bool needRestore = false;
558 if (CGImageIsMask(pat->image)) {
559 CGContextSaveGState(c);
560 CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
562 CGRect rect = CGRectMake(0, 0, w, h);
563 qt_mac_drawCGImage(c, &rect, pat->image);
565 CGContextRestoreGState(c);
567 static void qt_mac_dispose_pattern(void *info)
569 QMacPattern *pat = (QMacPattern*)info;
573 /*****************************************************************************
574 QCoreGraphicsPaintEngine member functions
575 *****************************************************************************/
577 inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
579 return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent
580 & ~QPaintEngine::PerspectiveTransform
581 & ~QPaintEngine::ConicalGradientFill
582 & ~QPaintEngine::LinearGradientFill
583 & ~QPaintEngine::RadialGradientFill
584 & ~QPaintEngine::BrushStroke);
587 QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
588 : QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
592 QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
593 : QPaintEngine(dptr, qt_mac_cg_features())
597 QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
602 QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
604 Q_D(QCoreGraphicsPaintEngine);
605 if (isActive()) { // already active painting
606 qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
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);
619 d->saveGraphicsState();
620 d->orig_xform = CGContextGetCTM(d->hd);
622 CGShadingRelease(d->shading);
625 d->setClip(0); //clear the context's clipping
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);
634 if ((w->windowType() == Qt::Desktop)) {
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");
641 } else if (d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap
642 QPixmap *pm = (QPixmap*)d->pdev;
644 qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
650 setDirty(QPaintEngine::DirtyPen);
651 setDirty(QPaintEngine::DirtyBrush);
652 setDirty(QPaintEngine::DirtyBackground);
653 setDirty(QPaintEngine::DirtyHints);
658 QCoreGraphicsPaintEngine::end()
660 Q_D(QCoreGraphicsPaintEngine);
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)
666 CGShadingRelease(d->shading);
671 d->restoreGraphicsState();
672 CGContextSynchronize(d->hd);
673 CGContextRelease(d->hd);
680 QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
682 Q_D(QCoreGraphicsPaintEngine);
683 QPaintEngine::DirtyFlags flags = state.state();
685 if (flags & DirtyTransform)
686 updateMatrix(state.transform());
688 if (flags & DirtyClipEnabled) {
689 if (state.isClipEnabled())
690 updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
692 updateClipPath(QPainterPath(), Qt::NoClip);
695 if (flags & DirtyClipPath) {
696 updateClipPath(state.clipPath(), state.clipOperation());
697 } else if (flags & DirtyClipRegion) {
698 updateClipRegion(state.clipRegion(), state.clipOperation());
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))
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());
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;
730 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
731 static const float sqrt2 = sqrt(2);
732 qreal width = d->current.pen.widthF();
735 d->cosmeticPenSize = sqrt(pow(d->pixelSize.y(), 2) + pow(d->pixelSize.x(), 2)) / sqrt2 * width;
741 QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
743 Q_D(QCoreGraphicsPaintEngine);
744 Q_ASSERT(isActive());
745 d->current.pen = pen;
746 d->setStrokePen(pen);
750 QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
752 Q_D(QCoreGraphicsPaintEngine);
753 Q_ASSERT(isActive());
754 d->current.brush = brush;
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;
762 gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
768 CGShadingRelease(d->shading);
771 d->setFillBrush(brushOrigin);
775 QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
777 Q_D(QCoreGraphicsPaintEngine);
778 CGContextSetAlpha(d->hd, opacity);
782 QCoreGraphicsPaintEngine::updateFont(const QFont &)
784 Q_D(QCoreGraphicsPaintEngine);
785 Q_ASSERT(isActive());
786 updatePen(d->current.pen);
790 QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
792 Q_D(QCoreGraphicsPaintEngine);
793 Q_ASSERT(isActive());
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()))
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);
808 QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
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();
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;
827 CGRect rect = CGRectMake(0, 0, 0, 0);
828 CGContextClipToRect(d->hd, rect);
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);
836 CGContextEOClip(d->hd);
839 } else if (op == Qt::IntersectClip) {
840 d->current.clip = d->current.clip.intersected(clipRegion);
841 d->setClip(&d->current.clip);
847 QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
849 Q_D(QCoreGraphicsPaintEngine);
850 Q_ASSERT(isActive());
851 if (op == Qt::NoClip) {
852 d->current.clipEnabled = false;
853 d->current.clip = QRegion();
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);
868 QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
870 Q_D(QCoreGraphicsPaintEngine);
871 Q_ASSERT(isActive());
873 if (state->compositionMode() == QPainter::CompositionMode_Destination)
876 CGMutablePathRef path = qt_mac_compose_path(p);
877 uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
878 if (p.fillRule() == Qt::WindingFill)
879 ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
881 ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
882 CGContextBeginPath(d->hd);
883 d->drawPath(ops, path);
888 QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
890 Q_D(QCoreGraphicsPaintEngine);
891 Q_ASSERT(isActive());
893 if (state->compositionMode() == QPainter::CompositionMode_Destination)
896 for (int i=0; i<rectCount; ++i) {
899 CGMutablePathRef path = CGPathCreateMutable();
900 CGPathAddRect(path, 0, qt_mac_compose_rect(r));
901 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
908 QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
910 Q_D(QCoreGraphicsPaintEngine);
911 Q_ASSERT(isActive());
913 if (state->compositionMode() == QPainter::CompositionMode_Destination)
916 if (d->current.pen.capStyle() == Qt::FlatCap)
917 CGContextSetLineCap(d->hd, kCGLineCapSquare);
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);
926 bool doRestore = false;
927 if (d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
928 //we don't want adjusted pens for point rendering
930 d->saveGraphicsState();
931 CGContextSetLineWidth(d->hd, d->current.pen.widthF());
933 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
935 d->restoreGraphicsState();
937 if (d->current.pen.capStyle() == Qt::FlatCap)
938 CGContextSetLineCap(d->hd, kCGLineCapButt);
942 QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
944 Q_D(QCoreGraphicsPaintEngine);
945 Q_ASSERT(isActive());
947 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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,
960 QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
962 Q_D(QCoreGraphicsPaintEngine);
963 Q_ASSERT(isActive());
965 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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);
983 QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
985 Q_D(QCoreGraphicsPaintEngine);
986 Q_ASSERT(isActive());
988 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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());
997 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
1001 void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
1003 Q_D(QCoreGraphicsPaintEngine);
1004 Q_ASSERT(isActive());
1006 if (state->compositionMode() == QPainter::CompositionMode_Destination)
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);
1018 d->saveGraphicsState();
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());
1026 image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
1028 image = qt_mac_image_to_cgimage(pm.toImage());
1030 qt_mac_drawCGImage(d->hd, &rect, image);
1032 d->restoreGraphicsState();
1035 static void drawImageReleaseData (void *info, const void *, size_t)
1037 delete static_cast<QImage *>(info);
1040 CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0)
1043 if (img.depth() != 32)
1044 image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
1046 image = new QImage(img);
1048 uint cgflags = kCGImageAlphaNone;
1049 switch (image->format()) {
1050 case QImage::Format_ARGB32_Premultiplied:
1051 cgflags = kCGImageAlphaPremultipliedFirst;
1053 case QImage::Format_ARGB32:
1054 cgflags = kCGImageAlphaFirst;
1056 case QImage::Format_RGB32:
1057 cgflags = kCGImageAlphaNoneSkipFirst;
1061 #if defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version
1062 cgflags |= kCGBitmapByteOrder32Host;
1064 QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
1065 static_cast<const QImage *>(image)->bits(),
1067 drawImageReleaseData);
1070 return CGImageCreate(image->width(), image->height(), 8, 32,
1071 image->bytesPerLine(),
1072 QCoreGraphicsPaintEngine::macGenericColorSpace(),
1073 cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
1077 void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
1078 Qt::ImageConversionFlags flags)
1080 Q_D(QCoreGraphicsPaintEngine);
1082 Q_ASSERT(isActive());
1084 if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
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);
1096 void QCoreGraphicsPaintEngine::initialize()
1100 void QCoreGraphicsPaintEngine::cleanup()
1105 QCoreGraphicsPaintEngine::handle() const
1107 return d_func()->hd;
1111 QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
1114 Q_D(QCoreGraphicsPaintEngine);
1115 Q_ASSERT(isActive());
1117 if (state->compositionMode() == QPainter::CompositionMode_Destination)
1120 //save the old state
1121 d->saveGraphicsState();
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);
1144 //fill the rectangle
1145 CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
1146 CGContextFillRect(d->hd, mac_rect);
1149 d->restoreGraphicsState();
1151 CGColorSpaceRelease(cs);
1152 CGPatternRelease(pat);
1155 void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
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
1163 QPaintEngine::drawTextItem(pos, item);
1167 if (state->compositionMode() == QPainter::CompositionMode_Destination)
1170 const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
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));
1178 Q_ASSERT(type() == QPaintEngine::CoreGraphics);
1180 QFontEngine *fe = ti.fontEngine;
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);
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());
1194 case QFontEngine::Box:
1195 d->drawBoxTextItem(pos, ti);
1202 if (textAA != lineAA)
1203 CGContextSetShouldAntialias(d->hd, !textAA);
1206 updateBrush(oldBrush, oldBrushOrigin);
1209 QPainter::RenderHints
1210 QCoreGraphicsPaintEngine::supportedRenderHints() const
1212 return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
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))
1230 extern void CGContextSetCompositeOperation(CGContextRef, int);
1231 } // private function, but is in all versions of OS X.
1233 QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
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;
1239 case QPainter::CompositionMode_Multiply:
1240 cg_mode = kCGBlendModeMultiply;
1242 case QPainter::CompositionMode_Screen:
1243 cg_mode = kCGBlendModeScreen;
1245 case QPainter::CompositionMode_Overlay:
1246 cg_mode = kCGBlendModeOverlay;
1248 case QPainter::CompositionMode_Darken:
1249 cg_mode = kCGBlendModeDarken;
1251 case QPainter::CompositionMode_Lighten:
1252 cg_mode = kCGBlendModeLighten;
1254 case QPainter::CompositionMode_ColorDodge:
1255 cg_mode = kCGBlendModeColorDodge;
1257 case QPainter::CompositionMode_ColorBurn:
1258 cg_mode = kCGBlendModeColorBurn;
1260 case QPainter::CompositionMode_HardLight:
1261 cg_mode = kCGBlendModeHardLight;
1263 case QPainter::CompositionMode_SoftLight:
1264 cg_mode = kCGBlendModeSoftLight;
1266 case QPainter::CompositionMode_Difference:
1267 cg_mode = kCGBlendModeDifference;
1269 case QPainter::CompositionMode_Exclusion:
1270 cg_mode = kCGBlendModeExclusion;
1272 case QPainter::CompositionMode_Plus:
1273 cg_mode = kCGBlendModePlusLighter;
1275 case QPainter::CompositionMode_SourceOver:
1276 cg_mode = kCGBlendModeNormal;
1278 case QPainter::CompositionMode_DestinationOver:
1279 cg_mode = kCGBlendModeDestinationOver;
1281 case QPainter::CompositionMode_Clear:
1282 cg_mode = kCGBlendModeClear;
1284 case QPainter::CompositionMode_Source:
1285 cg_mode = kCGBlendModeCopy;
1287 case QPainter::CompositionMode_Destination:
1290 case QPainter::CompositionMode_SourceIn:
1291 cg_mode = kCGBlendModeSourceIn;
1293 case QPainter::CompositionMode_DestinationIn:
1294 cg_mode = kCGCompositeModeDestinationIn;
1296 case QPainter::CompositionMode_SourceOut:
1297 cg_mode = kCGBlendModeSourceOut;
1299 case QPainter::CompositionMode_DestinationOut:
1300 cg_mode = kCGBlendModeDestinationOver;
1302 case QPainter::CompositionMode_SourceAtop:
1303 cg_mode = kCGBlendModeSourceAtop;
1305 case QPainter::CompositionMode_DestinationAtop:
1306 cg_mode = kCGBlendModeDestinationAtop;
1308 case QPainter::CompositionMode_Xor:
1309 cg_mode = kCGBlendModeXOR;
1315 CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
1319 // The standard porter duff ops.
1320 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3
1321 && mode <= QPainter::CompositionMode_Xor) {
1322 int cg_mode = kCGCompositeModeCopy;
1324 case QPainter::CompositionMode_SourceOver:
1325 cg_mode = kCGCompositeModeSourceOver;
1327 case QPainter::CompositionMode_DestinationOver:
1328 cg_mode = kCGCompositeModeDestinationOver;
1330 case QPainter::CompositionMode_Clear:
1331 cg_mode = kCGCompositeModeClear;
1334 qWarning("QCoreGraphicsPaintEngine: Unhandled composition mode %d", (int)mode);
1336 case QPainter::CompositionMode_Source:
1337 cg_mode = kCGCompositeModeCopy;
1339 case QPainter::CompositionMode_Destination:
1340 cg_mode = CGCompositeMode(-1);
1342 case QPainter::CompositionMode_SourceIn:
1343 cg_mode = kCGCompositeModeSourceIn;
1345 case QPainter::CompositionMode_DestinationIn:
1346 cg_mode = kCGCompositeModeDestinationIn;
1348 case QPainter::CompositionMode_SourceOut:
1349 cg_mode = kCGCompositeModeSourceOut;
1351 case QPainter::CompositionMode_DestinationOut:
1352 cg_mode = kCGCompositeModeDestinationOut;
1354 case QPainter::CompositionMode_SourceAtop:
1355 cg_mode = kCGCompositeModeSourceAtop;
1357 case QPainter::CompositionMode_DestinationAtop:
1358 cg_mode = kCGCompositeModeDestinationAtop;
1360 case QPainter::CompositionMode_Xor:
1361 cg_mode = kCGCompositeModeXOR;
1365 CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
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;
1372 case QPainter::CompositionMode_Multiply:
1373 cg_mode = kCGBlendModeMultiply;
1375 case QPainter::CompositionMode_Screen:
1376 cg_mode = kCGBlendModeScreen;
1378 case QPainter::CompositionMode_Overlay:
1379 cg_mode = kCGBlendModeOverlay;
1381 case QPainter::CompositionMode_Darken:
1382 cg_mode = kCGBlendModeDarken;
1384 case QPainter::CompositionMode_Lighten:
1385 cg_mode = kCGBlendModeLighten;
1387 case QPainter::CompositionMode_ColorDodge:
1388 cg_mode = kCGBlendModeColorDodge;
1390 case QPainter::CompositionMode_ColorBurn:
1391 cg_mode = kCGBlendModeColorBurn;
1393 case QPainter::CompositionMode_HardLight:
1394 cg_mode = kCGBlendModeHardLight;
1396 case QPainter::CompositionMode_SoftLight:
1397 cg_mode = kCGBlendModeSoftLight;
1399 case QPainter::CompositionMode_Difference:
1400 cg_mode = kCGBlendModeDifference;
1402 case QPainter::CompositionMode_Exclusion:
1403 cg_mode = kCGBlendModeExclusion;
1405 case QPainter::CompositionMode_Plus:
1406 needPrivateAPI = true;
1407 cg_mode = kCGCompositeModePlusLighter;
1412 if (!needPrivateAPI)
1413 CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
1415 CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
1422 QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
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);
1430 CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
1431 kCGInterpolationHigh : kCGInterpolationNone);
1433 bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing;
1434 if (!textAntialiasing || d->disabledSmoothFonts) {
1435 d->disabledSmoothFonts = !textAntialiasing;
1436 CGContextSetShouldSmoothFonts(d->hd, textAntialiasing);
1441 Returns the size of one device pixel in user-space coordinates.
1443 QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
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()));
1451 Adjusts the pen width so we get correct line widths in the
1452 non-transformed, aliased case.
1454 float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
1456 Q_Q(QCoreGraphicsPaintEngine);
1457 float ret = penWidth;
1458 if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
1461 else if (penWidth < 3)
1470 QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
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()));
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());
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);
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)) {
1520 linedashes[i] += cglinewidth/2;
1522 linedashes[i] -= cglinewidth/2;
1525 CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
1528 CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
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)
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];
1556 void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
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);
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 *>(¤t.brush),
1569 1, domain, 4, 0, &callbacks);
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);
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);
1589 CGFunctionRelease(fill_func);
1593 if (bs != Qt::SolidPattern && bs != Qt::NoBrush
1594 #ifndef QT_MAC_USE_NATIVE_GRADIENTS
1595 && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
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();
1613 qpattern->as_mask = true;
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();
1622 int width = qpattern->width(), height = qpattern->height();
1623 qpattern->foreground = current.brush.color();
1625 CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
1626 CGContextSetFillColorSpace(hd, fill_colorspace);
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());
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);
1641 CGPatternRelease(fill_pattern);
1642 CGColorSpaceRelease(fill_colorspace);
1643 } else if (bs != Qt::NoBrush) {
1644 CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
1649 QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
1651 Q_Q(QCoreGraphicsPaintEngine);
1654 QRegion sysClip = q->systemClip();
1655 if (!sysClip.isEmpty())
1656 qt_mac_clip_cg(hd, sysClip, &orig_xform);
1658 qt_mac_clip_cg(hd, *rgn, 0);
1662 struct qt_mac_cg_transform_path {
1663 CGMutablePathRef path;
1664 CGAffineTransform transform;
1667 void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
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);
1675 case kCGPathElementAddLineToPoint:
1676 CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
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);
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);
1687 case kCGPathElementCloseSubpath:
1688 CGPathCloseSubpath(t->path);
1691 qDebug() << "Unhandled path transform type: " << element->type;
1695 void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
1697 Q_Q(QCoreGraphicsPaintEngine);
1698 Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
1699 if ((ops & (CGFill | CGEOFill))) {
1702 CGContextBeginPath(hd);
1703 CGContextAddPath(hd, path);
1704 saveGraphicsState();
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));
1716 CGContextDrawShading(hd, shading);
1717 restoreGraphicsState();
1720 } else if (current.brush.style() == Qt::NoBrush) {
1725 if ((ops & CGStroke) && current.pen.style() == Qt::NoPen)
1728 if (ops & (CGEOFill | CGFill)) {
1729 CGContextBeginPath(hd);
1730 CGContextAddPath(hd, path);
1731 if (ops & CGEOFill) {
1732 CGContextEOFillPath(hd);
1734 CGContextFillPath(hd);
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);
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)
1756 CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
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);
1766 CGContextSetLineWidth(hd, cosmeticPenSize);
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);
1778 CGContextAddPath(hd, path);
1781 CGContextStrokePath(hd);
1782 if (needContextSave)
1783 restoreGraphicsState();