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