Bug 700926; reshuffle background image drawing. r=roc
authorNicholas Cameron <ncameron@mozilla.com>
Fri, 19 Jul 2013 20:40:02 +1200
changeset 151621 612b03080cb034bb3295a084d7ebe2a7c6de20fa
parent 151620 783e53214215836d14431eeb079d22249df8882f
child 151622 02e9ac646def047ce411227f41d1e4643ae1c859
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs700926
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 700926; reshuffle background image drawing. r=roc
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/generic/nsIFrame.h
layout/style/ImageLoader.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2618,24 +2618,18 @@ nsCSSRendering::PaintBackgroundWithSC(ns
     startLayer = (int32_t)bg->mImageCount - 1;
     nLayers = bg->mImageCount;
   }
 
   // Ensure we get invalidated for loads of the image.  We need to do
   // this here because this might be the only code that knows about the
   // association of the style data with the frame.
   if (aBackgroundSC != aForFrame->StyleContext()) {
-    ImageLoader* loader = aPresContext->Document()->StyleImageLoader();
-
     NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, bg, startLayer, nLayers) {
-      if (bg->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
-        imgIRequest *image = bg->mLayers[i].mImage.GetImageData();
-
-        loader->AssociateRequestToFrame(image, aForFrame);
-      }
+      aForFrame->AssociateImage(bg->mLayers[i].mImage, aPresContext);
     }
   }
 
   // The background color is rendered over the entire dirty area,
   // even if the image isn't.
   if (drawBackgroundColor && !isCanvasFrame) {
     DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
   }
@@ -2667,20 +2661,20 @@ nsCSSRendering::PaintBackgroundWithSC(ns
           clipSet = true;
         }
       }
       if ((aLayer < 0 || i == (uint32_t)startLayer) &&
           !clipState.mDirtyRectGfx.IsEmpty()) {
         nsBackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame,
             aFlags, aBorderArea, clipState.mBGClipArea, *bg, layer);
         if (!state.mFillArea.IsEmpty()) {
-          state.mImageRenderer.Draw(aPresContext, aRenderingContext,
-                                    state.mDestArea, state.mFillArea,
-                                    state.mAnchor + aBorderArea.TopLeft(),
-                                    clipState.mDirtyRect);
+          state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext,
+                                              state.mDestArea, state.mFillArea,
+                                              state.mAnchor + aBorderArea.TopLeft(),
+                                              clipState.mDirtyRect);
         }
       }
     }
   }
 }
 
 void
 nsCSSRendering::PaintBackgroundColorWithSC(nsPresContext* aPresContext,
@@ -4605,75 +4599,109 @@ nsImageRenderer::SetPreferredSize(const 
                   : aDefaultSize.width;
   mSize.height = aIntrinsicSize.mHasHeight
                   ? aIntrinsicSize.mHeight
                   : aDefaultSize.height;
 }
 
 void
 nsImageRenderer::Draw(nsPresContext*       aPresContext,
-                      nsRenderingContext& aRenderingContext,
-                      const nsRect&        aDest,
+                      nsRenderingContext&  aRenderingContext,
+                      const nsRect&        aDirtyRect,
                       const nsRect&        aFill,
-                      const nsPoint&       aAnchor,
-                      const nsRect&        aDirty)
+                      const nsRect&        aDest,
+                      uint32_t             aFlags /* = imgIContainer::FLAG_NONE */)
 {
   if (!mIsReady) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
     return;
   }
-
   if (aDest.IsEmpty() || aFill.IsEmpty() ||
-      mSize.width <= 0 || mSize.height <= 0)
+      mSize.width <= 0 || mSize.height <= 0) {
     return;
+  }
 
   gfxPattern::GraphicsFilter graphicsFilter =
     nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
 
   switch (mType) {
     case eStyleImageType_Image:
     {
-      uint32_t drawFlags = imgIContainer::FLAG_NONE;
-      if (mFlags & FLAG_SYNC_DECODE_IMAGES) {
-        drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
-      }
-      if (mFlags & FLAG_PAINTING_TO_WINDOW) {
-        drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
-      }
-
-      nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer,
-          nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
-                    nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
-          graphicsFilter,
-          aDest, aFill, aAnchor, aDirty, drawFlags);
-      break;
+      nsLayoutUtils::DrawSingleImage(&aRenderingContext, mImageContainer,
+                                     graphicsFilter, aFill, aDirtyRect,
+                                     nullptr, aFlags);
+      return;
     }
     case eStyleImageType_Gradient:
+    {
       nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
-          mGradientData, aDirty, aDest, aFill);
-      break;
+                                    mGradientData, aDirtyRect, aDest, aFill);
+      return;
+    }
     case eStyleImageType_Element:
+    {
       if (mPaintServerFrame) {
         nsSVGIntegrationUtils::DrawPaintServer(
             &aRenderingContext, mForFrame, mPaintServerFrame, graphicsFilter,
-            aDest, aFill, aAnchor, aDirty, mSize);
+            aDest, aFill, aDest.TopLeft(), aDirtyRect, mSize);
       } else {
         NS_ASSERTION(mImageElementSurface.mSurface, "Surface should be ready.");
         nsRefPtr<gfxDrawable> surfaceDrawable =
           new gfxSurfaceDrawable(mImageElementSurface.mSurface,
                                  mImageElementSurface.mSize);
         nsLayoutUtils::DrawPixelSnapped(
             &aRenderingContext, surfaceDrawable, graphicsFilter,
-            aDest, aFill, aAnchor, aDirty);
+            aDest, aFill, aDest.TopLeft(), aDirtyRect);
       }
-      break;
+      return;
+    }
     case eStyleImageType_Null:
     default:
-      break;
+      return;
+  }
+}
+
+void
+nsImageRenderer::DrawBackground(nsPresContext*       aPresContext,
+                                nsRenderingContext&  aRenderingContext,
+                                const nsRect&        aDest,
+                                const nsRect&        aFill,
+                                const nsPoint&       aAnchor,
+                                const nsRect&        aDirty)
+{
+  if (!mIsReady) {
+    NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
+    return;
+  }
+  if (aDest.IsEmpty() || aFill.IsEmpty() ||
+      mSize.width <= 0 || mSize.height <= 0) {
+    return;
   }
+
+  if (mType == eStyleImageType_Image) {
+    gfxPattern::GraphicsFilter graphicsFilter =
+      nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
+
+    uint32_t drawFlags = imgIContainer::FLAG_NONE;
+    if (mFlags & FLAG_SYNC_DECODE_IMAGES) {
+      drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
+    }
+    if (mFlags & FLAG_PAINTING_TO_WINDOW) {
+      drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
+    }
+
+    nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer,
+        nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
+                  nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
+        graphicsFilter,
+        aDest, aFill, aAnchor, aDirty, drawFlags);
+    return;
+  }
+
+  Draw(aPresContext, aRenderingContext, aDirty, aFill, aDest);
 }
 
 bool
 nsImageRenderer::IsRasterImage()
 {
   if (mType != eStyleImageType_Image || !mImageContainer)
     return false;
   return mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -90,17 +90,17 @@ struct CSSSizeOrRatio
 }
 
 /**
  * This is a small wrapper class to encapsulate image drawing that can draw an
  * nsStyleImage image, which may internally be a real image, a sub image, or a
  * CSS gradient.
  *
  * @note Always call the member functions in the order of PrepareImage(),
- * SetSize(), and Draw().
+ * SetSize(), and Draw*().
  */
 class nsImageRenderer {
 public:
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::ImageContainer ImageContainer;
 
   enum {
     FLAG_SYNC_DECODE_IMAGES = 0x01,
@@ -157,29 +157,42 @@ public:
    * specified and the default size where it is not. Used as the unscaled size
    * when rendering the image.
    */
   void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize,
                         const nsSize& aDefaultSize);
 
   /**
    * Draws the image to the target rendering context.
-   * @see nsLayoutUtils::DrawImage() for other parameters
+   * @see nsLayoutUtils::DrawImage() for other parameters.
    */
   void Draw(nsPresContext*       aPresContext,
-            nsRenderingContext& aRenderingContext,
-            const nsRect&        aDest,
+            nsRenderingContext&  aRenderingContext,
+            const nsRect&        aDirtyRect,
             const nsRect&        aFill,
-            const nsPoint&       aAnchor,
-            const nsRect&        aDirty);
+            const nsRect&        aDest,
+            uint32_t             aFlags = imgIContainer::FLAG_NONE);
+  /**
+   * Draws the image to the target rendering context using background-specific
+   * arguments.
+   * @see nsLayoutUtils::DrawImage() for parameters.
+   */
+  void DrawBackground(nsPresContext*       aPresContext,
+                      nsRenderingContext&  aRenderingContext,
+                      const nsRect&        aDest,
+                      const nsRect&        aFill,
+                      const nsPoint&       aAnchor,
+                      const nsRect&        aDirty);
 
   bool IsRasterImage();
   bool IsAnimatedImage();
   already_AddRefed<ImageContainer> GetContainer(LayerManager* aManager);
 
+  bool IsReady() { return mIsReady; }
+
 private:
   nsIFrame*                 mForFrame;
   const nsStyleImage*       mImage;
   nsStyleImageType          mType;
   nsCOMPtr<imgIContainer>   mImageContainer;
   nsRefPtr<nsStyleGradient> mGradientData;
   nsIFrame*                 mPaintServerFrame;
   nsLayoutUtils::SurfaceFromElementResult mImageElementSurface;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -25,16 +25,17 @@
 #include "nsStyleContext.h"
 #include "nsStyleStruct.h"
 #include "nsStyleStructFwd.h"
 #include "nsHTMLReflowMetrics.h"
 #include "nsFrameList.h"
 #include "nsIContent.h"
 #include "nsAlgorithm.h"
 #include "mozilla/layout/FrameChildList.h"
+#include "mozilla/css/ImageLoader.h"
 #include "FramePropertyTable.h"
 #include "mozilla/TypedEnum.h"
 #include <algorithm>
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/AccTypes.h"
 #endif
 
@@ -1347,16 +1348,34 @@ public:
   ContentOffsets GetContentOffsetsFromPoint(nsPoint aPoint,
                                             uint32_t aFlags = 0);
 
   virtual ContentOffsets GetContentOffsetsFromPointExternal(nsPoint aPoint,
                                                             uint32_t aFlags = 0)
   { return GetContentOffsetsFromPoint(aPoint, aFlags); }
 
   /**
+   * Ensure that aImage gets notifed when the underlying image request loads
+   * or animates.
+   */
+  void AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext)
+  {
+    if (aImage.GetType() != eStyleImageType_Image) {
+      return;
+    }
+
+    imgIRequest *req = aImage.GetImageData();
+    mozilla::css::ImageLoader* loader =
+      aPresContext->Document()->StyleImageLoader();
+
+    // If this fails there's not much we can do ...
+    loader->AssociateRequestToFrame(req, this);
+  }
+
+  /**
    * This structure holds information about a cursor. mContainer represents a
    * loaded image that should be preferred. If it is not possible to use it, or
    * if it is null, mCursor should be used.
    */
   struct MOZ_STACK_CLASS Cursor {
     nsCOMPtr<imgIContainer> mContainer;
     int32_t                 mCursor;
     bool                    mHaveHotspot;
--- a/layout/style/ImageLoader.h
+++ b/layout/style/ImageLoader.h
@@ -1,15 +1,18 @@
 /* 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/. */
 
 // A class that handles style system image loads (other image loads are handled
 // by the nodes in the content tree).
 
+#ifndef mozilla_css_ImageLoader_h___
+#define mozilla_css_ImageLoader_h___
+
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsCSSValue.h"
 #include "imgIRequest.h"
 #include "imgIOnloadBlocker.h"
 #include "imgINotificationObserver.h"
 #include "mozilla/Attributes.h"
 
@@ -112,8 +115,10 @@ private:
   ImageHashSet mImages;
 
   // Are we cloning?  If so, ignore any notifications we get.
   bool mInClone;
 };
 
 } // namespace css
 } // namespace mozilla
+
+#endif /* mozilla_css_ImageLoader_h___ */
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1704,16 +1704,38 @@ nsStyleImage::IsComplete() const
              (status & imgIRequest::STATUS_FRAME_COMPLETE);
     }
     default:
       NS_NOTREACHED("unexpected image type");
       return false;
   }
 }
 
+bool
+nsStyleImage::IsLoaded() const
+{
+  switch (mType) {
+    case eStyleImageType_Null:
+      return false;
+    case eStyleImageType_Gradient:
+    case eStyleImageType_Element:
+      return true;
+    case eStyleImageType_Image:
+    {
+      uint32_t status = imgIRequest::STATUS_ERROR;
+      return NS_SUCCEEDED(mImage->GetImageStatus(&status)) &&
+             !(status & imgIRequest::STATUS_ERROR) &&
+             (status & imgIRequest::STATUS_LOAD_COMPLETE);
+    }
+    default:
+      NS_NOTREACHED("unexpected image type");
+      return false;
+  }
+}
+
 static inline bool
 EqualRects(const nsStyleSides* aRect1, const nsStyleSides* aRect2)
 {
   return aRect1 == aRect2 || /* handles null== null, and optimize */
          (aRect1 && aRect2 && *aRect1 == *aRect2);
 }
 
 bool
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -238,16 +238,22 @@ struct nsStyleImage {
   bool IsOpaque() const;
   /**
    * @return true if this image is fully loaded, and its size is calculated;
    * always returns true if |mType| is |eStyleImageType_Gradient| or
    * |eStyleImageType_Element|.
    */
   bool IsComplete() const;
   /**
+   * @return true if this image is loaded without error;
+   * always returns true if |mType| is |eStyleImageType_Gradient| or
+   * |eStyleImageType_Element|.
+   */
+  bool IsLoaded() const;
+  /**
    * @return true if it is 100% confident that this image contains no pixel
    * to draw.
    */
   bool IsEmpty() const {
     // There are some other cases when the image will be empty, for example
     // when the crop rect is empty. However, checking the emptiness of crop
     // rect is non-trivial since each side value can be specified with
     // percentage unit, which can not be evaluated until the source image size