gfx/2d/QuartzSupport.mm

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial