Bug 841192. Part 9: Add extra APIs to DisplayItemClip to set the current state, intersect with another DisplayItemClip, test intersection with a rect, translate, and provide easy access to a DisplayItemClip object which doesn't clip anything. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 07 Mar 2013 00:08:11 +1300
changeset 127804 30928f33d63f060dc0f5fbf4d6773bf29d0f948a
parent 127803 b16876942c8df78107b10f0288f88f27198cbdc8
child 127805 072417fd4d8e18a840bda2fc7222481c88047182
push idunknown
push userunknown
push dateunknown
reviewersmattwoodrow
bugs841192
milestone23.0a1
Bug 841192. Part 9: Add extra APIs to DisplayItemClip to set the current state, intersect with another DisplayItemClip, test intersection with a rect, translate, and provide easy access to a DisplayItemClip object which doesn't clip anything. r=mattwoodrow
layout/base/DisplayItemClip.cpp
layout/base/DisplayItemClip.h
layout/build/nsLayoutStatics.cpp
--- a/layout/base/DisplayItemClip.cpp
+++ b/layout/base/DisplayItemClip.cpp
@@ -8,42 +8,51 @@
 #include "gfxContext.h"
 #include "nsPresContext.h"
 #include "nsDisplayList.h"
 #include "nsCSSRendering.h"
 #include "nsLayoutUtils.h"
 
 namespace mozilla {
 
-DisplayItemClip::DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem)
-  : mRoundedClipRects(aOther.mRoundedClipRects),
-    mHaveClipRect(true)
+void
+DisplayItemClip::SetTo(const nsRect& aRect)
+{
+  mHaveClipRect = true;
+  mClipRect = aRect;
+  mRoundedClipRects.Clear();
+}
+
+void
+DisplayItemClip::SetTo(const nsRect& aRect, const nscoord* aRadii)
 {
-  nsDisplayItem::Type type = aClipItem->GetType();
-  NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
-                    type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
-                    "unexpected display item type");
-  nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
-  // Always intersect with mClipRect, even if we're going to add a
-  // rounded rect.
-  if (aOther.mHaveClipRect) {
-    mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
-  } else {
-    mClipRect = item->GetClipRect();
+  mHaveClipRect = true;
+  mClipRect = aRect;
+  mRoundedClipRects.SetLength(1);
+  mRoundedClipRects[0].mRect = aRect;
+  memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord)*8);
+}
+
+bool
+DisplayItemClip::MayIntersect(const nsRect& aRect) const
+{
+  if (!mHaveClipRect) {
+    return !aRect.IsEmpty();
   }
-
-  if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
-    RoundedRect *rr = mRoundedClipRects.AppendElement();
-    if (rr) {
-      rr->mRect = item->GetClipRect();
-      static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
+  nsRect r = aRect.Intersect(mClipRect);
+  if (r.IsEmpty()) {
+    return false;
+  }
+  for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
+    const RoundedRect& rr = mRoundedClipRects[i];
+    if (!nsLayoutUtils::RoundedRectIntersectsRect(rr.mRect, rr.mRadii, r)) {
+      return false;
     }
   }
-
-  // FIXME: Optimize away excess rounded rectangles due to the new addition.
+  return true;
 }
 
 void
 DisplayItemClip::IntersectWith(const DisplayItemClip& aOther)
 {
   if (!aOther.mHaveClipRect) {
     return;
   }
@@ -119,17 +128,17 @@ DisplayItemClip::AddRoundedRectPathTo(gf
   clip.Round();
   clip.Condition();
 
   aContext->NewPath();
   aContext->RoundedRectangle(clip, pixelRadii);
 }
 
 nsRect
-DisplayItemClip::ApproximateIntersect(const nsRect& aRect) const
+DisplayItemClip::ApproximateIntersectInward(const nsRect& aRect) const
 {
   nsRect r = aRect;
   if (mHaveClipRect) {
     r.IntersectRect(r, mClipRect);
   }
   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
        i < iEnd; ++i) {
     const RoundedRect &rr = mRoundedClipRects[i];
@@ -342,9 +351,58 @@ DisplayItemClip::ComputeRegionInClips(Di
     aCombined->MoveBy(aShift);
     aCombined->Or(*aCombined, NonRoundedIntersection());
   } else {
     *aCombined = NonRoundedIntersection();
   }
   return true;
 }
 
+void
+DisplayItemClip::MoveBy(nsPoint aPoint)
+{
+  if (!mHaveClipRect)
+    return;
+  mClipRect += aPoint;
+  for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
+    mRoundedClipRects[i].mRect += aPoint;
+  }
 }
+
+static DisplayItemClip* gNoClip;
+
+const DisplayItemClip&
+DisplayItemClip::NoClip()
+{
+  if (!gNoClip) {
+    gNoClip = new DisplayItemClip();
+  }
+  return *gNoClip;
+}
+
+void
+DisplayItemClip::Shutdown()
+{
+  delete gNoClip;
+  gNoClip = nullptr;
+}
+
+#ifdef DEBUG
+nsCString
+DisplayItemClip::ToString() const
+{
+  nsAutoCString str;
+  if (mHaveClipRect) {
+    str.AppendPrintf("%d,%d,%d,%d", mClipRect.x, mClipRect.y,
+                     mClipRect.width, mClipRect.height);
+    for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
+      const RoundedRect& r = mRoundedClipRects[i];
+      str.AppendPrintf(" [%d,%d,%d,%d corners %d,%d,%d,%d,%d,%d,%d,%d]",
+                       r.mRect.x, r.mRect.y, r.mRect.width, r.mRect.height,
+                       r.mRadii[0], r.mRadii[1], r.mRadii[2], r.mRadii[3],
+                       r.mRadii[4], r.mRadii[5], r.mRadii[6], r.mRadii[7]);
+    }
+  }
+  return str;
+}
+#endif
+
+}
--- a/layout/base/DisplayItemClip.h
+++ b/layout/base/DisplayItemClip.h
@@ -48,21 +48,28 @@ public:
       return true;
     }
     bool operator!=(const RoundedRect& aOther) const {
       return !(*this == aOther);
     }
   };
 
   // Constructs a DisplayItemClip that does no clipping at all.
-  DisplayItemClip() : mHaveClipRect(false) {}
+  DisplayItemClip() : mHaveClipRect(false), mHasBeenDestroyed(false) {}
+  ~DisplayItemClip() { mHasBeenDestroyed = true; }
 
-  // Construct as the intersection of aOther and aClipItem.
-  DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem);
+  void MaybeDestroy() const
+  {
+    if (!mHasBeenDestroyed) {
+      this->~DisplayItemClip();
+    }
+  }
 
+  void SetTo(const nsRect& aRect);
+  void SetTo(const nsRect& aRect, const nscoord* aRadii);
   void IntersectWith(const DisplayItemClip& aOther);
 
   // Apply this |DisplayItemClip| to the given gfxContext.  Any saving of state
   // or clearing of other clips must be done by the caller.
   // See aBegin/aEnd note on ApplyRoundedRectsTo.
   void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext,
                uint32_t aBegin = 0, uint32_t aEnd = UINT32_MAX);
 
@@ -75,20 +82,26 @@ public:
 
   // Draw (fill) the rounded rects in this clip to aContext
   void DrawRoundedRectsTo(gfxContext* aContext, int32_t A2D,
                           uint32_t aBegin, uint32_t aEnd) const;
   // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
   void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
                             const RoundedRect &aRoundRect) const;
 
+  // Returns true if the intersection of aRect and this clip region is
+  // non-empty. This is precise for DisplayItemClips with at most one
+  // rounded rectangle. When multiple rounded rectangles are present, we just
+  // check that the rectangle intersects all of them (but possibly in different
+  // places). So it may return true when the correct answer is false.
+  bool MayIntersect(const nsRect& aRect) const;
   // Return a rectangle contained in the intersection of aRect with this
   // clip region. Tries to return the largest possible rectangle, but may
   // not succeed.
-  nsRect ApproximateIntersect(const nsRect& aRect) const;
+  nsRect ApproximateIntersectInward(const nsRect& aRect) const;
 
   /*
    * Computes a region which contains the clipped area of this DisplayItemClip,
    * or if aOldClip is non-null, the union of the clipped area of this
    * DisplayItemClip with the clipped area of aOldClip translated by aShift.
    * The result is stored in aCombined. If the result would be infinite
    * (because one or both of the clips does no clipping), returns false.
    */
@@ -133,28 +146,41 @@ public:
 
   bool HasClip() const { return mHaveClipRect; }
   const nsRect& GetClipRect() const
   {
     NS_ASSERTION(HasClip(), "No clip rect!");
     return mClipRect;
   }
 
+  void MoveBy(nsPoint aPoint);
+
+#ifdef DEBUG
+  nsCString ToString() const;
+#endif
+
   /**
    * Find the largest N such that the first N rounded rects in 'this' are
    * equal to the first N rounded rects in aOther, and N <= aMax.
    */
   uint32_t GetCommonRoundedRectCount(const DisplayItemClip& aOther,
                                      uint32_t aMax) const;
   uint32_t GetRoundedRectCount() const { return mRoundedClipRects.Length(); }
   void AppendRoundedRects(nsTArray<RoundedRect>* aArray, uint32_t aCount) const;
 
+  static const DisplayItemClip& NoClip();
+
+  static void Shutdown();
+
 private:
   nsRect mClipRect;
   nsTArray<RoundedRect> mRoundedClipRects;
   // If mHaveClipRect is false then this object represents no clipping at all
   // and mRoundedClipRects must be empty.
   bool mHaveClipRect;
+  // Set to true when the destructor has run. This is a bit of a hack
+  // to ensure that we can easily share arena-allocated DisplayItemClips.
+  bool mHasBeenDestroyed;
 };
 
 }
 
 #endif /* DISPLAYITEMCLIP_H_ */
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -55,16 +55,17 @@
 #include "nsListControlFrame.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "SVGElementFactory.h"
 #include "nsSVGUtils.h"
 #include "nsMathMLAtoms.h"
 #include "nsMathMLOperators.h"
 #include "Navigator.h"
 #include "nsDOMStorageBaseDB.h"
+#include "DisplayItemClip.h"
 
 #include "AudioChannelService.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
@@ -387,10 +388,12 @@ nsLayoutStatics::Shutdown()
   nsDOMMutationObserver::Shutdown();
 
   AudioChannelService::Shutdown();
 
   ContentParent::ShutDown();
 
   nsRefreshDriver::Shutdown();
 
+  DisplayItemClip::Shutdown();
+
   nsDocument::XPCOMShutdown();
 }