source: trunk/src/gui/painting/qpaintengine_mac.cpp@ 477

Last change on this file since 477 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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