1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/QuartzSupport.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,630 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim:set ts=2 sts=2 sw=2 et cin: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "QuartzSupport.h" 1.11 +#include "nsDebug.h" 1.12 +#include "MacIOSurface.h" 1.13 + 1.14 +#import <QuartzCore/QuartzCore.h> 1.15 +#import <AppKit/NSOpenGL.h> 1.16 +#include <dlfcn.h> 1.17 +#include "GLDefs.h" 1.18 + 1.19 +#define IOSURFACE_FRAMEWORK_PATH \ 1.20 + "/System/Library/Frameworks/IOSurface.framework/IOSurface" 1.21 +#define OPENGL_FRAMEWORK_PATH \ 1.22 + "/System/Library/Frameworks/OpenGL.framework/OpenGL" 1.23 +#define COREGRAPHICS_FRAMEWORK_PATH \ 1.24 + "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics" 1.25 + 1.26 +@interface CALayer (ContentsScale) 1.27 +- (double)contentsScale; 1.28 +- (void)setContentsScale:(double)scale; 1.29 +@end 1.30 + 1.31 +using mozilla::RefPtr; 1.32 +using mozilla::TemporaryRef; 1.33 + 1.34 +CGColorSpaceRef CreateSystemColorSpace() { 1.35 + CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); 1.36 + if (!cspace) { 1.37 + cspace = ::CGColorSpaceCreateDeviceRGB(); 1.38 + } 1.39 + return cspace; 1.40 +} 1.41 + 1.42 +nsCARenderer::~nsCARenderer() { 1.43 + Destroy(); 1.44 +} 1.45 + 1.46 +void cgdata_release_callback(void *aCGData, const void *data, size_t size) { 1.47 + if (aCGData) { 1.48 + free(aCGData); 1.49 + } 1.50 +} 1.51 + 1.52 +void nsCARenderer::Destroy() { 1.53 + if (mCARenderer) { 1.54 + CARenderer* caRenderer = (CARenderer*)mCARenderer; 1.55 + // Bug 556453: 1.56 + // Explicitly remove the layer from the renderer 1.57 + // otherwise it does not always happen right away. 1.58 + caRenderer.layer = nullptr; 1.59 + [caRenderer release]; 1.60 + } 1.61 + if (mWrapperCALayer) { 1.62 + CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; 1.63 + [wrapperLayer release]; 1.64 + } 1.65 + if (mOpenGLContext) { 1.66 + if (mFBO || mIOTexture || mFBOTexture) { 1.67 + // Release these resources with the context that allocated them 1.68 + CGLContextObj oldContext = ::CGLGetCurrentContext(); 1.69 + ::CGLSetCurrentContext(mOpenGLContext); 1.70 + 1.71 + if (mFBOTexture) { 1.72 + ::glDeleteTextures(1, &mFBOTexture); 1.73 + } 1.74 + if (mIOTexture) { 1.75 + ::glDeleteTextures(1, &mIOTexture); 1.76 + } 1.77 + if (mFBO) { 1.78 + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 1.79 + ::glDeleteFramebuffersEXT(1, &mFBO); 1.80 + } 1.81 + 1.82 + if (oldContext) 1.83 + ::CGLSetCurrentContext(oldContext); 1.84 + } 1.85 + ::CGLDestroyContext((CGLContextObj)mOpenGLContext); 1.86 + } 1.87 + if (mCGImage) { 1.88 + ::CGImageRelease(mCGImage); 1.89 + } 1.90 + // mCGData is deallocated by cgdata_release_callback 1.91 + 1.92 + mCARenderer = nil; 1.93 + mWrapperCALayer = nil; 1.94 + mFBOTexture = 0; 1.95 + mOpenGLContext = nullptr; 1.96 + mCGImage = nullptr; 1.97 + mIOSurface = nullptr; 1.98 + mFBO = 0; 1.99 + mIOTexture = 0; 1.100 +} 1.101 + 1.102 +nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight, 1.103 + double aContentsScaleFactor, 1.104 + AllowOfflineRendererEnum aAllowOfflineRenderer) { 1.105 + mAllowOfflineRenderer = aAllowOfflineRenderer; 1.106 + 1.107 + if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) 1.108 + return NS_ERROR_FAILURE; 1.109 + 1.110 + if (aWidth == mUnsupportedWidth && 1.111 + aHeight == mUnsupportedHeight) { 1.112 + return NS_ERROR_FAILURE; 1.113 + } 1.114 + 1.115 + CGLPixelFormatAttribute attributes[] = { 1.116 + kCGLPFAAccelerated, 1.117 + kCGLPFADepthSize, (CGLPixelFormatAttribute)24, 1.118 + kCGLPFAAllowOfflineRenderers, 1.119 + (CGLPixelFormatAttribute)0 1.120 + }; 1.121 + 1.122 + if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) { 1.123 + attributes[3] = (CGLPixelFormatAttribute)0; 1.124 + } 1.125 + 1.126 + GLint screen; 1.127 + CGLPixelFormatObj format; 1.128 + if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) { 1.129 + mUnsupportedWidth = aWidth; 1.130 + mUnsupportedHeight = aHeight; 1.131 + Destroy(); 1.132 + return NS_ERROR_FAILURE; 1.133 + } 1.134 + 1.135 + if (::CGLCreateContext(format, nullptr, &mOpenGLContext) != kCGLNoError) { 1.136 + mUnsupportedWidth = aWidth; 1.137 + mUnsupportedHeight = aHeight; 1.138 + Destroy(); 1.139 + return NS_ERROR_FAILURE; 1.140 + } 1.141 + ::CGLDestroyPixelFormat(format); 1.142 + 1.143 + CARenderer* caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext 1.144 + options:nil] retain]; 1.145 + if (caRenderer == nil) { 1.146 + mUnsupportedWidth = aWidth; 1.147 + mUnsupportedHeight = aHeight; 1.148 + Destroy(); 1.149 + return NS_ERROR_FAILURE; 1.150 + } 1.151 + CALayer* wrapperCALayer = [[CALayer layer] retain]; 1.152 + if (wrapperCALayer == nil) { 1.153 + [caRenderer release]; 1.154 + mUnsupportedWidth = aWidth; 1.155 + mUnsupportedHeight = aHeight; 1.156 + Destroy(); 1.157 + return NS_ERROR_FAILURE; 1.158 + } 1.159 + 1.160 + mCARenderer = caRenderer; 1.161 + mWrapperCALayer = wrapperCALayer; 1.162 + caRenderer.layer = wrapperCALayer; 1.163 + [wrapperCALayer addSublayer:(CALayer*)aCALayer]; 1.164 + mContentsScaleFactor = aContentsScaleFactor; 1.165 + size_t intScaleFactor = ceil(mContentsScaleFactor); 1.166 + SetBounds(aWidth, aHeight); 1.167 + 1.168 + // We target rendering to a CGImage if no shared IOSurface are given. 1.169 + if (!mIOSurface) { 1.170 + mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor); 1.171 + if (!mCGData) { 1.172 + mUnsupportedWidth = aWidth; 1.173 + mUnsupportedHeight = aHeight; 1.174 + Destroy(); 1.175 + return NS_ERROR_FAILURE; 1.176 + } 1.177 + memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor); 1.178 + 1.179 + CGDataProviderRef dataProvider = nullptr; 1.180 + dataProvider = ::CGDataProviderCreateWithData(mCGData, 1.181 + mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor, 1.182 + cgdata_release_callback); 1.183 + if (!dataProvider) { 1.184 + cgdata_release_callback(mCGData, mCGData, 1.185 + aHeight*intScaleFactor*aWidth*4*intScaleFactor); 1.186 + mUnsupportedWidth = aWidth; 1.187 + mUnsupportedHeight = aHeight; 1.188 + Destroy(); 1.189 + return NS_ERROR_FAILURE; 1.190 + } 1.191 + 1.192 + CGColorSpaceRef colorSpace = CreateSystemColorSpace(); 1.193 + 1.194 + mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor, 1.195 + 8, 32, aWidth * intScaleFactor * 4, colorSpace, 1.196 + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, 1.197 + dataProvider, nullptr, true, kCGRenderingIntentDefault); 1.198 + 1.199 + ::CGDataProviderRelease(dataProvider); 1.200 + ::CGColorSpaceRelease(colorSpace); 1.201 + if (!mCGImage) { 1.202 + mUnsupportedWidth = aWidth; 1.203 + mUnsupportedHeight = aHeight; 1.204 + Destroy(); 1.205 + return NS_ERROR_FAILURE; 1.206 + } 1.207 + } 1.208 + 1.209 + CGLContextObj oldContext = ::CGLGetCurrentContext(); 1.210 + ::CGLSetCurrentContext(mOpenGLContext); 1.211 + 1.212 + if (mIOSurface) { 1.213 + // Create the IOSurface mapped texture. 1.214 + ::glGenTextures(1, &mIOTexture); 1.215 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); 1.216 + ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.217 + ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.218 + MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, 1.219 + GL_RGBA, aWidth * intScaleFactor, 1.220 + aHeight * intScaleFactor, 1.221 + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1.222 + mIOSurface->mIOSurfacePtr, 0); 1.223 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); 1.224 + } else { 1.225 + ::glGenTextures(1, &mFBOTexture); 1.226 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture); 1.227 + ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.228 + ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.229 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); 1.230 + } 1.231 + 1.232 + // Create the fbo 1.233 + ::glGenFramebuffersEXT(1, &mFBO); 1.234 + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); 1.235 + if (mIOSurface) { 1.236 + ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 1.237 + GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0); 1.238 + } else { 1.239 + ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 1.240 + GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0); 1.241 + } 1.242 + 1.243 + 1.244 + // Make sure that the Framebuffer configuration is supported on the client machine 1.245 + GLenum fboStatus; 1.246 + fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 1.247 + if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { 1.248 + NS_ERROR("FBO not supported"); 1.249 + if (oldContext) 1.250 + ::CGLSetCurrentContext(oldContext); 1.251 + mUnsupportedWidth = aWidth; 1.252 + mUnsupportedHeight = aHeight; 1.253 + Destroy(); 1.254 + return NS_ERROR_FAILURE; 1.255 + } 1.256 + 1.257 + SetViewport(aWidth, aHeight); 1.258 + 1.259 + GLenum result = ::glGetError(); 1.260 + if (result != GL_NO_ERROR) { 1.261 + NS_ERROR("Unexpected OpenGL Error"); 1.262 + mUnsupportedWidth = aWidth; 1.263 + mUnsupportedHeight = aHeight; 1.264 + Destroy(); 1.265 + if (oldContext) 1.266 + ::CGLSetCurrentContext(oldContext); 1.267 + return NS_ERROR_FAILURE; 1.268 + } 1.269 + 1.270 + if (oldContext) 1.271 + ::CGLSetCurrentContext(oldContext); 1.272 + 1.273 + return NS_OK; 1.274 +} 1.275 + 1.276 +void nsCARenderer::SetBounds(int aWidth, int aHeight) { 1.277 + CARenderer* caRenderer = (CARenderer*)mCARenderer; 1.278 + CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; 1.279 + NSArray* sublayers = [wrapperLayer sublayers]; 1.280 + CALayer* pluginLayer = (CALayer*) [sublayers objectAtIndex:0]; 1.281 + 1.282 + // Create a transaction and disable animations 1.283 + // to make the position update instant. 1.284 + [CATransaction begin]; 1.285 + NSMutableDictionary *newActions = 1.286 + [[NSMutableDictionary alloc] initWithObjectsAndKeys: 1.287 + [NSNull null], @"onOrderIn", 1.288 + [NSNull null], @"onOrderOut", 1.289 + [NSNull null], @"sublayers", 1.290 + [NSNull null], @"contents", 1.291 + [NSNull null], @"position", 1.292 + [NSNull null], @"bounds", 1.293 + nil]; 1.294 + wrapperLayer.actions = newActions; 1.295 + [newActions release]; 1.296 + 1.297 + // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0. 1.298 + // For some reason, to make things work properly in HiDPI mode we need to 1.299 + // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds' 1.300 + // to the size of 'layer's backing store. And to avoid this possibly 1.301 + // confusing the plugin, we need to hide it's effects from the plugin by 1.302 + // making pluginLayer (usually the CALayer* provided by the plugin) a 1.303 + // sublayer of our own wrapperLayer (see bug 829284). 1.304 + size_t intScaleFactor = ceil(mContentsScaleFactor); 1.305 + [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration]; 1.306 + [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; 1.307 + [wrapperLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; 1.308 + [wrapperLayer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)]; 1.309 + [pluginLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; 1.310 + [pluginLayer setFrame:CGRectMake(0, 0, aWidth, aHeight)]; 1.311 + caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor); 1.312 + if (mContentsScaleFactor != 1.0) { 1.313 + CGAffineTransform affineTransform = [wrapperLayer affineTransform]; 1.314 + affineTransform.a = mContentsScaleFactor; 1.315 + affineTransform.d = mContentsScaleFactor; 1.316 + affineTransform.tx = ((double)aWidth)/mContentsScaleFactor; 1.317 + affineTransform.ty = ((double)aHeight)/mContentsScaleFactor; 1.318 + [wrapperLayer setAffineTransform:affineTransform]; 1.319 + } else { 1.320 + // These settings are the default values. But they might have been 1.321 + // changed as above if we were previously running in a HiDPI mode 1.322 + // (i.e. if we just switched from that to a non-HiDPI mode). 1.323 + [wrapperLayer setAffineTransform:CGAffineTransformIdentity]; 1.324 + } 1.325 + [CATransaction commit]; 1.326 +} 1.327 + 1.328 +void nsCARenderer::SetViewport(int aWidth, int aHeight) { 1.329 + size_t intScaleFactor = ceil(mContentsScaleFactor); 1.330 + aWidth *= intScaleFactor; 1.331 + aHeight *= intScaleFactor; 1.332 + 1.333 + ::glViewport(0.0, 0.0, aWidth, aHeight); 1.334 + ::glMatrixMode(GL_PROJECTION); 1.335 + ::glLoadIdentity(); 1.336 + ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1); 1.337 + 1.338 + // Render upside down to speed up CGContextDrawImage 1.339 + ::glTranslatef(0.0f, aHeight, 0.0); 1.340 + ::glScalef(1.0, -1.0, 1.0); 1.341 +} 1.342 + 1.343 +void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) { 1.344 + if (mIOSurface && 1.345 + aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) { 1.346 + // This object isn't needed since we already have a 1.347 + // handle to the same io surface. 1.348 + aSurface = nullptr; 1.349 + return; 1.350 + } 1.351 + 1.352 + mIOSurface = aSurface; 1.353 + 1.354 + // Update the framebuffer and viewport 1.355 + if (mCARenderer) { 1.356 + CARenderer* caRenderer = (CARenderer*)mCARenderer; 1.357 + size_t intScaleFactor = ceil(mContentsScaleFactor); 1.358 + int width = caRenderer.bounds.size.width / intScaleFactor; 1.359 + int height = caRenderer.bounds.size.height / intScaleFactor; 1.360 + 1.361 + CGLContextObj oldContext = ::CGLGetCurrentContext(); 1.362 + ::CGLSetCurrentContext(mOpenGLContext); 1.363 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); 1.364 + MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, 1.365 + GL_RGBA, mIOSurface->GetDevicePixelWidth(), 1.366 + mIOSurface->GetDevicePixelHeight(), 1.367 + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1.368 + mIOSurface->mIOSurfacePtr, 0); 1.369 + ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); 1.370 + 1.371 + // Rebind the FBO to make it live 1.372 + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); 1.373 + 1.374 + if (static_cast<int>(mIOSurface->GetWidth()) != width || 1.375 + static_cast<int>(mIOSurface->GetHeight()) != height) { 1.376 + width = mIOSurface->GetWidth(); 1.377 + height = mIOSurface->GetHeight(); 1.378 + SetBounds(width, height); 1.379 + SetViewport(width, height); 1.380 + } 1.381 + 1.382 + if (oldContext) { 1.383 + ::CGLSetCurrentContext(oldContext); 1.384 + } 1.385 + } 1.386 +} 1.387 + 1.388 +IOSurfaceID nsCARenderer::GetIOSurfaceID() { 1.389 + if (!mIOSurface) { 1.390 + return 0; 1.391 + } 1.392 + 1.393 + return mIOSurface->GetIOSurfaceID(); 1.394 +} 1.395 + 1.396 +nsresult nsCARenderer::Render(int aWidth, int aHeight, 1.397 + double aContentsScaleFactor, 1.398 + CGImageRef *aOutCGImage) { 1.399 + if (!aOutCGImage && !mIOSurface) { 1.400 + NS_ERROR("No target destination for rendering"); 1.401 + } else if (aOutCGImage) { 1.402 + // We are expected to return a CGImageRef, we will set 1.403 + // it to nullptr in case we fail before the image is ready. 1.404 + *aOutCGImage = nullptr; 1.405 + } 1.406 + 1.407 + if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) 1.408 + return NS_OK; 1.409 + 1.410 + if (!mCARenderer || !mWrapperCALayer) { 1.411 + return NS_ERROR_FAILURE; 1.412 + } 1.413 + 1.414 + CARenderer* caRenderer = (CARenderer*)mCARenderer; 1.415 + CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; 1.416 + size_t intScaleFactor = ceil(aContentsScaleFactor); 1.417 + int renderer_width = caRenderer.bounds.size.width / intScaleFactor; 1.418 + int renderer_height = caRenderer.bounds.size.height / intScaleFactor; 1.419 + 1.420 + if (renderer_width != aWidth || renderer_height != aHeight || 1.421 + mContentsScaleFactor != aContentsScaleFactor) { 1.422 + // XXX: This should be optimized to not rescale the buffer 1.423 + // if we are resizing down. 1.424 + // caLayer may be the CALayer* provided by the plugin, so we need to 1.425 + // preserve it across the call to Destroy(). 1.426 + NSArray* sublayers = [wrapperLayer sublayers]; 1.427 + CALayer* caLayer = (CALayer*) [sublayers objectAtIndex:0]; 1.428 + // mIOSurface is set by AttachIOSurface(), not by SetupRenderer(). So 1.429 + // since it may have been set by a prior call to AttachIOSurface(), we 1.430 + // need to preserve it across the call to Destroy(). 1.431 + mozilla::RefPtr<MacIOSurface> ioSurface = mIOSurface; 1.432 + Destroy(); 1.433 + mIOSurface = ioSurface; 1.434 + if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor, 1.435 + mAllowOfflineRenderer) != NS_OK) { 1.436 + return NS_ERROR_FAILURE; 1.437 + } 1.438 + 1.439 + caRenderer = (CARenderer*)mCARenderer; 1.440 + } 1.441 + 1.442 + CGLContextObj oldContext = ::CGLGetCurrentContext(); 1.443 + ::CGLSetCurrentContext(mOpenGLContext); 1.444 + if (!mIOSurface) { 1.445 + // If no shared IOSurface is given render to our own 1.446 + // texture for readback. 1.447 + ::glGenTextures(1, &mFBOTexture); 1.448 + } 1.449 + 1.450 + GLenum result = ::glGetError(); 1.451 + if (result != GL_NO_ERROR) { 1.452 + NS_ERROR("Unexpected OpenGL Error"); 1.453 + Destroy(); 1.454 + if (oldContext) 1.455 + ::CGLSetCurrentContext(oldContext); 1.456 + return NS_ERROR_FAILURE; 1.457 + } 1.458 + 1.459 + ::glClearColor(0.0, 0.0, 0.0, 0.0); 1.460 + ::glClear(GL_COLOR_BUFFER_BIT); 1.461 + 1.462 + [CATransaction commit]; 1.463 + double caTime = ::CACurrentMediaTime(); 1.464 + [caRenderer beginFrameAtTime:caTime timeStamp:nullptr]; 1.465 + [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor, 1.466 + aHeight * intScaleFactor)]; 1.467 + [caRenderer render]; 1.468 + [caRenderer endFrame]; 1.469 + 1.470 + // Read the data back either to the IOSurface or mCGImage. 1.471 + if (mIOSurface) { 1.472 + ::glFlush(); 1.473 + } else { 1.474 + ::glPixelStorei(GL_PACK_ALIGNMENT, 4); 1.475 + ::glPixelStorei(GL_PACK_ROW_LENGTH, 0); 1.476 + ::glPixelStorei(GL_PACK_SKIP_ROWS, 0); 1.477 + ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0); 1.478 + 1.479 + ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor, 1.480 + aHeight * intScaleFactor, 1.481 + GL_BGRA, GL_UNSIGNED_BYTE, 1.482 + mCGData); 1.483 + 1.484 + *aOutCGImage = mCGImage; 1.485 + } 1.486 + 1.487 + if (oldContext) { 1.488 + ::CGLSetCurrentContext(oldContext); 1.489 + } 1.490 + 1.491 + return NS_OK; 1.492 +} 1.493 + 1.494 +nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext, 1.495 + MacIOSurface *surf, 1.496 + CGColorSpaceRef aColorSpace, 1.497 + int aX, int aY, 1.498 + size_t aWidth, size_t aHeight) { 1.499 + surf->Lock(); 1.500 + size_t bytesPerRow = surf->GetBytesPerRow(); 1.501 + size_t ioWidth = surf->GetWidth(); 1.502 + size_t ioHeight = surf->GetHeight(); 1.503 + 1.504 + // We get rendering glitches if we use a width/height that falls 1.505 + // outside of the IOSurface. 1.506 + if (aWidth + aX > ioWidth) 1.507 + aWidth = ioWidth - aX; 1.508 + if (aHeight + aY > ioHeight) 1.509 + aHeight = ioHeight - aY; 1.510 + 1.511 + if (aX < 0 || static_cast<size_t>(aX) >= ioWidth || 1.512 + aY < 0 || static_cast<size_t>(aY) >= ioHeight) { 1.513 + surf->Unlock(); 1.514 + return NS_ERROR_FAILURE; 1.515 + } 1.516 + 1.517 + void* ioData = surf->GetBaseAddress(); 1.518 + double scaleFactor = surf->GetContentsScaleFactor(); 1.519 + size_t intScaleFactor = ceil(surf->GetContentsScaleFactor()); 1.520 + CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, 1.521 + ioData, ioHeight*intScaleFactor*(bytesPerRow)*4, 1.522 + nullptr); //No release callback 1.523 + if (!dataProvider) { 1.524 + surf->Unlock(); 1.525 + return NS_ERROR_FAILURE; 1.526 + } 1.527 + 1.528 + CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor, 1.529 + ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace, 1.530 + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, 1.531 + dataProvider, nullptr, true, kCGRenderingIntentDefault); 1.532 + ::CGDataProviderRelease(dataProvider); 1.533 + if (!cgImage) { 1.534 + surf->Unlock(); 1.535 + return NS_ERROR_FAILURE; 1.536 + } 1.537 + CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage, 1.538 + ::CGRectMake(aX * scaleFactor, 1.539 + aY * scaleFactor, 1.540 + aWidth * scaleFactor, 1.541 + aHeight * scaleFactor)); 1.542 + if (!subImage) { 1.543 + ::CGImageRelease(cgImage); 1.544 + surf->Unlock(); 1.545 + return NS_ERROR_FAILURE; 1.546 + } 1.547 + 1.548 + ::CGContextScaleCTM(aContext, 1.0f, -1.0f); 1.549 + ::CGContextDrawImage(aContext, 1.550 + CGRectMake(aX * scaleFactor, 1.551 + (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor, 1.552 + aWidth * scaleFactor, 1.553 + aHeight * scaleFactor), 1.554 + subImage); 1.555 + 1.556 + ::CGImageRelease(subImage); 1.557 + ::CGImageRelease(cgImage); 1.558 + surf->Unlock(); 1.559 + return NS_OK; 1.560 +} 1.561 + 1.562 +void nsCARenderer::DetachCALayer() { 1.563 + CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; 1.564 + NSArray* sublayers = [wrapperLayer sublayers]; 1.565 + CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; 1.566 + [oldLayer removeFromSuperlayer]; 1.567 +} 1.568 + 1.569 +void nsCARenderer::AttachCALayer(void *aCALayer) { 1.570 + CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; 1.571 + NSArray* sublayers = [wrapperLayer sublayers]; 1.572 + CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; 1.573 + [oldLayer removeFromSuperlayer]; 1.574 + [wrapperLayer addSublayer:(CALayer*)aCALayer]; 1.575 +} 1.576 + 1.577 +#ifdef DEBUG 1.578 + 1.579 +int sSaveToDiskSequence = 0; 1.580 +void nsCARenderer::SaveToDisk(MacIOSurface *surf) { 1.581 + surf->Lock(); 1.582 + size_t bytesPerRow = surf->GetBytesPerRow(); 1.583 + size_t ioWidth = surf->GetWidth(); 1.584 + size_t ioHeight = surf->GetHeight(); 1.585 + void* ioData = surf->GetBaseAddress(); 1.586 + CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, 1.587 + ioData, ioHeight*(bytesPerRow)*4, 1.588 + nullptr); //No release callback 1.589 + if (!dataProvider) { 1.590 + surf->Unlock(); 1.591 + return; 1.592 + } 1.593 + 1.594 + CGColorSpaceRef colorSpace = CreateSystemColorSpace(); 1.595 + CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow, 1.596 + colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, 1.597 + dataProvider, nullptr, true, kCGRenderingIntentDefault); 1.598 + ::CGDataProviderRelease(dataProvider); 1.599 + ::CGColorSpaceRelease(colorSpace); 1.600 + if (!cgImage) { 1.601 + surf->Unlock(); 1.602 + return; 1.603 + } 1.604 + 1.605 + char cstr[1000]; 1.606 + 1.607 + sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence); 1.608 + 1.609 + CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman); 1.610 + 1.611 + printf("Exporting: %s\n", cstr); 1.612 + CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr); 1.613 + ::CFRelease(cfStr); 1.614 + 1.615 + CFStringRef type = kUTTypePNG; 1.616 + size_t count = 1; 1.617 + CFDictionaryRef options = nullptr; 1.618 + CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options); 1.619 + ::CFRelease(url); 1.620 + 1.621 + ::CGImageDestinationAddImage(dest, cgImage, nullptr); 1.622 + 1.623 + ::CGImageDestinationFinalize(dest); 1.624 + ::CFRelease(dest); 1.625 + ::CGImageRelease(cgImage); 1.626 + 1.627 + surf->Unlock(); 1.628 + 1.629 + return; 1.630 + 1.631 +} 1.632 + 1.633 +#endif