gecko-dev/gfx/layers/ImageContainer.h

973 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_IMAGECONTAINER_H
#define GFX_IMAGECONTAINER_H
#include <stdint.h> // for uint32_t, uint8_t, uint64_t
#include <sys/types.h> // for int32_t
#include "ImageTypes.h" // for ImageFormat, etc
#include "gfxASurface.h" // for gfxASurface, etc
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/Mutex.h" // for Mutex
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "mozilla/gfx/Point.h" // For IntSize
#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc
#include "nsAutoRef.h" // for nsCountedRef
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for Image::Release, etc
#include "nsRect.h" // for nsIntRect
#include "nsSize.h" // for nsIntSize
#include "nsTArray.h" // for nsTArray
#include "mozilla/Atomics.h"
#include "nsThreadUtils.h"
#include "mozilla/gfx/2D.h"
#ifndef XPCOM_GLUE_AVOID_NSPR
/**
* 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.
*
* This should be removed after after Image::DeprecatedGetAsSurface is
* removed. It is replaced by nsMainThreadSourceSurfaceRef
*/
class nsMainThreadSurfaceRef;
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 nullptr; }
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();
}
};
/**
* Same purpose as nsMainThreadSurfaceRef byt holds a gfx::SourceSurface instead.
* The specialization of nsMainThreadSurfaceRef should be removed after
* Image::DeprecatedGetAsSurface is removed
*/
class nsMainThreadSourceSurfaceRef;
template <>
class nsAutoRefTraits<nsMainThreadSourceSurfaceRef> {
public:
typedef mozilla::gfx::SourceSurface* 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 nullptr; }
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
#ifdef XP_WIN
struct ID3D10Texture2D;
struct ID3D10Device;
struct ID3D10ShaderResourceView;
#endif
typedef void* HANDLE;
namespace mozilla {
class CrossProcessMutex;
namespace layers {
class ImageClient;
class SharedPlanarYCbCrImage;
class DeprecatedSharedPlanarYCbCrImage;
class TextureClient;
class SurfaceDescriptor;
struct ImageBackendData
{
virtual ~ImageBackendData() {}
protected:
ImageBackendData() {}
};
// sadly we'll need this until we get rid of Deprected image classes
class ISharedImage {
public:
virtual uint8_t* GetBuffer() = 0;
/**
* For use with the CompositableClient only (so that the later can
* synchronize the TextureClient with the TextureHost).
*/
virtual TextureClient* GetTextureClient() = 0;
};
/**
* 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 Image {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Image)
public:
virtual ~Image() {}
virtual ISharedImage* AsSharedImage() { return nullptr; }
ImageFormat GetFormat() { return mFormat; }
void* GetImplData() { return mImplData; }
virtual already_AddRefed<gfxASurface> DeprecatedGetAsSurface() = 0;
virtual gfx::IntSize GetSize() = 0;
virtual nsIntRect GetPictureRect()
{
return nsIntRect(0, 0, GetSize().width, GetSize().height);
}
ImageBackendData* GetBackendData(LayersBackend aBackend)
{ return mBackendData[size_t(aBackend)]; }
void SetBackendData(LayersBackend aBackend, ImageBackendData* aData)
{ mBackendData[size_t(aBackend)] = aData; }
int32_t GetSerial() { return mSerial; }
void MarkSent() { mSent = true; }
bool IsSentToCompositor() { return mSent; }
virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface();
protected:
Image(void* aImplData, ImageFormat aFormat) :
mImplData(aImplData),
mSerial(++sSerialCounter),
mFormat(aFormat),
mSent(false)
{}
nsAutoPtr<ImageBackendData> mBackendData[size_t(mozilla::layers::LayersBackend::LAYERS_LAST)];
void* mImplData;
int32_t mSerial;
ImageFormat mFormat;
static mozilla::Atomic<int32_t> sSerialCounter;
bool mSent;
};
/**
* A RecycleBin is owned by an ImageContainer. We store buffers in it that we
* want to recycle from one image to the next.It's a separate object from
* ImageContainer because images need to store a strong ref to their RecycleBin
* and we must avoid creating a reference loop between an ImageContainer and
* its active image.
*/
class BufferRecycleBin {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin)
//typedef mozilla::gl::GLContext GLContext;
public:
BufferRecycleBin();
void RecycleBuffer(uint8_t* aBuffer, uint32_t aSize);
// Returns a recycled buffer of the right size, or allocates a new buffer.
uint8_t* GetBuffer(uint32_t aSize);
private:
typedef mozilla::Mutex Mutex;
// This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
// and mRecycledTextureSizes
Mutex mLock;
// We should probably do something to prune this list on a timer so we don't
// eat excess memory while video is paused...
nsTArray<nsAutoArrayPtr<uint8_t> > mRecycledBuffers;
// This is only valid if mRecycledBuffers is non-empty
uint32_t mRecycledBufferSize;
};
class CompositionNotifySink
{
public:
virtual void DidComposite() = 0;
virtual ~CompositionNotifySink() {}
};
/**
* A class that manages Image creation for a LayerManager. The only reason
* we need a separate class here is that LayerManagers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to create images from any thread, to facilitate video playback
* without involving the main thread, for example.
* Different layer managers can implement child classes of this making it
* possible to create layer manager specific images.
* This class is not meant to be used directly but rather can be set on an
* image container. This is usually done by the layer system internally and
* not explicitly by users. For PlanarYCbCr or Cairo images the default
* implementation will creates images whose data lives in system memory, for
* MacIOSurfaces the default implementation will be a simple MacIOSurface
* wrapper.
*/
class ImageFactory
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
protected:
friend class ImageContainer;
ImageFactory() {}
virtual ~ImageFactory() {}
virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat,
const gfx::IntSize &aScaleHint,
BufferRecycleBin *aRecycleBin);
};
/**
* This struct is used to store RemoteImages, it is meant to be able to live in
* shared memory. Therefor it should not contain a vtable pointer. Remote
* users can manipulate the data in this structure to specify what image is to
* be drawn by the container. When accessing this data users should make sure
* the mutex synchronizing access to the structure is held!
*/
struct RemoteImageData {
enum Type {
/**
* This is a format that uses raw bitmap data.
*/
RAW_BITMAP,
/**
* This is a format that uses a pointer to a texture do draw directly
* from a shared texture. Any process may have created this texture handle,
* the process creating the texture handle is responsible for managing it's
* lifetime by managing the lifetime of the first D3D texture object this
* handle was created for. It must also ensure the handle is not set
* current anywhere when the last reference to this object is released.
*/
DXGI_TEXTURE_HANDLE
};
/* These formats describe the format in the memory byte-order */
enum Format {
/* 8 bits per channel */
BGRA32,
/* 8 bits per channel, alpha channel is ignored */
BGRX32
};
// This should be set to true if a change was made so that the ImageContainer
// knows to throw out any cached RemoteImage objects.
bool mWasUpdated;
Type mType;
Format mFormat;
gfx::IntSize mSize;
union {
struct {
/* This pointer is set by a remote process, however it will be set to
* the container process' address the memory of the raw bitmap resides
* at.
*/
unsigned char *mData;
int mStride;
} mBitmap;
#ifdef XP_WIN
HANDLE mTextureHandle;
#endif
};
};
/**
* 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.
*
* An ImageContainer can operate in one of three modes:
* 1) Normal. Triggered by constructing the ImageContainer with
* DISABLE_ASYNC or when compositing is happening on the main thread.
* SetCurrentImage changes ImageContainer state but nothing is sent to the
* compositor until the next layer transaction.
* 2) Asynchronous. Initiated by constructing the ImageContainer with
* ENABLE_ASYNC when compositing is happening on the main thread.
* SetCurrentImage sends a message through the ImageBridge to the compositor
* thread to update the image, without going through the main thread or
* a layer transaction.
* 3) Remote. Initiated by calling SetRemoteImageData on the ImageContainer
* before any other activity.
* The ImageContainer uses a shared memory block containing a cross-process mutex
* to communicate with the compositor thread. SetCurrentImage synchronously
* updates the shared state to point to the new image and the old image
* is immediately released (not true in Normal or Asynchronous modes).
*/
class ImageContainer {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
public:
enum { DISABLE_ASYNC = 0x0, ENABLE_ASYNC = 0x01 };
ImageContainer(int flag = 0);
~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.
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
already_AddRefed<Image> CreateImage(ImageFormat aFormat);
/**
* Set an Image as the current image to display. The Image must have
* been created by this ImageContainer.
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
* aImage can be null. While it's null, nothing will be painted.
*
* The Image data must not be modified after this method is called!
* Note that this must not be called if ENABLE_ASYNC has not been set.
*
* Implementations must call CurrentImageChanged() while holding
* mReentrantMonitor.
*
* If this ImageContainer has an ImageClient for async video:
* Schelude a task to send the image to the compositor using the
* PImageBridge protcol without using the main thread.
*/
void SetCurrentImage(Image* aImage);
/**
* Clear all images. Let ImageClient release all TextureClients.
*/
void ClearAllImages();
/**
* Clear all images except current one.
* Let ImageClient release all TextureClients except front one.
*/
void ClearAllImagesExceptFront();
/**
* Clear the current image.
* This function is expect to be called only from a CompositableClient
* that belongs to ImageBridgeChild. Created to prevent dead lock.
* See Bug 901224.
*/
void ClearCurrentImage();
/**
* Set an Image as the current image to display. The Image must have
* been created by this ImageContainer.
* Must be called on the main thread, within a layers transaction.
*
* This method takes mReentrantMonitor
* when accessing thread-shared state.
* aImage can be null. While it's null, nothing will be painted.
*
* The Image data must not be modified after this method is called!
* Note that this must not be called if ENABLE_ASYNC been set.
*
* Implementations must call CurrentImageChanged() while holding
* mReentrantMonitor.
*/
void SetCurrentImageInTransaction(Image* aImage);
/**
* Returns true if this ImageContainer uses the ImageBridge IPDL protocol.
*
* Can be called from any thread.
*/
bool IsAsync() const;
/**
* If this ImageContainer uses ImageBridge, returns the ID associated to
* this container, for use in the ImageBridge protocol.
* Returns 0 if this ImageContainer does not use ImageBridge. Note that
* 0 is always an invalid ID for asynchronous image containers.
*
* Can be called from any thread.
*/
uint64_t GetAsyncContainerID() const;
/**
* Returns if the container currently has an image.
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
bool HasCurrentImage();
/**
* Lock 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. This lock strictly guarantees the underlying image remains
* valid, it does not mean the current image cannot change.
* Can be called on any thread. This method will lock the cross-process
* mutex to ensure remote processes cannot alter underlying data. This call
* -must- be balanced by a call to UnlockCurrentImage and users should avoid
* holding the image locked for a long time.
*/
already_AddRefed<Image> LockCurrentImage();
/**
* This call unlocks the image. For remote images releasing the cross-process
* mutex.
*/
void UnlockCurrentImage();
/**
* 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.
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
* If the current image is a remote image, that is, if it is an image that
* may be shared accross processes, calling this function will make
* a copy of the image data while holding the mRemoteDataMutex. If possible,
* the lock methods should be used to avoid the copy, however this should be
* avoided if the surface is required for a long period of time.
*/
already_AddRefed<gfxASurface> DeprecatedGetCurrentAsSurface(gfx::IntSize* aSizeResult);
/**
* This is similar to GetCurrentAsSurface, however this does not make a copy
* of the image data and requires the user to call UnlockCurrentImage when
* done with the image data. Once UnlockCurrentImage has been called the
* surface returned by this function is no longer valid! This works for any
* type of image. Optionally a pointer can be passed to receive the current
* image.
*/
already_AddRefed<gfxASurface> DeprecatedLockCurrentAsSurface(gfx::IntSize* aSizeResult,
Image** aCurrentImage = nullptr);
/**
* Same as GetCurrentAsSurface but for Moz2D
*/
TemporaryRef<gfx::SourceSurface> GetCurrentAsSourceSurface(gfx::IntSize* aSizeResult);
/**
* Same as LockCurrentAsSurface but for Moz2D
*/
TemporaryRef<gfx::SourceSurface> LockCurrentAsSourceSurface(gfx::IntSize* aSizeResult,
Image** aCurrentImage = nullptr);
/**
* Returns the size of the image in pixels.
* Can be called on any thread. This method takes mReentrantMonitor when accessing
* thread-shared state.
*/
gfx::IntSize GetCurrentSize();
/**
* Sets a size that the image is expected to be rendered at.
* This is a hint for image backends to optimize scaling.
* Default implementation in this class is to ignore the hint.
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
void SetScaleHint(const gfx::IntSize& aScaleHint)
{ mScaleHint = aScaleHint; }
void SetImageFactory(ImageFactory *aFactory)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mImageFactory = aFactory ? aFactory : new ImageFactory();
}
/**
* Returns the time at which the currently contained image was first
* painted. This is reset every time a new image is set as the current
* image. Note this may return a null timestamp if the current image
* has not yet been painted. Can be called from any thread.
*/
TimeStamp GetPaintTime() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mPaintTime;
}
/**
* Returns the number of images which have been contained in this container
* and painted at least once. Can be called from any thread.
*/
uint32_t GetPaintCount() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mPaintCount;
}
/**
* Resets the paint count to zero.
* Can be called from any thread.
*/
void ResetPaintCount() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mPaintCount = 0;
}
/**
* Increments mPaintCount if this is the first time aPainted has been
* painted, and sets mPaintTime if the painted image is the current image.
* current image. Can be called from any thread.
*/
void NotifyPaintedImage(Image* aPainted) {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<Image> current = mActiveImage;
if (aPainted == current) {
if (mPaintTime.IsNull()) {
mPaintTime = TimeStamp::Now();
mPaintCount++;
}
} else if (!mPreviousImagePainted) {
// While we were painting this image, the current image changed. We
// still must count it as painted, but can't set mPaintTime, since we're
// no longer the current image.
mPaintCount++;
mPreviousImagePainted = true;
}
if (mCompositionNotifySink) {
mCompositionNotifySink->DidComposite();
}
}
void SetCompositionNotifySink(CompositionNotifySink *aSink) {
mCompositionNotifySink = aSink;
}
/**
* This function is called to tell the ImageContainer where the
* (cross-process) segment lives where the shared data about possible
* remote images are stored. In addition to this a CrossProcessMutex object
* is passed telling the container how to synchronize access to this data.
* NOTE: This should be called during setup of the container and not after
* usage has started.
*/
void SetRemoteImageData(RemoteImageData *aRemoteData,
CrossProcessMutex *aRemoteDataMutex);
/**
* This can be used to check if the container has RemoteData set.
*/
RemoteImageData *GetRemoteImageData() { return mRemoteData; }
protected:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
void SetCurrentImageInternal(Image* aImage);
// This is called to ensure we have an active image, this may not be true
// when we're storing image information in a RemoteImageData structure.
// NOTE: If we have remote data mRemoteDataMutex should be locked when
// calling this function!
void EnsureActiveImage();
// ReentrantMonitor to protect thread safe access to the "current
// image", and any other state which is shared between threads.
ReentrantMonitor mReentrantMonitor;
// Performs necessary housekeeping to ensure the painted frame statistics
// are accurate. Must be called by SetCurrentImage() implementations with
// mReentrantMonitor held.
void CurrentImageChanged() {
mReentrantMonitor.AssertCurrentThreadIn();
mPreviousImagePainted = !mPaintTime.IsNull();
mPaintTime = TimeStamp();
}
nsRefPtr<Image> mActiveImage;
// Number of contained images that have been painted at least once. It's up
// to the ImageContainer implementation to ensure accesses to this are
// threadsafe.
uint32_t mPaintCount;
// Time stamp at which the current image was first painted. It's up to the
// ImageContainer implementation to ensure accesses to this are threadsafe.
TimeStamp mPaintTime;
// Denotes whether the previous image was painted.
bool mPreviousImagePainted;
// This is the image factory used by this container, layer managers using
// this container can set an alternative image factory that will be used to
// create images for this container.
nsRefPtr<ImageFactory> mImageFactory;
gfx::IntSize mScaleHint;
nsRefPtr<BufferRecycleBin> mRecycleBin;
// This contains the remote image data for this container, if this is nullptr
// that means the container has no other process that may control its active
// image.
RemoteImageData *mRemoteData;
// This cross-process mutex is used to synchronise access to mRemoteData.
// When this mutex is held, we will always be inside the mReentrantMonitor
// however the same is not true vice versa.
CrossProcessMutex *mRemoteDataMutex;
CompositionNotifySink *mCompositionNotifySink;
// This member points to an ImageClient if this ImageContainer was
// sucessfully created with ENABLE_ASYNC, or points to null otherwise.
// 'unsuccessful' in this case only means that the ImageClient could not
// be created, most likely because off-main-thread compositing is not enabled.
// In this case the ImageContainer is perfectly usable, but it will forward
// frames to the compositor through transactions in the main thread rather than
// asynchronusly using the ImageBridge IPDL protocol.
ImageClient* mImageClient;
};
class AutoLockImage
{
public:
AutoLockImage(ImageContainer *aContainer) : mContainer(aContainer) { mImage = mContainer->LockCurrentImage(); }
AutoLockImage(ImageContainer *aContainer, gfxASurface **aSurface) : mContainer(aContainer) {
*aSurface = mContainer->DeprecatedLockCurrentAsSurface(&mSize, getter_AddRefs(mImage)).get();
}
~AutoLockImage() { if (mContainer) { mContainer->UnlockCurrentImage(); } }
Image* GetImage() { return mImage; }
const gfx::IntSize &GetSize() { return mSize; }
void Unlock() {
if (mContainer) {
mImage = nullptr;
mContainer->UnlockCurrentImage();
mContainer = nullptr;
}
}
/** Things get a little tricky here, because our underlying image can -still-
* change, and OS X requires a complicated callback mechanism to update this
* we need to support staying the lock and getting the new image in a proper
* way. This method makes any images retrieved with GetImage invalid!
*/
void Refresh() {
if (mContainer) {
mContainer->UnlockCurrentImage();
mImage = mContainer->LockCurrentImage();
}
}
private:
ImageContainer *mContainer;
nsRefPtr<Image> mImage;
gfx::IntSize mSize;
};
struct PlanarYCbCrData {
// Luminance buffer
uint8_t* mYChannel;
int32_t mYStride;
gfx::IntSize mYSize;
int32_t mYSkip;
// Chroma buffers
uint8_t* mCbChannel;
uint8_t* mCrChannel;
int32_t mCbCrStride;
gfx::IntSize mCbCrSize;
int32_t mCbSkip;
int32_t mCrSkip;
// Picture region
uint32_t mPicX;
uint32_t mPicY;
gfx::IntSize mPicSize;
StereoMode mStereoMode;
nsIntRect GetPictureRect() const {
return nsIntRect(mPicX, mPicY,
mPicSize.width,
mPicSize.height);
}
PlanarYCbCrData()
: mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
, mCbChannel(nullptr), mCrChannel(nullptr)
, mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
, mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
{}
};
/****** 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).
*
* The YCbCr format can be:
*
* 4:4:4 - CbCr width/height are the same as Y.
* 4:2:2 - CbCr width is half that of Y. Height is the same.
* 4:2:0 - CbCr width and height is half that of Y.
*
* The color format is detected based on the height/width ratios
* defined above.
*
* The Image that is rendered is the picture region defined by
* mPicX, mPicY and mPicSize. The size of the rendered image is
* mPicSize, not mYSize or mCbCrSize.
*
* mYSkip, mCbSkip, mCrSkip are added to support various output
* formats from hardware decoder. They are per-pixel skips in the
* source image.
*
* For example when image width is 640, mYStride is 670, mYSkip is 3,
* the mYChannel buffer looks like:
*
* |<----------------------- mYStride ----------------------------->|
* |<----------------- mYSize.width --------------->|
* 0 3 6 9 12 15 18 21 659 669
* |----------------------------------------------------------------|
* |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
* |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
* |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
* | |<->|
* mYSkip
*/
class PlanarYCbCrImage : public Image {
public:
typedef PlanarYCbCrData Data;
enum {
MAX_DIMENSION = 16384
};
virtual ~PlanarYCbCrImage();
/**
* This makes a copy of the data buffers, in order to support functioning
* in all different layer managers.
*/
virtual void SetData(const Data& aData);
/**
* This doesn't make a copy of the data buffers. Can be used when mBuffer is
* pre allocated with AllocateAndGetNewBuffer(size) and then SetDataNoCopy is
* called to only update the picture size, planes etc. fields in mData.
* The GStreamer media backend uses this to decode into PlanarYCbCrImage(s)
* directly.
*/
virtual void SetDataNoCopy(const Data &aData);
/**
* This allocates and returns a new buffer
*/
virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize);
/**
* Ask this Image to not convert YUV to RGB during SetData, and make
* the original data available through GetData. This is optional,
* and not all PlanarYCbCrImages will support it.
*/
virtual void SetDelayedConversion(bool aDelayed) { }
/**
* Grab the original YUV data. This is optional.
*/
virtual const Data* GetData() { return &mData; }
/**
* Return the number of bytes of heap memory used to store this image.
*/
virtual uint32_t GetDataSize() { return mBufferSize; }
virtual bool IsValid() { return !!mBufferSize; }
virtual gfx::IntSize GetSize() { return mSize; }
PlanarYCbCrImage(BufferRecycleBin *aRecycleBin);
virtual SharedPlanarYCbCrImage *AsSharedPlanarYCbCrImage() { return nullptr; }
virtual DeprecatedSharedPlanarYCbCrImage *AsDeprecatedSharedPlanarYCbCrImage() { return nullptr; }
protected:
/**
* Make a copy of the YCbCr data into local storage.
*
* @param aData Input image data.
*/
void CopyData(const Data& aData);
/**
* Return a buffer to store image data in.
* The default implementation returns memory that can
* be freed wit delete[]
*/
virtual uint8_t* AllocateBuffer(uint32_t aSize);
already_AddRefed<gfxASurface> DeprecatedGetAsSurface();
TemporaryRef<gfx::SourceSurface> GetAsSourceSurface();
void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
gfxImageFormat GetOffscreenFormat();
nsAutoArrayPtr<uint8_t> mBuffer;
uint32_t mBufferSize;
Data mData;
gfx::IntSize mSize;
gfxImageFormat mOffscreenFormat;
nsCountedRef<nsMainThreadSurfaceRef> mDeprecatedSurface;
nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
nsRefPtr<BufferRecycleBin> mRecycleBin;
};
/**
* Currently, the data in a CairoImage surface is treated as being in the
* device output color space. This class is very simple as all backends
* have to know about how to deal with drawing a cairo image.
*/
class CairoImage : public Image {
public:
struct Data {
gfxASurface* mDeprecatedSurface;
gfx::IntSize mSize;
// mSourceSurface wraps mDeprrecatedSurface's data, therefore it should not
// outlive mDeprecatedSurface
RefPtr<gfx::SourceSurface> mSourceSurface;
};
/**
* 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!!!
*/
void SetData(const Data& aData)
{
mDeprecatedSurface = aData.mDeprecatedSurface;
mSize = aData.mSize;
mSourceSurface = aData.mSourceSurface;
}
virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface()
{
return mSourceSurface.get();
}
virtual already_AddRefed<gfxASurface> DeprecatedGetAsSurface()
{
nsRefPtr<gfxASurface> surface = mDeprecatedSurface.get();
return surface.forget();
}
gfx::IntSize GetSize() { return mSize; }
CairoImage() : Image(nullptr, ImageFormat::CAIRO_SURFACE) {}
nsCountedRef<nsMainThreadSurfaceRef> mDeprecatedSurface;
gfx::IntSize mSize;
// mSourceSurface wraps mDeprrecatedSurface's data, therefore it should not
// outlive mDeprecatedSurface
nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
};
class RemoteBitmapImage : public Image {
public:
RemoteBitmapImage() : Image(nullptr, ImageFormat::REMOTE_IMAGE_BITMAP) {}
already_AddRefed<gfxASurface> DeprecatedGetAsSurface();
TemporaryRef<gfx::SourceSurface> GetAsSourceSurface();
gfx::IntSize GetSize() { return mSize; }
unsigned char *mData;
int mStride;
gfx::IntSize mSize;
RemoteImageData::Format mFormat;
};
} //namespace
} //namespace
#endif