/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ // vim:set ts=2 sts=2 sw=2 et cin: /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 2008 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Benoit Girard * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCoreAnimationSupport.h" #include "nsDebug.h" #import #include #define IOSURFACE_FRAMEWORK_PATH \ "/System/Library/Frameworks/IOSurface.framework/IOSurface" #define OPENGL_FRAMEWORK_PATH \ "/System/Library/Frameworks/OpenGL.framework/OpenGL" // IOSurface signatures typedef CFTypeRef IOSurfacePtr; typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties); typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id); typedef IOSurfaceID (*IOSurfaceGetIDFunc) (CFTypeRef io_surface); typedef IOReturn (*IOSurfaceLockFunc) (CFTypeRef io_surface, uint32_t options, uint32_t *seed); typedef IOReturn (*IOSurfaceUnlockFunc) (CFTypeRef io_surface, uint32_t options, uint32_t *seed); typedef void* (*IOSurfaceGetBaseAddressFunc) (CFTypeRef io_surface); typedef size_t (*IOSurfaceGetWidthFunc) (IOSurfacePtr io_surface); typedef size_t (*IOSurfaceGetHeightFunc) (IOSurfacePtr io_surface); typedef size_t (*IOSurfaceGetBytesPerRowFunc) (IOSurfacePtr io_surface); typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface, GLuint plane); #define GET_CONST(const_name) \ ((CFStringRef*) dlsym(sIOSurfaceFramework, const_name)) #define GET_IOSYM(dest,sym_name) \ (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name) #define GET_CGLSYM(dest,sym_name) \ (typeof(dest)) dlsym(sOpenGLFramework, sym_name) class nsIOSurfaceLib: public nsIOSurface { public: static void *sIOSurfaceFramework; static void *sOpenGLFramework; static bool isLoaded; static IOSurfaceCreateFunc sCreate; static IOSurfaceGetIDFunc sGetID; static IOSurfaceLookupFunc sLookup; static IOSurfaceGetBaseAddressFunc sGetBaseAddress; static IOSurfaceLockFunc sLock; static IOSurfaceUnlockFunc sUnlock; static IOSurfaceGetWidthFunc sWidth; static IOSurfaceGetHeightFunc sHeight; static IOSurfaceGetBytesPerRowFunc sBytesPerRow; static CGLTexImageIOSurface2DFunc sTexImage; static CFStringRef kPropWidth; static CFStringRef kPropHeight; static CFStringRef kPropBytesPerElem; static CFStringRef kPropBytesPerRow; static CFStringRef kPropIsGlobal; static bool isInit(); static CFStringRef GetIOConst(const char* symbole); static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties); static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID); static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr); static void *IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr); static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr); static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr); static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr); static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, uint32_t options, uint32_t *seed); static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, uint32_t options, uint32_t *seed); static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface, GLuint plane); static void LoadLibrary(); static void CloseLibrary(); // Static deconstructor static class LibraryUnloader { public: ~LibraryUnloader() { CloseLibrary(); } } sLibraryUnloader; }; nsIOSurfaceLib::LibraryUnloader nsIOSurfaceLib::sLibraryUnloader; bool nsIOSurfaceLib::isLoaded = false; void* nsIOSurfaceLib::sIOSurfaceFramework; void* nsIOSurfaceLib::sOpenGLFramework; IOSurfaceCreateFunc nsIOSurfaceLib::sCreate; IOSurfaceGetIDFunc nsIOSurfaceLib::sGetID; IOSurfaceLookupFunc nsIOSurfaceLib::sLookup; IOSurfaceGetBaseAddressFunc nsIOSurfaceLib::sGetBaseAddress; IOSurfaceGetHeightFunc nsIOSurfaceLib::sWidth; IOSurfaceGetWidthFunc nsIOSurfaceLib::sHeight; IOSurfaceGetBytesPerRowFunc nsIOSurfaceLib::sBytesPerRow; IOSurfaceLockFunc nsIOSurfaceLib::sLock; IOSurfaceUnlockFunc nsIOSurfaceLib::sUnlock; CGLTexImageIOSurface2DFunc nsIOSurfaceLib::sTexImage; CFStringRef nsIOSurfaceLib::kPropWidth; CFStringRef nsIOSurfaceLib::kPropHeight; CFStringRef nsIOSurfaceLib::kPropBytesPerElem; CFStringRef nsIOSurfaceLib::kPropBytesPerRow; CFStringRef nsIOSurfaceLib::kPropIsGlobal; bool nsIOSurfaceLib::isInit() { // Guard against trying to reload the library // if it is not available. if (!isLoaded) LoadLibrary(); if (!sIOSurfaceFramework) { NS_ERROR("nsIOSurfaceLib failed to initialize"); } return sIOSurfaceFramework; } IOSurfacePtr nsIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) { return sCreate(properties); } IOSurfacePtr nsIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) { return sLookup(aIOSurfaceID); } IOSurfaceID nsIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) { return sGetID(aIOSurfacePtr); } void* nsIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) { return sGetBaseAddress(aIOSurfacePtr); } size_t nsIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr) { return sWidth(aIOSurfacePtr); } size_t nsIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) { return sHeight(aIOSurfacePtr); } size_t nsIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) { return sBytesPerRow(aIOSurfacePtr); } IOReturn nsIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, uint32_t options, uint32_t *seed) { return sLock(aIOSurfacePtr, options, seed); } IOReturn nsIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, uint32_t options, uint32_t *seed) { return sUnlock(aIOSurfacePtr, options, seed); } CGLError nsIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface, GLuint plane) { return sTexImage(ctxt, target, internalFormat, width, height, format, type, ioSurface, plane); } CFStringRef nsIOSurfaceLib::GetIOConst(const char* symbole) { CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole); if (!address) return nsnull; return *address; } void nsIOSurfaceLib::LoadLibrary() { if (isLoaded) { return; } isLoaded = true; sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL); sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL); if (!sIOSurfaceFramework) { return; } if (!sOpenGLFramework) { dlclose(sIOSurfaceFramework); sIOSurfaceFramework = nsnull; return; } kPropWidth = GetIOConst("kIOSurfaceWidth"); kPropHeight = GetIOConst("kIOSurfaceHeight"); kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement"); kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow"); kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal"); sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate"); sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID"); sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth"); sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight"); sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow"); sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup"); sLock = GET_IOSYM(sLock, "IOSurfaceLock"); sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock"); sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress"); sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D"); if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress || !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal || !sLock || !sUnlock || !sWidth || !sHeight || !kPropBytesPerRow || !sBytesPerRow) { CloseLibrary(); } } void nsIOSurfaceLib::CloseLibrary() { if (sIOSurfaceFramework) { dlclose(sIOSurfaceFramework); } if (sOpenGLFramework) { dlclose(sOpenGLFramework); } sIOSurfaceFramework = nsnull; sOpenGLFramework = nsnull; } already_AddRefed nsIOSurface::CreateIOSurface(int aWidth, int aHeight) { if (!nsIOSurfaceLib::isInit()) return nsnull; CFMutableDictionaryRef props = ::CFDictionaryCreateMutable( kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!props) return nsnull; int32_t bytesPerElem = 4; CFNumberRef cfWidth = ::CFNumberCreate(NULL, kCFNumberSInt32Type, &aWidth); CFNumberRef cfHeight = ::CFNumberCreate(NULL, kCFNumberSInt32Type, &aHeight); CFNumberRef cfBytesPerElem = ::CFNumberCreate(NULL, kCFNumberSInt32Type, &bytesPerElem); ::CFDictionaryAddValue(props, nsIOSurfaceLib::kPropWidth, cfWidth); ::CFRelease(cfWidth); ::CFDictionaryAddValue(props, nsIOSurfaceLib::kPropHeight, cfHeight); ::CFRelease(cfHeight); ::CFDictionaryAddValue(props, nsIOSurfaceLib::kPropBytesPerElem, cfBytesPerElem); ::CFRelease(cfBytesPerElem); ::CFDictionaryAddValue(props, nsIOSurfaceLib::kPropIsGlobal, kCFBooleanTrue); IOSurfacePtr surfaceRef = nsIOSurfaceLib::IOSurfaceCreate(props); ::CFRelease(props); if (!surfaceRef) return nsnull; nsRefPtr ioSurface = new nsIOSurface(surfaceRef); if (!ioSurface) { ::CFRelease(surfaceRef); return nsnull; } return ioSurface.forget(); } already_AddRefed nsIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID) { if (!nsIOSurfaceLib::isInit()) return nsnull; IOSurfacePtr surfaceRef = nsIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID); if (!surfaceRef) return nsnull; nsRefPtr ioSurface = new nsIOSurface(surfaceRef); if (!ioSurface) { ::CFRelease(surfaceRef); return nsnull; } return ioSurface.forget(); } IOSurfaceID nsIOSurface::GetIOSurfaceID() { return nsIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr); } void* nsIOSurface::GetBaseAddress() { return nsIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr); } size_t nsIOSurface::GetWidth() { return nsIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr); } size_t nsIOSurface::GetHeight() { return nsIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr); } size_t nsIOSurface::GetBytesPerRow() { return nsIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr); } #define READ_ONLY 0x1 void nsIOSurface::Lock() { nsIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, READ_ONLY, NULL); } void nsIOSurface::Unlock() { nsIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, NULL); } CGLError nsIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum internalFormat, GLenum format, GLenum type, GLuint plane) { return nsIOSurfaceLib::CGLTexImageIOSurface2D(ctxt, GL_TEXTURE_RECTANGLE_ARB, internalFormat, GetWidth(), GetHeight(), format, type, mIOSurfacePtr, plane); } nsCARenderer::~nsCARenderer() { Destroy(); } CGColorSpaceRef CreateSystemColorSpace() { CMProfileRef system_profile = nsnull; CGColorSpaceRef cspace = nsnull; if (::CMGetSystemProfile(&system_profile) == noErr) { // Create a colorspace with the systems profile cspace = ::CGColorSpaceCreateWithPlatformColorSpace(system_profile); ::CMCloseProfile(system_profile); } else { // Default to generic cspace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); } return cspace; } void cgdata_release_callback(void *aCGData, const void *data, size_t size) { if (aCGData) { free(aCGData); } } void nsCARenderer::Destroy() { if (mCARenderer) { CARenderer* caRenderer = (CARenderer*)mCARenderer; // Bug 556453: // Explicitly remove the layer from the renderer // otherwise it does not always happen right away. caRenderer.layer = nsnull; [caRenderer release]; } if (mOpenGLContext) { if (mFBO || mIOTexture || mFBOTexture) { // Release these resources with the context that allocated them CGLContextObj oldContext = ::CGLGetCurrentContext(); ::CGLSetCurrentContext(mOpenGLContext); if (mFBOTexture) { ::glDeleteTextures(1, &mFBOTexture); } if (mIOTexture) { ::glDeleteTextures(1, &mIOTexture); } if (mFBO) { ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); ::glDeleteFramebuffersEXT(1, &mFBO); } if (oldContext) ::CGLSetCurrentContext(oldContext); } ::CGLDestroyContext((CGLContextObj)mOpenGLContext); } if (mCGImage) { ::CGImageRelease(mCGImage); } // mCGData is deallocated by cgdata_release_callback mCARenderer = nil; mFBOTexture = 0; mOpenGLContext = nsnull; mCGImage = nsnull; mIOSurface = nsnull; mFBO = nsnull; mIOTexture = nsnull; } nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight, AllowOfflineRendererEnum aAllowOfflineRenderer) { mAllowOfflineRenderer = aAllowOfflineRenderer; if (aWidth == 0 || aHeight == 0) return NS_ERROR_FAILURE; if (aWidth == mUnsupportedWidth && aHeight == mUnsupportedHeight) { return NS_ERROR_FAILURE; } CALayer* layer = (CALayer*)aCALayer; CARenderer* caRenderer = nsnull; CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, kCGLPFADepthSize, (CGLPixelFormatAttribute)24, kCGLPFAAllowOfflineRenderers, (CGLPixelFormatAttribute)0 }; if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) { attributes[3] = (CGLPixelFormatAttribute)0; } GLint screen; CGLPixelFormatObj format; if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } if (::CGLCreateContext(format, nsnull, &mOpenGLContext) != kCGLNoError) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } ::CGLDestroyPixelFormat(format); caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext options:nil] retain]; mCARenderer = caRenderer; if (caRenderer == nil) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } // Create a transaction and disable animations // to make the position update instant. [CATransaction begin]; NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"onOrderIn", [NSNull null], @"onOrderOut", [NSNull null], @"sublayers", [NSNull null], @"contents", [NSNull null], @"position", [NSNull null], @"bounds", nil]; layer.actions = newActions; [newActions release]; [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration]; [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; [layer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; [layer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)]; caRenderer.layer = layer; caRenderer.bounds = CGRectMake(0, 0, aWidth, aHeight); [CATransaction commit]; // We target rendering to a CGImage if no shared IOSurface are given. if (!mIOSurface) { mCGData = malloc(aWidth*aHeight*4); if (!mCGData) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); } memset(mCGData, 0, aWidth*aHeight*4); CGDataProviderRef dataProvider = nsnull; dataProvider = ::CGDataProviderCreateWithData(mCGData, mCGData, aHeight*aWidth*4, cgdata_release_callback); if (!dataProvider) { cgdata_release_callback(mCGData, mCGData, aHeight*aWidth*4); mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } CGColorSpaceRef colorSpace = CreateSystemColorSpace(); mCGImage = ::CGImageCreate(aWidth, aHeight, 8, 32, aWidth * 4, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider, NULL, true, kCGRenderingIntentDefault); ::CGDataProviderRelease(dataProvider); if (colorSpace) { ::CGColorSpaceRelease(colorSpace); } if (!mCGImage) { mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } } CGLContextObj oldContext = ::CGLGetCurrentContext(); ::CGLSetCurrentContext(mOpenGLContext); if (mIOSurface) { // Create the IOSurface mapped texture. ::glGenTextures(1, &mIOTexture); ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nsIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, aWidth, aHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, mIOSurface->mIOSurfacePtr, 0); ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); } else { ::glGenTextures(1, &mFBOTexture); ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture); ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); } // Create the fbo ::glGenFramebuffersEXT(1, &mFBO); ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); if (mIOSurface) { ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0); } else { ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0); } // Make sure that the Framebuffer configuration is supported on the client machine GLenum fboStatus; fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { NS_ERROR("FBO not supported"); if (oldContext) ::CGLSetCurrentContext(oldContext); mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); return NS_ERROR_FAILURE; } ::glViewport(0.0, 0.0, aWidth, aHeight); ::glMatrixMode(GL_PROJECTION); ::glLoadIdentity(); ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1); // Render upside down to speed up CGContextDrawImage ::glTranslatef(0.0f, aHeight, 0.0); ::glScalef(1.0, -1.0, 1.0); GLenum result = ::glGetError(); if (result != GL_NO_ERROR) { NS_ERROR("Unexpected OpenGL Error"); mUnsupportedWidth = aWidth; mUnsupportedHeight = aHeight; Destroy(); if (oldContext) ::CGLSetCurrentContext(oldContext); return NS_ERROR_FAILURE; } if (oldContext) ::CGLSetCurrentContext(oldContext); return NS_OK; } void nsCARenderer::AttachIOSurface(nsRefPtr aSurface) { if (mIOSurface && aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) { // This object isn't needed since we already have a // handle to the same io surface. aSurface = nsnull; return; } if (mCARenderer) { // We are attaching a larger IOSurface, we need to // resize our elements. Destroy(); } mIOSurface = aSurface; } IOSurfaceID nsCARenderer::GetIOSurfaceID() { if (!mIOSurface) { return 0; } return mIOSurface->GetIOSurfaceID(); } nsresult nsCARenderer::Render(int aWidth, int aHeight, CGImageRef *aOutCGImage) { if (!aOutCGImage && !mIOSurface) { NS_ERROR("No target destination for rendering"); } else if (aOutCGImage) { // We are expected to return a CGImageRef, we will set // it to NULL in case we fail before the image is ready. *aOutCGImage = NULL; } if (aWidth == 0 || aHeight == 0) return NS_OK; if (!mCARenderer) { return NS_ERROR_FAILURE; } CARenderer* caRenderer = (CARenderer*)mCARenderer; int renderer_width = caRenderer.bounds.size.width; int renderer_height = caRenderer.bounds.size.height; if (renderer_width != aWidth || renderer_height != aHeight) { // XXX: This should be optimized to not rescale the buffer // if we are resizing down. CALayer* caLayer = [caRenderer layer]; Destroy(); if (SetupRenderer(caLayer, aWidth, aHeight, mAllowOfflineRenderer) != NS_OK) { return NS_ERROR_FAILURE; } caRenderer = (CARenderer*)mCARenderer; } CGLContextObj oldContext = ::CGLGetCurrentContext(); ::CGLSetCurrentContext(mOpenGLContext); if (!mIOSurface) { // If no shared IOSurface is given render to our own // texture for readback. ::glGenTextures(1, &mFBOTexture); } GLenum result = ::glGetError(); if (result != GL_NO_ERROR) { NS_ERROR("Unexpected OpenGL Error"); Destroy(); if (oldContext) ::CGLSetCurrentContext(oldContext); return NS_ERROR_FAILURE; } ::glClearColor(0.0, 0.0, 0.0, 0.0); ::glClear(GL_COLOR_BUFFER_BIT); [CATransaction commit]; double caTime = ::CACurrentMediaTime(); [caRenderer beginFrameAtTime:caTime timeStamp:NULL]; [caRenderer addUpdateRect:CGRectMake(0,0, aWidth, aHeight)]; [caRenderer render]; [caRenderer endFrame]; // Read the data back either to the IOSurface or mCGImage. if (mIOSurface) { ::glFlush(); } else { ::glPixelStorei(GL_PACK_ALIGNMENT, 4); ::glPixelStorei(GL_PACK_ROW_LENGTH, 0); ::glPixelStorei(GL_PACK_SKIP_ROWS, 0); ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0); ::glReadPixels(0.0f, 0.0f, aWidth, aHeight, GL_BGRA, GL_UNSIGNED_BYTE, mCGData); *aOutCGImage = mCGImage; } if (oldContext) { ::CGLSetCurrentContext(oldContext); } return NS_OK; } nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext, nsIOSurface *surf, CGColorSpaceRef aColorSpace, int aX, int aY, size_t aWidth, size_t aHeight) { surf->Lock(); size_t bytesPerRow = surf->GetBytesPerRow(); size_t ioWidth = surf->GetWidth(); size_t ioHeight = surf->GetHeight(); void* ioData = surf->GetBaseAddress(); CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, ioData, ioHeight*(bytesPerRow)*4, NULL); //No release callback if (!dataProvider) { surf->Unlock(); return NS_ERROR_FAILURE; } // We get rendering glitches if we use a width/height that falls // outside of the IOSurface. if (aWidth + aX > ioWidth) aWidth = ioWidth - aX; if (aHeight + aY > ioHeight) aHeight = ioHeight - aY; if (aX < 0 || aX >= ioWidth || aY < 0 || aY >= ioHeight) { return NS_ERROR_FAILURE; } CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow, aColorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider, NULL, true, kCGRenderingIntentDefault); ::CGDataProviderRelease(dataProvider); if (!cgImage) { surf->Unlock(); return NS_ERROR_FAILURE; } CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage, ::CGRectMake(aX, aY, aWidth, aHeight)); if (!subImage) { ::CGImageRelease(cgImage); surf->Unlock(); return NS_ERROR_FAILURE; } ::CGContextScaleCTM(aContext, 1.0f, -1.0f); ::CGContextDrawImage(aContext, CGRectMake(aX, -(CGFloat)aY - (CGFloat)aHeight, aWidth, aHeight), subImage); ::CGImageRelease(subImage); ::CGImageRelease(cgImage); surf->Unlock(); return NS_OK; } void nsCARenderer::DettachCALayer() { CARenderer* caRenderer = (CARenderer*)mCARenderer; caRenderer.layer = nil; } void nsCARenderer::AttachCALayer(void *aCALayer) { CARenderer* caRenderer = (CARenderer*)mCARenderer; CALayer* caLayer = (CALayer*)aCALayer; caRenderer.layer = caLayer; } #ifdef DEBUG int sSaveToDiskSequence = 0; void nsCARenderer::SaveToDisk(nsIOSurface *surf) { surf->Lock(); size_t bytesPerRow = surf->GetBytesPerRow(); size_t ioWidth = surf->GetWidth(); size_t ioHeight = surf->GetHeight(); void* ioData = surf->GetBaseAddress(); CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, ioData, ioHeight*(bytesPerRow)*4, NULL); //No release callback if (!dataProvider) { surf->Unlock(); return; } CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow, CreateSystemColorSpace(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider, NULL, true, kCGRenderingIntentDefault); ::CGDataProviderRelease(dataProvider); if (!cgImage) { surf->Unlock(); return; } char cstr[1000]; sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence); CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman); printf("Exporting: %s\n", cstr); CFURLRef url = ::CFURLCreateWithString( NULL, cfStr, NULL); CFStringRef type = kUTTypePNG; size_t count = 1; CFDictionaryRef options = NULL; CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options); ::CGImageDestinationAddImage(dest, cgImage, NULL); ::CGImageDestinationFinalize(dest); ::CFRelease(dest); surf->Unlock(); return; } #endif