Bug 586683 - Part 4 - Move images into a separate ImageLayer when they are the only item in a ThebesLayer. r=roc a=blocking2.0
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 18 Jan 2011 10:47:18 +1300
changeset 60712 d41e933f589aaefcc6543228effd6f7eaaf3cd7b
parent 60711 16d3af58499c1fe9a08653508df425568eef902d
child 60713 853736ad614411fbeb6b3cbea759903b693e78b2
push idunknown
push userunknown
push dateunknown
reviewersroc, blocking2
bugs586683
milestone2.0b10pre
Bug 586683 - Part 4 - Move images into a separate ImageLayer when they are the only item in a ThebesLayer. r=roc a=blocking2.0
gfx/layers/ImageLayers.h
layout/base/FrameLayerBuilder.cpp
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/generic/nsObjectFrame.cpp
layout/generic/nsObjectFrame.h
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -213,17 +213,22 @@ protected:
  */
 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; }
+  void SetContainer(ImageContainer* aContainer) 
+  {
+    NS_ASSERTION(aContainer->Manager() == Manager(), 
+                 "ImageContainer must have the same manager as the ImageLayer");
+    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; }
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -41,16 +41,17 @@
 #include "nsPresContext.h"
 #include "nsLayoutUtils.h"
 #include "Layers.h"
 #include "BasicLayers.h"
 #include "nsSubDocumentFrame.h"
 #include "nsCSSRendering.h"
 #include "nsCSSFrameConstructor.h"
 #include "gfxUtils.h"
+#include "nsImageFrame.h"
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 using namespace mozilla::layers;
 
 namespace mozilla {
@@ -117,17 +118,17 @@ class ContainerState {
 public:
   ContainerState(nsDisplayListBuilder* aBuilder,
                  LayerManager* aManager,
                  nsIFrame* aContainerFrame,
                  ContainerLayer* aContainerLayer) :
     mBuilder(aBuilder), mManager(aManager),
     mContainerFrame(aContainerFrame), mContainerLayer(aContainerLayer),
     mNextFreeRecycledThebesLayer(0), mNextFreeRecycledColorLayer(0),
-    mInvalidateAllThebesContent(PR_FALSE)
+    mNextFreeRecycledImageLayer(0), mInvalidateAllThebesContent(PR_FALSE)
   {
     CollectOldLayers();
   }
 
   void SetInvalidThebesContent(const nsIntRegion& aRegion)
   {
     mInvalidThebesContent = aRegion;
   }
@@ -164,17 +165,18 @@ protected:
    * layer, giving us the best chance of getting subpixel AA.
    */
   class ThebesLayerData {
   public:
     ThebesLayerData() :
       mActiveScrolledRoot(nsnull), mLayer(nsnull),
       mIsSolidColorInVisibleRegion(PR_FALSE),
       mNeedComponentAlpha(PR_FALSE),
-      mForceTransparentSurface(PR_FALSE) {}
+      mForceTransparentSurface(PR_FALSE),
+      mImage(nsnull) {}
     /**
      * Record that an item has been added to the ThebesLayer, so we
      * need to update our regions.
      * @param aVisibleRect the area of the item that's visible
      * @param aDrawRect the area of the item that would be drawn if it
      * was completely visible
      * @param aOpaqueRect if non-null, the area of the item that's opaque.
      * We pass in a separate opaque rect because the opaque rect can be
@@ -186,16 +188,23 @@ protected:
     void Accumulate(nsDisplayListBuilder* aBuilder,
                     nsDisplayItem* aItem,
                     const nsIntRect& aVisibleRect,
                     const nsIntRect& aDrawRect,
                     const FrameLayerBuilder::Clip& aClip);
     nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
 
     /**
+     * If this represents only a nsDisplayImage, and the image type
+     * supports being optimized to an ImageLayer (TYPE_RASTER only) returns
+     * an ImageContainer for the image.
+     */
+    nsRefPtr<ImageContainer> CanOptimizeImageLayer(LayerManager* aManager);
+
+    /**
      * The region of visible content in the layer, relative to the
      * container layer (which is at the snapped top-left of the display
      * list reference frame).
      */
     nsIntRegion  mVisibleRegion;
     /**
      * The region of visible content above the layer and below the
      * next ThebesLayerData currently in the stack, if any. Note that not
@@ -246,31 +255,42 @@ protected:
     PRPackedBool mNeedComponentAlpha;
     /**
      * Set if the layer should be treated as transparent, even if its entire
      * area is covered by opaque display items. For example, this needs to
      * be set if something is going to "punch holes" in the layer by clearing
      * part of its surface.
      */
     PRPackedBool mForceTransparentSurface;
+
+    /**
+     * Stores the pointer to the nsDisplayImage if we want to
+     * convert this to an ImageLayer.
+     */
+    nsDisplayImage* mImage;
   };
 
   /**
    * Grab the next recyclable ThebesLayer, or create one if there are no
    * more recyclable ThebesLayers. Does any necessary invalidation of
    * a recycled ThebesLayer, and sets up the transform on the ThebesLayer
    * to account for scrolling.
    */
   already_AddRefed<ThebesLayer> CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot);
   /**
    * Grab the next recyclable ColorLayer, or create one if there are no
    * more recyclable ColorLayers.
    */
   already_AddRefed<ColorLayer> CreateOrRecycleColorLayer();
   /**
+   * Grab the next recyclable ImageLayer, or create one if there are no
+   * more recyclable ImageLayers.
+   */
+  already_AddRefed<ImageLayer> CreateOrRecycleImageLayer();
+  /**
    * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them
    * available for recycling.
    */
   void CollectOldLayers();
   /**
    * If aItem used to belong to a ThebesLayer, invalidates the area of
    * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
    * aItem in that layer.
@@ -331,18 +351,20 @@ protected:
    * We collect the list of children in here. During ProcessDisplayItems,
    * the layers in this array either have mContainerLayer as their parent,
    * or no parent.
    */
   typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
   AutoLayersArray                  mNewChildLayers;
   nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers;
   nsTArray<nsRefPtr<ColorLayer> >  mRecycledColorLayers;
+  nsTArray<nsRefPtr<ImageLayer> >  mRecycledImageLayers;
   PRUint32                         mNextFreeRecycledThebesLayer;
   PRUint32                         mNextFreeRecycledColorLayer;
+  PRUint32                         mNextFreeRecycledImageLayer;
   PRPackedBool                     mInvalidateAllThebesContent;
 };
 
 class ThebesDisplayItemLayerUserData : public LayerUserData
 {
 public:
   ThebesDisplayItemLayerUserData() :
     mForcedBackgroundColor(NS_RGBA(0,0,0,0)) {}
@@ -366,16 +388,22 @@ public:
 PRUint8 gThebesDisplayItemLayerUserData;
 /**
  * The address of gColorLayerUserData is used as the user
  * data key for ColorLayers created by FrameLayerBuilder.
  * The user data is null.
  */
 PRUint8 gColorLayerUserData;
 /**
+ * The address of gImageLayerUserData is used as the user
+ * data key for ImageLayers created by FrameLayerBuilder.
+ * The user data is null.
+ */
+PRUint8 gImageLayerUserData;
+/**
  * The address of gLayerManagerUserData is used as the user
  * data key for retained LayerManagers managed by FrameLayerBuilder.
  * The user data is a LayerManagerData.
  */
 PRUint8 gLayerManagerUserData;
 
 } // anonymous namespace
 
@@ -659,16 +687,38 @@ ContainerState::CreateOrRecycleColorLaye
     if (!layer)
       return nsnull;
     // Mark this layer as being used for Thebes-painting display items
     layer->SetUserData(&gColorLayerUserData, nsnull);
   }
   return layer.forget();
 }
 
+already_AddRefed<ImageLayer>
+ContainerState::CreateOrRecycleImageLayer()
+{
+  nsRefPtr<ImageLayer> layer;
+  if (mNextFreeRecycledImageLayer < mRecycledImageLayers.Length()) {
+    // Recycle a layer
+    layer = mRecycledImageLayers[mNextFreeRecycledImageLayer];
+    ++mNextFreeRecycledImageLayer;
+    // Clear clip rect so we don't accidentally stay clipped. We will
+    // reapply any necessary clipping.
+    layer->SetClipRect(nsnull);
+  } else {
+    // Create a new layer
+    layer = mManager->CreateImageLayer();
+    if (!layer)
+      return nsnull;
+    // Mark this layer as being used for Thebes-painting display items
+    layer->SetUserData(&gImageLayerUserData, nsnull);
+  }
+  return layer.forget();
+}
+
 already_AddRefed<ThebesLayer>
 ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
 {
   // We need a new thebes layer
   nsRefPtr<ThebesLayer> layer;
   if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
     // Recycle a layer
     layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
@@ -790,66 +840,91 @@ ContainerState::FindOpaqueBackgroundColo
     nsRect rect =
       target->mVisibleRegion.GetBounds().ToAppUnits(appUnitsPerDevPixel);
     return mBuilder->LayerBuilder()->
       FindOpaqueColorCovering(mBuilder, candidate->mLayer, rect);
   }
   return NS_RGBA(0,0,0,0);
 }
 
+nsRefPtr<ImageContainer>
+ContainerState::ThebesLayerData::CanOptimizeImageLayer(LayerManager* aManager)
+{
+  if (!mImage) {
+    return nsnull;
+  }
+
+  return mImage->GetContainer(aManager);
+}
+
 void
 ContainerState::PopThebesLayerData()
 {
   NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
 
   PRInt32 lastIndex = mThebesLayerDataStack.Length() - 1;
   ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
 
-  Layer* layer;
-  if (data->mIsSolidColorInVisibleRegion) {
-    nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer();
-    colorLayer->SetColor(data->mSolidColor);
+  nsRefPtr<Layer> layer;
+  nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mManager); 
+
+  if (data->mIsSolidColorInVisibleRegion || imageContainer) {
+    NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
+                 "Can't be a solid color as well as an image!");
+    if (imageContainer) {
+      nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer();
+      imageLayer->SetContainer(imageContainer);
+      data->mImage->ConfigureLayer(imageLayer);
+      layer = imageLayer;
+    } else {
+      nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer();
+      colorLayer->SetColor(data->mSolidColor);
 
-    NS_ASSERTION(!mNewChildLayers.Contains(colorLayer), "Layer already in list???");
+      // Copy transform
+      colorLayer->SetTransform(data->mLayer->GetTransform());
+      
+      // Clip colorLayer to its visible region, since ColorLayers are
+      // allowed to paint outside the visible region. Here we rely on the
+      // fact that uniform display items fill rectangles; obviously the
+      // area to fill must contain the visible region, and because it's
+      // a rectangle, it must therefore contain the visible region's GetBounds.
+      // Note that the visible region is already clipped appropriately.
+      nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
+      colorLayer->SetClipRect(&visibleRect);
+
+      layer = colorLayer;
+    }
+
+    NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
     AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer);
     NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?");
-    mNewChildLayers.InsertElementAt(index + 1, colorLayer);
-
-    // Copy transform and clip rect
-    colorLayer->SetTransform(data->mLayer->GetTransform());
-    // Clip colorLayer to its visible region, since ColorLayers are
-    // allowed to paint outside the visible region. Here we rely on the
-    // fact that uniform display items fill rectangles; obviously the
-    // area to fill must contain the visible region, and because it's
-    // a rectangle, it must therefore contain the visible region's GetBounds.
-    // Note that the visible region is already clipped appropriately.
-    nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
-    colorLayer->SetClipRect(&visibleRect);
+    mNewChildLayers.InsertElementAt(index + 1, layer);
 
     // Hide the ThebesLayer. We leave it in the layer tree so that we
     // can find and recycle it later.
     data->mLayer->IntersectClipRect(nsIntRect());
     data->mLayer->SetVisibleRegion(nsIntRegion());
-
-    layer = colorLayer;
   } else {
     layer = data->mLayer;
   }
 
   gfxMatrix transform;
-  if (layer->GetTransform().Is2D(&transform)) {
+  if (!layer->GetTransform().Is2D(&transform)) {
+    NS_ERROR("Only 2D transformations currently supported");
+  }
+  
+  //ImageLayers are already configured with a visible region
+  if (!imageContainer) {
     NS_ASSERTION(!transform.HasNonIntegerTranslation(),
                  "Matrix not just an integer translation?");
     // Convert from relative to the container to relative to the
     // ThebesLayer itself.
     nsIntRegion rgn = data->mVisibleRegion;
     rgn.MoveBy(-nsIntPoint(PRInt32(transform.x0), PRInt32(transform.y0)));
     layer->SetVisibleRegion(rgn);
-  } else {
-    NS_ERROR("Only 2D transformations currently supported");
   }
 
   nsIntRegion transparentRegion;
   transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
   PRBool isOpaque = transparentRegion.IsEmpty();
   // For translucent ThebesLayers, try to find an opaque background
   // color that covers the entire area beneath it so we can pull that
   // color into this layer to make it opaque.
@@ -964,16 +1039,25 @@ ContainerState::ThebesLayerData::Accumul
     }
 
     mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
     mVisibleRegion.SimplifyOutward(4);
     mDrawRegion.Or(mDrawRegion, aDrawRect);
     mDrawRegion.SimplifyOutward(4);
   }
 
+  /* Mark as available for conversion to image layer if this is a nsDisplayImage and
+   * we are the first visible item in the ThebesLayerData object.
+   */
+  if (aItem->GetType() == nsDisplayItem::TYPE_IMAGE && mVisibleRegion.IsEmpty()) {
+    mImage = static_cast<nsDisplayImage*>(aItem);
+  } else {
+    mImage = nsnull;
+  }
+  
   PRBool forceTransparentSurface = PR_FALSE;
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &forceTransparentSurface);
   if (!opaque.IsEmpty()) {
     nsRegionRectIterator iter(opaque);
     nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
     for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
       // We don't use SimplifyInward here since it's not defined exactly
       // what it will discard. For our purposes the most important case
@@ -1327,16 +1411,18 @@ FrameLayerBuilder::FindOpaqueColorCoveri
 
 void
 ContainerState::CollectOldLayers()
 {
   for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
        layer = layer->GetNextSibling()) {
     if (layer->HasUserData(&gColorLayerUserData)) {
       mRecycledColorLayers.AppendElement(static_cast<ColorLayer*>(layer));
+    } else if (layer->HasUserData(&gImageLayerUserData)) {
+      mRecycledImageLayers.AppendElement(static_cast<ImageLayer*>(layer));
     } else if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
       NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type");
       mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer));
     }
   }
 }
 
 void
@@ -1605,16 +1691,17 @@ FrameLayerBuilder::HasDedicatedLayer(nsI
     return PR_FALSE;
 
   nsTArray<DisplayItemData>* array =
     (reinterpret_cast<nsTArray<DisplayItemData>*>(&propValue));
   for (PRUint32 i = 0; i < array->Length(); ++i) {
     if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
       Layer* layer = array->ElementAt(i).mLayer;
       if (!layer->HasUserData(&gColorLayerUserData) &&
+          !layer->HasUserData(&gImageLayerUserData) &&
           !layer->HasUserData(&gThebesDisplayItemLayerUserData))
         return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 /* static */ void
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -98,28 +98,33 @@
 
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIEventStateManager.h"
 #include "nsLayoutErrors.h"
 #include "nsBidiUtils.h"
 #include "nsBidiPresUtils.h"
 
+#include "gfxRect.h"
+
 // sizes (pixels) for image icon, padding and border frame
 #define ICON_SIZE        (16)
 #define ICON_PADDING     (3)
 #define ALT_BORDER_WIDTH (1)
 
 
 //we must add hooks soon
 #define IMAGE_EDITOR_CHECK 1
 
 // Default alignment value (so we can tell an unset value from a set value)
 #define ALIGN_UNSET PRUint8(-1)
 
+using namespace mozilla;
+using namespace mozilla::layers;
+
 // static icon information
 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nsnull;
 
 // cached IO service for loading icons
 nsIIOService* nsImageFrame::sIOService;
 
 // test if the width and height are fixed, looking at the style data
 static PRBool HaveFixedSize(const nsStylePosition* aStylePosition)
@@ -582,16 +587,17 @@ nsImageFrame::OnDataAvailable(imgIReques
     SourceRectToDest(*aRect);
 
 #ifdef DEBUG_decode
   printf("Source rect (%d,%d,%d,%d) -> invalidate dest rect (%d,%d,%d,%d)\n",
          aRect->x, aRect->y, aRect->width, aRect->height,
          r.x, r.y, r.width, r.height);
 #endif
 
+  mImageContainer = nsnull;
   Invalidate(r);
   
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnStopDecode(imgIRequest *aRequest,
                            nsresult aStatus,
@@ -661,16 +667,17 @@ nsImageFrame::FrameChanged(imgIContainer
   }
 
   nsRect r = (*aDirtyRect == nsIntRect::GetMaxSizedIntRect()) ?
     GetInnerArea() :
     SourceRectToDest(*aDirtyRect);
 
   // Update border+content to account for image change
   Invalidate(r);
+  mImageContainer = nsnull;
   return NS_OK;
 }
 
 void
 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
 {
   // if mIntrinsicSize.width and height are 0, then we should
   // check to see if the size is already known by the image container.
@@ -1168,49 +1175,99 @@ static void PaintDebugImageMap(nsIFrame*
   aCtx->SetColor(NS_RGB(0, 0, 0));
   aCtx->PushState();
   aCtx->Translate(inner.x, inner.y);
   f->GetImageMap(pc)->Draw(aFrame, *aCtx);
   aCtx->PopState();
 }
 #endif
 
-/**
- * Note that nsDisplayImage does not receive events. However, an image element
- * is replaced content so its background will be z-adjacent to the
- * image itself, and hence receive events just as if the image itself
- * received events.
- */
-class nsDisplayImage : public nsDisplayItem {
-public:
-  nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame,
-                 imgIContainer* aImage)
-    : nsDisplayItem(aBuilder, aFrame), mImage(aImage) {
-    MOZ_COUNT_CTOR(nsDisplayImage);
-  }
-  virtual ~nsDisplayImage() {
-    MOZ_COUNT_DTOR(nsDisplayImage);
-  }
-  virtual void Paint(nsDisplayListBuilder* aBuilder,
-                     nsIRenderingContext* aCtx);
-  NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE)
-private:
-  nsCOMPtr<imgIContainer> mImage;
-};
-
 void
 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
                       nsIRenderingContext* aCtx) {
   static_cast<nsImageFrame*>(mFrame)->
     PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage,
                aBuilder->ShouldSyncDecodeImages()
                  ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
                  : (PRUint32) imgIContainer::FLAG_NONE);
 }
 
+nsCOMPtr<imgIContainer>
+nsDisplayImage::GetImage()
+{
+  return mImage;
+}
+
+nsRefPtr<ImageContainer>
+nsDisplayImage::GetContainer(LayerManager* aManager)
+{
+  return static_cast<nsImageFrame*>(mFrame)->GetContainer(aManager, mImage);
+}
+
+void
+nsDisplayImage::ConfigureLayer(ImageLayer* aLayer)
+{
+  aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
+  
+  PRInt32 factor = nsPresContext::AppUnitsPerCSSPixel();
+  nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
+
+  nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
+  gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
+  destRect.ScaleInverse(factor); 
+
+  PRInt32 imageWidth;
+  PRInt32 imageHeight;
+  mImage->GetWidth(&imageWidth);
+  mImage->GetHeight(&imageHeight);
+
+  gfxMatrix transform;
+  transform.Translate(destRect.pos);
+  transform.Scale(destRect.size.width/imageWidth,
+                  destRect.size.height/imageHeight);
+  aLayer->SetTransform(gfx3DMatrix::From2D(transform));
+
+  aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
+}
+
+nsRefPtr<ImageContainer>
+nsImageFrame::GetContainer(LayerManager* aManager, imgIContainer* aImage)
+{
+  if (mImageContainer && mImageContainer->Manager() == aManager) {
+    return mImageContainer;
+  }
+
+  if (aImage->GetType() != imgIContainer::TYPE_RASTER) {
+    return nsnull;
+  }
+  
+  CairoImage::Data cairoData;
+  nsRefPtr<gfxASurface> imageSurface;
+  aImage->GetFrame(imgIContainer::FRAME_CURRENT,
+                   imgIContainer::FLAG_SYNC_DECODE,
+                   getter_AddRefs(imageSurface));
+  cairoData.mSurface = imageSurface;
+  aImage->GetWidth(&cairoData.mSize.width);
+  aImage->GetHeight(&cairoData.mSize.height);
+
+  mImageContainer = aManager->CreateImageContainer();
+  NS_ASSERTION(mImageContainer, "Failed to create ImageContainer!");
+  
+  // Now create a CairoImage to display the surface.
+  Image::Format cairoFormat = Image::CAIRO_SURFACE;
+  nsRefPtr<Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
+  NS_ASSERTION(image, "Failed to create Image");
+
+  NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
+  static_cast<CairoImage*>(image.get())->SetData(cairoData);
+  mImageContainer->SetCurrentImage(image);
+
+  return mImageContainer;
+}
+
 void
 nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
                          const nsRect& aDirtyRect, imgIContainer* aImage,
                          PRUint32 aFlags)
 {
   // Render the image into our content area (the area inside
   // the borders and padding)
   NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -47,28 +47,36 @@
 #include "nsIImageFrame.h"
 #include "nsIIOService.h"
 #include "nsIObserver.h"
 
 #include "imgIRequest.h"
 #include "nsStubImageDecoderObserver.h"
 #include "imgIDecoderObserver.h"
 
+#include "Layers.h"
+#include "ImageLayers.h"
+#include "nsDisplayList.h"
+#include "imgIContainer.h"
+
 class nsIFrame;
 class nsImageMap;
 class nsIURI;
 class nsILoadGroup;
 struct nsHTMLReflowState;
 struct nsHTMLReflowMetrics;
 struct nsSize;
 class nsDisplayImage;
 class nsPresContext;
 class nsImageFrame;
 class nsTransform2D;
 
+using namespace mozilla;
+using namespace mozilla::layers;
+
 class nsImageListener : public nsStubImageDecoderObserver
 {
 public:
   nsImageListener(nsImageFrame *aFrame);
   virtual ~nsImageListener();
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
@@ -173,16 +181,19 @@ public:
 
   nsRect GetInnerArea() const;
 
   nsImageMap* GetImageMap(nsPresContext* aPresContext);
 
   virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
                                  InlineMinWidthData *aData);
 
+  nsRefPtr<ImageContainer> GetContainer(LayerManager* aManager,
+                                        imgIContainer* aImage);
+
 protected:
   virtual ~nsImageFrame();
 
   void EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext);
 
   virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
                              nsSize aCBSize, nscoord aAvailableWidth,
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
@@ -213,17 +224,17 @@ protected:
   void DisplayAltText(nsPresContext*      aPresContext,
                       nsIRenderingContext& aRenderingContext,
                       const nsString&      aAltText,
                       const nsRect&        aRect);
 
   void PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
                   const nsRect& aDirtyRect, imgIContainer* aImage,
                   PRUint32 aFlags);
-                  
+
 protected:
   friend class nsImageListener;
   nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   nsresult OnDataAvailable(imgIRequest *aRequest, PRBool aCurrentFrame,
                            const nsIntRect *rect);
   nsresult OnStopDecode(imgIRequest *aRequest,
                         nsresult aStatus,
                         const PRUnichar *aStatusArg);
@@ -288,16 +299,18 @@ private:
 
   nsSize mComputedSize;
   nsIFrame::IntrinsicSize mIntrinsicSize;
   nsSize mIntrinsicRatio;
 
   PRBool mDisplayingIcon;
 
   static nsIIOService* sIOService;
+  
+  nsRefPtr<ImageContainer> mImageContainer; 
 
   /* loading / broken image icon support */
 
   // XXXbz this should be handled by the prescontext, I think; that
   // way we would have a single iconload per mozilla session instead
   // of one per document...
 
   // LoadIcons: initiate the loading of the static icons used to show
@@ -355,9 +368,46 @@ private:
   };
   
 public:
   static IconLoad* gIconLoad; // singleton pattern: one LoadIcons instance is used
   
   friend class nsDisplayImage;
 };
 
+/**
+ * Note that nsDisplayImage does not receive events. However, an image element
+ * is replaced content so its background will be z-adjacent to the
+ * image itself, and hence receive events just as if the image itself
+ * received events.
+ */
+class nsDisplayImage : public nsDisplayItem {
+public:
+  nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame,
+                 imgIContainer* aImage)
+    : nsDisplayItem(aBuilder, aFrame), mImage(aImage) {
+    MOZ_COUNT_CTOR(nsDisplayImage);
+  }
+  virtual ~nsDisplayImage() {
+    MOZ_COUNT_DTOR(nsDisplayImage);
+  }
+  virtual void Paint(nsDisplayListBuilder* aBuilder,
+                     nsIRenderingContext* aCtx);
+  nsCOMPtr<imgIContainer> GetImage();
+ 
+  /**
+   * Returns an ImageContainer for this image if the image type
+   * supports it (TYPE_RASTER only).
+   */
+  nsRefPtr<ImageContainer> GetContainer(LayerManager* aManager);
+  
+  /**
+   * Configure an ImageLayer for this display item.
+   * Set the required filter and scaling transform.
+   */
+  void ConfigureLayer(ImageLayer* aLayer);
+
+  NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE)
+private:
+  nsCOMPtr<imgIContainer> mImage;
+};
+
 #endif /* nsImageFrame_h___ */
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -1713,20 +1713,23 @@ nsObjectFrame::PrintPlugin(nsIRenderingC
   // XXX Nav 4.x always sent a SetWindow call after print. Should we do the same?
   // XXX Calling DidReflow here makes no sense!!!
   nsDidReflowStatus status = NS_FRAME_REFLOW_FINISHED; // should we use a special status?
   frame->DidReflow(presContext,
                    nsnull, status);  // DidReflow will take care of it
 }
 
 ImageContainer*
-nsObjectFrame::GetImageContainer()
-{
-  nsRefPtr<LayerManager> manager =
-    nsContentUtils::LayerManagerForDocument(mContent->GetOwnerDoc());
+nsObjectFrame::GetImageContainer(LayerManager* aManager)
+{
+  nsRefPtr<LayerManager> manager = aManager;
+
+  if (!manager) {
+    manager = nsContentUtils::LayerManagerForDocument(mContent->GetOwnerDoc());
+  }
   if (!manager) {
     return nsnull;
   }
 
   // XXX - in the future image containers will be manager independent and
   // we can remove the manager equals check and only check the backend type.
   if (mImageContainer && 
       (!mImageContainer->Manager() || mImageContainer->Manager() == manager) &&
@@ -1836,17 +1839,17 @@ nsObjectFrame::BuildLayer(nsDisplayListB
     layer = aManager->CreateImageLayer();
   }
 
   if (!layer)
     return nsnull;
 
   NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "ObjectFrame works only with ImageLayer");
   // Create image
-  nsRefPtr<ImageContainer> container = GetImageContainer();
+  nsRefPtr<ImageContainer> container = GetImageContainer(aManager);
   if (!container)
     return nsnull;
 
   if (!mInstanceOwner->SetCurrentImage(container)) {
     return nsnull;
   }
 
   ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get());
--- a/layout/generic/nsObjectFrame.h
+++ b/layout/generic/nsObjectFrame.h
@@ -178,17 +178,17 @@ public:
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem);
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager);
 
-  ImageContainer* GetImageContainer();
+  ImageContainer* GetImageContainer(LayerManager* aManager = nsnull);
 
   /**
    * If aContent has a nsObjectFrame, then prepare it for a DocShell swap.
    * @see nsSubDocumentFrame::BeginSwapDocShells.
    * There will be a call to EndSwapDocShells after we were moved to the
    * new view tree.
    */
   static void BeginSwapDocShells(nsIContent* aContent, void*);