Bug 555281 - Implement Core Animation NPAPI Drawing Model for OOPP. r=joe,josh,cjones

--HG--
extra : rebase_source : 8cc45083e0b513902c467c8c89248474c21b7923
This commit is contained in:
Benoit Girard 2010-04-20 10:52:19 -04:00
parent 38d93a6104
commit 9897b0a83e
15 changed files with 1151 additions and 280 deletions

View File

@ -83,6 +83,9 @@ child:
// special cases where we need to a shared memory buffer
rpc NPP_HandleEvent_Shmem(NPRemoteEvent event, Shmem buffer)
returns (int16_t handled, Shmem rtnbuffer);
// special cases where we need an iosurface
rpc NPP_HandleEvent_IOSurface(NPRemoteEvent event, uint32_t surfaceid)
returns (int16_t handled);
// special cases of HandleEvent to make mediating races simpler
rpc Paint(NPRemoteEvent event)
returns (int16_t handled);

View File

@ -80,7 +80,6 @@ using mozilla::gfx::SharedDIB;
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
#elif defined(XP_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#include "nsPluginUtilsOSX.h"
#endif // defined(XP_MACOSX)
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
@ -101,8 +100,9 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
#endif // OS_WIN
, mAsyncCallMutex("PluginInstanceChild::mAsyncCallMutex")
#if defined(OS_MACOSX)
, mShColorSpace(NULL)
, mShContext(NULL)
, mShColorSpace(nsnull)
, mShContext(nsnull)
, mDrawingModel(NPDrawingModelCoreGraphics)
#endif
{
memset(&mWindow, 0, sizeof(mWindow));
@ -344,7 +344,7 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
}
case NPNVsupportsCoreAnimationBool: {
*((NPBool*)aValue) = false;
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
@ -407,6 +407,7 @@ PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
if (!CallNPN_SetValue_NPPVpluginDrawingModel(drawingModel, &rv))
return NPERR_GENERIC_ERROR;
mDrawingModel = drawingModel;
return rv;
}
@ -589,6 +590,7 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
}
#ifdef XP_MACOSX
bool
PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
Shmem& mem,
@ -658,6 +660,65 @@ PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
}
#endif
#ifdef XP_MACOSX
bool
PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
const uint32_t &surfaceid,
int16_t* handled)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
NPCocoaEvent evcopy = event.event;
nsIOSurface* surf = nsIOSurface::LookupSurface(surfaceid);
if (!surf) {
NS_ERROR("Invalid IOSurface.\n");
*handled = false;
return false;
}
if (evcopy.type == NPCocoaEventDrawRect) {
mCARenderer.AttachIOSurface(surf);
if (!mCARenderer.isInit()) {
void *caLayer = nsnull;
NPError result = mPluginIface->getvalue(GetNPP(),
NPPVpluginCoreAnimationLayer,
&caLayer);
if (result != NPERR_NO_ERROR || !caLayer) {
PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
"provide CALayer."));
*handled = false;
return false;
}
mCARenderer.SetupRenderer(caLayer, mWindow.width, mWindow.height);
// Flash needs to have the window set again after this step
if (mPluginIface->setwindow)
(void) mPluginIface->setwindow(&mData, &mWindow);
}
} else {
PLUGIN_LOG_DEBUG(("Invalid event type for "
"AnswerNNP_HandleEvent_IOSurface."));
*handled = false;
return false;
}
mCARenderer.Render(mWindow.width, mWindow.height, nsnull);
return true;
}
#else
bool
PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
const uint32_t &surfaceid,
int16_t* handled)
{
NS_RUNTIMEABORT("NPP_HandleEvent_IOSurface is a OSX-only message");
return false;
}
#endif
bool
PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
{
@ -799,7 +860,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
// Release the shared context so that it is reallocated
// with the new size.
::CGContextRelease(mShContext);
mShContext = NULL;
mShContext = nsnull;
}
if (mPluginIface->setwindow)

View File

@ -44,6 +44,9 @@
#include "mozilla/plugins/StreamNotifyChild.h"
#if defined(OS_WIN)
#include "mozilla/gfx/SharedDIBWin.h"
#elif defined(OS_MACOSX)
#include "nsCoreAnimationSupport.h"
#include "base/timer.h"
#endif
#include "npfunctions.h"
@ -90,6 +93,8 @@ protected:
AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled);
virtual bool
AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event, Shmem& mem, int16_t* handled, Shmem* rtnmem);
virtual bool
AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event, const uint32_t& surface, int16_t* handled);
NS_OVERRIDE
virtual bool
@ -323,7 +328,9 @@ private:
#if defined(OS_MACOSX)
private:
CGColorSpaceRef mShColorSpace;
CGContextRef mShContext;
CGContextRef mShContext;
int16_t mDrawingModel;
nsCARenderer mCARenderer;
#endif
};

View File

@ -61,13 +61,13 @@ UINT gOOPPStopNativeLoopEvent =
#include <gdk/gdk.h>
#elif defined(XP_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#include "nsPluginUtilsOSX.h"
#endif // defined(XP_MACOSX)
using namespace mozilla::plugins;
PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
NPP npp,
const nsCString& aMimeType,
const NPNetscapeFuncs* npniface)
: mParent(parent)
, mNPP(npp)
@ -78,12 +78,29 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
, mPluginWndProc(NULL)
, mNestedEventState(false)
#endif // defined(XP_WIN)
, mQuirks(0)
#if defined(XP_MACOSX)
, mShWidth(0)
, mShHeight(0)
, mShColorSpace(NULL)
, mShColorSpace(nsnull)
, mDrawingModel(NPDrawingModelCoreGraphics)
, mIOSurface(nsnull)
#endif
{
InitQuirksModes(aMimeType);
}
void
PluginInstanceParent::InitQuirksModes(const nsCString& aMimeType)
{
#ifdef OS_MACOSX
NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
// Flash sends us Invalidate events so we will use those
// instead of the refresh timer.
if (!FindInReadable(flash, aMimeType)) {
mQuirks |= COREANIMATION_REFRESH_TIMER;
}
#endif
}
PluginInstanceParent::~PluginInstanceParent()
@ -96,7 +113,13 @@ PluginInstanceParent::~PluginInstanceParent()
"Subclass was not reset correctly before the dtor was reached!");
#endif
#if defined(OS_MACOSX)
::CGColorSpaceRelease(mShColorSpace);
if (mShColorSpace)
::CGColorSpaceRelease(mShColorSpace);
if (mIOSurface)
delete mIOSurface;
if (mDrawingModel == NPDrawingModelCoreAnimation) {
mParent->RemoveFromRefreshTimer(this);
}
#endif
}
@ -329,8 +352,22 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginDrawingModel(
const int& drawingModel, NPError* result)
{
#ifdef XP_MACOSX
*result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
if (drawingModel == NPDrawingModelCoreAnimation) {
// We need to request CoreGraphics otherwise
// the nsObjectFrame will try to draw a CALayer
// that can not be shared across process.
mDrawingModel = NPDrawingModelCoreAnimation;
*result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
(void*)NPDrawingModelCoreGraphics);
if (mQuirks & COREANIMATION_REFRESH_TIMER) {
abort();
mParent->AddToRefreshTimer(this);
}
} else {
mDrawingModel = drawingModel;
*result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
(void*)drawingModel);
}
return true;
#else
*result = NPERR_GENERIC_ERROR;
@ -476,14 +513,24 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
#endif
#if defined(XP_MACOSX)
if (mShWidth * mShHeight != window.width * window.height) {
// XXX: benwa: OMG MEMORY LEAK!
// There is currently no way dealloc the shmem
// so for now we will leak the memory and will fix this ASAP!
if (!AllocShmem(window.width * window.height * 4, SharedMemory::TYPE_BASIC,
&mShSurface)) {
PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
return NPERR_GENERIC_ERROR;
if (mShWidth != window.width || mShHeight != window.height) {
if (mDrawingModel == NPDrawingModelCoreAnimation) {
if (mIOSurface) {
delete mIOSurface;
}
mIOSurface = nsIOSurface::CreateIOSurface(window.width, window.height);
} else if (mShWidth * mShHeight != window.width * window.height) {
// Uncomment me when DeallocShmem lands.
//if (mShWidth != 0 && mShHeight != 0) {
// DeallocShmem(&mShSurface);
//}
if (window.width != 0 && window.height != 0) {
if (!AllocShmem(window.width * window.height*4,
SharedMemory::TYPE_BASIC, &mShSurface)) {
PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
return NPERR_GENERIC_ERROR;
}
}
}
mShWidth = window.width;
mShHeight = window.height;
@ -680,48 +727,85 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
#ifdef XP_MACOSX
if (npevent->type == NPCocoaEventDrawRect) {
if (mShWidth == 0 && mShHeight == 0) {
PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
return false;
}
if (!mShSurface.IsReadable()) {
PLUGIN_LOG_DEBUG(("Shmem is not readable."));
return false;
}
if (mDrawingModel == NPDrawingModelCoreAnimation) {
if (!mIOSurface) {
NS_ERROR("No IOSurface allocated.");
return false;
}
if (!CallNPP_HandleEvent_IOSurface(npremoteevent,
mIOSurface->GetIOSurfaceID(),
&handled))
return false; // no good way to handle errors here...
if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface, &handled, &mShSurface))
return false; // no good way to handle errors here...
if (!mShSurface.IsReadable()) {
PLUGIN_LOG_DEBUG(("Shmem not returned. Either the plugin crashed or we have a bug."));
return false;
}
char* shContextByte = mShSurface.get<char>();
if (!mShColorSpace) {
mShColorSpace = CreateSystemColorSpace();
}
if (!mShColorSpace) {
PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
return true;
}
CGContextRef shContext = ::CGBitmapContextCreate(shContextByte,
mShWidth, mShHeight, 8, mShWidth*4, mShColorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
if (!shContext) {
PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
return true;
}
CGImageRef shImage = ::CGBitmapContextCreateImage(shContext);
if (shImage) {
CGContextRef cgContext = npevent->data.draw.context;
::CGContextDrawImage(cgContext, CGRectMake(0,0,mShWidth,mShHeight), shImage);
::CGImageRelease(shImage);
if (!mShColorSpace) {
mShColorSpace = CreateSystemColorSpace();
}
if (!mShColorSpace) {
PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
return false;
}
nsCARenderer::DrawSurfaceToCGContext(cgContext, mIOSurface,
mShColorSpace,
npevent->data.draw.x,
npevent->data.draw.y,
npevent->data.draw.width,
npevent->data.draw.height);
return false;
} else {
if (mShWidth == 0 && mShHeight == 0) {
PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
return false;
}
if (!mShSurface.IsReadable()) {
PLUGIN_LOG_DEBUG(("Shmem is not readable."));
return false;
}
if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface,
&handled, &mShSurface))
return false; // no good way to handle errors here...
if (!mShSurface.IsReadable()) {
PLUGIN_LOG_DEBUG(("Shmem not returned. Either the plugin crashed "
"or we have a bug."));
return false;
}
char* shContextByte = mShSurface.get<char>();
if (!mShColorSpace) {
mShColorSpace = CreateSystemColorSpace();
}
if (!mShColorSpace) {
PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
return false;
}
CGContextRef shContext = ::CGBitmapContextCreate(shContextByte,
mShWidth, mShHeight, 8,
mShWidth*4, mShColorSpace,
kCGImageAlphaPremultipliedFirst |
kCGBitmapByteOrder32Host);
if (!shContext) {
PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
return false;
}
CGImageRef shImage = ::CGBitmapContextCreateImage(shContext);
if (shImage) {
CGContextRef cgContext = npevent->data.draw.context;
::CGContextDrawImage(cgContext,
CGRectMake(0,0,mShWidth,mShHeight),
shImage);
::CGImageRelease(shImage);
} else {
::CGContextRelease(shContext);
return false;
}
::CGContextRelease(shContext);
return true;
}
::CGContextRelease(shContext);
return handled;
}
#endif
@ -1257,3 +1341,12 @@ PluginInstanceParent::RecvSetNestedEventState(const bool& aState)
return false;
#endif
}
#ifdef OS_MACOSX
void
PluginInstanceParent::Invalidate()
{
NPRect windowRect = {0, 0, mShWidth, mShHeight};
RecvNPN_InvalidateRect(windowRect);
}
#endif

View File

@ -43,6 +43,8 @@
#include "mozilla/plugins/PluginScriptableObjectParent.h"
#if defined(OS_WIN)
#include "mozilla/gfx/SharedDIBWin.h"
#elif defined(OS_MACOSX)
#include "nsCoreAnimationSupport.h"
#endif
#include "npfunctions.h"
@ -66,6 +68,7 @@ class PluginInstanceParent : public PPluginInstanceParent
public:
PluginInstanceParent(PluginModuleParent* parent,
NPP npp,
const nsCString& mimeType,
const NPNetscapeFuncs* npniface);
virtual ~PluginInstanceParent();
@ -246,7 +249,21 @@ public:
virtual bool
RecvSetNestedEventState(const bool& aState);
#if defined(OS_MACOSX)
void Invalidate();
#endif // definied(OS_MACOSX)
private:
// Quirks mode support for various plugin mime types
enum PluginQuirks {
// OSX: Don't use the refresh timer for plug-ins
// using this quirk. These plug-in most have another
// way to refresh the window.
COREANIMATION_REFRESH_TIMER = 1,
};
void InitQuirksModes(const nsCString& aMimeType);
bool InternalGetValueForNPObject(NPNVariable aVariable,
PPluginScriptableObjectParent** aValue,
NPError* aResult);
@ -256,6 +273,7 @@ private:
NPP mNPP;
const NPNetscapeFuncs* mNPNIface;
NPWindowType mWindowType;
int mQuirks;
nsDataHashtable<nsVoidPtrHashKey, PluginScriptableObjectParent*> mScriptableObjects;
@ -282,10 +300,12 @@ private:
#endif // defined(XP_WIN)
#if defined(OS_MACOSX)
private:
Shmem mShSurface;
size_t mShWidth;
size_t mShHeight;
CGColorSpaceRef mShColorSpace;
Shmem mShSurface;
size_t mShWidth;
size_t mShHeight;
CGColorSpaceRef mShColorSpace;
int16_t mDrawingModel;
nsIOSurface *mIOSurface;
#endif // definied(OS_MACOSX)
};

View File

@ -87,6 +87,8 @@ MediateRace(const RPCChannel::Message& parent,
switch (parent.type()) {
case PPluginInstance::Msg_Paint__ID:
case PPluginInstance::Msg_NPP_SetWindow__ID:
case PPluginInstance::Msg_NPP_HandleEvent_Shmem__ID:
case PPluginInstance::Msg_NPP_HandleEvent_IOSurface__ID:
// our code relies on the frame list not changing during paints and
// reflows
return RPCChannel::RRPParentWins;

View File

@ -714,7 +714,8 @@ PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
}
PluginInstanceParent* parentInstance =
new PluginInstanceParent(this, instance, mNPNIface);
new PluginInstanceParent(this, instance,
nsDependentCString(pluginType), mNPNIface);
if (!parentInstance->Init()) {
delete parentInstance;
@ -781,3 +782,35 @@ PluginModuleParent::AnswerProcessSomeEvents()
return true;
}
#endif
#ifdef OS_MACOSX
#define DEFAULT_REFRESH_MS 20 // CoreAnimation: 50 FPS
void
PluginModuleParent::AddToRefreshTimer(PluginInstanceParent *aInstance) {
if (mCATimerTargets.Contains(aInstance)) {
return;
}
mCATimerTargets.AppendElement(aInstance);
if (mCATimerTargets.Length() == 1) {
mCATimer.Start(base::TimeDelta::FromMilliseconds(DEFAULT_REFRESH_MS),
this, &PluginModuleParent::CAUpdate);
}
}
void
PluginModuleParent::RemoveFromRefreshTimer(PluginInstanceParent *aInstance) {
PRBool visibleRemoved = mCATimerTargets.RemoveElement(aInstance);
if (visibleRemoved && mCATimerTargets.IsEmpty()) {
mCATimer.Stop();
}
}
void
PluginModuleParent::CAUpdate() {
nsTObserverArray<PluginInstanceParent*>::ForwardIterator iter(mCATimerTargets);
while (iter.HasMore()) {
iter.GetNext()->Invalidate();
}
}
#endif

View File

@ -60,6 +60,7 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIFileStreams.h"
#include "nsTObserverArray.h"
namespace mozilla {
namespace plugins {
@ -134,6 +135,11 @@ public:
PPluginIdentifierParent*
GetIdentifierForNPIdentifier(NPIdentifier aIdentifier);
#ifdef OS_MACOSX
void AddToRefreshTimer(PluginInstanceParent *aInstance);
void RemoveFromRefreshTimer(PluginInstanceParent *aInstance);
#endif
protected:
NS_OVERRIDE
virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
@ -240,6 +246,12 @@ private:
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
#ifdef OS_MACOSX
void CAUpdate();
base::RepeatingTimer<PluginModuleParent> mCATimer;
nsTObserverArray<PluginInstanceParent*> mCATimerTargets;
#endif
};
} // namespace plugins

View File

@ -55,11 +55,20 @@ CPPSRCS = \
gfxBlur.cpp \
$(NULL)
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CMMSRCS += \
nsCoreAnimationSupport.mm \
$(NULL)
OS_LIBS += -framework OpenGL -framework QuartzCore
endif
EXTRA_DSO_LIBS = gkgfx thebes
EXPORTS += \
gfxThebesUtils.h \
gfxBlur.h \
nsCoreAnimationSupport.h \
$(NULL)
EXTRA_DSO_LDOPTS += \

View File

@ -0,0 +1,113 @@
/* -*- 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):
* bgirard <b56girard@gmail.com>
*
* 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 ***** */
#ifndef nsCoreAnimationSupport_h__
#define nsCoreAnimationSupport_h__
#ifdef XP_MACOSX
#import "ApplicationServices/ApplicationServices.h"
#include "nscore.h"
#include "gfxTypes.h"
// Get the system color space.
CGColorSpaceRef CreateSystemColorSpace();
// Manages a CARenderer
struct _CGLPBufferObject;
struct _CGLContextObject;
class nsIOSurface;
class THEBES_API nsCARenderer {
public:
nsCARenderer() : mCARenderer(nsnull), mPixelBuffer(nsnull), mOpenGLContext(nsnull),
mCGImage(nsnull), mCGData(nsnull), mIOSurface(nsnull), mFBO(nsnull),
mIOTexture(nsnull) {}
~nsCARenderer();
nsresult SetupRenderer(void* aCALayer, int aWidth, int aHeight);
nsresult Render(int aWidth, int aHeight, CGImageRef *aOutCAImage);
bool isInit() { return mCARenderer != nsnull; }
/*
* Render the CALayer to an IOSurface. If no IOSurface
* is attached then an internal pixel buffer will be
* used.
*/
void AttachIOSurface(nsIOSurface *aSurface);
static nsresult DrawSurfaceToCGContext(CGContextRef aContext,
nsIOSurface *surf,
CGColorSpaceRef aColorSpace,
int aX, int aY,
int aWidth, int aHeight);
private:
void Destroy();
void *mCARenderer;
_CGLPBufferObject *mPixelBuffer;
_CGLContextObject *mOpenGLContext;
CGImageRef mCGImage;
void *mCGData;
nsIOSurface *mIOSurface;
uint32_t mFBO;
uint32_t mIOTexture;
};
typedef uint32_t IOSurfaceID;
class nsIOSurface {
public:
static nsIOSurface *CreateIOSurface(int aWidth, int aHeight);
static void ReleaseIOSurface(nsIOSurface *aIOSurface);
static nsIOSurface *LookupSurface(IOSurfaceID aSurfaceID);
nsIOSurface(CFTypeRef aIOSurfacePtr) : mIOSurfacePtr(aIOSurfacePtr) {}
~nsIOSurface() { CFRelease(mIOSurfacePtr); }
IOSurfaceID GetIOSurfaceID();
void *GetBaseAddress();
size_t GetWidth();
size_t GetHeight();
size_t GetBytesPerRow();
void Lock();
void Unlock();
private:
friend class nsCARenderer;
CFTypeRef mIOSurfacePtr;
};
#endif // XP_MACOSX
#endif // nsCoreAnimationSupport_h__

View File

@ -0,0 +1,722 @@
/* -*- 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 <b56girard@gmail.com>
*
* 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 <QuartzCore/QuartzCore.h>
#include <dlfcn.h>
#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;
}
nsIOSurface* 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;
nsIOSurface* ioSurface = new nsIOSurface(surfaceRef);
if (!ioSurface) {
::CFRelease(surfaceRef);
return nsnull;
}
return ioSurface;
}
nsIOSurface* nsIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID) {
if (!nsIOSurfaceLib::isInit())
return nsnull;
IOSurfacePtr surfaceRef = nsIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
if (!surfaceRef)
return nsnull;
// IOSurfaceLookup does not retain the object for us,
// we want IOSurfacePtr to remain for the lifetime of
// nsIOSurface.
CFRetain(surfaceRef);
nsIOSurface* ioSurface = new nsIOSurface(surfaceRef);
if (!ioSurface) {
::CFRelease(ioSurface);
return nsnull;
}
return ioSurface;
}
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);
}
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 (mPixelBuffer) {
::CGLDestroyPBuffer((CGLPBufferObj)mPixelBuffer);
}
if (mOpenGLContext) {
if (mFBO || mIOTexture) {
// Release these resources with the context that allocated them
CGLContextObj oldContext = ::CGLGetCurrentContext();
::CGLSetCurrentContext(mOpenGLContext);
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);
}
if (mIOSurface) {
delete mIOSurface;
}
// mCGData is deallocated by cgdata_release_callback
mCARenderer = nil;
mPixelBuffer = nsnull;
mOpenGLContext = nsnull;
mCGImage = nsnull;
mIOSurface = nsnull;
mFBO = nsnull;
mIOTexture = nsnull;
}
nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight) {
CALayer* layer = (CALayer*)aCALayer;
CARenderer* caRenderer = nsnull;
CGLPixelFormatAttribute attributes[] = {
kCGLPFANoRecovery,
kCGLPFAAccelerated,
kCGLPFAPBuffer,
kCGLPFADepthSize, (CGLPixelFormatAttribute)24,
(CGLPixelFormatAttribute)0
};
if (!mIOSurface) {
CGLError result = ::CGLCreatePBuffer(aWidth, aHeight,
GL_TEXTURE_2D, GL_RGBA, 0, &mPixelBuffer);
if (result != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
}
GLint screen;
CGLPixelFormatObj format;
if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
if (::CGLCreateContext(format, nsnull, &mOpenGLContext) != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
::CGLDestroyPixelFormat(format);
caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext
options:nil] retain];
mCARenderer = caRenderer;
if (caRenderer == nil) {
Destroy();
return NS_ERROR_FAILURE;
}
[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);
if (aWidth == 0 || aHeight == 0) {
// No need to allocate if size is 0
return NS_OK;
}
// We either target rendering to a CGImage or IOSurface.
if (!mIOSurface) {
mCGData = malloc(aWidth*aHeight*4);
if (!mCGData) {
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);
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) {
Destroy();
return NS_ERROR_FAILURE;
}
} else {
CGLContextObj oldContext = ::CGLGetCurrentContext();
::CGLSetCurrentContext(mOpenGLContext);
// 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);
// Create the fbo
::glGenFramebuffersEXT(1, &mFBO);
::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 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\n");
if (oldContext)
::CGLSetCurrentContext(oldContext);
Destroy();
return NS_ERROR_FAILURE;
}
if (oldContext)
::CGLSetCurrentContext(oldContext);
}
CGLContextObj oldContext = ::CGLGetCurrentContext();
::CGLSetCurrentContext(mOpenGLContext);
::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\n");
Destroy();
if (oldContext)
::CGLSetCurrentContext(oldContext);
return NS_ERROR_FAILURE;
}
if (oldContext)
::CGLSetCurrentContext(oldContext);
return NS_OK;
}
void nsCARenderer::AttachIOSurface(nsIOSurface *aSurface) {
if (mIOSurface &&
aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
delete aSurface;
return;
}
if (mCARenderer) {
// We are attaching a larger IOSurface, we need to
// resize our elements.
Destroy();
}
if (mIOSurface)
delete mIOSurface;
mIOSurface = aSurface;
}
nsresult nsCARenderer::Render(int aWidth, int aHeight,
CGImageRef *aOutCGImage) {
if (aOutCGImage && mIOSurface) {
NS_WARNING("CGImageRef should not be passed if we are "
"drawing to an IOSurface");
} 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) != NS_OK) {
return NS_ERROR_FAILURE;
}
caRenderer = (CARenderer*)mCARenderer;
}
CGLContextObj oldContext = ::CGLGetCurrentContext();
::CGLSetCurrentContext(mOpenGLContext);
if (!mIOSurface) {
::CGLSetPBuffer(mOpenGLContext, mPixelBuffer, 0, 0, 0);
}
GLenum result = ::glGetError();
if (result != GL_NO_ERROR) {
NS_ERROR("Unexpected OpenGL Error\n");
Destroy();
if (oldContext)
::CGLSetCurrentContext(oldContext);
return NS_ERROR_FAILURE;
}
::glClearColor(0.0, 0.0, 0.0, 0.0);
::glClear(GL_COLOR_BUFFER_BIT);
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,
int aWidth, int 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;
}
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;
}
::CGContextTranslateCTM(aContext, 0.0f, float(aHeight));
::CGContextScaleCTM(aContext, 1.0f, -1.0f);
::CGContextTranslateCTM(aContext, aX, -aY);
CGContextDrawImage(aContext, CGRectMake(0, 0, aWidth, aHeight), subImage);
::CGImageRelease(subImage);
::CGImageRelease(cgImage);
surf->Unlock();
return NS_OK;
}

View File

@ -69,7 +69,6 @@ EXPORTS = \
nsIScrollableFrame.h \
nsIStatefulFrame.h \
nsFrameSelection.h \
nsPluginUtilsOSX.h \
$(NULL)
ifdef IBMBIDI

View File

@ -164,6 +164,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#ifdef XP_MACOSX
#include "gfxQuartzNativeDrawing.h"
#include "nsPluginUtilsOSX.h"
#include "nsCoreAnimationSupport.h"
#endif
#ifdef MOZ_X11
@ -3591,13 +3592,24 @@ void nsPluginInstanceOwner::SetupCARenderer(int aWidth, int aHeight)
if (!caLayer) {
return;
}
mCARenderer.SetupRenderer(caLayer, aWidth, aHeight);
nsresult rt = mCARenderer.SetupRenderer(caLayer, aWidth, aHeight);
if (rt != NS_OK)
return;
AddToCARefreshTimer(this);
}
void nsPluginInstanceOwner::RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight)
{
mCARenderer.Render(aCGContext, aWidth, aHeight);
CGImageRef caImage = NULL;
nsresult rt = mCARenderer.Render(aWidth, aHeight, &caImage);
if (rt == NS_OK && caImage != NULL) {
// Significant speed up by resetting the scaling
::CGContextSetInterpolationQuality(aCGContext, kCGInterpolationNone );
::CGContextTranslateCTM(aCGContext, 0, aHeight);
::CGContextScaleCTM(aCGContext, 1.0, -1.0);
::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage);
}
}
void* nsPluginInstanceOwner::GetPluginPortCopy()

View File

@ -64,25 +64,3 @@ NPBool NS_NPAPI_ConvertPointCocoa(void* inView,
double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace);
// Get the system color space.
CGColorSpaceRef CreateSystemColorSpace();
// Manages a CARenderer
struct _CGLPBufferObject;
struct _CGLContextObject;
class nsCARenderer {
public:
nsCARenderer() : mCARenderer(NULL), mPixelBuffer(NULL), mOpenGLContext(NULL),
mCGImage(NULL), mCGData(NULL) {}
~nsCARenderer();
nsresult SetupRenderer(void* aCALayer, int aWidth, int aHeight);
nsresult Render(CGContextRef aCGContext, int aWidth, int aHeight);
private:
void Destroy();
void *mCARenderer;
_CGLPBufferObject *mPixelBuffer;
_CGLContextObject *mOpenGLContext;
CGImageRef mCGImage;
void *mCGData;
};

View File

@ -217,196 +217,3 @@ NPBool NS_NPAPI_ConvertPointCocoa(void* inView,
return PR_TRUE;
}
nsCARenderer::~nsCARenderer() {
Destroy();
}
CGColorSpaceRef CreateSystemColorSpace() {
CMProfileRef system_profile = NULL;
CGColorSpaceRef cspace = NULL;
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 = NULL;
[caRenderer release];
}
if (mPixelBuffer) {
::CGLDestroyPBuffer((CGLPBufferObj)mPixelBuffer);
}
if (mOpenGLContext) {
::CGLDestroyContext((CGLContextObj)mOpenGLContext);
}
if (mCGImage) {
::CGImageRelease(mCGImage);
}
// mCGData is deallocated by cgdata_release_callback
mCARenderer = nil;
mPixelBuffer = NULL;
mOpenGLContext = NULL;
mCGImage = NULL;
}
nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight) {
CALayer* layer = (CALayer*)aCALayer;
CARenderer* caRenderer = NULL;
CGLPixelFormatAttribute attributes[] = {
kCGLPFANoRecovery,
kCGLPFAAccelerated,
kCGLPFADepthSize, (CGLPixelFormatAttribute)24,
(CGLPixelFormatAttribute)0
};
CGLError result = ::CGLCreatePBuffer(aWidth, aHeight,
GL_TEXTURE_RECTANGLE_EXT, GL_RGBA, 0, &mPixelBuffer);
if (result != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
GLint screen;
CGLPixelFormatObj format;
if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
if (::CGLCreateContext(format, NULL, &mOpenGLContext) != kCGLNoError) {
Destroy();
return NS_ERROR_FAILURE;
}
::CGLDestroyPixelFormat(format);
caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext options:NULL] retain];
mCARenderer = caRenderer;
if (caRenderer == nil) {
Destroy();
return NS_ERROR_FAILURE;
}
[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);
mCGData = malloc(aWidth*aHeight*4);
if (!mCGData) {
Destroy();
}
CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(mCGData,
mCGData, aHeight*aWidth*4, cgdata_release_callback);
if (!dataProvider) {
cgdata_release_callback(mCGData, mCGData, aHeight*aWidth*4);
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) {
Destroy();
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult nsCARenderer::Render(CGContextRef aCGContext, int aWidth, int aHeight) {
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) != NPERR_NO_ERROR) {
return NS_ERROR_FAILURE;
}
caRenderer = (CARenderer*)mCARenderer;
}
GLint screen;
CGLContextObj oldContext = ::CGLGetCurrentContext();
::CGLSetCurrentContext(mOpenGLContext);
::CGLGetVirtualScreen(mOpenGLContext, &screen);
::CGLSetPBuffer(mOpenGLContext, mPixelBuffer, 0, 0, screen);
::glClearColor(0.0, 0.0, 0.0, 0.0);
::glViewport(0.0, 0.0, aWidth, aHeight);
::glMatrixMode(GL_PROJECTION);
::glLoadIdentity();
::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
::glClear(GL_COLOR_BUFFER_BIT);
// Render upside down to speed up CGContextDrawImage
::glTranslatef(0.0f, aHeight, 0.0);
::glScalef(1.0, -1.0, 1.0);
double caTime = ::CACurrentMediaTime();
[caRenderer beginFrameAtTime:caTime timeStamp:NULL];
[caRenderer addUpdateRect:CGRectMake(0, 0, aWidth, aHeight)];
[caRenderer render];
[caRenderer endFrame];
::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);
if (oldContext) {
::CGLSetCurrentContext(oldContext);
}
// Significant speed up by reseting the scaling
::CGContextSetInterpolationQuality( aCGContext, kCGInterpolationNone );
::CGContextTranslateCTM( aCGContext, 0, aHeight);
::CGContextScaleCTM( aCGContext, 1.0, -1.0);
CGRect drawRect;
drawRect.origin.x = 0;
drawRect.origin.y = 0;
drawRect.size.width = aWidth;
drawRect.size.height = aHeight;
::CGContextDrawImage( aCGContext, drawRect, mCGImage );
return NS_OK;
}