Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qcoregraphics_p.h"
41
42#include <private/qcore_mac_p.h>
43#include <qpa/qplatformpixmap.h>
44#include <QtGui/qicon.h>
45#include <QtGui/private/qpaintengine_p.h>
46#include <QtCore/qdebug.h>
47#include <QtCore/qcoreapplication.h>
48#include <QtCore/qoperatingsystemversion.h>
49
50QT_BEGIN_NAMESPACE
51
52// ---------------------- Images ----------------------
53
54CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image)
55{
56 CGBitmapInfo bitmapInfo = kCGImageAlphaNone;
57 switch (image.format()) {
58 case QImage::Format_ARGB32:
59 bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
60 break;
61 case QImage::Format_RGB32:
62 bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
63 break;
64 case QImage::Format_RGBA8888_Premultiplied:
65 bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
66 break;
67 case QImage::Format_RGBA8888:
68 bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrder32Big;
69 break;
70 case QImage::Format_RGBX8888:
71 bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big;
72 break;
73 case QImage::Format_ARGB32_Premultiplied:
74 bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
75 break;
76 default: break;
77 }
78 return bitmapInfo;
79}
80
81CGImageRef qt_mac_toCGImage(const QImage &inImage)
82{
83 CGImageRef cgImage = inImage.toCGImage();
84 if (cgImage)
85 return cgImage;
86
87 // Convert image data to a known-good format if the fast conversion fails.
88 return inImage.convertToFormat(QImage::Format_ARGB32_Premultiplied).toCGImage();
89}
90
91CGImageRef qt_mac_toCGImageMask(const QImage &image)
92{
93 static const auto deleter = [](void *image, const void *, size_t) { delete static_cast<QImage *>(image); };
94 QCFType<CGDataProviderRef> dataProvider =
95 CGDataProviderCreateWithData(new QImage(image), image.bits(),
96 image.sizeInBytes(), deleter);
97
98 return CGImageMaskCreate(image.width(), image.height(), 8, image.depth(),
99 image.bytesPerLine(), dataProvider, NULL, false);
100}
101
102void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
103{
104 CGContextSaveGState( inContext );
105 CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
106 CGContextScaleCTM(inContext, 1, -1);
107
108 CGContextDrawImage(inContext, *inBounds, inImage);
109
110 CGContextRestoreGState(inContext);
111}
112
113QImage qt_mac_toQImage(CGImageRef image)
114{
115 const size_t w = CGImageGetWidth(image),
116 h = CGImageGetHeight(image);
117 QImage ret(w, h, QImage::Format_ARGB32_Premultiplied);
118 ret.fill(Qt::transparent);
119 CGRect rect = CGRectMake(0, 0, w, h);
120 QMacCGContext ctx(&ret);
121 qt_mac_drawCGImage(ctx, &rect, image);
122 return ret;
123}
124
125#ifdef Q_OS_MACOS
126
127static NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
128{
129 NSImage *newImage = [[NSImage alloc] initWithCGImage:image size:NSZeroSize];
130 return newImage;
131}
132
133NSImage *qt_mac_create_nsimage(const QPixmap &pm)
134{
135 if (pm.isNull())
136 return 0;
137 QImage image = pm.toImage();
138 CGImageRef cgImage = qt_mac_toCGImage(image);
139 NSImage *nsImage = qt_mac_cgimage_to_nsimage(cgImage);
140 nsImage.size = (pm.size() / pm.devicePixelRatioF()).toCGSize();
141 CGImageRelease(cgImage);
142 return nsImage;
143}
144
145NSImage *qt_mac_create_nsimage(const QIcon &icon, int defaultSize)
146{
147 if (icon.isNull())
148 return nil;
149
150 NSImage *nsImage = [[NSImage alloc] init];
151 QList<QSize> availableSizes = icon.availableSizes();
152 if (availableSizes.isEmpty() && defaultSize > 0)
153 availableSizes << QSize(defaultSize, defaultSize);
154 for (QSize size : qAsConst(availableSizes)) {
155 QPixmap pm = icon.pixmap(size);
156 if (pm.isNull())
157 continue;
158 QImage image = pm.toImage();
159 CGImageRef cgImage = qt_mac_toCGImage(image);
160 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
161 [nsImage addRepresentation:imageRep];
162 [imageRep release];
163 CGImageRelease(cgImage);
164 }
165 return nsImage;
166}
167
168QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size)
169{
170 const NSSize pixmapSize = NSMakeSize(size.width(), size.height());
171 QPixmap pixmap(pixmapSize.width, pixmapSize.height);
172 pixmap.fill(Qt::transparent);
173 [image setSize:pixmapSize];
174 const NSRect iconRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height);
175 QMacCGContext ctx(&pixmap);
176 if (!ctx)
177 return QPixmap();
178 NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
179 if (!gc)
180 return QPixmap();
181 [NSGraphicsContext saveGraphicsState];
182 [NSGraphicsContext setCurrentContext:gc];
183 [image drawInRect:iconRect fromRect:iconRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES hints:nil];
184 [NSGraphicsContext restoreGraphicsState];
185 return pixmap;
186}
187
188#endif // Q_OS_MACOS
189
190// ---------------------- Colors and Brushes ----------------------
191
192QColor qt_mac_toQColor(CGColorRef color)
193{
194 QColor qtColor;
195 CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color));
196 const CGFloat *components = CGColorGetComponents(color);
197 if (model == kCGColorSpaceModelRGB) {
198 qtColor.setRgbF(components[0], components[1], components[2], components[3]);
199 } else if (model == kCGColorSpaceModelCMYK) {
200 qtColor.setCmykF(components[0], components[1], components[2], components[3]);
201 } else if (model == kCGColorSpaceModelMonochrome) {
202 qtColor.setRgbF(components[0], components[0], components[0], components[1]);
203 } else {
204 // Colorspace we can't deal with.
205 qWarning("Qt: qt_mac_toQColor: cannot convert from colorspace model: %d", model);
206 Q_ASSERT(false);
207 }
208 return qtColor;
209}
210
211#ifdef Q_OS_MACOS
212QColor qt_mac_toQColor(const NSColor *color)
213{
214 QColor qtColor;
215 NSString *colorSpace = [color colorSpaceName];
216 if (colorSpace == NSDeviceCMYKColorSpace) {
217 CGFloat cyan, magenta, yellow, black, alpha;
218 [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
219 qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
220 } else {
221 NSColor *tmpColor;
222 tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
223 CGFloat red, green, blue, alpha;
224 [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
225 qtColor.setRgbF(red, green, blue, alpha);
226 }
227 return qtColor;
228}
229#endif
230
231QBrush qt_mac_toQBrush(CGColorRef color)
232{
233 QBrush qtBrush;
234 CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color));
235 if (model == kCGColorSpaceModelPattern) {
236 // Colorspace we can't deal with; the color is drawn directly using a callback.
237 qWarning("Qt: qt_mac_toQBrush: cannot convert from colorspace model: %d", model);
238 Q_ASSERT(false);
239 } else {
240 qtBrush.setStyle(Qt::SolidPattern);
241 qtBrush.setColor(qt_mac_toQColor(color));
242 }
243 return qtBrush;
244}
245
246#ifdef Q_OS_MACOS
247static bool qt_mac_isSystemColorOrInstance(const NSColor *color, NSString *colorNameComponent, NSString *className)
248{
249 // We specifically do not want isKindOfClass: here
250 if ([color.className isEqualToString:className]) // NSPatternColorSpace
251 return true;
252 if ([color.catalogNameComponent isEqualToString:@"System"] &&
253 [color.colorNameComponent isEqualToString:colorNameComponent] &&
254 [color.colorSpaceName isEqualToString:NSNamedColorSpace])
255 return true;
256 return false;
257}
258
259QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup)
260{
261 QBrush qtBrush;
262
263 // QTBUG-49773: This calls NSDrawMenuItemBackground to render a 1 by n gradient; could use HITheme
264 if ([color.className isEqualToString:@"NSMenuItemHighlightColor"]) {
265 qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMenuItemHighlightColor");
266 return qtBrush;
267 }
268
269 // Not a catalog color or a manifestation of System.windowBackgroundColor;
270 // only retrieved from NSWindow.backgroundColor directly
271 if ([color.className isEqualToString:@"NSMetalPatternColor"]) {
272 // NSTexturedBackgroundWindowMask, could theoretically handle this without private API by
273 // creating a window with the appropriate properties and then calling NSWindow.backgroundColor.patternImage,
274 // which returns a texture sized 1 by (window height, including frame), backed by a CGPattern
275 // which follows the window key state... probably need to allow QBrush to store a function pointer
276 // like CGPattern does
277 qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMetalPatternColor");
278 return qtBrush;
279 }
280
281 // No public API to get these colors/stops;
282 // both accurately obtained through runtime object inspection on OS X 10.11
283 // (the NSColor object has NSGradient i-vars for both color groups)
284 if (qt_mac_isSystemColorOrInstance(color, @"_sourceListBackgroundColor", @"NSSourceListBackgroundColor")) {
285 QLinearGradient gradient;
286 if (colorGroup == QPalette::Active) {
287 gradient.setColorAt(0, QColor(233, 237, 242));
288 gradient.setColorAt(0.5, QColor(225, 229, 235));
289 gradient.setColorAt(1, QColor(209, 216, 224));
290 } else {
291 gradient.setColorAt(0, QColor(248, 248, 248));
292 gradient.setColorAt(0.5, QColor(240, 240, 240));
293 gradient.setColorAt(1, QColor(235, 235, 235));
294 }
295 return QBrush(gradient);
296 }
297
298 // A couple colors are special... they are actually instances of NSGradientPatternColor, which
299 // override set/setFill/setStroke to instead initialize an internal color
300 // ([NSColor colorWithCalibratedWhite:0.909804 alpha:1.000000]) while still returning the
301 // ruled lines pattern image (from OS X 10.4) to the user from -[NSColor patternImage]
302 // (and providing no public API to get the underlying color without this insanity)
303 if (qt_mac_isSystemColorOrInstance(color, @"controlColor", @"NSGradientPatternColor") ||
304 qt_mac_isSystemColorOrInstance(color, @"windowBackgroundColor", @"NSGradientPatternColor")) {
305 qtBrush.setStyle(Qt::SolidPattern);
306 qtBrush.setColor(qt_mac_toQColor(color.CGColor));
307 return qtBrush;
308 }
309
310 if (NSColor *patternColor = [color colorUsingColorSpaceName:NSPatternColorSpace]) {
311 NSImage *patternImage = patternColor.patternImage;
312 const QSizeF sz(patternImage.size.width, patternImage.size.height);
313 // FIXME: QBrush is not resolution independent (QTBUG-49774)
314 qtBrush.setTexture(qt_mac_toQPixmap(patternImage, sz));
315 } else {
316 qtBrush.setStyle(Qt::SolidPattern);
317 qtBrush.setColor(qt_mac_toQColor(color));
318 }
319 return qtBrush;
320}
321#endif
322
323// ---------------------- Geometry Helpers ----------------------
324
325void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
326{
327 CGAffineTransform old_xform = CGAffineTransformIdentity;
328 if (orig_xform) { //setup xforms
329 old_xform = CGContextGetCTM(hd);
330 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
331 CGContextConcatCTM(hd, *orig_xform);
332 }
333
334 //do the clipping
335 CGContextBeginPath(hd);
336 if (rgn.isEmpty()) {
337 CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
338 } else {
339 for (const QRect &r : rgn) {
340 CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
341 CGContextAddRect(hd, mac_r);
342 }
343 }
344 CGContextClip(hd);
345
346 if (orig_xform) {//reset xforms
347 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
348 CGContextConcatCTM(hd, old_xform);
349 }
350}
351
352// move to QRegion?
353void qt_mac_scale_region(QRegion *region, qreal scaleFactor)
354{
355 if (!region || !region->rectCount())
356 return;
357
358 QVector<QRect> scaledRects;
359 scaledRects.reserve(region->rectCount());
360
361 for (const QRect &rect : *region)
362 scaledRects.append(QRect(rect.topLeft() * scaleFactor, rect.size() * scaleFactor));
363
364 region->setRects(&scaledRects[0], scaledRects.count());
365}
366
367// ---------------------- QMacCGContext ----------------------
368
369QMacCGContext::QMacCGContext(QPaintDevice *paintDevice)
370{
371 initialize(paintDevice);
372}
373
374void QMacCGContext::initialize(QPaintDevice *paintDevice)
375{
376 // Find the underlying QImage of the paint device
377 switch (int deviceType = paintDevice->devType()) {
378 case QInternal::Pixmap: {
379 auto *platformPixmap = static_cast<QPixmap*>(paintDevice)->handle();
380 if (platformPixmap && platformPixmap->classId() == QPlatformPixmap::RasterClass)
381 initialize(platformPixmap->buffer());
382 else
383 qWarning() << "QMacCGContext: Unsupported pixmap class" << platformPixmap->classId();
384 break;
385 }
386 case QInternal::Image:
387 initialize(static_cast<const QImage *>(paintDevice));
388 break;
389 case QInternal::Widget:
390 qWarning() << "QMacCGContext: not implemented: Widget class";
391 break;
392 default:
393 qWarning() << "QMacCGContext:: Unsupported paint device type" << deviceType;
394 }
395}
396
397QMacCGContext::QMacCGContext(QPainter *painter)
398{
399 QPaintEngine *paintEngine = painter->paintEngine();
400
401 // Handle the case of QMacPrintEngine, which has an internal QCoreGraphicsPaintEngine
402 while (QPaintEngine *aggregateEngine = QPaintEnginePrivate::get(paintEngine)->aggregateEngine())
403 paintEngine = aggregateEngine;
404
405 paintEngine->syncState();
406
407 if (Qt::HANDLE handle = QPaintEnginePrivate::get(paintEngine)->nativeHandle()) {
408 context = static_cast<CGContextRef>(handle);
409 return;
410 }
411
412 if (paintEngine->type() != QPaintEngine::Raster) {
413 qWarning() << "QMacCGContext:: Unsupported paint engine type" << paintEngine->type();
414 return;
415 }
416
417 // The raster paint engine always operates on a QImage
418 Q_ASSERT(paintEngine->paintDevice()->devType() == QInternal::Image);
419
420 // On behalf of one of these supported painter devices
421 switch (int painterDeviceType = painter->device()->devType()) {
422 case QInternal::Pixmap:
423 case QInternal::Image:
424 case QInternal::Widget:
425 break;
426 default:
427 qWarning() << "QMacCGContext:: Unsupported paint device type" << painterDeviceType;
428 return;
429 }
430
431 // Applying the clip is so entangled with the rest of the context setup
432 // that for simplicity we just pass in the painter.
433 initialize(static_cast<const QImage *>(paintEngine->paintDevice()), painter);
434}
435
436void QMacCGContext::initialize(const QImage *image, QPainter *painter)
437{
438 QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
439 context = CGBitmapContextCreate((void *)image->bits(), image->width(), image->height(), 8,
440 image->bytesPerLine(), colorSpace, qt_mac_bitmapInfoForImage(*image));
441
442 // Invert y axis
443 CGContextTranslateCTM(context, 0, image->height());
444 CGContextScaleCTM(context, 1, -1);
445
446 const qreal devicePixelRatio = image->devicePixelRatio();
447
448 if (painter && painter->device()->devType() == QInternal::Widget) {
449 // Set the clip rect which is an intersection of the system clip and the painter clip
450 QRegion clip = painter->paintEngine()->systemClip();
451 QTransform deviceTransform = painter->deviceTransform();
452
453 if (painter->hasClipping()) {
454 // To make matters more interesting the painter clip is in device-independent pixels,
455 // so we need to scale it to match the device-pixels of the system clip.
456 QRegion painterClip = painter->clipRegion();
457 qt_mac_scale_region(&painterClip, devicePixelRatio);
458
459 painterClip.translate(deviceTransform.dx(), deviceTransform.dy());
460
461 if (clip.isEmpty())
462 clip = painterClip;
463 else
464 clip &= painterClip;
465 }
466
467 qt_mac_clip_cg(context, clip, 0);
468
469 CGContextTranslateCTM(context, deviceTransform.dx(), deviceTransform.dy());
470 }
471
472 // Scale the context so that painting happens in device-independent pixels
473 CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio);
474}
475
476QT_END_NAMESPACE
477

Warning: That file was not part of the compilation database. It may have many parsing errors.