-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathqcoregraphics.mm
202 lines (178 loc) · 7.36 KB
/
qcoregraphics.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include "pch.h"
#include "qcoregraphics.h"
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
// this adds const to non-const objects (like std::as_const)
template <typename T>
Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; }
// prevent rvalue arguments:
template <typename T>
void qAsConst(const T &&) = delete;
// like std::exchange
template <typename T, typename U = T>
Q_DECL_RELAXED_CONSTEXPR T qExchange(T &t, U &&newValue)
{
T old = std::move(t);
t = std::forward<U>(newValue);
return old;
}
#endif
template <typename T, typename U, U (*RetainFunction)(U), void (*ReleaseFunction)(U)>
class QAppleRefCounted
{
public:
QAppleRefCounted() : value() {}
QAppleRefCounted(const T &t) : value(t) {}
QAppleRefCounted(T &&t) noexcept(std::is_nothrow_move_constructible<T>::value)
: value(std::move(t)) {}
QAppleRefCounted(QAppleRefCounted &&other)
noexcept(std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value)
: value(qExchange(other.value, T())) {}
QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); }
~QAppleRefCounted() { if (value) ReleaseFunction(value); }
operator T() const { return value; }
void swap(QAppleRefCounted &other) noexcept(noexcept(qSwap(value, other.value)))
{ qSwap(value, other.value); }
QAppleRefCounted &operator=(const QAppleRefCounted &other)
{ QAppleRefCounted copy(other); swap(copy); return *this; }
QAppleRefCounted &operator=(QAppleRefCounted &&other)
noexcept(std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value)
{ QAppleRefCounted moved(std::move(other)); swap(moved); return *this; }
T *operator&() { return &value; }
protected:
T value;
};
/*
Helper class that automates refernce counting for CFtypes.
After constructing the QCFType object, it can be copied like a
value-based type.
Note that you must own the object you are wrapping.
This is typically the case if you get the object from a Core
Foundation function with the word "Create" or "Copy" in it. If
you got the object from a "Get" function, either retain it or use
constructFromGet(). One exception to this rule is the
HIThemeGet*Shape functions, which in reality are "Copy" functions.
*/
template <typename T>
class QCFType : public QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>
{
using Base = QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>;
public:
using Base::Base;
explicit QCFType(CFTypeRef r) : Base(static_cast<T>(r)) {}
template <typename X> X as() const { return reinterpret_cast<X>(this->value); }
static QCFType constructFromGet(const T &t)
{
if (t)
CFRetain(t);
return QCFType<T>(t);
}
};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
#define QImage2CGImageRef(img) img.toCGImage()
#define QSize2CGSize(sz) sz.toCGSize()
#else
#define QImage2CGImageRef(img) toCGImage(img)
#define QSize2CGSize(sz) CGSizeMake(sz.width(), sz.height())
CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image)
{
CGBitmapInfo bitmapInfo = kCGImageAlphaNone;
switch (image.format()) {
case QImage::Format_ARGB32:
bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
break;
case QImage::Format_RGB32:
bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
break;
case QImage::Format_RGBA8888_Premultiplied:
bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
break;
case QImage::Format_RGBA8888:
bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrder32Big;
break;
case QImage::Format_RGBX8888:
bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big;
break;
case QImage::Format_ARGB32_Premultiplied:
bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
break;
default: break;
}
return bitmapInfo;
}
CGImageRef toCGImage(const QImage &img)
{
if (img.isNull())
return nil;
CGBitmapInfo bitmapInfo = qt_mac_bitmapInfoForImage(img);
// Format not supported: return nil CGImageRef
if (bitmapInfo == kCGImageAlphaNone)
return nil;
// Create a data provider that owns a copy of the QImage and references the image data.
auto deleter = [](void *image, const void *, size_t)
{ delete static_cast<QImage *>(image); };
QCFType<CGDataProviderRef> dataProvider =
CGDataProviderCreateWithData(new QImage(img), img.bits(), img.byteCount(), deleter);
QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
const size_t bitsPerComponent = 8;
const size_t bitsPerPixel = 32;
const CGFloat *decode = nullptr;
const bool shouldInterpolate = false;
return CGImageCreate(img.width(), img.height(), bitsPerComponent, bitsPerPixel,
img.bytesPerLine(), colorSpace, bitmapInfo, dataProvider,
decode, shouldInterpolate, kCGRenderingIntentDefault);
}
#endif
@implementation NSImage (QtExtras)
+ (instancetype)imageFromQImage:(const QImage &)image
{
if (image.isNull())
return nil;
QCFType<CGImageRef> cgImage = QImage2CGImageRef(image);
if (!cgImage)
return nil;
// We set up the NSImage using an explicit NSBitmapImageRep, instead of
// [NSImage initWithCGImage:size:], as the former allows us to correctly
// set the size of the representation to account for the device pixel
// ratio of the original image, which in turn will be reflected by the
// NSImage.
auto nsImage = [[NSImage alloc] initWithSize:NSZeroSize];
auto *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
imageRep.size = QSize2CGSize((image.size() / image.devicePixelRatioF()));
[nsImage addRepresentation:[imageRep autorelease]];
Q_ASSERT(CGSizeEqualToSize(nsImage.size, imageRep.size));
return [nsImage autorelease];
}
+ (instancetype)imageFromQIcon:(const QIcon &)icon
{
return [NSImage imageFromQIcon:icon withSize:0];
}
+ (instancetype)imageFromQIcon:(const QIcon &)icon withSize:(int)size
{
if (icon.isNull())
return nil;
auto availableSizes = icon.availableSizes();
if (availableSizes.isEmpty() && size > 0)
availableSizes << QSize(size, size);
auto nsImage = [[[NSImage alloc] initWithSize:NSZeroSize] autorelease];
for (QSize size : std::as_const(availableSizes))
{
QImage image = icon.pixmap(size).toImage();
if (image.isNull())
continue;
QCFType<CGImageRef> cgImage = QImage2CGImageRef(image);
if (!cgImage)
continue;
auto *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
imageRep.size = QSize2CGSize((image.size() / image.devicePixelRatioF()));
[nsImage addRepresentation:[imageRep autorelease]];
}
if (!nsImage.representations.count)
return nil;
[nsImage setTemplate:icon.isMask()];
if (size)
nsImage.size = CGSizeMake(size, size);
return nsImage;
}
@end