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

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

trunk: Merged in qt 4.6.2 sources.

File size: 63.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia 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 uint flags = kCGImageAlphaPremultipliedFirst;
106#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
107 flags |= kCGBitmapByteOrder32Host;
108#endif
109 const QImage *image = (const QImage *) pe->paintDevice();
110
111 context = CGBitmapContextCreate((void *) image->bits(), image->width(), image->height(),
112 8, image->bytesPerLine(), colorspace, flags);
113
114 CGContextTranslateCTM(context, 0, image->height());
115 CGContextScaleCTM(context, 1, -1);
116
117 if (devType == QInternal::Widget) {
118 QRegion clip = p->paintEngine()->systemClip();
119 QTransform native = p->deviceTransform();
120 QTransform logical = p->combinedTransform();
121 if (p->hasClipping()) {
122 QRegion r = p->clipRegion();
123 r.translate(native.dx() - logical.dx(), native.dy() - logical.dy());
124 if (clip.isEmpty())
125 clip = r;
126 else
127 clip &= r;
128 }
129 qt_mac_clip_cg(context, clip, 0);
130
131 QPainterState *state = static_cast<QPainterState *>(pe->state);
132 Q_ASSERT(state);
133 if (!state->redirectionMatrix.isIdentity())
134 CGContextTranslateCTM(context, state->redirectionMatrix.dx(), state->redirectionMatrix.dy());
135 }
136 }
137 CGContextRetain(context);
138}
139
140
141/*****************************************************************************
142 QCoreGraphicsPaintEngine utility functions
143 *****************************************************************************/
144
145//conversion
146inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; }
147inline static int qt_mac_convert_color_from_cg(float c) { return qRound(c * 255); }
148CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
149 return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy());
150}
151
152CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
153{
154 bool isWidget = (paintDevice->devType() == QInternal::Widget);
155 return QCoreGraphicsPaintEngine::macDisplayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)
156 : 0);
157}
158
159inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
160{
161 CGFloat components[] = {
162 qt_mac_convert_color_to_cg(col.red()),
163 qt_mac_convert_color_to_cg(col.green()),
164 qt_mac_convert_color_to_cg(col.blue()),
165 qt_mac_convert_color_to_cg(col.alpha())
166 };
167 return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
168}
169
170// There's architectural problems with using native gradients
171// on the Mac at the moment, so disable them.
172// #define QT_MAC_USE_NATIVE_GRADIENTS
173
174#ifdef QT_MAC_USE_NATIVE_GRADIENTS
175static bool drawGradientNatively(const QGradient *gradient)
176{
177 return gradient->spread() == QGradient::PadSpread;
178}
179
180// gradiant callback
181static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
182{
183 QBrush *brush = static_cast<QBrush *>(info);
184 Q_ASSERT(brush && brush->gradient());
185
186 const QGradientStops stops = brush->gradient()->stops();
187 const int n = stops.count();
188 Q_ASSERT(n >= 1);
189 const QGradientStop *begin = stops.constBegin();
190 const QGradientStop *end = begin + n;
191
192 qreal p = in[0];
193 const QGradientStop *i = begin;
194 while (i != end && i->first < p)
195 ++i;
196
197 QRgb c;
198 if (i == begin) {
199 c = begin->second.rgba();
200 } else if (i == end) {
201 c = (end - 1)->second.rgba();
202 } else {
203 const QGradientStop &s1 = *(i - 1);
204 const QGradientStop &s2 = *i;
205 qreal p1 = s1.first;
206 qreal p2 = s2.first;
207 QRgb c1 = s1.second.rgba();
208 QRgb c2 = s2.second.rgba();
209 int idist = 256 * (p - p1) / (p2 - p1);
210 int dist = 256 - idist;
211 c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist),
212 INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist),
213 INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist),
214 INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist));
215 }
216
217 out[0] = qt_mac_convert_color_to_cg(qRed(c));
218 out[1] = qt_mac_convert_color_to_cg(qGreen(c));
219 out[2] = qt_mac_convert_color_to_cg(qBlue(c));
220 out[3] = qt_mac_convert_color_to_cg(qAlpha(c));
221}
222#endif
223
224//clipping handling
225void QCoreGraphicsPaintEnginePrivate::resetClip()
226{
227 static bool inReset = false;
228 if (inReset)
229 return;
230 inReset = true;
231
232 CGAffineTransform old_xform = CGContextGetCTM(hd);
233
234 //setup xforms
235 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
236 while (stackCount > 0) {
237 restoreGraphicsState();
238 }
239 saveGraphicsState();
240 inReset = false;
241 //reset xforms
242 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
243 CGContextConcatCTM(hd, old_xform);
244}
245
246static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
247{
248 return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
249}
250
251static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
252{
253 CGMutablePathRef ret = CGPathCreateMutable();
254 QPointF startPt;
255 for (int i=0; i<p.elementCount(); ++i) {
256 const QPainterPath::Element &elm = p.elementAt(i);
257 switch (elm.type) {
258 case QPainterPath::MoveToElement:
259 if(i > 0
260 && p.elementAt(i - 1).x == startPt.x()
261 && p.elementAt(i - 1).y == startPt.y())
262 CGPathCloseSubpath(ret);
263 startPt = QPointF(elm.x, elm.y);
264 CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off);
265 break;
266 case QPainterPath::LineToElement:
267 CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
268 break;
269 case QPainterPath::CurveToElement:
270 Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement);
271 Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement);
272 CGPathAddCurveToPoint(ret, 0,
273 elm.x+off, elm.y+off,
274 p.elementAt(i+1).x+off, p.elementAt(i+1).y+off,
275 p.elementAt(i+2).x+off, p.elementAt(i+2).y+off);
276 i+=2;
277 break;
278 default:
279 qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
280 break;
281 }
282 }
283 if(!p.isEmpty()
284 && p.elementAt(p.elementCount() - 1).x == startPt.x()
285 && p.elementAt(p.elementCount() - 1).y == startPt.y())
286 CGPathCloseSubpath(ret);
287 return ret;
288}
289
290CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
291QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
292bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
293
294CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
295{
296#if 0
297 if (!m_genericColorSpace) {
298#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
299 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
300 m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
301 } else
302#endif
303 {
304 m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
305 }
306 if (!m_postRoutineRegistered) {
307 m_postRoutineRegistered = true;
308 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
309 }
310 }
311 return m_genericColorSpace;
312#else
313 // Just return the main display colorspace for the moment.
314 return macDisplayColorSpace();
315#endif
316}
317
318/*
319 Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
320 to support multiple displays correctly.
321*/
322CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
323{
324 CGColorSpaceRef colorSpace;
325
326 CGDirectDisplayID displayID;
327 CMProfileRef displayProfile = 0;
328 if (widget == 0) {
329 displayID = CGMainDisplayID();
330 } else {
331 const QRect &qrect = widget->window()->geometry();
332 CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
333 CGDisplayCount throwAway;
334 CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
335 if (dErr != kCGErrorSuccess)
336 return macDisplayColorSpace(0); // fall back on main display
337 }
338 if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
339 return colorSpace;
340
341 CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
342 if (err == noErr) {
343 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
344 } else if (widget) {
345 return macDisplayColorSpace(0); // fall back on main display
346 }
347
348 if (colorSpace == 0)
349 colorSpace = CGColorSpaceCreateDeviceRGB();
350
351 m_displayColorSpaceHash.insert(displayID, colorSpace);
352 CMCloseProfile(displayProfile);
353 if (!m_postRoutineRegistered) {
354 m_postRoutineRegistered = true;
355 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
356 }
357 return colorSpace;
358}
359
360void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
361{
362 if (m_genericColorSpace) {
363 CFRelease(m_genericColorSpace);
364 m_genericColorSpace = 0;
365 }
366 QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
367 while (it != m_displayColorSpaceHash.constEnd()) {
368 if (it.value())
369 CFRelease(it.value());
370 ++it;
371 }
372 m_displayColorSpaceHash.clear();
373}
374
375void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
376{
377 CGAffineTransform old_xform = CGAffineTransformIdentity;
378 if(orig_xform) { //setup xforms
379 old_xform = CGContextGetCTM(hd);
380 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
381 CGContextConcatCTM(hd, *orig_xform);
382 }
383
384 //do the clipping
385 CGContextBeginPath(hd);
386 if(rgn.isEmpty()) {
387 CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
388 } else {
389#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
390 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
391 QCFType<HIMutableShapeRef> shape = rgn.toHIMutableShape();
392 Q_ASSERT(!HIShapeIsEmpty(shape));
393 HIShapeReplacePathInCGContext(shape, hd);
394 } else
395#endif
396 {
397 QVector<QRect> rects = rgn.rects();
398 const int count = rects.size();
399 for(int i = 0; i < count; i++) {
400 const QRect &r = rects[i];
401 CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
402 CGContextAddRect(hd, mac_r);
403 }
404 }
405