зеркало из https://github.com/mozilla/gecko-dev.git
Bug 651192 - Part 5: Support remote access to ImageContainers. r=roc
This commit is contained in:
Родитель
048a0c15c9
Коммит
b798e874b1
|
@ -212,9 +212,10 @@ nsPluginInstanceOwner::GetImageContainer()
|
|||
mInstance->GetImageContainer(getter_AddRefs(container));
|
||||
if (container) {
|
||||
#ifdef XP_MACOSX
|
||||
nsRefPtr<Image> image = container->GetCurrentImage();
|
||||
AutoLockImage autoLock(container);
|
||||
Image* image = autoLock.GetImage();
|
||||
if (image && image->GetFormat() == Image::MAC_IO_SURFACE && mObjectFrame) {
|
||||
MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image.get());
|
||||
MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image);
|
||||
NS_ADDREF_THIS();
|
||||
oglImage->SetUpdateCallback(&DrawPlugin, this);
|
||||
oglImage->SetDestroyCallback(&OnDestroyImage);
|
||||
|
@ -3719,10 +3720,11 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
|
|||
nsRefPtr<ImageContainer> container = mObjectFrame->GetImageContainer();
|
||||
if (container) {
|
||||
#ifdef XP_MACOSX
|
||||
nsRefPtr<Image> image = container->GetCurrentImage();
|
||||
AutoLockImage autoLock(container);
|
||||
Image *image = autoLock.GetImage();
|
||||
if (image && (image->GetFormat() == Image::MAC_IO_SURFACE) && mObjectFrame) {
|
||||
// Undo what we did to the current image in SetCurrentImage().
|
||||
MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image.get());
|
||||
MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image);
|
||||
oglImage->SetUpdateCallback(nsnull, nsnull);
|
||||
oglImage->SetDestroyCallback(nsnull);
|
||||
// If we have a current image here, its destructor hasn't yet been
|
||||
|
@ -3730,6 +3732,9 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
|
|||
// to do ourselves what OnDestroyImage() would have done.
|
||||
NS_RELEASE_THIS();
|
||||
}
|
||||
// Important! Unlock here otherwise SetCurrentImage will deadlock with
|
||||
// our lock if we have a RemoteImage.
|
||||
autoLock.Unlock();
|
||||
#endif
|
||||
container->SetCurrentImage(nsnull);
|
||||
}
|
||||
|
|
|
@ -367,7 +367,8 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginDrawingModel(
|
|||
#endif
|
||||
{
|
||||
if (!GetImageContainer()) {
|
||||
return false;
|
||||
*result = NPERR_GENERIC_ERROR;
|
||||
return true;
|
||||
}
|
||||
|
||||
mDrawingModel = drawingModel;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "mozilla/ipc/Shmem.h"
|
||||
#include "mozilla/ipc/CrossProcessMutex.h"
|
||||
#include "ImageLayers.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "yuv_convert.h"
|
||||
|
@ -119,15 +120,91 @@ ImageContainer::SetCurrentImage(Image *aImage)
|
|||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
|
||||
mRemoteDataMutex->Lock();
|
||||
// This is important since it ensures we won't change the active image
|
||||
// when we currently have a locked image that depends on mRemoteData.
|
||||
}
|
||||
|
||||
mActiveImage = aImage;
|
||||
CurrentImageChanged();
|
||||
|
||||
if (mRemoteData) {
|
||||
mRemoteDataMutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ImageContainer::HasCurrentImage()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
|
||||
|
||||
EnsureActiveImage();
|
||||
|
||||
return !!mActiveImage.get();
|
||||
}
|
||||
|
||||
return !!mActiveImage.get();
|
||||
}
|
||||
|
||||
already_AddRefed<Image>
|
||||
ImageContainer::LockCurrentImage()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
|
||||
mRemoteDataMutex->Lock();
|
||||
}
|
||||
|
||||
EnsureActiveImage();
|
||||
|
||||
nsRefPtr<Image> retval = mActiveImage;
|
||||
return retval.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
|
||||
ImageContainer::LockCurrentAsSurface(gfxIntSize *aSize, Image** aCurrentImage)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
|
||||
mRemoteDataMutex->Lock();
|
||||
|
||||
EnsureActiveImage();
|
||||
|
||||
if (aCurrentImage) {
|
||||
NS_IF_ADDREF(mActiveImage);
|
||||
*aCurrentImage = mActiveImage.get();
|
||||
}
|
||||
|
||||
if (!mActiveImage) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (mActiveImage->GetFormat() == Image::REMOTE_IMAGE_BITMAP) {
|
||||
nsRefPtr<gfxImageSurface> newSurf =
|
||||
new gfxImageSurface(mRemoteData->mBitmap.mData, mRemoteData->mSize, mRemoteData->mBitmap.mStride,
|
||||
mRemoteData->mFormat == RemoteImageData::BGRX32 ?
|
||||
gfxASurface::ImageFormatARGB32 :
|
||||
gfxASurface::ImageFormatRGB24);
|
||||
|
||||
*aSize = newSurf->GetSize();
|
||||
|
||||
return newSurf.forget();
|
||||
}
|
||||
}
|
||||
|
||||
if (aCurrentImage) {
|
||||
NS_IF_ADDREF(mActiveImage);
|
||||
*aCurrentImage = mActiveImage.get();
|
||||
}
|
||||
|
||||
if (!mActiveImage) {
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -136,11 +213,45 @@ ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
|
|||
return mActiveImage->GetAsSurface();
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::UnlockCurrentImage()
|
||||
{
|
||||
if (mRemoteData) {
|
||||
NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
|
||||
mRemoteDataMutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
|
||||
EnsureActiveImage();
|
||||
|
||||
*aSize = mRemoteData->mSize;
|
||||
return mActiveImage ? mActiveImage->GetAsSurface() : nsnull;
|
||||
}
|
||||
|
||||
*aSize = mActiveImage->GetSize();
|
||||
return mActiveImage ? mActiveImage->GetAsSurface() : nsnull;
|
||||
}
|
||||
|
||||
gfxIntSize
|
||||
ImageContainer::GetCurrentSize()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mRemoteData) {
|
||||
CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
|
||||
|
||||
// We don't need to ensure we have an active image here, as we need to
|
||||
// be in the mutex anyway, and this is easiest to return from there.
|
||||
return mRemoteData->mSize;
|
||||
}
|
||||
|
||||
if (!mActiveImage) {
|
||||
return gfxIntSize(0,0);
|
||||
}
|
||||
|
@ -148,6 +259,47 @@ ImageContainer::GetCurrentSize()
|
|||
return mActiveImage->GetSize();
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::SetRemoteImageData(RemoteImageData *aData, CrossProcessMutex *aMutex)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
NS_ASSERTION(!mActiveImage, "No active image expected when SetRemoteImageData is called.");
|
||||
NS_ASSERTION(!mRemoteData, "No remote data expected when SetRemoteImageData is called.");
|
||||
|
||||
mRemoteData = aData;
|
||||
|
||||
if (aData) {
|
||||
memset(aData, 0, sizeof(RemoteImageData));
|
||||
} else {
|
||||
mActiveImage = nsnull;
|
||||
}
|
||||
|
||||
mRemoteDataMutex = aMutex;
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::EnsureActiveImage()
|
||||
{
|
||||
if (mRemoteData) {
|
||||
if (mRemoteData->mWasUpdated) {
|
||||
mActiveImage = nsnull;
|
||||
}
|
||||
|
||||
if (mRemoteData->mType == RemoteImageData::RAW_BITMAP &&
|
||||
mRemoteData->mBitmap.mData && !mActiveImage) {
|
||||
nsRefPtr<RemoteBitmapImage> newImg = new RemoteBitmapImage();
|
||||
|
||||
newImg->mFormat = mRemoteData->mFormat;
|
||||
newImg->mData = mRemoteData->mBitmap.mData;
|
||||
newImg->mSize = mRemoteData->mSize;
|
||||
newImg->mStride = mRemoteData->mBitmap.mStride;
|
||||
mRemoteData->mWasUpdated = false;
|
||||
|
||||
mActiveImage = newImg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
|
||||
: Image(nsnull, PLANAR_YCBCR)
|
||||
, mBufferSize(0)
|
||||
|
@ -270,6 +422,21 @@ MacIOSurfaceImage::Update(ImageContainer* aContainer)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
already_AddRefed<gfxASurface>
|
||||
RemoteBitmapImage::GetAsSurface()
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> newSurf =
|
||||
new gfxImageSurface(mSize,
|
||||
mFormat == RemoteImageData::BGRX32 ? gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32);
|
||||
|
||||
for (int y = 0; y < mSize.height; y++) {
|
||||
memcpy(newSurf->Data() + newSurf->Stride() * y,
|
||||
mData + mStride * y,
|
||||
mSize.width * 4);
|
||||
}
|
||||
|
||||
return newSurf.forget();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,12 @@
|
|||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CrossProcessMutex;
|
||||
namespace ipc {
|
||||
class Shmem;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
||||
enum StereoMode {
|
||||
|
@ -120,7 +126,12 @@ public:
|
|||
*
|
||||
* It wraps an IOSurface object and binds it directly to a GL texture.
|
||||
*/
|
||||
MAC_IO_SURFACE
|
||||
MAC_IO_SURFACE,
|
||||
|
||||
/**
|
||||
* An bitmap image that can be shared with a remote process.
|
||||
*/
|
||||
REMOTE_IMAGE_BITMAP
|
||||
};
|
||||
|
||||
Format GetFormat() { return mFormat; }
|
||||
|
@ -225,6 +236,46 @@ protected:
|
|||
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
|
||||
};
|
||||
/* 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;
|
||||
gfxIntSize 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;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A class that manages Images for an ImageLayer. The only reason
|
||||
|
@ -242,7 +293,9 @@ public:
|
|||
mPaintCount(0),
|
||||
mPreviousImagePainted(false),
|
||||
mImageFactory(new ImageFactory()),
|
||||
mRecycleBin(new BufferRecycleBin())
|
||||
mRecycleBin(new BufferRecycleBin()),
|
||||
mRemoteData(nsnull),
|
||||
mRemoteDataMutex(nsnull)
|
||||
{}
|
||||
|
||||
~ImageContainer();
|
||||
|
@ -266,26 +319,37 @@ public:
|
|||
* aImage can be null. While it's null, nothing will be painted.
|
||||
*
|
||||
* The Image data must not be modified after this method is called!
|
||||
*
|
||||
* Implementations must call CurrentImageChanged() while holding
|
||||
* mReentrantMonitor.
|
||||
*/
|
||||
void SetCurrentImage(Image* aImage);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns if the container currently has an image.
|
||||
* Can be called on any thread. This method takes mReentrantMonitor
|
||||
* when accessing thread-shared state.
|
||||
* Implementations must call CurrentImageChanged() while holding
|
||||
* mReentrantMonitor.
|
||||
*/
|
||||
already_AddRefed<Image> GetCurrentImage()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
bool HasCurrentImage();
|
||||
|
||||
nsRefPtr<Image> retval = mActiveImage;
|
||||
return retval.forget();
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
|
@ -301,9 +365,25 @@ public:
|
|||
* 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> GetCurrentAsSurface(gfxIntSize* 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> LockCurrentAsSurface(gfxIntSize* aSizeResult,
|
||||
Image** aCurrentImage = nsnull);
|
||||
|
||||
/**
|
||||
* Returns the size of the image in pixels.
|
||||
* Can be called on any thread. This method takes mReentrantMonitor when accessing
|
||||
|
@ -354,7 +434,8 @@ public:
|
|||
*/
|
||||
void NotifyPaintedImage(Image* aPainted) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsRefPtr<Image> current = GetCurrentImage();
|
||||
|
||||
nsRefPtr<Image> current = mActiveImage;
|
||||
if (aPainted == current) {
|
||||
if (mPaintTime.IsNull()) {
|
||||
mPaintTime = TimeStamp::Now();
|
||||
|
@ -369,9 +450,30 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// 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;
|
||||
|
@ -407,6 +509,54 @@ protected:
|
|||
gfxIntSize mScaleHint;
|
||||
|
||||
nsRefPtr<BufferRecycleBin> mRecycleBin;
|
||||
|
||||
// This contains the remote image data for this container, if this is NULL
|
||||
// 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;
|
||||
};
|
||||
|
||||
class AutoLockImage
|
||||
{
|
||||
public:
|
||||
AutoLockImage(ImageContainer *aContainer) : mContainer(aContainer) { mImage = mContainer->LockCurrentImage(); }
|
||||
AutoLockImage(ImageContainer *aContainer, gfxASurface **aSurface) : mContainer(aContainer) {
|
||||
*aSurface = mContainer->LockCurrentAsSurface(&mSize, getter_AddRefs(mImage)).get();
|
||||
}
|
||||
~AutoLockImage() { if (mContainer) { mContainer->UnlockCurrentImage(); } }
|
||||
|
||||
Image* GetImage() { return mImage; }
|
||||
const gfxIntSize &GetSize() { return mSize; }
|
||||
|
||||
void Unlock() {
|
||||
if (mContainer) {
|
||||
mImage = nsnull;
|
||||
mContainer->UnlockCurrentImage();
|
||||
mContainer = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
gfxIntSize mSize;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -676,6 +826,20 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
class RemoteBitmapImage : public Image {
|
||||
public:
|
||||
RemoteBitmapImage() : Image(NULL, REMOTE_IMAGE_BITMAP) {}
|
||||
|
||||
already_AddRefed<gfxASurface> GetAsSurface();
|
||||
|
||||
gfxIntSize GetSize() { return mSize; }
|
||||
|
||||
unsigned char *mData;
|
||||
int mStride;
|
||||
gfxIntSize mSize;
|
||||
RemoteImageData::Format mFormat;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -904,9 +904,11 @@ BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext,
|
|||
|
||||
mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nsnull : BasicManager()->GetImageFactory());
|
||||
|
||||
nsRefPtr<Image> image = mContainer->GetCurrentImage();
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
AutoLockImage autoLock(mContainer, getter_AddRefs(surface));
|
||||
Image *image = autoLock.GetImage();
|
||||
mSize = autoLock.GetSize();
|
||||
|
||||
nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&mSize);
|
||||
if (!surface || surface->CairoStatus()) {
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -2522,13 +2524,16 @@ BasicShadowableImageLayer::Paint(gfxContext* aContext)
|
|||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Image> image = mContainer->GetCurrentImage();
|
||||
AutoLockImage autoLock(mContainer);
|
||||
|
||||
Image *image = autoLock.GetImage();
|
||||
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image->GetFormat() == Image::PLANAR_YCBCR && BasicManager()->IsCompositingCheap()) {
|
||||
PlanarYCbCrImage *YCbCrImage = static_cast<PlanarYCbCrImage*>(image.get());
|
||||
PlanarYCbCrImage *YCbCrImage = static_cast<PlanarYCbCrImage*>(image);
|
||||
const PlanarYCbCrImage::Data *data = YCbCrImage->GetData();
|
||||
NS_ASSERTION(data, "Must be able to retrieve yuv data from image!");
|
||||
|
||||
|
|
|
@ -44,6 +44,34 @@
|
|||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static already_AddRefed<ID3D10Texture2D>
|
||||
DataToTexture(ID3D10Device *aDevice,
|
||||
unsigned char *data,
|
||||
int stride,
|
||||
const gfxIntSize &aSize)
|
||||
{
|
||||
D3D10_SUBRESOURCE_DATA srdata;
|
||||
|
||||
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
1, 1);
|
||||
desc.Usage = D3D10_USAGE_IMMUTABLE;
|
||||
|
||||
srdata.pSysMem = data;
|
||||
srdata.SysMemPitch = stride;
|
||||
|
||||
nsRefPtr<ID3D10Texture2D> texture;
|
||||
HRESULT hr = aDevice->CreateTexture2D(&desc, &srdata, getter_AddRefs(texture));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for data"),
|
||||
hr);
|
||||
}
|
||||
|
||||
return texture.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<ID3D10Texture2D>
|
||||
SurfaceToTexture(ID3D10Device *aDevice,
|
||||
|
@ -78,26 +106,7 @@ SurfaceToTexture(ID3D10Device *aDevice,
|
|||
context->Paint();
|
||||
}
|
||||
|
||||
D3D10_SUBRESOURCE_DATA data;
|
||||
|
||||
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
imageSurface->GetSize().width,
|
||||
imageSurface->GetSize().height,
|
||||
1, 1);
|
||||
desc.Usage = D3D10_USAGE_IMMUTABLE;
|
||||
|
||||
data.pSysMem = imageSurface->Data();
|
||||
data.SysMemPitch = imageSurface->Stride();
|
||||
|
||||
nsRefPtr<ID3D10Texture2D> texture;
|
||||
HRESULT hr = aDevice->CreateTexture2D(&desc, &data, getter_AddRefs(texture));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"),
|
||||
hr);
|
||||
}
|
||||
|
||||
return texture.forget();
|
||||
return DataToTexture(aDevice, imageSurface->Data(), imageSurface->Stride(), aSize);
|
||||
}
|
||||
|
||||
Layer*
|
||||
|
@ -109,11 +118,14 @@ ImageLayerD3D10::GetLayer()
|
|||
void
|
||||
ImageLayerD3D10::RenderLayer()
|
||||
{
|
||||
if (!GetContainer()) {
|
||||
ImageContainer *container = GetContainer();
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Image> image = GetContainer()->GetCurrentImage();
|
||||
AutoLockImage autoLock(container);
|
||||
|
||||
Image *image = autoLock.GetImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
@ -122,27 +134,51 @@ ImageLayerD3D10::RenderLayer()
|
|||
|
||||
ID3D10EffectTechnique *technique;
|
||||
|
||||
if (image->GetFormat() == Image::CAIRO_SURFACE)
|
||||
if (image->GetFormat() == Image::CAIRO_SURFACE || image->GetFormat() == Image::REMOTE_IMAGE_BITMAP)
|
||||
{
|
||||
CairoImage *cairoImage =
|
||||
static_cast<CairoImage*>(image.get());
|
||||
bool hasAlpha = false;
|
||||
gfxIntSize size;
|
||||
|
||||
if (!cairoImage->mSurface) {
|
||||
return;
|
||||
}
|
||||
if (image->GetFormat() == Image::REMOTE_IMAGE_BITMAP) {
|
||||
RemoteBitmapImage *remoteImage =
|
||||
static_cast<RemoteBitmapImage*>(image);
|
||||
|
||||
if (!image->GetBackendData(LayerManager::LAYERS_D3D10)) {
|
||||
nsAutoPtr<TextureD3D10BackendData> dat = new TextureD3D10BackendData();
|
||||
dat->mTexture = DataToTexture(device(), remoteImage->mData, remoteImage->mStride, remoteImage->mSize);
|
||||
|
||||
if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D10)) {
|
||||
nsAutoPtr<CairoD3D10BackendData> dat(new CairoD3D10BackendData());
|
||||
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
|
||||
|
||||
if (dat->mTexture) {
|
||||
device()->CreateShaderResourceView(dat->mTexture, NULL, getter_AddRefs(dat->mSRView));
|
||||
cairoImage->SetBackendData(LayerManager::LAYERS_D3D10, dat.forget());
|
||||
if (dat->mTexture) {
|
||||
device()->CreateShaderResourceView(dat->mTexture, NULL, getter_AddRefs(dat->mSRView));
|
||||
image->SetBackendData(LayerManager::LAYERS_D3D10, dat.forget());
|
||||
}
|
||||
}
|
||||
|
||||
hasAlpha = remoteImage->mFormat == RemoteImageData::BGRA32;
|
||||
size = remoteImage->mSize;
|
||||
} else {
|
||||
CairoImage *cairoImage =
|
||||
static_cast<CairoImage*>(image);
|
||||
|
||||
if (!cairoImage->mSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!image->GetBackendData(LayerManager::LAYERS_D3D10)) {
|
||||
nsAutoPtr<TextureD3D10BackendData> dat = new TextureD3D10BackendData();
|
||||
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
|
||||
|
||||
if (dat->mTexture) {
|
||||
device()->CreateShaderResourceView(dat->mTexture, NULL, getter_AddRefs(dat->mSRView));
|
||||
image->SetBackendData(LayerManager::LAYERS_D3D10, dat.forget());
|
||||
}
|
||||
}
|
||||
|
||||
hasAlpha = cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA;
|
||||
size = cairoImage->mSize;
|
||||
}
|
||||
|
||||
CairoD3D10BackendData *data =
|
||||
static_cast<CairoD3D10BackendData*>(cairoImage->GetBackendData(LayerManager::LAYERS_D3D10));
|
||||
TextureD3D10BackendData *data =
|
||||
static_cast<TextureD3D10BackendData*>(image->GetBackendData(LayerManager::LAYERS_D3D10));
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
|
@ -154,7 +190,7 @@ ImageLayerD3D10::RenderLayer()
|
|||
return;
|
||||
}
|
||||
|
||||
if (cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
|
||||
if (hasAlpha) {
|
||||
if (mFilter == gfxPattern::FILTER_NEAREST) {
|
||||
technique = effect()->GetTechniqueByName("RenderRGBALayerPremulPoint");
|
||||
} else {
|
||||
|
@ -174,12 +210,12 @@ ImageLayerD3D10::RenderLayer()
|
|||
ShaderConstantRectD3D10(
|
||||
(float)0,
|
||||
(float)0,
|
||||
(float)cairoImage->mSize.width,
|
||||
(float)cairoImage->mSize.height)
|
||||
(float)size.width,
|
||||
(float)size.height)
|
||||
);
|
||||
} else if (image->GetFormat() == Image::PLANAR_YCBCR) {
|
||||
PlanarYCbCrImage *yuvImage =
|
||||
static_cast<PlanarYCbCrImage*>(image.get());
|
||||
static_cast<PlanarYCbCrImage*>(image);
|
||||
|
||||
if (!yuvImage->mBufferSize) {
|
||||
return;
|
||||
|
@ -262,11 +298,15 @@ ImageLayerD3D10::RenderLayer()
|
|||
(float)yuvImage->mData.mPicSize.height / yuvImage->mData.mYSize.height)
|
||||
);
|
||||
}
|
||||
|
||||
bool resetTexCoords = image->GetFormat() == Image::PLANAR_YCBCR;
|
||||
image = nsnull;
|
||||
autoLock.Unlock();
|
||||
|
||||
technique->GetPassByIndex(0)->Apply(0);
|
||||
device()->Draw(4, 0);
|
||||
|
||||
if (image->GetFormat() == Image::PLANAR_YCBCR) {
|
||||
if (resetTexCoords) {
|
||||
effect()->GetVariableByName("vTextureCoords")->AsVector()->
|
||||
SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ struct PlanarYCbCrD3D10BackendData : public ImageBackendData
|
|||
nsRefPtr<ID3D10ShaderResourceView> mCrView;
|
||||
};
|
||||
|
||||
struct CairoD3D10BackendData : public ImageBackendData
|
||||
struct TextureD3D10BackendData : public ImageBackendData
|
||||
{
|
||||
nsRefPtr<ID3D10Texture2D> mTexture;
|
||||
nsRefPtr<ID3D10ShaderResourceView> mSRView;
|
||||
|
|
|
@ -53,23 +53,11 @@ namespace mozilla {
|
|||
namespace layers {
|
||||
|
||||
static already_AddRefed<IDirect3DTexture9>
|
||||
SurfaceToTexture(IDirect3DDevice9 *aDevice,
|
||||
gfxASurface *aSurface,
|
||||
const gfxIntSize &aSize)
|
||||
DataToTexture(IDirect3DDevice9 *aDevice,
|
||||
unsigned char *aData,
|
||||
int aStride,
|
||||
const gfxIntSize &aSize)
|
||||
{
|
||||
|
||||
nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface();
|
||||
|
||||
if (!imageSurface) {
|
||||
imageSurface = new gfxImageSurface(aSize,
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
|
||||
context->SetSource(aSurface);
|
||||
context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
context->Paint();
|
||||
}
|
||||
|
||||
nsRefPtr<IDirect3DTexture9> texture;
|
||||
nsRefPtr<IDirect3DDevice9Ex> deviceEx;
|
||||
aDevice->QueryInterface(IID_IDirect3DDevice9Ex,
|
||||
|
@ -103,7 +91,7 @@ SurfaceToTexture(IDirect3DDevice9 *aDevice,
|
|||
surface->LockRect(&lockedRect, NULL, 0);
|
||||
for (int y = 0; y < aSize.height; y++) {
|
||||
memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y,
|
||||
imageSurface->Data() + imageSurface->Stride() * y,
|
||||
aData + aStride * y,
|
||||
aSize.width * 4);
|
||||
}
|
||||
surface->UnlockRect();
|
||||
|
@ -127,7 +115,7 @@ SurfaceToTexture(IDirect3DDevice9 *aDevice,
|
|||
// use memcpy
|
||||
for (int y = 0; y < aSize.height; y++) {
|
||||
memcpy((char*)lockrect.pBits + lockrect.Pitch * y,
|
||||
imageSurface->Data() + imageSurface->Stride() * y,
|
||||
aData + aStride * y,
|
||||
aSize.width * 4);
|
||||
}
|
||||
|
||||
|
@ -137,6 +125,27 @@ SurfaceToTexture(IDirect3DDevice9 *aDevice,
|
|||
return texture.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<IDirect3DTexture9>
|
||||
SurfaceToTexture(IDirect3DDevice9 *aDevice,
|
||||
gfxASurface *aSurface,
|
||||
const gfxIntSize &aSize)
|
||||
{
|
||||
|
||||
nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface();
|
||||
|
||||
if (!imageSurface) {
|
||||
imageSurface = new gfxImageSurface(aSize,
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
|
||||
context->SetSource(aSurface);
|
||||
context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
context->Paint();
|
||||
}
|
||||
|
||||
return DataToTexture(aDevice, imageSurface->Data(), imageSurface->Stride(), aSize);
|
||||
}
|
||||
|
||||
static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage,
|
||||
IDirect3DDevice9 *aDevice,
|
||||
LayerManagerD3D9 *aManager)
|
||||
|
@ -302,37 +311,61 @@ ImageLayerD3D9::GetLayer()
|
|||
void
|
||||
ImageLayerD3D9::RenderLayer()
|
||||
{
|
||||
if (!GetContainer()) {
|
||||
ImageContainer *container = GetContainer();
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Image> image = GetContainer()->GetCurrentImage();
|
||||
AutoLockImage autoLock(container);
|
||||
|
||||
Image *image = autoLock.GetImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetShaderTransformAndOpacity();
|
||||
|
||||
if (image->GetFormat() == Image::CAIRO_SURFACE)
|
||||
if (image->GetFormat() == Image::CAIRO_SURFACE || image->GetFormat() == Image::REMOTE_IMAGE_BITMAP)
|
||||
{
|
||||
CairoImage *cairoImage =
|
||||
static_cast<CairoImage*>(image.get());
|
||||
bool hasAlpha = false;
|
||||
gfxIntSize size;
|
||||
|
||||
if (!cairoImage->mSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D9)) {
|
||||
nsAutoPtr<CairoD3D9BackendData> dat(new CairoD3D9BackendData());
|
||||
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
|
||||
|
||||
if (dat->mTexture) {
|
||||
cairoImage->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget());
|
||||
if (image->GetFormat() == Image::REMOTE_IMAGE_BITMAP) {
|
||||
RemoteBitmapImage *remoteImage =
|
||||
static_cast<RemoteBitmapImage*>(image);
|
||||
|
||||
if (!image->GetBackendData(LayerManager::LAYERS_D3D9)) {
|
||||
nsAutoPtr<TextureD3D9BackendData> dat = new TextureD3D9BackendData();
|
||||
dat->mTexture = DataToTexture(device(), remoteImage->mData, remoteImage->mStride, remoteImage->mSize);
|
||||
if (dat->mTexture) {
|
||||
image->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget());
|
||||
}
|
||||
}
|
||||
|
||||
hasAlpha = remoteImage->mFormat == RemoteImageData::BGRA32;
|
||||
size = remoteImage->mSize;
|
||||
} else {
|
||||
CairoImage *cairoImage =
|
||||
static_cast<CairoImage*>(image);
|
||||
|
||||
if (!cairoImage->mSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!image->GetBackendData(LayerManager::LAYERS_D3D9)) {
|
||||
nsAutoPtr<TextureD3D9BackendData> dat = new TextureD3D9BackendData();
|
||||
dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize);
|
||||
if (dat->mTexture) {
|
||||
image->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget());
|
||||
}
|
||||
}
|
||||
|
||||
hasAlpha = cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA;
|
||||
size = cairoImage->mSize;
|
||||
}
|
||||
|
||||
CairoD3D9BackendData *data =
|
||||
static_cast<CairoD3D9BackendData*>(cairoImage->GetBackendData(LayerManager::LAYERS_D3D9));
|
||||
TextureD3D9BackendData *data =
|
||||
static_cast<TextureD3D9BackendData*>(image->GetBackendData(LayerManager::LAYERS_D3D9));
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
|
@ -344,12 +377,6 @@ ImageLayerD3D9::RenderLayer()
|
|||
return;
|
||||
}
|
||||
|
||||
gfxIntSize size;
|
||||
nsRefPtr<gfxASurface> surface =
|
||||
GetContainer()->GetCurrentAsSurface(&size);
|
||||
nsRefPtr<IDirect3DTexture9> texture =
|
||||
SurfaceToTexture(device(), surface, size);
|
||||
|
||||
device()->SetVertexShaderConstantF(CBvLayerQuad,
|
||||
ShaderConstantRect(0,
|
||||
0,
|
||||
|
@ -357,7 +384,7 @@ ImageLayerD3D9::RenderLayer()
|
|||
size.height),
|
||||
1);
|
||||
|
||||
if (surface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
|
||||
if (hasAlpha) {
|
||||
mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
|
||||
} else {
|
||||
mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
|
||||
|
@ -367,7 +394,12 @@ ImageLayerD3D9::RenderLayer()
|
|||
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
|
||||
device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
|
||||
}
|
||||
device()->SetTexture(0, texture);
|
||||
device()->SetTexture(0, data->mTexture);
|
||||
|
||||
image = nsnull;
|
||||
data = nsnull;
|
||||
autoLock.Unlock();
|
||||
|
||||
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
||||
if (mFilter == gfxPattern::FILTER_NEAREST) {
|
||||
device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
|
@ -375,7 +407,7 @@ ImageLayerD3D9::RenderLayer()
|
|||
}
|
||||
} else {
|
||||
PlanarYCbCrImage *yuvImage =
|
||||
static_cast<PlanarYCbCrImage*>(image.get());
|
||||
static_cast<PlanarYCbCrImage*>(image);
|
||||
|
||||
if (!yuvImage->mBufferSize) {
|
||||
return;
|
||||
|
@ -459,6 +491,10 @@ ImageLayerD3D9::RenderLayer()
|
|||
device()->SetTexture(1, data->mCbTexture);
|
||||
device()->SetTexture(2, data->mCrTexture);
|
||||
|
||||
image = nsnull;
|
||||
data = nsnull;
|
||||
autoLock.Unlock();
|
||||
|
||||
device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
||||
|
||||
device()->SetVertexShaderConstantF(CBvTextureCoords,
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
};
|
||||
|
||||
|
||||
struct CairoD3D9BackendData : public ImageBackendData
|
||||
struct TextureD3D9BackendData : public ImageBackendData
|
||||
{
|
||||
nsRefPtr<IDirect3DTexture9> mTexture;
|
||||
};
|
||||
|
|
|
@ -214,19 +214,26 @@ void
|
|||
ImageLayerOGL::RenderLayer(int,
|
||||
const nsIntPoint& aOffset)
|
||||
{
|
||||
if (!GetContainer())
|
||||
nsRefPtr<ImageContainer> container = GetContainer();
|
||||
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
mOGLManager->MakeCurrent();
|
||||
|
||||
nsRefPtr<Image> image = GetContainer()->GetCurrentImage();
|
||||
AutoLockImage autoLock(container);
|
||||
|
||||
Image *image = autoLock.GetImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(image->GetFormat() != Image::REMOTE_IMAGE_BITMAP,
|
||||
"Remote images aren't handled yet in OGL layers!");
|
||||
|
||||
if (image->GetFormat() == Image::PLANAR_YCBCR) {
|
||||
PlanarYCbCrImage *yuvImage =
|
||||
static_cast<PlanarYCbCrImage*>(image.get());
|
||||
static_cast<PlanarYCbCrImage*>(image);
|
||||
|
||||
if (!yuvImage->mBufferSize) {
|
||||
return;
|
||||
|
@ -276,7 +283,7 @@ ImageLayerOGL::RenderLayer(int,
|
|||
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
} else if (image->GetFormat() == Image::CAIRO_SURFACE) {
|
||||
CairoImage *cairoImage =
|
||||
static_cast<CairoImage*>(image.get());
|
||||
static_cast<CairoImage*>(image);
|
||||
|
||||
if (!cairoImage->mSurface) {
|
||||
return;
|
||||
|
@ -424,16 +431,18 @@ ImageLayerOGL::RenderLayer(int,
|
|||
#ifdef XP_MACOSX
|
||||
} else if (image->GetFormat() == Image::MAC_IO_SURFACE) {
|
||||
MacIOSurfaceImage *ioImage =
|
||||
static_cast<MacIOSurfaceImage*>(image.get());
|
||||
static_cast<MacIOSurfaceImage*>(image);
|
||||
|
||||
if (!mOGLManager->GetThebesLayerCallback()) {
|
||||
// If its an empty transaction we still need to update
|
||||
// the plugin IO Surface and make sure we grab the
|
||||
// new image
|
||||
ioImage->Update(GetContainer());
|
||||
image = GetContainer()->GetCurrentImage();
|
||||
image = nsnull;
|
||||
autoLock.Refresh();
|
||||
image = autoLock.GetImage();
|
||||
gl()->MakeCurrent();
|
||||
ioImage = static_cast<MacIOSurfaceImage*>(image.get());
|
||||
ioImage = static_cast<MacIOSurfaceImage*>(image);
|
||||
}
|
||||
|
||||
if (!ioImage) {
|
||||
|
|
|
@ -1190,8 +1190,7 @@ nsObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
nsRefPtr<ImageContainer> container = GetImageContainer();
|
||||
nsRefPtr<Image> currentImage = container ? container->GetCurrentImage() : nsnull;
|
||||
if (!currentImage || !isVisible ||
|
||||
if (container && container->HasCurrentImage() || !isVisible ||
|
||||
container->GetCurrentSize() != gfxIntSize(window->width, window->height)) {
|
||||
mInstanceOwner->NotifyPaintWaiter(aBuilder);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче