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