Bug 832400 - Add an imgIContainer flag HIGH_QUALITY_SCALING that controls whether to use the high quality scaler, and only use it when we're drawing to a window. r=tn
authorJoe Drew <joe@drew.ca>
Thu, 25 Apr 2013 18:08:58 -0400
changeset 129932 2a4a6de0cd6bcfef5ddf1dde57eac647b03ab1a8
parent 129931 8a2536988191431002107005e92781caa80d8e78
child 129933 120f3f2296432373954788f829bdbff4086e7cc5
push id27098
push userjdrew@mozilla.com
push dateThu, 25 Apr 2013 22:09:22 +0000
treeherdermozilla-inbound@2a4a6de0cd6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs832400
milestone23.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 832400 - Add an imgIContainer flag HIGH_QUALITY_SCALING that controls whether to use the high quality scaler, and only use it when we're drawing to a window. r=tn
image/public/imgIContainer.idl
image/src/RasterImage.cpp
image/src/RasterImage.h
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/generic/nsImageFrame.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -52,17 +52,17 @@ native nsSize(nsSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(0c1caf24-bce7-4db5-971d-8e1b6ed07540)]
+[scriptable, builtinclass, uuid(55531bcd-7d4a-4da0-ab87-7c64c5ae5e12)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -125,23 +125,28 @@ interface imgIContainer : nsISupports
    * it's not already premultiplied in the image data.
    *
    * FLAG_DECODE_NO_COLORSPACE_CONVERSION: Do not do any colorspace conversion;
    * ignore any embedded profiles, and don't convert to any particular destination
    * space.
    *
    * FLAG_CLAMP: Extend the image to the fill area by clamping image sample
    * coordinates instead of by tiling. This only affects 'draw'.
+   *
+   * FLAG_HIGH_QUALITY_SCALING: A hint as to whether this image should be
+   * scaled using the high quality scaler. Do not set this if not drawing to
+   * a window or not listening to invalidations.
    */
 
   const long FLAG_NONE            = 0x0;
   const long FLAG_SYNC_DECODE     = 0x1;
   const long FLAG_DECODE_NO_PREMULTIPLY_ALPHA = 0x2;
   const long FLAG_DECODE_NO_COLORSPACE_CONVERSION = 0x4;
   const long FLAG_CLAMP           = 0x8;
+  const long FLAG_HIGH_QUALITY_SCALING = 0x10;
 
   /**
     * Constants for specifying various "special" frames.
     *
     * FRAME_FIRST: The first frame
     * FRAME_CURRENT: The current frame
     *
     * FRAME_MAX_VALUE should be set to the value of the maximum constant above,
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -2939,24 +2939,27 @@ RasterImage::CanQualityScale(const gfxSi
       return false;
   }
 
   return true;
 }
 
 bool
 RasterImage::CanScale(gfxPattern::GraphicsFilter aFilter,
-                      gfxSize aScale)
+                      gfxSize aScale, uint32_t aFlags)
 {
 // The high-quality scaler requires Skia.
 #ifdef MOZ_ENABLE_SKIA
   // We don't use the scaler for animated or multipart images to avoid doing a
   // bunch of work on an image that just gets thrown away.
+  // We only use the scaler when drawing to the window because, if we're not
+  // drawing to a window (eg a canvas), updates to that image will be ignored.
   if (gHQDownscaling && aFilter == gfxPattern::FILTER_GOOD &&
-      !mAnim && mDecoded && !mMultipart && CanQualityScale(aScale)) {
+      !mAnim && mDecoded && !mMultipart && CanQualityScale(aScale) &&
+      (aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
     gfxFloat factor = gHQDownscalingMinFactor / 1000.0;
 
     return (aScale.width < factor || aScale.height < factor);
   }
 #endif
 
   return false;
 }
@@ -3002,27 +3005,28 @@ RasterImage::ScalingDone(ScaleRequest* r
 }
 
 void
 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                           gfxContext *aContext,
                                           gfxPattern::GraphicsFilter aFilter,
                                           const gfxMatrix &aUserSpaceToImageSpace,
                                           const gfxRect &aFill,
-                                          const nsIntRect &aSubimage)
+                                          const nsIntRect &aSubimage,
+                                          uint32_t aFlags)
 {
   imgFrame *frame = aFrame;
   nsIntRect framerect = frame->GetRect();
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
   nsIntRect subimage = aSubimage;
 
-  if (CanScale(aFilter, scale)) {
+  if (CanScale(aFilter, scale, aFlags)) {
     // If scale factor is still the same that we scaled for and
     // ScaleWorker isn't still working, then we can use pre-downscaled frame.
     // If scale factor has changed, order new request.
     // FIXME: Current implementation doesn't support pre-downscale
     // mechanism for multiple sizes from same src, since we cache
     // pre-downscaled frame only for the latest requested scale.
     // The solution is to cache more than one scaled image frame
     // for each RasterImage.
@@ -3145,17 +3149,17 @@ RasterImage::Draw(gfxContext *aContext,
 
   uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
                                                    : GetCurrentImgFrameIndex();
   imgFrame* frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
   }
 
-  DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage);
+  DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage, aFlags);
 
   if (mDecoded && !mDrawStartTime.IsNull()) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds()));
       // clear the value of mDrawStartTime
       mDrawStartTime = TimeStamp();
   }
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -580,17 +580,18 @@ private:
   nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
                                 DecodeRequest* request = nullptr);
 
   void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     gfxPattern::GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
-                                    const nsIntRect &aSubimage);
+                                    const nsIntRect &aSubimage,
+                                    uint32_t aFlags);
 
   nsresult CopyFrame(uint32_t aWhichFrame,
                      uint32_t aFlags,
                      gfxImageSurface **_retval);
   /**
    * Advances the animation. Typically, this will advance a single frame, but it
    * may advance multiple frames. This may happen if we have infrequently
    * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
@@ -816,17 +817,17 @@ private: // data
   nsresult SyncDecode();
   nsresult InitDecoder(bool aDoSizeDecode, bool aIsSynchronous = false);
   nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
   nsresult DecodeSomeData(uint32_t aMaxBytes);
   bool     IsDecodeFinished();
   TimeStamp mDrawStartTime;
 
   inline bool CanQualityScale(const gfxSize& scale);
-  inline bool CanScale(gfxPattern::GraphicsFilter aFilter, gfxSize aScale);
+  inline bool CanScale(gfxPattern::GraphicsFilter aFilter, gfxSize aScale, uint32_t aFlags);
 
   struct ScaleResult
   {
     ScaleResult()
      : status(SCALE_INVALID)
     {}
 
     gfxSize scale;
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2945,16 +2945,19 @@ nsCSSRendering::PrepareBackgroundLayer(n
    *   background-position
    *   background-repeat
    */
 
   uint32_t irFlags = 0;
   if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
     irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
   }
+  if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
+    irFlags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
+  }
 
   nsBackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
   if (!state.mImageRenderer.PrepareImage()) {
     // There's no image or it's not ready to be painted.
     return state;
   }
 
   // The frame to which the background is attached
@@ -4632,19 +4635,24 @@ nsImageRenderer::Draw(nsPresContext*    
     return;
 
   gfxPattern::GraphicsFilter graphicsFilter =
     nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
 
   switch (mType) {
     case eStyleImageType_Image:
     {
-      uint32_t drawFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
-                             ? (uint32_t) imgIContainer::FLAG_SYNC_DECODE
-                             : (uint32_t) imgIContainer::FLAG_NONE;
+      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;
     }
     case eStyleImageType_Gradient:
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -28,17 +28,18 @@ class nsRenderingContext;
  * ComputeSize(), and Draw().
  */
 class nsImageRenderer {
 public:
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::ImageContainer ImageContainer;
 
   enum {
-    FLAG_SYNC_DECODE_IMAGES = 0x01
+    FLAG_SYNC_DECODE_IMAGES = 0x01,
+    FLAG_PAINTING_TO_WINDOW = 0x02
   };
   nsImageRenderer(nsIFrame* aForFrame, const nsStyleImage* aImage, uint32_t aFlags);
   ~nsImageRenderer();
   /**
    * Populates member variables to get ready for rendering.
    * @return true iff the image is ready, and there is at least a pixel to
    * draw.
    */
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1213,21 +1213,25 @@ static void PaintDebugImageMap(nsIFrame*
   f->GetImageMap()->Draw(aFrame, *aCtx);
   aCtx->PopState();
 }
 #endif
 
 void
 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
                       nsRenderingContext* aCtx) {
+  uint32_t flags = imgIContainer::FLAG_NONE;
+  if (aBuilder->ShouldSyncDecodeImages()) {
+    flags |= imgIContainer::FLAG_SYNC_DECODE;
+  }
+  if (aBuilder->IsPaintingToWindow()) {
+    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
+  }
   static_cast<nsImageFrame*>(mFrame)->
-    PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage,
-               aBuilder->ShouldSyncDecodeImages()
-                 ? (uint32_t) imgIContainer::FLAG_SYNC_DECODE
-                 : (uint32_t) imgIContainer::FLAG_NONE);
+    PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
 }
 
 already_AddRefed<ImageContainer>
 nsDisplayImage::GetContainer(LayerManager* aManager,
                              nsDisplayListBuilder* aBuilder)
 {
   nsRefPtr<ImageContainer> container;
   nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -338,21 +338,24 @@ nsImageBoxFrame::PaintImage(nsRenderingC
         nsLayoutUtils::GetGraphicsFilterForFrame(this),
         rect, dirty, nullptr, aFlags, hasSubRect ? &mSubRect : nullptr);
   }
 }
 
 void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
                               nsRenderingContext* aCtx)
 {
+  uint32_t flags = imgIContainer::FLAG_NONE;
+  if (aBuilder->ShouldSyncDecodeImages())
+    flags |= imgIContainer::FLAG_SYNC_DECODE;
+  if (aBuilder->IsPaintingToWindow())
+    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
+
   static_cast<nsImageBoxFrame*>(mFrame)->
-    PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(),
-               aBuilder->ShouldSyncDecodeImages()
-                 ? (uint32_t) imgIContainer::FLAG_SYNC_DECODE
-                 : (uint32_t) imgIContainer::FLAG_NONE);
+    PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
 }
 
 void
 nsDisplayXULImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
 {
   aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
 
   int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();