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