Bug 619992 - Fix location calculations for light sources. r=longsonr
authorRobert O'Callahan <roc@ocallahan.org>
Wed, 14 Sep 2011 15:48:32 +0100
changeset 76963 061b6c26a01920d722e39c46e76492ff0515c012
parent 76962 f29f86ffe8c415c55c3a72d2f5bd8ea739964a26
child 76964 03d57c393397727e0858787036edca4297b807ce
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewerslongsonr
bugs619992
milestone9.0a1
Bug 619992 - Fix location calculations for light sources. r=longsonr
content/svg/content/src/nsSVGFilters.cpp
layout/reftests/svg/objectBoundingBox-and-fePointLight-02-ref.svg
layout/reftests/svg/objectBoundingBox-and-fePointLight-02.svg
layout/reftests/svg/reftest.list
layout/svg/base/src/nsSVGFilterInstance.cpp
layout/svg/base/src/nsSVGFilterInstance.h
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -4940,21 +4940,23 @@ nsSVGFELightingElement::Filter(nsSVGFilt
     for (PRInt32 x = dataRect.x; x < dataRect.XMost(); x++) {
       PRInt32 index = y * stride + x * 4;
 
       float N[3];
       GenerateNormal(N, sourceData, stride, surfaceWidth, surfaceHeight,
                      x, y, surfaceScale);
 
       if (pointLight || spotLight) {
-        float Z =
-          surfaceScale * sourceData[index + GFX_ARGB32_OFFSET_A] / 255;
-
-        L[0] = lightPos[0] - x;
-        L[1] = lightPos[1] - y;
+        gfxPoint pt = instance->FilterSpaceToUserSpace(
+                gfxPoint(x + instance->GetSurfaceRect().x,
+                         y + instance->GetSurfaceRect().y));
+        float Z = surfaceScale * sourceData[index + GFX_ARGB32_OFFSET_A] / 255;
+
+        L[0] = lightPos[0] - pt.x;
+        L[1] = lightPos[1] - pt.y;
         L[2] = lightPos[2] - Z;
         NORMALIZE(L);
       }
 
       nscolor color;
 
       if (spotLight) {
         float S[3];
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/objectBoundingBox-and-fePointLight-02-ref.svg
@@ -0,0 +1,22 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg width="100%" height="100%"
+  viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+  <title>Reference for objectBoundingBox with fePointLight</title>
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=619992 -->
+
+  <defs>
+    <filter id="f" primitiveUnits="objectBoundingBox">
+      <feSpecularLighting lighting-color="blue" surfaceScale="5" specularConstant="10" specularExponent="6">
+        <fePointLight x="0.875" y="0.875" z="-0.0625"/>
+      </feSpecularLighting>
+    </filter>
+  </defs>
+
+  <g transform="translate(50 50)">
+    <rect x="-40" y="-40" width="80" height="80" filter="url(#f)" fill="none"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/objectBoundingBox-and-fePointLight-02.svg
@@ -0,0 +1,22 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg width="100%" height="100%"
+  viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+  <title>Testcase for objectBoundingBox with fePointLight</title>
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=619992 -->
+
+  <defs>
+    <filter id="f" primitiveUnits="userSpaceOnUse">
+      <feSpecularLighting lighting-color="blue" surfaceScale="5" specularConstant="10" specularExponent="6">
+        <fePointLight x="30" y="30" z="-5"/>
+      </feSpecularLighting>
+    </filter>
+  </defs>
+
+  <g transform="translate(50 50)">
+    <rect x="-40" y="-40" width="80" height="80" filter="url(#f)" fill="none"/>
+  </g>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -141,16 +141,17 @@ fails == inline-in-xul-basic-01.xul pass
 == mask-containing-masked-content-01.svg pass.svg
 # Bug 456323
 # == mask-transformed-01.svg mask-transformed-01-ref.svg
 == nested-viewBox-01.svg pass.svg
 == nesting-invalid-01.svg nesting-invalid-01-ref.svg
 == objectBoundingBox-and-clipPath.svg pass.svg
 # Bug 588684
 random-if(gtk2Widget) == objectBoundingBox-and-fePointLight-01.svg objectBoundingBox-and-fePointLight-01-ref.svg
+random-if(gtk2Widget) == objectBoundingBox-and-fePointLight-02.svg objectBoundingBox-and-fePointLight-02-ref.svg
 == objectBoundingBox-and-mask.svg pass.svg
 == objectBoundingBox-and-mask-02.svg pass.svg
 == objectBoundingBox-and-pattern-01a.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-01b.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-01c.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-02.svg pass.svg
 == objectBoundingBox-and-pattern-03.svg objectBoundingBox-and-pattern-03-ref.svg
 == opacity-and-gradient-01.svg pass.svg
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -73,22 +73,32 @@ nsSVGFilterInstance::GetPrimitiveNumber(
       sqrt(Square(mFilterSpaceSize.width) + Square(mFilterSpaceSize.height)) /
       sqrt(Square(mFilterRect.Width()) + Square(mFilterRect.Height()));
   }
 }
 
 void
 nsSVGFilterInstance::ConvertLocation(float aValues[3]) const
 {
-  if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-    aValues[0] *= mTargetBBox.Width();
-    aValues[1] *= mTargetBBox.Height();
-    aValues[2] *= nsSVGUtils::ComputeNormalizedHypotenuse(
-                    mTargetBBox.Width(), mTargetBBox.Height());
-  }
+  nsSVGLength2 val[4];
+  val[0].Init(nsSVGUtils::X, 0xff, aValues[0],
+              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
+  val[1].Init(nsSVGUtils::Y, 0xff, aValues[1],
+              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
+  // Dummy width/height values
+  val[2].Init(nsSVGUtils::X, 0xff, 0,
+              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
+  val[3].Init(nsSVGUtils::Y, 0xff, 0,
+              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
+
+  gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
+    val, mTargetBBox, mTargetFrame);
+  aValues[0] = feArea.X();
+  aValues[1] = feArea.Y();
+  aValues[2] = GetPrimitiveNumber(nsSVGUtils::XY, aValues[2]);
 }
 
 already_AddRefed<gfxImageSurface>
 nsSVGFilterInstance::CreateImage()
 {
   nsRefPtr<gfxImageSurface> surface =
     new gfxImageSurface(gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
                         gfxASurface::ImageFormatARGB32);
@@ -107,16 +117,23 @@ gfxRect
 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
 {
   gfxRect r = aRect - mFilterRect.TopLeft();
   r.Scale(mFilterSpaceSize.width / mFilterRect.Width(),
           mFilterSpaceSize.height / mFilterRect.Height());
   return r;
 }
 
+gfxPoint
+nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxPoint& aPt) const
+{
+  return gfxPoint(aPt.x * mFilterRect.Width() / mFilterSpaceSize.width + mFilterRect.X(),
+                  aPt.y * mFilterRect.Height() / mFilterSpaceSize.height + mFilterRect.Y());
+}
+
 gfxMatrix
 nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
 {
   gfxFloat widthScale = mFilterSpaceSize.width / mFilterRect.Width();
   gfxFloat heightScale = mFilterSpaceSize.height / mFilterRect.Height();
   return gfxMatrix(widthScale, 0.0f,
                    0.0f, heightScale,
                    -mFilterRect.X() * widthScale, -mFilterRect.Y() * heightScale);
--- a/layout/svg/base/src/nsSVGFilterInstance.h
+++ b/layout/svg/base/src/nsSVGFilterInstance.h
@@ -58,18 +58,16 @@ struct gfxRect;
  * 
  * We build a graph of the filter image data flow, essentially
  * converting the filter graph to SSA. This lets us easily propagate
  * analysis data (such as bounding-boxes) over the filter primitive graph.
  */
 class NS_STACK_CLASS nsSVGFilterInstance
 {
 public:
-  void ConvertLocation(float aValues[3]) const;
-
   nsSVGFilterInstance(nsIFrame *aTargetFrame,
                       nsSVGFilterPaintCallback *aPaintCallback,
                       nsSVGFilterElement *aFilterElement,
                       const gfxRect &aTargetBBox,
                       const gfxRect& aFilterRect,
                       const nsIntSize& aFilterSpaceSize,
                       const gfxMatrix &aFilterSpaceToDeviceSpaceTransform,
                       const nsIntRect& aDirtyOutputRect,
@@ -110,20 +108,28 @@ public:
   {
     return GetPrimitiveNumber(aCtxType, aNumber->GetAnimValue());
   }
   float GetPrimitiveNumber(PRUint8 aCtxType, const nsSVGNumberPair *aNumberPair,
                            nsSVGNumberPair::PairIndex aIndex) const
   {
     return GetPrimitiveNumber(aCtxType, aNumberPair->GetAnimValue(aIndex));
   }
+  /**
+   * Converts a point and a length in filter primitive units into filter space.
+   * For object-bounding-box units, the object bounding box offset is applied
+   * to the point.
+   */
+  void ConvertLocation(float aValues[3]) const;
+
   gfxMatrix GetUserSpaceToFilterSpaceTransform() const;
   gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
     return mFilterSpaceToDeviceSpaceTransform;
   }
+  gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
 
 private:
   typedef nsSVGFE::Image Image;
   typedef nsSVGFE::ColorModel ColorModel;
 
   struct PrimitiveInfo {
     nsSVGFE*  mFE;
     // the bounding box of the result image produced by this primitive, in