Bug 276431 Patch 10: Make imgContainer::Draw take image-viewport-size, and improve nsLayoutUtils drawing wrappers. r=roc r=joe a=blocking
authorDaniel Holbert <dholbert@cs.stanford.edu>
Wed, 08 Sep 2010 13:40:39 -0700
changeset 52215 6eb8d33411ab394b179171ec14ef4c1eccae0757
parent 52214 bf93d8c0a86996cbd0a4ade004c217aa8ebcd9e2
child 52216 f65c60f334258eae4ee11a62ef29355d68afefa7
push idunknown
push userunknown
push dateunknown
reviewersroc, joe, blocking
bugs276431
milestone2.0b6pre
Bug 276431 Patch 10: Make imgContainer::Draw take image-viewport-size, and improve nsLayoutUtils drawing wrappers. r=roc r=joe a=blocking
layout/base/nsCSSRendering.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
modules/libpr0n/public/imgIContainer.idl
modules/libpr0n/src/RasterImage.cpp
widget/src/xpwidgets/nsBaseDragService.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -3709,21 +3709,28 @@ ImageRenderer::ComputeSize(const nsSize&
 {
   NS_ASSERTION(mIsReady, "Ensure PrepareImage() has returned true "
                          "before calling me");
 
   switch (mType) {
     case eStyleImageType_Image:
     {
       nsIntSize imageIntSize;
-      mImageContainer->GetWidth(&imageIntSize.width);
-      mImageContainer->GetHeight(&imageIntSize.height);
-
-      mSize.width = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);
-      mSize.height = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);
+      PRBool gotHeight, gotWidth;
+      nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, imageIntSize,
+                                           gotWidth, gotHeight);
+
+      mSize.width = gotWidth ?
+        nsPresContext::CSSPixelsToAppUnits(imageIntSize.width) :
+        aDefault.width;
+
+      mSize.height = gotHeight ?
+        nsPresContext::CSSPixelsToAppUnits(imageIntSize.height) :
+        aDefault.height;
+
       break;
     }
     case eStyleImageType_Gradient:
       mSize = aDefault;
       break;
 #ifdef MOZ_SVG
     case eStyleImageType_Element:
     {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3038,17 +3038,18 @@ DrawImageInternal(nsIRenderingContext* a
     return NS_OK;
 
   gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
   if (drawingParams.mResetCTM) {
     ctx->IdentityMatrix();
   }
 
   aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
-               drawingParams.mFillRect, drawingParams.mSubimage, aImageFlags);
+               drawingParams.mFillRect, drawingParams.mSubimage, aImageSize,
+               aImageFlags);
   return NS_OK;
 }
 
 /* static */ void
 nsLayoutUtils::DrawPixelSnapped(nsIRenderingContext* aRenderingContext,
                                 gfxDrawable*         aDrawable,
                                 gfxPattern::GraphicsFilter aFilter,
                                 const nsRect&        aDest,
@@ -3129,18 +3130,23 @@ nsLayoutUtils::DrawSingleImage(nsIRender
                                imgIContainer*       aImage,
                                gfxPattern::GraphicsFilter aGraphicsFilter,
                                const nsRect&        aDest,
                                const nsRect&        aDirty,
                                PRUint32             aImageFlags,
                                const nsRect*        aSourceArea)
 {
   nsIntSize imageSize;
-  aImage->GetWidth(&imageSize.width);
-  aImage->GetHeight(&imageSize.height);
+  if (aImage->GetType() == imgIContainer::TYPE_VECTOR) {
+    imageSize.width  = nsPresContext::AppUnitsToIntCSSPixels(aDest.width);
+    imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aDest.height);
+  } else {
+    aImage->GetWidth(&imageSize.width);
+    aImage->GetHeight(&imageSize.height);
+  }
   NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
 
   nsRect source;
   if (aSourceArea) {
     source = *aSourceArea;
   } else {
     nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
     source.SizeTo(imageSize.width*appUnitsPerCSSPixel,
@@ -3153,30 +3159,80 @@ nsLayoutUtils::DrawSingleImage(nsIRender
   // outside the image bounds, we want to honor the aSourceArea-to-aDest
   // transform but we don't want to actually tile the image.
   nsRect fill;
   fill.IntersectRect(aDest, dest);
   return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill,
                            fill.TopLeft(), aDirty, imageSize, aImageFlags);
 }
 
+/* static */ void
+nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
+                                     nsIntSize&     aImageSize, /*outparam*/
+                                     PRBool&        aGotWidth,  /*outparam*/
+                                     PRBool&        aGotHeight  /*outparam*/)
+{
+  aGotWidth  = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));
+  aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));
+
+  if ((aGotWidth && aGotHeight) ||    // Trivial success!
+      (!aGotWidth && !aGotHeight)) {  // Trivial failure!
+    return;
+  }
+
+  // If we get here, we succeeded at querying *either* the width *or* the
+  // height, but not both.
+  NS_ASSERTION(aImage->GetType() == imgIContainer::TYPE_VECTOR,
+               "GetWidth and GetHeight should only fail for vector images");
+
+  nsIFrame* rootFrame = aImage->GetRootLayoutFrame();
+  NS_ASSERTION(rootFrame,
+               "We should have a VectorImage, which should have a rootFrame");
+
+  // This falls back on failure, if we somehow end up without a rootFrame.
+  nsSize ratio = rootFrame ? rootFrame->GetIntrinsicRatio() : nsSize(0,0);
+  if (!aGotWidth) { // Have height, missing width
+    if (ratio.height != 0) { // don't divide by zero
+      aImageSize.width = NSToCoordRound(aImageSize.height *
+                                        float(ratio.width) /
+                                        float(ratio.height));
+      aGotWidth = PR_TRUE;
+    }
+  } else { // Have width, missing height
+    if (ratio.width != 0) { // don't divide by zero
+      aImageSize.height = NSToCoordRound(aImageSize.width *
+                                         float(ratio.height) /
+                                         float(ratio.width));
+      aGotHeight = PR_TRUE;
+    }
+  }
+}
+
+
 /* static */ nsresult
 nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
                          imgIContainer*       aImage,
                          gfxPattern::GraphicsFilter aGraphicsFilter,
                          const nsRect&        aDest,
                          const nsRect&        aFill,
                          const nsPoint&       aAnchor,
                          const nsRect&        aDirty,
                          PRUint32             aImageFlags)
 {
   nsIntSize imageSize;
-  aImage->GetWidth(&imageSize.width);
-  aImage->GetHeight(&imageSize.height);
-  NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
+  PRBool gotHeight, gotWidth;
+  ComputeSizeForDrawing(aImage, imageSize, gotWidth, gotHeight);
+
+  // fallback size based on aFill.
+  if (!gotWidth) {
+    imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width);
+  }
+  if (!gotHeight) {
+    imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height);
+  }
 
   return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
                            aDest, aFill, aAnchor, aDirty,
                            imageSize, aImageFlags);
 }
 
 /* static */ nsRect
 nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1012,16 +1012,35 @@ public:
                                   imgIContainer*       aImage,
                                   gfxPattern::GraphicsFilter aGraphicsFilter,
                                   const nsRect&        aDest,
                                   const nsRect&        aDirty,
                                   PRUint32             aImageFlags,
                                   const nsRect*        aSourceArea = nsnull);
 
   /**
+   * Given an imgIContainer, this method attempts to obtain an intrinsic
+   * px-valued height & width for it.  If the imgIContainer has a non-pixel
+   * value for either height or width, this method tries to generate a pixel
+   * value for that dimension using the intrinsic ratio (if available).
+   *
+   * This method will always set aGotWidth and aGotHeight to indicate whether
+   * we were able to successfully obtain (or compute) a value for each
+   * dimension.
+   *
+   * NOTE: This method is similar to ComputeSizeWithIntrinsicDimensions.  The
+   * difference is that this one is simpler and is suited to places where we
+   * have less information about the frame tree.
+   */
+  static void ComputeSizeForDrawing(imgIContainer* aImage,
+                                    nsIntSize&     aImageSize,
+                                    PRBool&        aGotWidth,
+                                    PRBool&        aGotHeight);
+
+  /**
    * Given a source area of an image (in appunits) and a destination area
    * that we want to map that source area too, computes the area that
    * would be covered by the whole image. This is useful for passing to
    * the aDest parameter of DrawImage, when we want to draw a subimage
    * of an overall image.
    */
   static nsRect GetWholeImageDestination(const nsIntSize& aWholeImageSize,
                                          const nsRect& aImageSourceArea,
--- a/modules/libpr0n/public/imgIContainer.idl
+++ b/modules/libpr0n/public/imgIContainer.idl
@@ -47,16 +47,17 @@ interface imgIDecoderObserver;
 %{C++
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "gfxPattern.h"
 #include "gfxASurface.h"
 #include "nsRect.h"
+#include "nsSize.h"
 #include "limits.h"
 
 class nsIFrame;
 
 namespace mozilla  {
 namespace imagelib {
   /**
    * A sentinel value that can be passed to various imagelib methods
@@ -71,27 +72,28 @@ namespace imagelib {
 [ptr] native gfxImageSurface(gfxImageSurface);
 [ptr] native gfxASurface(gfxASurface);
 native gfxImageFormat(gfxASurface::gfxImageFormat);
 [ptr] native gfxContext(gfxContext);
 [ref] native gfxMatrix(gfxMatrix);
 [ref] native gfxRect(gfxRect);
 native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
 [ref] native nsIntRect(nsIntRect);
+[ref] native nsIntSize(nsIntSize);
 [ptr] native nsIFrame(nsIFrame);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces, and permits users to extract subregions
  * as other imgIContainers. It also allows drawing of images on to Thebes
  * contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, uuid(8bb94fa2-f57a-482c-bef8-e0b0424b0b3c)]
+[scriptable, uuid(239dfa70-2285-4d63-99cd-e9b7ff9555c7)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.
    */
   readonly attribute PRInt32 width;
 
   /**
@@ -200,23 +202,31 @@ interface imgIContainer : nsISupports
    * @param aContext The Thebes context to draw the image to.
    * @param aFilter The filter to be used if we're scaling the image.
    * @param aUserSpaceToImageSpace The transformation from user space (e.g.,
    *                               appunits) to image space.
    * @param aFill The area in the context to draw pixels to. Image will be
    *              automatically tiled as necessary.
    * @param aSubimage The area of the image, in pixels, that we are allowed to
    *                  sample from.
+   * @param aViewportSize
+   *          The size (in CSS pixels) of the viewport that would be available
+   *          for the full image to occupy, if we were drawing the full image.
+   *          (Note that we might not actually be drawing the full image -- we
+   *          might be restricted by aSubimage -- but we still need the full
+   *          image's viewport-size in order for SVG images with the "viewBox"
+   *          attribute to position their content correctly.)
    * @param aFlags Flags of the FLAG_* variety
    */
   [noscript] void draw(in gfxContext aContext,
                        in gfxGraphicsFilter aFilter,
                        [const] in gfxMatrix aUserSpaceToImageSpace,
                        [const] in gfxRect aFill,
                        [const] in nsIntRect aSubimage,
+                       [const] in nsIntSize aViewportSize,
                        in PRUint32 aFlags);
 
   /**
    * If this image is TYPE_VECTOR, i.e. is really an embedded SVG document,
    * this method returns a pointer to the root nsIFrame of that document. If
    * not (or if the root nsIFrame isn't available for some reason), this method
    * returns nsnull.
    *
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -2420,23 +2420,25 @@ RasterImage::SyncDecode()
 }
 
 //******************************************************************************
 /* [noscript] void draw(in gfxContext aContext,
  *                      in gfxGraphicsFilter aFilter,
  *                      [const] in gfxMatrix aUserSpaceToImageSpace,
  *                      [const] in gfxRect aFill,
  *                      [const] in nsIntRect aSubimage,
+ *                      [const] in nsIntSize aViewportSize,
  *                      in PRUint32 aFlags); */
 NS_IMETHODIMP
 RasterImage::Draw(gfxContext *aContext,
                   gfxPattern::GraphicsFilter aFilter,
                   const gfxMatrix &aUserSpaceToImageSpace,
                   const gfxRect &aFill,
                   const nsIntRect &aSubimage,
+                  const nsIntSize& /*aViewportSize - ignored*/,
                   PRUint32 aFlags)
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   // Disallowed in the API
   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
     return NS_ERROR_FAILURE;
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -623,17 +623,17 @@ nsBaseDragService::DrawDragForImage(nsPr
   NS_ADDREF(*aSurface);
 
   if (aImageLoader) {
     gfxRect outRect(0, 0, destSize.width, destSize.height);
     gfxMatrix scale =
       gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
     nsIntRect imgSize(0, 0, srcSize.width, srcSize.height);
     imgContainer->Draw(ctx, gfxPattern::FILTER_GOOD, scale, outRect, imgSize,
-                       imgIContainer::FLAG_SYNC_DECODE);
+                       destSize, imgIContainer::FLAG_SYNC_DECODE);
     return NS_OK;
   } else {
     return aCanvas->RenderContextsExternal(ctx, gfxPattern::FILTER_GOOD);
   }
 }
 
 void
 nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,