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:
272
gfx/layers/ImageLayers.h
Normal file
272
gfx/layers/ImageLayers.h
Normal 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 */
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
278
gfx/layers/basic/BasicImages.cpp
Normal file
278
gfx/layers/basic/BasicImages.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user