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