Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:set ts=2 sts=2 sw=2 et cin:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "QuartzSupport.h"
8 #include "nsDebug.h"
9 #include "MacIOSurface.h"
11 #import <QuartzCore/QuartzCore.h>
12 #import <AppKit/NSOpenGL.h>
13 #include <dlfcn.h>
14 #include "GLDefs.h"
16 #define IOSURFACE_FRAMEWORK_PATH \
17 "/System/Library/Frameworks/IOSurface.framework/IOSurface"
18 #define OPENGL_FRAMEWORK_PATH \
19 "/System/Library/Frameworks/OpenGL.framework/OpenGL"
20 #define COREGRAPHICS_FRAMEWORK_PATH \
21 "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics"
23 @interface CALayer (ContentsScale)
24 - (double)contentsScale;
25 - (void)setContentsScale:(double)scale;
26 @end
28 using mozilla::RefPtr;
29 using mozilla::TemporaryRef;
31 CGColorSpaceRef CreateSystemColorSpace() {
32 CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
33 if (!cspace) {
34 cspace = ::CGColorSpaceCreateDeviceRGB();
35 }
36 return cspace;
37 }
39 nsCARenderer::~nsCARenderer() {
40 Destroy();
41 }
43 void cgdata_release_callback(void *aCGData, const void *data, size_t size) {
44 if (aCGData) {
45 free(aCGData);
46 }
47 }
49 void nsCARenderer::Destroy() {
50 if (mCARenderer) {
51 CARenderer* caRenderer = (CARenderer*)mCARenderer;
52 // Bug 556453:
53 // Explicitly remove the layer from the renderer
54 // otherwise it does not always happen right away.
55 caRenderer.layer = nullptr;
56 [caRenderer release];
57 }
58 if (mWrapperCALayer) {
59 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
60 [wrapperLayer release];
61 }
62 if (mOpenGLContext) {
63 if (mFBO || mIOTexture || mFBOTexture) {
64 // Release these resources with the context that allocated them
65 CGLContextObj oldContext = ::CGLGetCurrentContext();
66 ::CGLSetCurrentContext(mOpenGLContext);
68 if (mFBOTexture) {
69 ::glDeleteTextures(1, &mFBOTexture);
70 }
71 if (mIOTexture) {
72 ::glDeleteTextures(1, &mIOTexture);
73 }
74 if (mFBO) {
75 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
76 ::glDeleteFramebuffersEXT(1, &mFBO);
77 }
79 if (oldContext)
80 ::CGLSetCurrentContext(oldContext);
81 }
82 ::CGLDestroyContext((CGLContextObj)mOpenGLContext);
83 }
84 if (mCGImage) {
85 ::CGImageRelease(mCGImage);
86 }
87 // mCGData is deallocated by cgdata_release_callback
89 mCARenderer = nil;
90 mWrapperCALayer = nil;
91 mFBOTexture = 0;
92 mOpenGLContext = nullptr;
93 mCGImage = nullptr;
94 mIOSurface = nullptr;
95 mFBO = 0;
96 mIOTexture = 0;
97 }
99 nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight,
100 double aContentsScaleFactor,
101 AllowOfflineRendererEnum aAllowOfflineRenderer) {
102 mAllowOfflineRenderer = aAllowOfflineRenderer;
104 if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
105 return NS_ERROR_FAILURE;
107 if (aWidth == mUnsupportedWidth &&
108 aHeight == mUnsupportedHeight) {
109 return NS_ERROR_FAILURE;
110 }
112 CGLPixelFormatAttribute attributes[] = {
113 kCGLPFAAccelerated,
114 kCGLPFADepthSize, (CGLPixelFormatAttribute)24,
115 kCGLPFAAllowOfflineRenderers,
116 (CGLPixelFormatAttribute)0
117 };
119 if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) {
120 attributes[3] = (CGLPixelFormatAttribute)0;
121 }
123 GLint screen;
124 CGLPixelFormatObj format;
125 if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) {
126 mUnsupportedWidth = aWidth;
127 mUnsupportedHeight = aHeight;
128 Destroy();
129 return NS_ERROR_FAILURE;
130 }
132 if (::CGLCreateContext(format, nullptr, &mOpenGLContext) != kCGLNoError) {
133 mUnsupportedWidth = aWidth;
134 mUnsupportedHeight = aHeight;
135 Destroy();
136 return NS_ERROR_FAILURE;
137 }
138 ::CGLDestroyPixelFormat(format);
140 CARenderer* caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext
141 options:nil] retain];
142 if (caRenderer == nil) {
143 mUnsupportedWidth = aWidth;
144 mUnsupportedHeight = aHeight;
145 Destroy();
146 return NS_ERROR_FAILURE;
147 }
148 CALayer* wrapperCALayer = [[CALayer layer] retain];
149 if (wrapperCALayer == nil) {
150 [caRenderer release];
151 mUnsupportedWidth = aWidth;
152 mUnsupportedHeight = aHeight;
153 Destroy();
154 return NS_ERROR_FAILURE;
155 }
157 mCARenderer = caRenderer;
158 mWrapperCALayer = wrapperCALayer;
159 caRenderer.layer = wrapperCALayer;
160 [wrapperCALayer addSublayer:(CALayer*)aCALayer];
161 mContentsScaleFactor = aContentsScaleFactor;
162 size_t intScaleFactor = ceil(mContentsScaleFactor);
163 SetBounds(aWidth, aHeight);
165 // We target rendering to a CGImage if no shared IOSurface are given.
166 if (!mIOSurface) {
167 mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor);
168 if (!mCGData) {
169 mUnsupportedWidth = aWidth;
170 mUnsupportedHeight = aHeight;
171 Destroy();
172 return NS_ERROR_FAILURE;
173 }
174 memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor);
176 CGDataProviderRef dataProvider = nullptr;
177 dataProvider = ::CGDataProviderCreateWithData(mCGData,
178 mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor,
179 cgdata_release_callback);
180 if (!dataProvider) {
181 cgdata_release_callback(mCGData, mCGData,
182 aHeight*intScaleFactor*aWidth*4*intScaleFactor);
183 mUnsupportedWidth = aWidth;
184 mUnsupportedHeight = aHeight;
185 Destroy();
186 return NS_ERROR_FAILURE;
187 }
189 CGColorSpaceRef colorSpace = CreateSystemColorSpace();
191 mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor,
192 8, 32, aWidth * intScaleFactor * 4, colorSpace,
193 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
194 dataProvider, nullptr, true, kCGRenderingIntentDefault);
196 ::CGDataProviderRelease(dataProvider);
197 ::CGColorSpaceRelease(colorSpace);
198 if (!mCGImage) {
199 mUnsupportedWidth = aWidth;
200 mUnsupportedHeight = aHeight;
201 Destroy();
202 return NS_ERROR_FAILURE;
203 }
204 }
206 CGLContextObj oldContext = ::CGLGetCurrentContext();
207 ::CGLSetCurrentContext(mOpenGLContext);
209 if (mIOSurface) {
210 // Create the IOSurface mapped texture.
211 ::glGenTextures(1, &mIOTexture);
212 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
213 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
214 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
215 MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
216 GL_RGBA, aWidth * intScaleFactor,
217 aHeight * intScaleFactor,
218 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
219 mIOSurface->mIOSurfacePtr, 0);
220 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
221 } else {
222 ::glGenTextures(1, &mFBOTexture);
223 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture);
224 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
225 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
226 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
227 }
229 // Create the fbo
230 ::glGenFramebuffersEXT(1, &mFBO);
231 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
232 if (mIOSurface) {
233 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
234 GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0);
235 } else {
236 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
237 GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0);
238 }
241 // Make sure that the Framebuffer configuration is supported on the client machine
242 GLenum fboStatus;
243 fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
244 if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) {
245 NS_ERROR("FBO not supported");
246 if (oldContext)
247 ::CGLSetCurrentContext(oldContext);
248 mUnsupportedWidth = aWidth;
249 mUnsupportedHeight = aHeight;
250 Destroy();
251 return NS_ERROR_FAILURE;
252 }
254 SetViewport(aWidth, aHeight);
256 GLenum result = ::glGetError();
257 if (result != GL_NO_ERROR) {
258 NS_ERROR("Unexpected OpenGL Error");
259 mUnsupportedWidth = aWidth;
260 mUnsupportedHeight = aHeight;
261 Destroy();
262 if (oldContext)
263 ::CGLSetCurrentContext(oldContext);
264 return NS_ERROR_FAILURE;
265 }
267 if (oldContext)
268 ::CGLSetCurrentContext(oldContext);
270 return NS_OK;
271 }
273 void nsCARenderer::SetBounds(int aWidth, int aHeight) {
274 CARenderer* caRenderer = (CARenderer*)mCARenderer;
275 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
276 NSArray* sublayers = [wrapperLayer sublayers];
277 CALayer* pluginLayer = (CALayer*) [sublayers objectAtIndex:0];
279 // Create a transaction and disable animations
280 // to make the position update instant.
281 [CATransaction begin];
282 NSMutableDictionary *newActions =
283 [[NSMutableDictionary alloc] initWithObjectsAndKeys:
284 [NSNull null], @"onOrderIn",
285 [NSNull null], @"onOrderOut",
286 [NSNull null], @"sublayers",
287 [NSNull null], @"contents",
288 [NSNull null], @"position",
289 [NSNull null], @"bounds",
290 nil];
291 wrapperLayer.actions = newActions;
292 [newActions release];
294 // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0.
295 // For some reason, to make things work properly in HiDPI mode we need to
296 // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds'
297 // to the size of 'layer's backing store. And to avoid this possibly
298 // confusing the plugin, we need to hide it's effects from the plugin by
299 // making pluginLayer (usually the CALayer* provided by the plugin) a
300 // sublayer of our own wrapperLayer (see bug 829284).
301 size_t intScaleFactor = ceil(mContentsScaleFactor);
302 [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration];
303 [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
304 [wrapperLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)];
305 [wrapperLayer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)];
306 [pluginLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)];
307 [pluginLayer setFrame:CGRectMake(0, 0, aWidth, aHeight)];
308 caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor);
309 if (mContentsScaleFactor != 1.0) {
310 CGAffineTransform affineTransform = [wrapperLayer affineTransform];
311 affineTransform.a = mContentsScaleFactor;
312 affineTransform.d = mContentsScaleFactor;
313 affineTransform.tx = ((double)aWidth)/mContentsScaleFactor;
314 affineTransform.ty = ((double)aHeight)/mContentsScaleFactor;
315 [wrapperLayer setAffineTransform:affineTransform];
316 } else {
317 // These settings are the default values. But they might have been
318 // changed as above if we were previously running in a HiDPI mode
319 // (i.e. if we just switched from that to a non-HiDPI mode).
320 [wrapperLayer setAffineTransform:CGAffineTransformIdentity];
321 }
322 [CATransaction commit];
323 }
325 void nsCARenderer::SetViewport(int aWidth, int aHeight) {
326 size_t intScaleFactor = ceil(mContentsScaleFactor);
327 aWidth *= intScaleFactor;
328 aHeight *= intScaleFactor;
330 ::glViewport(0.0, 0.0, aWidth, aHeight);
331 ::glMatrixMode(GL_PROJECTION);
332 ::glLoadIdentity();
333 ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
335 // Render upside down to speed up CGContextDrawImage
336 ::glTranslatef(0.0f, aHeight, 0.0);
337 ::glScalef(1.0, -1.0, 1.0);
338 }
340 void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) {
341 if (mIOSurface &&
342 aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
343 // This object isn't needed since we already have a
344 // handle to the same io surface.
345 aSurface = nullptr;
346 return;
347 }
349 mIOSurface = aSurface;
351 // Update the framebuffer and viewport
352 if (mCARenderer) {
353 CARenderer* caRenderer = (CARenderer*)mCARenderer;
354 size_t intScaleFactor = ceil(mContentsScaleFactor);
355 int width = caRenderer.bounds.size.width / intScaleFactor;
356 int height = caRenderer.bounds.size.height / intScaleFactor;
358 CGLContextObj oldContext = ::CGLGetCurrentContext();
359 ::CGLSetCurrentContext(mOpenGLContext);
360 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
361 MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
362 GL_RGBA, mIOSurface->GetDevicePixelWidth(),
363 mIOSurface->GetDevicePixelHeight(),
364 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
365 mIOSurface->mIOSurfacePtr, 0);
366 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
368 // Rebind the FBO to make it live
369 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
371 if (static_cast<int>(mIOSurface->GetWidth()) != width ||
372 static_cast<int>(mIOSurface->GetHeight()) != height) {
373 width = mIOSurface->GetWidth();
374 height = mIOSurface->GetHeight();
375 SetBounds(width, height);
376 SetViewport(width, height);
377 }
379 if (oldContext) {
380 ::CGLSetCurrentContext(oldContext);
381 }
382 }
383 }
385 IOSurfaceID nsCARenderer::GetIOSurfaceID() {
386 if (!mIOSurface) {
387 return 0;
388 }
390 return mIOSurface->GetIOSurfaceID();
391 }
393 nsresult nsCARenderer::Render(int aWidth, int aHeight,
394 double aContentsScaleFactor,
395 CGImageRef *aOutCGImage) {
396 if (!aOutCGImage && !mIOSurface) {
397 NS_ERROR("No target destination for rendering");
398 } else if (aOutCGImage) {
399 // We are expected to return a CGImageRef, we will set
400 // it to nullptr in case we fail before the image is ready.
401 *aOutCGImage = nullptr;
402 }
404 if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
405 return NS_OK;
407 if (!mCARenderer || !mWrapperCALayer) {
408 return NS_ERROR_FAILURE;
409 }
411 CARenderer* caRenderer = (CARenderer*)mCARenderer;
412 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
413 size_t intScaleFactor = ceil(aContentsScaleFactor);
414 int renderer_width = caRenderer.bounds.size.width / intScaleFactor;
415 int renderer_height = caRenderer.bounds.size.height / intScaleFactor;
417 if (renderer_width != aWidth || renderer_height != aHeight ||
418 mContentsScaleFactor != aContentsScaleFactor) {
419 // XXX: This should be optimized to not rescale the buffer
420 // if we are resizing down.
421 // caLayer may be the CALayer* provided by the plugin, so we need to
422 // preserve it across the call to Destroy().
423 NSArray* sublayers = [wrapperLayer sublayers];
424 CALayer* caLayer = (CALayer*) [sublayers objectAtIndex:0];
425 // mIOSurface is set by AttachIOSurface(), not by SetupRenderer(). So
426 // since it may have been set by a prior call to AttachIOSurface(), we
427 // need to preserve it across the call to Destroy().
428 mozilla::RefPtr<MacIOSurface> ioSurface = mIOSurface;
429 Destroy();
430 mIOSurface = ioSurface;
431 if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor,
432 mAllowOfflineRenderer) != NS_OK) {
433 return NS_ERROR_FAILURE;
434 }
436 caRenderer = (CARenderer*)mCARenderer;
437 }
439 CGLContextObj oldContext = ::CGLGetCurrentContext();
440 ::CGLSetCurrentContext(mOpenGLContext);
441 if (!mIOSurface) {
442 // If no shared IOSurface is given render to our own
443 // texture for readback.
444 ::glGenTextures(1, &mFBOTexture);
445 }
447 GLenum result = ::glGetError();
448 if (result != GL_NO_ERROR) {
449 NS_ERROR("Unexpected OpenGL Error");
450 Destroy();
451 if (oldContext)
452 ::CGLSetCurrentContext(oldContext);
453 return NS_ERROR_FAILURE;
454 }
456 ::glClearColor(0.0, 0.0, 0.0, 0.0);
457 ::glClear(GL_COLOR_BUFFER_BIT);
459 [CATransaction commit];
460 double caTime = ::CACurrentMediaTime();
461 [caRenderer beginFrameAtTime:caTime timeStamp:nullptr];
462 [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor,
463 aHeight * intScaleFactor)];
464 [caRenderer render];
465 [caRenderer endFrame];
467 // Read the data back either to the IOSurface or mCGImage.
468 if (mIOSurface) {
469 ::glFlush();
470 } else {
471 ::glPixelStorei(GL_PACK_ALIGNMENT, 4);
472 ::glPixelStorei(GL_PACK_ROW_LENGTH, 0);
473 ::glPixelStorei(GL_PACK_SKIP_ROWS, 0);
474 ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
476 ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor,
477 aHeight * intScaleFactor,
478 GL_BGRA, GL_UNSIGNED_BYTE,
479 mCGData);
481 *aOutCGImage = mCGImage;
482 }
484 if (oldContext) {
485 ::CGLSetCurrentContext(oldContext);
486 }
488 return NS_OK;
489 }
491 nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext,
492 MacIOSurface *surf,
493 CGColorSpaceRef aColorSpace,
494 int aX, int aY,
495 size_t aWidth, size_t aHeight) {
496 surf->Lock();
497 size_t bytesPerRow = surf->GetBytesPerRow();
498 size_t ioWidth = surf->GetWidth();
499 size_t ioHeight = surf->GetHeight();
501 // We get rendering glitches if we use a width/height that falls
502 // outside of the IOSurface.
503 if (aWidth + aX > ioWidth)
504 aWidth = ioWidth - aX;
505 if (aHeight + aY > ioHeight)
506 aHeight = ioHeight - aY;
508 if (aX < 0 || static_cast<size_t>(aX) >= ioWidth ||
509 aY < 0 || static_cast<size_t>(aY) >= ioHeight) {
510 surf->Unlock();
511 return NS_ERROR_FAILURE;
512 }
514 void* ioData = surf->GetBaseAddress();
515 double scaleFactor = surf->GetContentsScaleFactor();
516 size_t intScaleFactor = ceil(surf->GetContentsScaleFactor());
517 CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData,
518 ioData, ioHeight*intScaleFactor*(bytesPerRow)*4,
519 nullptr); //No release callback
520 if (!dataProvider) {
521 surf->Unlock();
522 return NS_ERROR_FAILURE;
523 }
525 CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor,
526 ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace,
527 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
528 dataProvider, nullptr, true, kCGRenderingIntentDefault);
529 ::CGDataProviderRelease(dataProvider);
530 if (!cgImage) {
531 surf->Unlock();
532 return NS_ERROR_FAILURE;
533 }
534 CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage,
535 ::CGRectMake(aX * scaleFactor,
536 aY * scaleFactor,
537 aWidth * scaleFactor,
538 aHeight * scaleFactor));
539 if (!subImage) {
540 ::CGImageRelease(cgImage);
541 surf->Unlock();
542 return NS_ERROR_FAILURE;
543 }
545 ::CGContextScaleCTM(aContext, 1.0f, -1.0f);
546 ::CGContextDrawImage(aContext,
547 CGRectMake(aX * scaleFactor,
548 (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor,
549 aWidth * scaleFactor,
550 aHeight * scaleFactor),
551 subImage);
553 ::CGImageRelease(subImage);
554 ::CGImageRelease(cgImage);
555 surf->Unlock();
556 return NS_OK;
557 }
559 void nsCARenderer::DetachCALayer() {
560 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
561 NSArray* sublayers = [wrapperLayer sublayers];
562 CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0];
563 [oldLayer removeFromSuperlayer];
564 }
566 void nsCARenderer::AttachCALayer(void *aCALayer) {
567 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer;
568 NSArray* sublayers = [wrapperLayer sublayers];
569 CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0];
570 [oldLayer removeFromSuperlayer];
571 [wrapperLayer addSublayer:(CALayer*)aCALayer];
572 }
574 #ifdef DEBUG
576 int sSaveToDiskSequence = 0;
577 void nsCARenderer::SaveToDisk(MacIOSurface *surf) {
578 surf->Lock();
579 size_t bytesPerRow = surf->GetBytesPerRow();
580 size_t ioWidth = surf->GetWidth();
581 size_t ioHeight = surf->GetHeight();
582 void* ioData = surf->GetBaseAddress();
583 CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData,
584 ioData, ioHeight*(bytesPerRow)*4,
585 nullptr); //No release callback
586 if (!dataProvider) {
587 surf->Unlock();
588 return;
589 }
591 CGColorSpaceRef colorSpace = CreateSystemColorSpace();
592 CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow,
593 colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
594 dataProvider, nullptr, true, kCGRenderingIntentDefault);
595 ::CGDataProviderRelease(dataProvider);
596 ::CGColorSpaceRelease(colorSpace);
597 if (!cgImage) {
598 surf->Unlock();
599 return;
600 }
602 char cstr[1000];
604 sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence);
606 CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman);
608 printf("Exporting: %s\n", cstr);
609 CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr);
610 ::CFRelease(cfStr);
612 CFStringRef type = kUTTypePNG;
613 size_t count = 1;
614 CFDictionaryRef options = nullptr;
615 CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options);
616 ::CFRelease(url);
618 ::CGImageDestinationAddImage(dest, cgImage, nullptr);
620 ::CGImageDestinationFinalize(dest);
621 ::CFRelease(dest);
622 ::CGImageRelease(cgImage);
624 surf->Unlock();
626 return;
628 }
630 #endif