Bug 538323. Part 1: create ImageLayers and associated API for displaying pixel-buffers in various formats. r=jrmuizel,sr=dbaron

This commit is contained in:
Robert O'Callahan
2010-03-02 12:09:35 +13:00
parent d93a92019f
commit a5402c4dfd
7 changed files with 758 additions and 2 deletions

272
gfx/layers/ImageLayers.h Normal file
View File

@@ -0,0 +1,272 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 GFX_IMAGELAYER_H
#define GFX_IMAGELAYER_H
#include "Layers.h"
#include "gfxPattern.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace layers {
/**
* A class representing a buffer of pixel data. The data can be in one
* of various formats including YCbCr.
*
* Create an image using an ImageContainer. Fill the image with data, and
* then call ImageContainer::SetImage to display it. An image must not be
* modified after calling SetImage. Image implementations do not need to
* perform locking; when filling an Image, the Image client is responsible
* for ensuring only one thread accesses the Image at a time, and after
* SetImage the image is immutable.
*
* When resampling an Image, only pixels within the buffer should be
* sampled. For example, cairo images should be sampled in EXTEND_PAD mode.
*/
class THEBES_API Image {
THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(Image)
public:
virtual ~Image() {}
enum Format {
/**
* The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
* support this format, because the Ogg video decoder depends on it.
* The maximum image width and height is 16384.
*/
PLANAR_YCBCR,
/**
* The CAIRO_SURFACE format creates a CairoImage. All backends should
* support this format, because video rendering sometimes requires it.
*
* This format is useful even though a ThebesLayer could be used.
* It makes it easy to render a cairo surface when another Image format
* could be used. It can also avoid copying the surface data in some
* cases.
*
* Images in CAIRO_SURFACE format should only be created and
* manipulated on the main thread, since the underlying cairo surface
* is main-thread-only.
*/
CAIRO_SURFACE
};
Format GetFormat() { return mFormat; }
void* GetImplData() { return mImplData; }
protected:
Image(void* aImplData, Format aFormat) :
mImplData(aImplData),
mFormat(aFormat)
{}
void* mImplData;
Format mFormat;
};
/**
* A class that manages Images for an ImageLayer. The only reason
* we need a separate class here is that ImageLayers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to set the current Image from any thread, to facilitate
* video playback without involving the main thread, for example.
*/
class THEBES_API ImageContainer {
THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
public:
virtual ~ImageContainer() {}
/**
* Create an Image in one of the given formats.
* Picks the "best" format from the list and creates an Image of that
* format.
* Returns null if this backend does not support any of the formats.
*/
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats) = 0;
/**
* Set an Image as the current image to display. The Image must have
* been created by this ImageContainer.
*
* The Image data must not be modified after this method is called!
*/
virtual void SetCurrentImage(Image* aImage) = 0;
/**
* Get the current Image.
* This has to add a reference since otherwise there are race conditions
* where the current image is destroyed before the caller can add
* a reference.
*/
virtual already_AddRefed<Image> GetCurrentImage() = 0;
/**
* Get the current image as a gfxASurface. This is useful for fallback
* rendering.
* This can only be called from the main thread, since cairo objects
* can only be used from the main thread.
* This is defined here and not on Image because it's possible (likely)
* that some backends will make an Image "ready to draw" only when it
* becomes the current image for an image container.
* Returns null if there is no current image.
* Returns the size in aSize.
* The returned surface will never be modified. The caller must not
* modify it.
*/
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult) = 0;
/**
* Returns the layer manager for this container. This can only
* be used on the main thread, since layer managers should only be
* accessed on the main thread.
*/
LayerManager* Manager()
{
NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread");
return mManager;
}
protected:
LayerManager* mManager;
ImageContainer(LayerManager* aManager) : mManager(aManager) {}
};
/**
* A Layer which renders an Image.
*/
class THEBES_API ImageLayer : public Layer {
public:
/**
* CONSTRUCTION PHASE ONLY
* Set the ImageContainer. aContainer must have the same layer manager
* as this layer.
*/
void SetContainer(ImageContainer* aContainer) { mContainer = aContainer; }
/**
* CONSTRUCTION PHASE ONLY
* Set the filter used to resample this image if necessary.
*/
void SetFilter(gfxPattern::GraphicsFilter aFilter) { mFilter = aFilter; }
ImageContainer* GetContainer() { return mContainer; }
gfxPattern::GraphicsFilter GetFilter() { return mFilter; }
protected:
ImageLayer(LayerManager* aManager, void* aImplData)
: Layer(aManager, aImplData), mFilter(gfxPattern::FILTER_GOOD) {}
nsRefPtr<ImageContainer> mContainer;
gfxPattern::GraphicsFilter mFilter;
};
/****** Image subtypes for the different formats ******/
/**
* We assume that the image data is in the REC 470M color space (see
* Theora specification, section 4.3.1).
* XXX Eventually we should some color space parameter(s) here.
*/
class THEBES_API PlanarYCbCrImage : public Image {
public:
struct Data {
// Luminance buffer
PRUint8* mYChannel;
PRInt32 mYStride;
gfxIntSize mYSize;
// Chroma buffers
PRUint8* mCbChannel;
PRUint8* mCrChannel;
PRInt32 mCbCrStride;
gfxIntSize mCbCrSize;
};
typedef void (* ToARGBHook)(const Data& aData, PRUint8* aOutput);
/**
* XXX this is just a hack until we can get YCbCr conversion code into
* gfx
* This must be called before SetData().
*/
virtual void SetRGBConverter(ToARGBHook aHook) {}
/**
* This makes a copy of the data buffers.
* XXX Eventually we will change this to not make a copy of the data,
* but we can't do that until we have tighter control of nsOggDecoder's
* buffer management (i.e. not going through liboggplay). Right now
* it doesn't matter because the BasicLayer implementation does YCbCr
* conversion here anyway.
*/
virtual void SetData(const Data& aData) = 0;
protected:
PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {}
};
/**
* Currently, the data in a CairoImage surface is treated as being in the
* device output color space.
*/
class THEBES_API CairoImage : public Image {
public:
struct Data {
gfxASurface* mSurface;
gfxIntSize mSize;
};
/**
* This can only be called on the main thread. It may add a reference
* to the surface (which will eventually be released on the main thread).
* The surface must not be modified after this call!!!
*/
virtual void SetData(const Data& aData) = 0;
protected:
CairoImage(void* aImplData) : Image(aImplData, CAIRO_SURFACE) {}
};
}
}
#endif /* GFX_IMAGELAYER_H */

View File

@@ -53,6 +53,8 @@ namespace layers {
class Layer;
class ThebesLayer;
class ContainerLayer;
class ImageLayer;
class ImageContainer;
/*
* Motivation: For truly smooth animation and video playback, we need to
@@ -150,6 +152,16 @@ public:
* Create a ContainerLayer for this manager's layer tree.
*/
virtual already_AddRefed<ContainerLayer> CreateContainerLayer() = 0;
/**
* CONSTRUCTION PHASE ONLY
* Create an ImageLayer for this manager's layer tree.
*/
virtual already_AddRefed<ImageLayer> CreateImageLayer() = 0;
/**
* Can be called anytime
*/
virtual already_AddRefed<ImageContainer> CreateImageContainer() = 0;
};
/**

View File

@@ -53,11 +53,13 @@ LIBXUL_LIBRARY = 1
DEFINES += -DIMPL_THEBES
EXPORTS = \
Layers.h \
BasicLayers.h \
ImageLayers.h \
Layers.h \
$(NULL)
CPPSRCS = \
BasicImages.cpp \
BasicLayers.cpp \
$(NULL)
@@ -75,3 +77,5 @@ EXTRA_DSO_LDOPTS += \
$(NULL)
include $(topsrcdir)/config/rules.mk
CXXFLAGS += $(MOZ_CAIRO_CFLAGS)

View File

@@ -0,0 +1,278 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 "mozilla/Monitor.h"
#include "ImageLayers.h"
#include "BasicLayers.h"
#include "gfxImageSurface.h"
#ifdef XP_MACOSX
#include "gfxQuartzImageSurface.h"
#endif
#include "cairo.h"
using mozilla::Monitor;
namespace mozilla {
namespace layers {
/**
* All our images can yield up a cairo surface and their size.
*/
class BasicImageImplData {
public:
/**
* This must be called on the main thread.
*/
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
gfxIntSize GetSize() { return mSize; }
protected:
gfxIntSize mSize;
};
/**
* Since BasicLayers only paint on the main thread, handling a CairoImage
* is extremely simple. We just hang on to a reference to the surface and
* return that surface when BasicImageLayer::Paint asks for it via
* BasicImageContainer::GetAsSurface.
*/
class BasicCairoImage : public CairoImage, public BasicImageImplData {
public:
BasicCairoImage() : CairoImage(static_cast<BasicImageImplData*>(this)) {}
virtual void SetData(const Data& aData)
{
mSurface = aData.mSurface;
mSize = aData.mSize;
}
virtual already_AddRefed<gfxASurface> GetAsSurface()
{
NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
nsRefPtr<gfxASurface> surface = mSurface.get();
return surface.forget();
}
protected:
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
};
/**
* We handle YCbCr by converting to RGB when the image is initialized
* (which should be done off the main thread). The RGB results are stored
* in a memory buffer and converted to a cairo surface lazily.
*/
class BasicPlanarYCbCrImage : public PlanarYCbCrImage, public BasicImageImplData {
public:
BasicPlanarYCbCrImage() :
PlanarYCbCrImage(static_cast<BasicImageImplData*>(this)),
mToARGB(nsnull)
{}
virtual void SetRGBConverter(ToARGBHook aHook) { mToARGB = aHook; }
virtual void SetData(const Data& aData);
virtual already_AddRefed<gfxASurface> GetAsSurface();
protected:
ToARGBHook mToARGB;
nsAutoArrayPtr<PRUint8> mBuffer;
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
};
void
BasicPlanarYCbCrImage::SetData(const Data& aData)
{
// Do some sanity checks to prevent integer overflow
if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) {
NS_ERROR("Illegal width or height");
return;
}
size_t size = aData.mYSize.width*aData.mYSize.height*4;
mBuffer = new PRUint8[size];
if (!mBuffer) {
// out of memory
return;
}
// Convert from YCbCr to RGB now
mToARGB(aData, mBuffer);
mSize = aData.mYSize;
}
static cairo_user_data_key_t imageSurfaceDataKey;
static void
DestroyBuffer(void* aBuffer)
{
delete[] static_cast<PRUint8*>(aBuffer);
}
already_AddRefed<gfxASurface>
BasicPlanarYCbCrImage::GetAsSurface()
{
NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
if (mSurface) {
nsRefPtr<gfxASurface> result = mSurface.get();
return result.forget();
}
if (!mBuffer) {
return nsnull;
}
nsRefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface(mBuffer, mSize,
mSize.width * 4,
gfxASurface::ImageFormatRGB24);
if (!imgSurface) {
return nsnull;
}
// Pass ownership of the buffer to the surface
imgSurface->SetData(&imageSurfaceDataKey, mBuffer.forget(), DestroyBuffer);
nsRefPtr<gfxASurface> result = imgSurface.get();
#if defined(XP_MACOSX)
nsRefPtr<gfxQuartzImageSurface> quartzSurface =
new gfxQuartzImageSurface(imgSurface);
if (quartzSurface) {
result = quartzSurface.forget();
}
#endif
mSurface = result.get();
return result.forget();
}
/**
* Our image container is very simple. It's really just a factory
* for the image objects. We use a Monitor to synchronize access to
* mImage.
*/
class BasicImageContainer : public ImageContainer {
public:
BasicImageContainer(BasicLayerManager* aManager) :
ImageContainer(aManager), mMonitor("BasicImageContainer")
{}
virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats);
virtual void SetCurrentImage(Image* aImage);
virtual already_AddRefed<Image> GetCurrentImage();
virtual already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSize);
protected:
Monitor mMonitor;
nsRefPtr<Image> mImage;
};
/**
* Returns true if aFormat is in the given format array.
*/
static PRBool
FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
Image::Format aFormat)
{
for (PRUint32 i = 0; i < aNumFormats; ++i) {
if (aFormats[i] == aFormat) {
return PR_TRUE;
}
}
return PR_FALSE;
}
already_AddRefed<Image>
BasicImageContainer::CreateImage(const Image::Format* aFormats,
PRUint32 aNumFormats)
{
nsRefPtr<Image> image;
// Prefer cairo surfaces because they're native for us
if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
image = new BasicCairoImage();
} else if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
image = new BasicPlanarYCbCrImage();
}
return image.forget();
}
void
BasicImageContainer::SetCurrentImage(Image* aImage)
{
MonitorAutoEnter mon(mMonitor);
mImage = aImage;
}
already_AddRefed<Image>
BasicImageContainer::GetCurrentImage()
{
MonitorAutoEnter mon(mMonitor);
nsRefPtr<Image> image = mImage;
return image.forget();
}
static BasicImageImplData*
ToImageData(Image* aImage)
{
return static_cast<BasicImageImplData*>(aImage->GetImplData());
}
already_AddRefed<gfxASurface>
BasicImageContainer::GetCurrentAsSurface(gfxIntSize* aSizeResult)
{
NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread");
MonitorAutoEnter mon(mMonitor);
if (!mImage) {
return nsnull;
}
*aSizeResult = ToImageData(mImage)->GetSize();
return ToImageData(mImage)->GetAsSurface();
}
already_AddRefed<ImageContainer>
BasicLayerManager::CreateImageContainer()
{
nsRefPtr<ImageContainer> container = new BasicImageContainer(this);
return container.forget();
}
}
}

View File

@@ -36,9 +36,14 @@
* ***** END LICENSE BLOCK ***** */
#include "BasicLayers.h"
#include "ImageLayers.h"
#include "nsTArray.h"
#include "nsGUIEvent.h"
#include "nsIRenderingContext.h"
#include "gfxContext.h"
#include "gfxASurface.h"
#include "gfxPattern.h"
namespace mozilla {
namespace layers {
@@ -47,7 +52,25 @@ class BasicContainerLayer;
/**
* This is the ImplData for all Basic layers. It also exposes methods
* private to the Basic implementation.
* private to the Basic implementation that are common to all Basic layer types.
* In particular, there is an internal Paint() method that we can use
* to paint the contents of non-Thebes layers.
*
* The class hierarchy for Basic layers is like this:
* BasicImplData
* Layer | | |
* | | | |
* +-> ContainerLayer | | |
* | | | | |
* | +-> BasicContainerLayer <--+ | |
* | | |
* +-> ThebesLayer | |
* | | | |
* | +-> BasicThebesLayer <---------+ |
* | |
* +-> ImageLayer |
* | |
* +-> BasicImageLayer <--------------+
*/
class BasicImplData {
public:
@@ -62,6 +85,14 @@ public:
const nsIntRegion& GetVisibleRegion() { return mVisibleRegion; }
/**
* Layers that paint themselves, such as ImageLayers, should paint
* in response to this method call. aContext will already have been
* set up to account for all the properties of the layer (transform,
* opacity, etc).
*/
virtual void Paint(gfxContext* aContext) {}
protected:
nsIntRegion mVisibleRegion;
};
@@ -261,6 +292,76 @@ BasicThebesLayer::CopyFrom(ThebesLayer* aSource,
// so no need to mark anything invalid.
}
class BasicImageLayer : public ImageLayer, BasicImplData {
public:
BasicImageLayer(BasicLayerManager* aLayerManager) :
ImageLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicImageLayer);
}
virtual ~BasicImageLayer()
{
MOZ_COUNT_DTOR(BasicImageLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
mVisibleRegion = aRegion;
}
virtual void Paint(gfxContext* aContext);
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
};
void
BasicImageLayer::Paint(gfxContext* aContext)
{
if (!mContainer)
return;
gfxIntSize size;
nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&size);
if (!surface) {
return;
}
nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
if (!pat) {
return;
}
pat->SetFilter(mFilter);
// Set PAD mode so that when the video is being scaled, we do not sample
// outside the bounds of the video image.
gfxPattern::GraphicsExtend extend = gfxPattern::EXTEND_PAD;
// PAD is slow with X11 and Quartz surfaces, so prefer speed over correctness
// and use NONE.
nsRefPtr<gfxASurface> target = aContext->CurrentSurface();
gfxASurface::gfxSurfaceType type = target->GetType();
if (type == gfxASurface::SurfaceTypeXlib ||
type == gfxASurface::SurfaceTypeXcb ||
type == gfxASurface::SurfaceTypeQuartz) {
extend = gfxPattern::EXTEND_NONE;
}
pat->SetExtend(extend);
/* Draw RGB surface onto frame */
aContext->NewPath();
aContext->PixelSnappedRectangleAndSetPattern(
gfxRect(0, 0, size.width, size.height), pat);
aContext->Fill();
}
BasicLayerManager::BasicLayerManager(gfxContext* aContext) :
mDefaultTarget(aContext), mLastPainted(nsnull)
#ifdef DEBUG
@@ -419,6 +520,10 @@ BasicLayerManager::BeginPaintingLayer(Layer* aLayer)
}
mLastPainted = aLayer;
// For layers that paint themselves (e.g., BasicImageLayer), paint
// them now.
ToData(aLayer)->Paint(mTarget);
}
void
@@ -490,6 +595,14 @@ BasicLayerManager::CreateContainerLayer()
return layer.forget();
}
already_AddRefed<ImageLayer>
BasicLayerManager::CreateImageLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ImageLayer> layer = new BasicImageLayer(this);
return layer.forget();
}
#ifdef DEBUG
static void
AppendAncestors(Layer* aLayer, nsTArray<Layer*>* aAncestors)

View File

@@ -41,6 +41,8 @@
#include "Layers.h"
#include "gfxContext.h"
#include "nsAutoRef.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace layers {
@@ -83,6 +85,8 @@ public:
virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
virtual already_AddRefed<ImageLayer> CreateImageLayer();
virtual already_AddRefed<ImageContainer> CreateImageContainer();
#ifdef DEBUG
PRBool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
@@ -127,4 +131,54 @@ private:
}
}
/**
* We need to be able to hold a reference to a gfxASurface from Image
* subclasses. This is potentially a problem since Images can be addrefed
* or released off the main thread. We can ensure that we never AddRef
* a gfxASurface off the main thread, but we might want to Release due
* to an Image being destroyed off the main thread.
*
* We use nsCountedRef<nsMainThreadSurfaceRef> to reference the
* gfxASurface. When AddRefing, we assert that we're on the main thread.
* When Releasing, if we're not on the main thread, we post an event to
* the main thread to do the actual release.
*/
class nsMainThreadSurfaceRef;
NS_SPECIALIZE_TEMPLATE
class nsAutoRefTraits<nsMainThreadSurfaceRef> {
public:
typedef gfxASurface* RawRef;
/**
* The XPCOM event that will do the actual release on the main thread.
*/
class SurfaceReleaser : public nsRunnable {
public:
SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
NS_IMETHOD Run() {
mRef->Release();
return NS_OK;
}
RawRef mRef;
};
static RawRef Void() { return nsnull; }
static void Release(RawRef aRawRef)
{
if (NS_IsMainThread()) {
aRawRef->Release();
return;
}
nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
NS_DispatchToMainThread(runnable);
}
static void AddRef(RawRef aRawRef)
{
NS_ASSERTION(NS_IsMainThread(),
"Can only add a reference on the main thread");
aRawRef->AddRef();
}
};
#endif /* GFX_BASICLAYERS_H */

View File

@@ -120,4 +120,27 @@ protected: \
nsAutoRefCnt mRefCnt; \
public:
#define THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(_class) \
public: \
nsrefcnt AddRef(void) { \
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); \
nsrefcnt count = PR_AtomicIncrement((PRInt32*)&mRefCnt); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
return count; \
} \
nsrefcnt Release(void) { \
NS_PRECONDITION(0 != mRefCnt, "dup release"); \
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
mRefCnt = 1; /* stabilize */ \
NS_DELETEXPCOM(this); \
return 0; \
} \
return count; \
} \
protected: \
nsAutoRefCnt mRefCnt; \
public:
#endif /* GFX_TYPES_H */