1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "gfxPlatformMac.h"
7
8 #include "gfxQuartzSurface.h"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/MacIOSurface.h"
11
12 #include "gfxMacPlatformFontList.h"
13 #include "gfxMacFont.h"
14 #include "gfxCoreTextShaper.h"
15 #include "gfxTextRun.h"
16 #include "gfxUserFontSet.h"
17
18 #include "nsTArray.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/VsyncDispatcher.h"
21 #include "qcms.h"
22 #include "gfx2DGlue.h"
23
24 #include <dlfcn.h>
25 #include <CoreVideo/CoreVideo.h>
26
27 #include "mozilla/layers/CompositorBridgeParent.h"
28 #include "VsyncSource.h"
29
30 using namespace mozilla;
31 using namespace mozilla::gfx;
32
33 // cribbed from CTFontManager.h
34 enum {
35 kAutoActivationDisabled = 1
36 };
37 typedef uint32_t AutoActivationSetting;
38
39 // bug 567552 - disable auto-activation of fonts
40
41 static void
DisableFontActivation()42 DisableFontActivation()
43 {
44 // get the main bundle identifier
45 CFBundleRef mainBundle = ::CFBundleGetMainBundle();
46 CFStringRef mainBundleID = nullptr;
47
48 if (mainBundle) {
49 mainBundleID = ::CFBundleGetIdentifier(mainBundle);
50 }
51
52 // bug 969388 and bug 922590 - mainBundlID as null is sometimes problematic
53 if (!mainBundleID) {
54 NS_WARNING("missing bundle ID, packaging set up incorrectly");
55 return;
56 }
57
58 // if possible, fetch CTFontManagerSetAutoActivationSetting
59 void (*CTFontManagerSetAutoActivationSettingPtr)
60 (CFStringRef, AutoActivationSetting);
61 CTFontManagerSetAutoActivationSettingPtr =
62 (void (*)(CFStringRef, AutoActivationSetting))
63 dlsym(RTLD_DEFAULT, "CTFontManagerSetAutoActivationSetting");
64
65 // bug 567552 - disable auto-activation of fonts
66 if (CTFontManagerSetAutoActivationSettingPtr) {
67 CTFontManagerSetAutoActivationSettingPtr(mainBundleID,
68 kAutoActivationDisabled);
69 }
70 }
71
gfxPlatformMac()72 gfxPlatformMac::gfxPlatformMac()
73 {
74 DisableFontActivation();
75 mFontAntiAliasingThreshold = ReadAntiAliasingThreshold();
76
77 uint32_t canvasMask = BackendTypeBit(BackendType::SKIA);
78 uint32_t contentMask = BackendTypeBit(BackendType::SKIA);
79 InitBackendPrefs(canvasMask, BackendType::SKIA,
80 contentMask, BackendType::SKIA);
81
82 // XXX: Bug 1036682 - we run out of fds on Mac when using tiled layers because
83 // with 256x256 tiles we can easily hit the soft limit of 800 when using double
84 // buffered tiles in e10s, so let's bump the soft limit to the hard limit for the OS
85 // up to a new cap of OPEN_MAX.
86 struct rlimit limits;
87 if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
88 limits.rlim_cur = std::min(rlim_t(OPEN_MAX), limits.rlim_max);
89 if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
90 NS_WARNING("Unable to bump RLIMIT_NOFILE to the maximum number on this OS");
91 }
92 }
93
94 MacIOSurfaceLib::LoadLibrary();
95 }
96
~gfxPlatformMac()97 gfxPlatformMac::~gfxPlatformMac()
98 {
99 gfxCoreTextShaper::Shutdown();
100 }
101
102 gfxPlatformFontList*
CreatePlatformFontList()103 gfxPlatformMac::CreatePlatformFontList()
104 {
105 gfxPlatformFontList* list = new gfxMacPlatformFontList();
106 if (NS_SUCCEEDED(list->InitFontList())) {
107 return list;
108 }
109 gfxPlatformFontList::Shutdown();
110 return nullptr;
111 }
112
113 already_AddRefed<gfxASurface>
CreateOffscreenSurface(const IntSize & aSize,gfxImageFormat aFormat)114 gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize,
115 gfxImageFormat aFormat)
116 {
117 if (!Factory::AllowedSurfaceSize(aSize)) {
118 return nullptr;
119 }
120
121 RefPtr<gfxASurface> newSurface =
122 new gfxQuartzSurface(aSize, aFormat);
123 return newSurface.forget();
124 }
125
126 already_AddRefed<ScaledFont>
GetScaledFontForFont(DrawTarget * aTarget,gfxFont * aFont)127 gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
128 {
129 gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
130 return font->GetScaledFont(aTarget);
131 }
132
133 gfxFontGroup *
CreateFontGroup(const FontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,gfxTextPerfMetrics * aTextPerf,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)134 gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
135 const gfxFontStyle *aStyle,
136 gfxTextPerfMetrics* aTextPerf,
137 gfxUserFontSet *aUserFontSet,
138 gfxFloat aDevToCssSize)
139 {
140 return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
141 aUserFontSet, aDevToCssSize);
142 }
143
144 bool
IsFontFormatSupported(nsIURI * aFontURI,uint32_t aFormatFlags)145 gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
146 {
147 // check for strange format flags
148 NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
149 "strange font format hint set");
150
151 // accept supported formats
152 if (aFormatFlags & (gfxUserFontSet::FLAG_FORMATS_COMMON |
153 gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT)) {
154 return true;
155 }
156
157 // reject all other formats, known and unknown
158 if (aFormatFlags != 0) {
159 return false;
160 }
161
162 // no format hint set, need to look at data
163 return true;
164 }
165
166 static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
167 static const char kFontAppleBraille[] = "Apple Braille";
168 static const char kFontAppleColorEmoji[] = "Apple Color Emoji";
169 static const char kFontAppleSymbols[] = "Apple Symbols";
170 static const char kFontDevanagariSangamMN[] = "Devanagari Sangam MN";
171 static const char kFontEuphemiaUCAS[] = "Euphemia UCAS";
172 static const char kFontGeneva[] = "Geneva";
173 static const char kFontGeezaPro[] = "Geeza Pro";
174 static const char kFontGujaratiSangamMN[] = "Gujarati Sangam MN";
175 static const char kFontGurmukhiMN[] = "Gurmukhi MN";
176 static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN";
177 static const char kFontHiraginoSansGB[] = "Hiragino Sans GB";
178 static const char kFontKefa[] = "Kefa";
179 static const char kFontKhmerMN[] = "Khmer MN";
180 static const char kFontLaoMN[] = "Lao MN";
181 static const char kFontLucidaGrande[] = "Lucida Grande";
182 static const char kFontMenlo[] = "Menlo";
183 static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
184 static const char kFontMingLiUExtB[] = "MingLiU-ExtB";
185 static const char kFontMyanmarMN[] = "Myanmar MN";
186 static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
187 static const char kFontSimSunExtB[] = "SimSun-ExtB";
188 static const char kFontSongtiSC[] = "Songti SC";
189 static const char kFontSTHeiti[] = "STHeiti";
190 static const char kFontSTIXGeneral[] = "STIXGeneral";
191 static const char kFontTamilMN[] = "Tamil MN";
192
193 void
GetCommonFallbackFonts(uint32_t aCh,uint32_t aNextCh,Script aRunScript,nsTArray<const char * > & aFontList)194 gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
195 Script aRunScript,
196 nsTArray<const char*>& aFontList)
197 {
198 if (aNextCh == 0xfe0f) {
199 aFontList.AppendElement(kFontAppleColorEmoji);
200 }
201
202 aFontList.AppendElement(kFontLucidaGrande);
203
204 if (!IS_IN_BMP(aCh)) {
205 uint32_t p = aCh >> 16;
206 uint32_t b = aCh >> 8;
207 if (p == 1) {
208 if (b >= 0x1f0 && b < 0x1f7) {
209 aFontList.AppendElement(kFontAppleColorEmoji);
210 } else {
211 aFontList.AppendElement(kFontAppleSymbols);
212 aFontList.AppendElement(kFontSTIXGeneral);
213 aFontList.AppendElement(kFontGeneva);
214 }
215 } else if (p == 2) {
216 // OSX installations with MS Office may have these fonts
217 aFontList.AppendElement(kFontMingLiUExtB);
218 aFontList.AppendElement(kFontSimSunExtB);
219 }
220 } else {
221 uint32_t b = (aCh >> 8) & 0xff;
222
223 switch (b) {
224 case 0x03:
225 case 0x05:
226 aFontList.AppendElement(kFontGeneva);
227 break;
228 case 0x07:
229 aFontList.AppendElement(kFontGeezaPro);
230 break;
231 case 0x09:
232 aFontList.AppendElement(kFontDevanagariSangamMN);
233 break;
234 case 0x0a:
235 aFontList.AppendElement(kFontGurmukhiMN);
236 aFontList.AppendElement(kFontGujaratiSangamMN);
237 break;
238 case 0x0b:
239 aFontList.AppendElement(kFontTamilMN);
240 break;
241 case 0x0e:
242 aFontList.AppendElement(kFontLaoMN);
243 break;
244 case 0x0f:
245 aFontList.AppendElement(kFontSongtiSC);
246 break;
247 case 0x10:
248 aFontList.AppendElement(kFontMenlo);
249 aFontList.AppendElement(kFontMyanmarMN);
250 break;
251 case 0x13: // Cherokee
252 aFontList.AppendElement(kFontPlantagenetCherokee);
253 aFontList.AppendElement(kFontKefa);
254 break;
255 case 0x14: // Unified Canadian Aboriginal Syllabics
256 case 0x15:
257 case 0x16:
258 aFontList.AppendElement(kFontEuphemiaUCAS);
259 aFontList.AppendElement(kFontGeneva);
260 break;
261 case 0x18: // Mongolian, UCAS
262 aFontList.AppendElement(kFontSTHeiti);
263 aFontList.AppendElement(kFontEuphemiaUCAS);
264 break;
265 case 0x19: // Khmer
266 aFontList.AppendElement(kFontKhmerMN);
267 aFontList.AppendElement(kFontMicrosoftTaiLe);
268 break;
269 case 0x1d:
270 case 0x1e:
271 aFontList.AppendElement(kFontGeneva);
272 break;
273 case 0x20: // Symbol ranges
274 case 0x21:
275 case 0x22:
276 case 0x23:
277 case 0x24:
278 case 0x25:
279 case 0x26:
280 case 0x27:
281 case 0x29:
282 case 0x2a:
283 case 0x2b:
284 case 0x2e:
285 aFontList.AppendElement(kFontHiraginoKakuGothic);
286 aFontList.AppendElement(kFontAppleSymbols);
287 aFontList.AppendElement(kFontMenlo);
288 aFontList.AppendElement(kFontSTIXGeneral);
289 aFontList.AppendElement(kFontGeneva);
290 aFontList.AppendElement(kFontAppleColorEmoji);
291 break;
292 case 0x2c:
293 aFontList.AppendElement(kFontGeneva);
294 break;
295 case 0x2d:
296 aFontList.AppendElement(kFontKefa);
297 aFontList.AppendElement(kFontGeneva);
298 break;
299 case 0x28: // Braille
300 aFontList.AppendElement(kFontAppleBraille);
301 break;
302 case 0x31:
303 aFontList.AppendElement(kFontHiraginoSansGB);
304 break;
305 case 0x4d:
306 aFontList.AppendElement(kFontAppleSymbols);
307 break;
308 case 0xa0: // Yi
309 case 0xa1:
310 case 0xa2:
311 case 0xa3:
312 case 0xa4:
313 aFontList.AppendElement(kFontSTHeiti);
314 break;
315 case 0xa6:
316 case 0xa7:
317 aFontList.AppendElement(kFontGeneva);
318 aFontList.AppendElement(kFontAppleSymbols);
319 break;
320 case 0xab:
321 aFontList.AppendElement(kFontKefa);
322 break;
323 case 0xfc:
324 case 0xff:
325 aFontList.AppendElement(kFontAppleSymbols);
326 break;
327 default:
328 break;
329 }
330 }
331
332 // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort
333 aFontList.AppendElement(kFontArialUnicodeMS);
334 }
335
336 /*static*/ void
LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,nsAString & aSystemFontName,gfxFontStyle & aFontStyle,float aDevPixPerCSSPixel)337 gfxPlatformMac::LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
338 nsAString& aSystemFontName,
339 gfxFontStyle& aFontStyle,
340 float aDevPixPerCSSPixel)
341 {
342 gfxMacPlatformFontList* pfl = gfxMacPlatformFontList::PlatformFontList();
343 return pfl->LookupSystemFont(aSystemFontID, aSystemFontName, aFontStyle,
344 aDevPixPerCSSPixel);
345 }
346
347 uint32_t
ReadAntiAliasingThreshold()348 gfxPlatformMac::ReadAntiAliasingThreshold()
349 {
350 uint32_t threshold = 0; // default == no threshold
351
352 // first read prefs flag to determine whether to use the setting or not
353 bool useAntiAliasingThreshold = Preferences::GetBool("gfx.use_text_smoothing_setting", false);
354
355 // if the pref setting is disabled, return 0 which effectively disables this feature
356 if (!useAntiAliasingThreshold)
357 return threshold;
358
359 // value set via Appearance pref panel, "Turn off text smoothing for font sizes xxx and smaller"
360 CFNumberRef prefValue = (CFNumberRef)CFPreferencesCopyAppValue(CFSTR("AppleAntiAliasingThreshold"), kCFPreferencesCurrentApplication);
361
362 if (prefValue) {
363 if (!CFNumberGetValue(prefValue, kCFNumberIntType, &threshold)) {
364 threshold = 0;
365 }
366 CFRelease(prefValue);
367 }
368
369 return threshold;
370 }
371
372 bool
AccelerateLayersByDefault()373 gfxPlatformMac::AccelerateLayersByDefault()
374 {
375 return true;
376 }
377
378 // This is the renderer output callback function, called on the vsync thread
379 static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
380 const CVTimeStamp* aNow,
381 const CVTimeStamp* aOutputTime,
382 CVOptionFlags aFlagsIn,
383 CVOptionFlags* aFlagsOut,
384 void* aDisplayLinkContext);
385
386 class OSXVsyncSource final : public VsyncSource
387 {
388 public:
OSXVsyncSource()389 OSXVsyncSource()
390 {
391 }
392
GetGlobalDisplay()393 virtual Display& GetGlobalDisplay() override
394 {
395 return mGlobalDisplay;
396 }
397
398 class OSXDisplay final : public VsyncSource::Display
399 {
400 public:
OSXDisplay()401 OSXDisplay()
402 : mDisplayLink(nullptr)
403 {
404 MOZ_ASSERT(NS_IsMainThread());
405 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
406 }
407
~OSXDisplay()408 ~OSXDisplay()
409 {
410 MOZ_ASSERT(NS_IsMainThread());
411 }
412
RetryEnableVsync(nsITimer * aTimer,void * aOsxDisplay)413 static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
414 {
415 MOZ_ASSERT(NS_IsMainThread());
416 OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
417 MOZ_ASSERT(osxDisplay);
418 osxDisplay->EnableVsync();
419 }
420
EnableVsync()421 virtual void EnableVsync() override
422 {
423 MOZ_ASSERT(NS_IsMainThread());
424 if (IsVsyncEnabled()) {
425 return;
426 }
427
428 // Create a display link capable of being used with all active displays
429 // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
430 // situations. According to the docs, it is compatible with all displays running on the computer
431 // But if we have different monitors at different display rates, we may hit issues.
432 if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
433 NS_WARNING("Could not create a display link with all active displays. Retrying");
434 CVDisplayLinkRelease(mDisplayLink);
435 mDisplayLink = nullptr;
436
437 // bug 1142708 - When coming back from sleep,
438 // or when changing displays, active displays may not be ready yet,
439 // even if listening for the kIOMessageSystemHasPoweredOn event
440 // from OS X sleep notifications.
441 // Active displays are those that are drawable.
442 // bug 1144638 - When changing display configurations and getting
443 // notifications from CGDisplayReconfigurationCallBack, the
444 // callback gets called twice for each active display
445 // so it's difficult to know when all displays are active.
446 // Instead, try again soon. The delay is arbitrary. 100ms chosen
447 // because on a late 2013 15" retina, it takes about that
448 // long to come back up from sleep.
449 uint32_t delay = 100;
450 mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
451 return;
452 }
453
454 if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
455 NS_WARNING("Could not set displaylink output callback");
456 CVDisplayLinkRelease(mDisplayLink);
457 mDisplayLink = nullptr;
458 return;
459 }
460
461 mPreviousTimestamp = TimeStamp::Now();
462 if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
463 NS_WARNING("Could not activate the display link");
464 CVDisplayLinkRelease(mDisplayLink);
465 mDisplayLink = nullptr;
466 }
467
468 CVTime vsyncRate = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink);
469 if (vsyncRate.flags & kCVTimeIsIndefinite) {
470 NS_WARNING("Could not get vsync rate, setting to 60.");
471 mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
472 } else {
473 int64_t timeValue = vsyncRate.timeValue;
474 int64_t timeScale = vsyncRate.timeScale;
475 const int milliseconds = 1000;
476 float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds;
477 mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
478 }
479 }
480
DisableVsync()481 virtual void DisableVsync() override
482 {
483 MOZ_ASSERT(NS_IsMainThread());
484 if (!IsVsyncEnabled()) {
485 return;
486 }
487
488 // Release the display link
489 if (mDisplayLink) {
490 CVDisplayLinkRelease(mDisplayLink);
491 mDisplayLink = nullptr;
492 }
493 }
494
IsVsyncEnabled()495 virtual bool IsVsyncEnabled() override
496 {
497 MOZ_ASSERT(NS_IsMainThread());
498 return mDisplayLink != nullptr;
499 }
500
GetVsyncRate()501 virtual TimeDuration GetVsyncRate() override
502 {
503 return mVsyncRate;
504 }
505
Shutdown()506 virtual void Shutdown() override
507 {
508 MOZ_ASSERT(NS_IsMainThread());
509 mTimer->Cancel();
510 mTimer = nullptr;
511 DisableVsync();
512 }
513
514 // The vsync timestamps given by the CVDisplayLinkCallback are
515 // in the future for the NEXT frame. Large parts of Gecko, such
516 // as animations assume a timestamp at either now or in the past.
517 // Normalize the timestamps given to the VsyncDispatchers to the vsync
518 // that just occured, not the vsync that is upcoming.
519 TimeStamp mPreviousTimestamp;
520
521 private:
522 // Manages the display link render thread
523 CVDisplayLinkRef mDisplayLink;
524 RefPtr<nsITimer> mTimer;
525 TimeDuration mVsyncRate;
526 }; // OSXDisplay
527
528 private:
~OSXVsyncSource()529 virtual ~OSXVsyncSource()
530 {
531 }
532
533 OSXDisplay mGlobalDisplay;
534 }; // OSXVsyncSource
535
VsyncCallback(CVDisplayLinkRef aDisplayLink,const CVTimeStamp * aNow,const CVTimeStamp * aOutputTime,CVOptionFlags aFlagsIn,CVOptionFlags * aFlagsOut,void * aDisplayLinkContext)536 static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
537 const CVTimeStamp* aNow,
538 const CVTimeStamp* aOutputTime,
539 CVOptionFlags aFlagsIn,
540 CVOptionFlags* aFlagsOut,
541 void* aDisplayLinkContext)
542 {
543 // Executed on OS X hardware vsync thread
544 OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
545 int64_t nextVsyncTimestamp = aOutputTime->hostTime;
546
547 mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
548 mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
549 mozilla::TimeStamp now = TimeStamp::Now();
550
551 // Snow leopard sometimes sends vsync timestamps very far in the past.
552 // Normalize the vsync timestamps to now.
553 if (nextVsync <= previousVsync) {
554 nextVsync = now;
555 previousVsync = now;
556 } else if (now < previousVsync) {
557 // Bug 1158321 - The VsyncCallback can sometimes execute before the reported
558 // vsync time. In those cases, normalize the timestamp to Now() as sending
559 // timestamps in the future has undefined behavior. See the comment above
560 // OSXDisplay::mPreviousTimestamp
561 previousVsync = now;
562 }
563
564 display->mPreviousTimestamp = nextVsync;
565
566 display->NotifyVsync(previousVsync);
567 return kCVReturnSuccess;
568 }
569
570 already_AddRefed<mozilla::gfx::VsyncSource>
CreateHardwareVsyncSource()571 gfxPlatformMac::CreateHardwareVsyncSource()
572 {
573 RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
574 VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
575 primaryDisplay.EnableVsync();
576 if (!primaryDisplay.IsVsyncEnabled()) {
577 NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.");
578 return gfxPlatform::CreateHardwareVsyncSource();
579 }
580
581 primaryDisplay.DisableVsync();
582 return osxVsyncSource.forget();
583 }
584
585 void
GetPlatformCMSOutputProfile(void * & mem,size_t & size)586 gfxPlatformMac::GetPlatformCMSOutputProfile(void* &mem, size_t &size)
587 {
588 mem = nullptr;
589 size = 0;
590
591 CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
592 if (!cspace) {
593 cspace = ::CGColorSpaceCreateDeviceRGB();
594 }
595 if (!cspace) {
596 return;
597 }
598
599 CFDataRef iccp = ::CGColorSpaceCopyICCProfile(cspace);
600
601 ::CFRelease(cspace);
602
603 if (!iccp) {
604 return;
605 }
606
607 // copy to external buffer
608 size = static_cast<size_t>(::CFDataGetLength(iccp));
609 if (size > 0) {
610 void *data = malloc(size);
611 if (data) {
612 memcpy(data, ::CFDataGetBytePtr(iccp), size);
613 mem = data;
614 } else {
615 size = 0;
616 }
617 }
618
619 ::CFRelease(iccp);
620 }
621