michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:set ts=2 sts=2 sw=2 et cin: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "QuartzSupport.h" michael@0: #include "nsDebug.h" michael@0: #include "MacIOSurface.h" michael@0: michael@0: #import michael@0: #import michael@0: #include michael@0: #include "GLDefs.h" michael@0: michael@0: #define IOSURFACE_FRAMEWORK_PATH \ michael@0: "/System/Library/Frameworks/IOSurface.framework/IOSurface" michael@0: #define OPENGL_FRAMEWORK_PATH \ michael@0: "/System/Library/Frameworks/OpenGL.framework/OpenGL" michael@0: #define COREGRAPHICS_FRAMEWORK_PATH \ michael@0: "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics" michael@0: michael@0: @interface CALayer (ContentsScale) michael@0: - (double)contentsScale; michael@0: - (void)setContentsScale:(double)scale; michael@0: @end michael@0: michael@0: using mozilla::RefPtr; michael@0: using mozilla::TemporaryRef; michael@0: michael@0: CGColorSpaceRef CreateSystemColorSpace() { michael@0: CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); michael@0: if (!cspace) { michael@0: cspace = ::CGColorSpaceCreateDeviceRGB(); michael@0: } michael@0: return cspace; michael@0: } michael@0: michael@0: nsCARenderer::~nsCARenderer() { michael@0: Destroy(); michael@0: } michael@0: michael@0: void cgdata_release_callback(void *aCGData, const void *data, size_t size) { michael@0: if (aCGData) { michael@0: free(aCGData); michael@0: } michael@0: } michael@0: michael@0: void nsCARenderer::Destroy() { michael@0: if (mCARenderer) { michael@0: CARenderer* caRenderer = (CARenderer*)mCARenderer; michael@0: // Bug 556453: michael@0: // Explicitly remove the layer from the renderer michael@0: // otherwise it does not always happen right away. michael@0: caRenderer.layer = nullptr; michael@0: [caRenderer release]; michael@0: } michael@0: if (mWrapperCALayer) { michael@0: CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; michael@0: [wrapperLayer release]; michael@0: } michael@0: if (mOpenGLContext) { michael@0: if (mFBO || mIOTexture || mFBOTexture) { michael@0: // Release these resources with the context that allocated them michael@0: CGLContextObj oldContext = ::CGLGetCurrentContext(); michael@0: ::CGLSetCurrentContext(mOpenGLContext); michael@0: michael@0: if (mFBOTexture) { michael@0: ::glDeleteTextures(1, &mFBOTexture); michael@0: } michael@0: if (mIOTexture) { michael@0: ::glDeleteTextures(1, &mIOTexture); michael@0: } michael@0: if (mFBO) { michael@0: ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); michael@0: ::glDeleteFramebuffersEXT(1, &mFBO); michael@0: } michael@0: michael@0: if (oldContext) michael@0: ::CGLSetCurrentContext(oldContext); michael@0: } michael@0: ::CGLDestroyContext((CGLContextObj)mOpenGLContext); michael@0: } michael@0: if (mCGImage) { michael@0: ::CGImageRelease(mCGImage); michael@0: } michael@0: // mCGData is deallocated by cgdata_release_callback michael@0: michael@0: mCARenderer = nil; michael@0: mWrapperCALayer = nil; michael@0: mFBOTexture = 0; michael@0: mOpenGLContext = nullptr; michael@0: mCGImage = nullptr; michael@0: mIOSurface = nullptr; michael@0: mFBO = 0; michael@0: mIOTexture = 0; michael@0: } michael@0: michael@0: nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight, michael@0: double aContentsScaleFactor, michael@0: AllowOfflineRendererEnum aAllowOfflineRenderer) { michael@0: mAllowOfflineRenderer = aAllowOfflineRenderer; michael@0: michael@0: if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aWidth == mUnsupportedWidth && michael@0: aHeight == mUnsupportedHeight) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: CGLPixelFormatAttribute attributes[] = { michael@0: kCGLPFAAccelerated, michael@0: kCGLPFADepthSize, (CGLPixelFormatAttribute)24, michael@0: kCGLPFAAllowOfflineRenderers, michael@0: (CGLPixelFormatAttribute)0 michael@0: }; michael@0: michael@0: if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) { michael@0: attributes[3] = (CGLPixelFormatAttribute)0; michael@0: } michael@0: michael@0: GLint screen; michael@0: CGLPixelFormatObj format; michael@0: if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) { michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (::CGLCreateContext(format, nullptr, &mOpenGLContext) != kCGLNoError) { michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: ::CGLDestroyPixelFormat(format); michael@0: michael@0: CARenderer* caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext michael@0: options:nil] retain]; michael@0: if (caRenderer == nil) { michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: CALayer* wrapperCALayer = [[CALayer layer] retain]; michael@0: if (wrapperCALayer == nil) { michael@0: [caRenderer release]; michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mCARenderer = caRenderer; michael@0: mWrapperCALayer = wrapperCALayer; michael@0: caRenderer.layer = wrapperCALayer; michael@0: [wrapperCALayer addSublayer:(CALayer*)aCALayer]; michael@0: mContentsScaleFactor = aContentsScaleFactor; michael@0: size_t intScaleFactor = ceil(mContentsScaleFactor); michael@0: SetBounds(aWidth, aHeight); michael@0: michael@0: // We target rendering to a CGImage if no shared IOSurface are given. michael@0: if (!mIOSurface) { michael@0: mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor); michael@0: if (!mCGData) { michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor); michael@0: michael@0: CGDataProviderRef dataProvider = nullptr; michael@0: dataProvider = ::CGDataProviderCreateWithData(mCGData, michael@0: mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor, michael@0: cgdata_release_callback); michael@0: if (!dataProvider) { michael@0: cgdata_release_callback(mCGData, mCGData, michael@0: aHeight*intScaleFactor*aWidth*4*intScaleFactor); michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: CGColorSpaceRef colorSpace = CreateSystemColorSpace(); michael@0: michael@0: mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor, michael@0: 8, 32, aWidth * intScaleFactor * 4, colorSpace, michael@0: kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, michael@0: dataProvider, nullptr, true, kCGRenderingIntentDefault); michael@0: michael@0: ::CGDataProviderRelease(dataProvider); michael@0: ::CGColorSpaceRelease(colorSpace); michael@0: if (!mCGImage) { michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: CGLContextObj oldContext = ::CGLGetCurrentContext(); michael@0: ::CGLSetCurrentContext(mOpenGLContext); michael@0: michael@0: if (mIOSurface) { michael@0: // Create the IOSurface mapped texture. michael@0: ::glGenTextures(1, &mIOTexture); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); michael@0: ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); michael@0: ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); michael@0: MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, michael@0: GL_RGBA, aWidth * intScaleFactor, michael@0: aHeight * intScaleFactor, michael@0: GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, michael@0: mIOSurface->mIOSurfacePtr, 0); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); michael@0: } else { michael@0: ::glGenTextures(1, &mFBOTexture); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture); michael@0: ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); michael@0: ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); michael@0: } michael@0: michael@0: // Create the fbo michael@0: ::glGenFramebuffersEXT(1, &mFBO); michael@0: ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); michael@0: if (mIOSurface) { michael@0: ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, michael@0: GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0); michael@0: } else { michael@0: ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, michael@0: GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0); michael@0: } michael@0: michael@0: michael@0: // Make sure that the Framebuffer configuration is supported on the client machine michael@0: GLenum fboStatus; michael@0: fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); michael@0: if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { michael@0: NS_ERROR("FBO not supported"); michael@0: if (oldContext) michael@0: ::CGLSetCurrentContext(oldContext); michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: SetViewport(aWidth, aHeight); michael@0: michael@0: GLenum result = ::glGetError(); michael@0: if (result != GL_NO_ERROR) { michael@0: NS_ERROR("Unexpected OpenGL Error"); michael@0: mUnsupportedWidth = aWidth; michael@0: mUnsupportedHeight = aHeight; michael@0: Destroy(); michael@0: if (oldContext) michael@0: ::CGLSetCurrentContext(oldContext); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (oldContext) michael@0: ::CGLSetCurrentContext(oldContext); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsCARenderer::SetBounds(int aWidth, int aHeight) { michael@0: CARenderer* caRenderer = (CARenderer*)mCARenderer; michael@0: CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; michael@0: NSArray* sublayers = [wrapperLayer sublayers]; michael@0: CALayer* pluginLayer = (CALayer*) [sublayers objectAtIndex:0]; michael@0: michael@0: // Create a transaction and disable animations michael@0: // to make the position update instant. michael@0: [CATransaction begin]; michael@0: NSMutableDictionary *newActions = michael@0: [[NSMutableDictionary alloc] initWithObjectsAndKeys: michael@0: [NSNull null], @"onOrderIn", michael@0: [NSNull null], @"onOrderOut", michael@0: [NSNull null], @"sublayers", michael@0: [NSNull null], @"contents", michael@0: [NSNull null], @"position", michael@0: [NSNull null], @"bounds", michael@0: nil]; michael@0: wrapperLayer.actions = newActions; michael@0: [newActions release]; michael@0: michael@0: // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0. michael@0: // For some reason, to make things work properly in HiDPI mode we need to michael@0: // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds' michael@0: // to the size of 'layer's backing store. And to avoid this possibly michael@0: // confusing the plugin, we need to hide it's effects from the plugin by michael@0: // making pluginLayer (usually the CALayer* provided by the plugin) a michael@0: // sublayer of our own wrapperLayer (see bug 829284). michael@0: size_t intScaleFactor = ceil(mContentsScaleFactor); michael@0: [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration]; michael@0: [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; michael@0: [wrapperLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; michael@0: [wrapperLayer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)]; michael@0: [pluginLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; michael@0: [pluginLayer setFrame:CGRectMake(0, 0, aWidth, aHeight)]; michael@0: caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor); michael@0: if (mContentsScaleFactor != 1.0) { michael@0: CGAffineTransform affineTransform = [wrapperLayer affineTransform]; michael@0: affineTransform.a = mContentsScaleFactor; michael@0: affineTransform.d = mContentsScaleFactor; michael@0: affineTransform.tx = ((double)aWidth)/mContentsScaleFactor; michael@0: affineTransform.ty = ((double)aHeight)/mContentsScaleFactor; michael@0: [wrapperLayer setAffineTransform:affineTransform]; michael@0: } else { michael@0: // These settings are the default values. But they might have been michael@0: // changed as above if we were previously running in a HiDPI mode michael@0: // (i.e. if we just switched from that to a non-HiDPI mode). michael@0: [wrapperLayer setAffineTransform:CGAffineTransformIdentity]; michael@0: } michael@0: [CATransaction commit]; michael@0: } michael@0: michael@0: void nsCARenderer::SetViewport(int aWidth, int aHeight) { michael@0: size_t intScaleFactor = ceil(mContentsScaleFactor); michael@0: aWidth *= intScaleFactor; michael@0: aHeight *= intScaleFactor; michael@0: michael@0: ::glViewport(0.0, 0.0, aWidth, aHeight); michael@0: ::glMatrixMode(GL_PROJECTION); michael@0: ::glLoadIdentity(); michael@0: ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1); michael@0: michael@0: // Render upside down to speed up CGContextDrawImage michael@0: ::glTranslatef(0.0f, aHeight, 0.0); michael@0: ::glScalef(1.0, -1.0, 1.0); michael@0: } michael@0: michael@0: void nsCARenderer::AttachIOSurface(RefPtr aSurface) { michael@0: if (mIOSurface && michael@0: aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) { michael@0: // This object isn't needed since we already have a michael@0: // handle to the same io surface. michael@0: aSurface = nullptr; michael@0: return; michael@0: } michael@0: michael@0: mIOSurface = aSurface; michael@0: michael@0: // Update the framebuffer and viewport michael@0: if (mCARenderer) { michael@0: CARenderer* caRenderer = (CARenderer*)mCARenderer; michael@0: size_t intScaleFactor = ceil(mContentsScaleFactor); michael@0: int width = caRenderer.bounds.size.width / intScaleFactor; michael@0: int height = caRenderer.bounds.size.height / intScaleFactor; michael@0: michael@0: CGLContextObj oldContext = ::CGLGetCurrentContext(); michael@0: ::CGLSetCurrentContext(mOpenGLContext); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); michael@0: MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, michael@0: GL_RGBA, mIOSurface->GetDevicePixelWidth(), michael@0: mIOSurface->GetDevicePixelHeight(), michael@0: GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, michael@0: mIOSurface->mIOSurfacePtr, 0); michael@0: ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); michael@0: michael@0: // Rebind the FBO to make it live michael@0: ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); michael@0: michael@0: if (static_cast(mIOSurface->GetWidth()) != width || michael@0: static_cast(mIOSurface->GetHeight()) != height) { michael@0: width = mIOSurface->GetWidth(); michael@0: height = mIOSurface->GetHeight(); michael@0: SetBounds(width, height); michael@0: SetViewport(width, height); michael@0: } michael@0: michael@0: if (oldContext) { michael@0: ::CGLSetCurrentContext(oldContext); michael@0: } michael@0: } michael@0: } michael@0: michael@0: IOSurfaceID nsCARenderer::GetIOSurfaceID() { michael@0: if (!mIOSurface) { michael@0: return 0; michael@0: } michael@0: michael@0: return mIOSurface->GetIOSurfaceID(); michael@0: } michael@0: michael@0: nsresult nsCARenderer::Render(int aWidth, int aHeight, michael@0: double aContentsScaleFactor, michael@0: CGImageRef *aOutCGImage) { michael@0: if (!aOutCGImage && !mIOSurface) { michael@0: NS_ERROR("No target destination for rendering"); michael@0: } else if (aOutCGImage) { michael@0: // We are expected to return a CGImageRef, we will set michael@0: // it to nullptr in case we fail before the image is ready. michael@0: *aOutCGImage = nullptr; michael@0: } michael@0: michael@0: if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) michael@0: return NS_OK; michael@0: michael@0: if (!mCARenderer || !mWrapperCALayer) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: CARenderer* caRenderer = (CARenderer*)mCARenderer; michael@0: CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; michael@0: size_t intScaleFactor = ceil(aContentsScaleFactor); michael@0: int renderer_width = caRenderer.bounds.size.width / intScaleFactor; michael@0: int renderer_height = caRenderer.bounds.size.height / intScaleFactor; michael@0: michael@0: if (renderer_width != aWidth || renderer_height != aHeight || michael@0: mContentsScaleFactor != aContentsScaleFactor) { michael@0: // XXX: This should be optimized to not rescale the buffer michael@0: // if we are resizing down. michael@0: // caLayer may be the CALayer* provided by the plugin, so we need to michael@0: // preserve it across the call to Destroy(). michael@0: NSArray* sublayers = [wrapperLayer sublayers]; michael@0: CALayer* caLayer = (CALayer*) [sublayers objectAtIndex:0]; michael@0: // mIOSurface is set by AttachIOSurface(), not by SetupRenderer(). So michael@0: // since it may have been set by a prior call to AttachIOSurface(), we michael@0: // need to preserve it across the call to Destroy(). michael@0: mozilla::RefPtr ioSurface = mIOSurface; michael@0: Destroy(); michael@0: mIOSurface = ioSurface; michael@0: if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor, michael@0: mAllowOfflineRenderer) != NS_OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: caRenderer = (CARenderer*)mCARenderer; michael@0: } michael@0: michael@0: CGLContextObj oldContext = ::CGLGetCurrentContext(); michael@0: ::CGLSetCurrentContext(mOpenGLContext); michael@0: if (!mIOSurface) { michael@0: // If no shared IOSurface is given render to our own michael@0: // texture for readback. michael@0: ::glGenTextures(1, &mFBOTexture); michael@0: } michael@0: michael@0: GLenum result = ::glGetError(); michael@0: if (result != GL_NO_ERROR) { michael@0: NS_ERROR("Unexpected OpenGL Error"); michael@0: Destroy(); michael@0: if (oldContext) michael@0: ::CGLSetCurrentContext(oldContext); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: ::glClearColor(0.0, 0.0, 0.0, 0.0); michael@0: ::glClear(GL_COLOR_BUFFER_BIT); michael@0: michael@0: [CATransaction commit]; michael@0: double caTime = ::CACurrentMediaTime(); michael@0: [caRenderer beginFrameAtTime:caTime timeStamp:nullptr]; michael@0: [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor, michael@0: aHeight * intScaleFactor)]; michael@0: [caRenderer render]; michael@0: [caRenderer endFrame]; michael@0: michael@0: // Read the data back either to the IOSurface or mCGImage. michael@0: if (mIOSurface) { michael@0: ::glFlush(); michael@0: } else { michael@0: ::glPixelStorei(GL_PACK_ALIGNMENT, 4); michael@0: ::glPixelStorei(GL_PACK_ROW_LENGTH, 0); michael@0: ::glPixelStorei(GL_PACK_SKIP_ROWS, 0); michael@0: ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0); michael@0: michael@0: ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor, michael@0: aHeight * intScaleFactor, michael@0: GL_BGRA, GL_UNSIGNED_BYTE, michael@0: mCGData); michael@0: michael@0: *aOutCGImage = mCGImage; michael@0: } michael@0: michael@0: if (oldContext) { michael@0: ::CGLSetCurrentContext(oldContext); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext, michael@0: MacIOSurface *surf, michael@0: CGColorSpaceRef aColorSpace, michael@0: int aX, int aY, michael@0: size_t aWidth, size_t aHeight) { michael@0: surf->Lock(); michael@0: size_t bytesPerRow = surf->GetBytesPerRow(); michael@0: size_t ioWidth = surf->GetWidth(); michael@0: size_t ioHeight = surf->GetHeight(); michael@0: michael@0: // We get rendering glitches if we use a width/height that falls michael@0: // outside of the IOSurface. michael@0: if (aWidth + aX > ioWidth) michael@0: aWidth = ioWidth - aX; michael@0: if (aHeight + aY > ioHeight) michael@0: aHeight = ioHeight - aY; michael@0: michael@0: if (aX < 0 || static_cast(aX) >= ioWidth || michael@0: aY < 0 || static_cast(aY) >= ioHeight) { michael@0: surf->Unlock(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void* ioData = surf->GetBaseAddress(); michael@0: double scaleFactor = surf->GetContentsScaleFactor(); michael@0: size_t intScaleFactor = ceil(surf->GetContentsScaleFactor()); michael@0: CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, michael@0: ioData, ioHeight*intScaleFactor*(bytesPerRow)*4, michael@0: nullptr); //No release callback michael@0: if (!dataProvider) { michael@0: surf->Unlock(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor, michael@0: ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace, michael@0: kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, michael@0: dataProvider, nullptr, true, kCGRenderingIntentDefault); michael@0: ::CGDataProviderRelease(dataProvider); michael@0: if (!cgImage) { michael@0: surf->Unlock(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage, michael@0: ::CGRectMake(aX * scaleFactor, michael@0: aY * scaleFactor, michael@0: aWidth * scaleFactor, michael@0: aHeight * scaleFactor)); michael@0: if (!subImage) { michael@0: ::CGImageRelease(cgImage); michael@0: surf->Unlock(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: ::CGContextScaleCTM(aContext, 1.0f, -1.0f); michael@0: ::CGContextDrawImage(aContext, michael@0: CGRectMake(aX * scaleFactor, michael@0: (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor, michael@0: aWidth * scaleFactor, michael@0: aHeight * scaleFactor), michael@0: subImage); michael@0: michael@0: ::CGImageRelease(subImage); michael@0: ::CGImageRelease(cgImage); michael@0: surf->Unlock(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsCARenderer::DetachCALayer() { michael@0: CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; michael@0: NSArray* sublayers = [wrapperLayer sublayers]; michael@0: CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; michael@0: [oldLayer removeFromSuperlayer]; michael@0: } michael@0: michael@0: void nsCARenderer::AttachCALayer(void *aCALayer) { michael@0: CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; michael@0: NSArray* sublayers = [wrapperLayer sublayers]; michael@0: CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; michael@0: [oldLayer removeFromSuperlayer]; michael@0: [wrapperLayer addSublayer:(CALayer*)aCALayer]; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: int sSaveToDiskSequence = 0; michael@0: void nsCARenderer::SaveToDisk(MacIOSurface *surf) { michael@0: surf->Lock(); michael@0: size_t bytesPerRow = surf->GetBytesPerRow(); michael@0: size_t ioWidth = surf->GetWidth(); michael@0: size_t ioHeight = surf->GetHeight(); michael@0: void* ioData = surf->GetBaseAddress(); michael@0: CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, michael@0: ioData, ioHeight*(bytesPerRow)*4, michael@0: nullptr); //No release callback michael@0: if (!dataProvider) { michael@0: surf->Unlock(); michael@0: return; michael@0: } michael@0: michael@0: CGColorSpaceRef colorSpace = CreateSystemColorSpace(); michael@0: CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow, michael@0: colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, michael@0: dataProvider, nullptr, true, kCGRenderingIntentDefault); michael@0: ::CGDataProviderRelease(dataProvider); michael@0: ::CGColorSpaceRelease(colorSpace); michael@0: if (!cgImage) { michael@0: surf->Unlock(); michael@0: return; michael@0: } michael@0: michael@0: char cstr[1000]; michael@0: michael@0: sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence); michael@0: michael@0: CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman); michael@0: michael@0: printf("Exporting: %s\n", cstr); michael@0: CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr); michael@0: ::CFRelease(cfStr); michael@0: michael@0: CFStringRef type = kUTTypePNG; michael@0: size_t count = 1; michael@0: CFDictionaryRef options = nullptr; michael@0: CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options); michael@0: ::CFRelease(url); michael@0: michael@0: ::CGImageDestinationAddImage(dest, cgImage, nullptr); michael@0: michael@0: ::CGImageDestinationFinalize(dest); michael@0: ::CFRelease(dest); michael@0: ::CGImageRelease(cgImage); michael@0: michael@0: surf->Unlock(); michael@0: michael@0: return; michael@0: michael@0: } michael@0: michael@0: #endif