LCOV - code coverage report
Current view: top level - layout/painting - DisplayItemClip.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 162 242 66.9 %
Date: 2017-07-14 16:53:18 Functions: 21 27 77.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "DisplayItemClip.h"
       7             : 
       8             : #include "gfxContext.h"
       9             : #include "gfxUtils.h"
      10             : #include "mozilla/gfx/2D.h"
      11             : #include "mozilla/gfx/PathHelpers.h"
      12             : #include "nsPresContext.h"
      13             : #include "nsCSSRendering.h"
      14             : #include "nsLayoutUtils.h"
      15             : #include "nsRegion.h"
      16             : 
      17             : using namespace mozilla::gfx;
      18             : 
      19             : namespace mozilla {
      20             : 
      21             : void
      22         829 : DisplayItemClip::SetTo(const nsRect& aRect)
      23             : {
      24         829 :   SetTo(aRect, nullptr);
      25         829 : }
      26             : 
      27             : void
      28         949 : DisplayItemClip::SetTo(const nsRect& aRect, const nscoord* aRadii)
      29             : {
      30         949 :   mHaveClipRect = true;
      31         949 :   mClipRect = aRect;
      32         949 :   if (aRadii) {
      33         120 :     mRoundedClipRects.SetLength(1);
      34         120 :     mRoundedClipRects[0].mRect = aRect;
      35         120 :     memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord)*8);
      36             :   } else {
      37         829 :     mRoundedClipRects.Clear();
      38             :   }
      39         949 : }
      40             : 
      41             : void
      42           0 : DisplayItemClip::SetTo(const nsRect& aRect,
      43             :                        const nsRect& aRoundedRect,
      44             :                        const nscoord* aRadii)
      45             : {
      46           0 :   mHaveClipRect = true;
      47           0 :   mClipRect = aRect;
      48           0 :   mRoundedClipRects.SetLength(1);
      49           0 :   mRoundedClipRects[0].mRect = aRoundedRect;
      50           0 :   memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord)*8);
      51           0 : }
      52             : 
      53             : bool
      54         171 : DisplayItemClip::MayIntersect(const nsRect& aRect) const
      55             : {
      56         171 :   if (!mHaveClipRect) {
      57          36 :     return !aRect.IsEmpty();
      58             :   }
      59         270 :   nsRect r = aRect.Intersect(mClipRect);
      60         135 :   if (r.IsEmpty()) {
      61           0 :     return false;
      62             :   }
      63         135 :   for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
      64           0 :     const RoundedRect& rr = mRoundedClipRects[i];
      65           0 :     if (!nsLayoutUtils::RoundedRectIntersectsRect(rr.mRect, rr.mRadii, r)) {
      66           0 :       return false;
      67             :     }
      68             :   }
      69         135 :   return true;
      70             : }
      71             : 
      72             : void
      73        1073 : DisplayItemClip::IntersectWith(const DisplayItemClip& aOther)
      74             : {
      75        1073 :   if (!aOther.mHaveClipRect) {
      76           0 :     return;
      77             :   }
      78        1073 :   if (!mHaveClipRect) {
      79         268 :     *this = aOther;
      80         268 :     return;
      81             :   }
      82         805 :   if (!mClipRect.IntersectRect(mClipRect, aOther.mClipRect)) {
      83           0 :     mRoundedClipRects.Clear();
      84           0 :     return;
      85             :   }
      86         805 :   mRoundedClipRects.AppendElements(aOther.mRoundedClipRects);
      87             : }
      88             : 
      89             : void
      90         201 : DisplayItemClip::ApplyTo(gfxContext* aContext,
      91             :                          nsPresContext* aPresContext,
      92             :                          uint32_t aBegin, uint32_t aEnd)
      93             : {
      94         201 :   int32_t A2D = aPresContext->AppUnitsPerDevPixel();
      95         201 :   ApplyRectTo(aContext, A2D);
      96         201 :   ApplyRoundedRectClipsTo(aContext, A2D, aBegin, aEnd);
      97         201 : }
      98             : 
      99             : void
     100         201 : DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
     101             : {
     102         201 :   aContext->NewPath();
     103         201 :   gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
     104         201 :   aContext->Rectangle(clip, true);
     105         201 :   aContext->Clip();
     106         201 : }
     107             : 
     108             : void
     109         201 : DisplayItemClip::ApplyRoundedRectClipsTo(gfxContext* aContext,
     110             :                                          int32_t A2D,
     111             :                                          uint32_t aBegin, uint32_t aEnd) const
     112             : {
     113         201 :   DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
     114             : 
     115         201 :   aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
     116             : 
     117         222 :   for (uint32_t i = aBegin; i < aEnd; ++i) {
     118             :     RefPtr<Path> roundedRect =
     119          42 :       MakeRoundedRectPath(aDrawTarget, A2D, mRoundedClipRects[i]);
     120          21 :     aContext->Clip(roundedRect);
     121             :   }
     122         201 : }
     123             : 
     124             : void
     125           0 : DisplayItemClip::FillIntersectionOfRoundedRectClips(gfxContext* aContext,
     126             :                                                     const Color& aColor,
     127             :                                                     int32_t aAppUnitsPerDevPixel,
     128             :                                                     uint32_t aBegin,
     129             :                                                     uint32_t aEnd) const
     130             : {
     131           0 :   DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
     132             : 
     133           0 :   aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
     134             : 
     135           0 :   if (aBegin >= aEnd) {
     136           0 :     return;
     137             :   }
     138             : 
     139             :   // Push clips for any rects that come BEFORE the rect at |aEnd - 1|, if any:
     140           0 :   ApplyRoundedRectClipsTo(aContext, aAppUnitsPerDevPixel, aBegin, aEnd - 1);
     141             : 
     142             :   // Now fill the rect at |aEnd - 1|:
     143           0 :   RefPtr<Path> roundedRect = MakeRoundedRectPath(aDrawTarget,
     144             :                                                  aAppUnitsPerDevPixel,
     145           0 :                                                  mRoundedClipRects[aEnd - 1]);
     146           0 :   ColorPattern color(ToDeviceColor(aColor));
     147           0 :   aDrawTarget.Fill(roundedRect, color);
     148             : 
     149             :   // Finally, pop any clips that we may have pushed:
     150           0 :   for (uint32_t i = aBegin; i < aEnd - 1; ++i) {
     151           0 :     aContext->PopClip();
     152             :   }
     153             : }
     154             : 
     155             : already_AddRefed<Path>
     156          21 : DisplayItemClip::MakeRoundedRectPath(DrawTarget& aDrawTarget,
     157             :                                      int32_t A2D,
     158             :                                      const RoundedRect &aRoundRect) const
     159             : {
     160          21 :   RectCornerRadii pixelRadii;
     161          21 :   nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
     162             : 
     163          21 :   Rect rect = NSRectToSnappedRect(aRoundRect.mRect, A2D, aDrawTarget);
     164             : 
     165          21 :   return MakePathForRoundedRect(aDrawTarget, rect, pixelRadii);
     166             : }
     167             : 
     168             : nsRect
     169         235 : DisplayItemClip::ApproximateIntersectInward(const nsRect& aRect) const
     170             : {
     171         235 :   nsRect r = aRect;
     172         235 :   if (mHaveClipRect) {
     173         174 :     r.IntersectRect(r, mClipRect);
     174             :   }
     175         294 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     176         294 :        i < iEnd; ++i) {
     177          59 :     const RoundedRect &rr = mRoundedClipRects[i];
     178         118 :     nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
     179          59 :     r = rgn.GetLargestRectangle();
     180             :   }
     181         235 :   return r;
     182             : }
     183             : 
     184             : // Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
     185             : // and radii aXRadius, aYRadius.
     186          21 : bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
     187             :                      nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
     188             : {
     189          21 :   float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
     190          21 :   float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
     191          21 :   return scaledX * scaledX + scaledY * scaledY < 1.0f;
     192             : }
     193             : 
     194             : bool
     195          29 : DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
     196             : {
     197          29 :   if (mRoundedClipRects.IsEmpty())
     198           0 :     return false;
     199             : 
     200          58 :   nsRect rect;
     201          29 :   rect.IntersectRect(aRect, NonRoundedIntersection());
     202          37 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     203          37 :        i < iEnd; ++i) {
     204          29 :     const RoundedRect &rr = mRoundedClipRects[i];
     205             :     // top left
     206          46 :     if (rect.x < rr.mRect.x + rr.mRadii[eCornerTopLeftX] &&
     207          17 :         rect.y < rr.mRect.y + rr.mRadii[eCornerTopLeftY]) {
     208          68 :       if (!IsInsideEllipse(rr.mRadii[eCornerTopLeftX],
     209          17 :                            rr.mRect.x + rr.mRadii[eCornerTopLeftX],
     210             :                            rect.x,
     211          17 :                            rr.mRadii[eCornerTopLeftY],
     212          17 :                            rr.mRect.y + rr.mRadii[eCornerTopLeftY],
     213             :                            rect.y)) {
     214          17 :         return true;
     215             :       }
     216             :     }
     217             :     // top right
     218          16 :     if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[eCornerTopRightX] &&
     219           4 :         rect.y < rr.mRect.y + rr.mRadii[eCornerTopRightY]) {
     220          16 :       if (!IsInsideEllipse(rr.mRadii[eCornerTopRightX],
     221           4 :                            rr.mRect.XMost() - rr.mRadii[eCornerTopRightX],
     222             :                            rect.XMost(),
     223           4 :                            rr.mRadii[eCornerTopRightY],
     224           4 :                            rr.mRect.y + rr.mRadii[eCornerTopRightY],
     225             :                            rect.y)) {
     226           4 :         return true;
     227             :       }
     228             :     }
     229             :     // bottom left
     230           8 :     if (rect.x < rr.mRect.x + rr.mRadii[eCornerBottomLeftX] &&
     231           0 :         rect.YMost() > rr.mRect.YMost() - rr.mRadii[eCornerBottomLeftY]) {
     232           0 :       if (!IsInsideEllipse(rr.mRadii[eCornerBottomLeftX],
     233           0 :                            rr.mRect.x + rr.mRadii[eCornerBottomLeftX],
     234             :                            rect.x,
     235           0 :                            rr.mRadii[eCornerBottomLeftY],
     236           0 :                            rr.mRect.YMost() - rr.mRadii[eCornerBottomLeftY],
     237             :                            rect.YMost())) {
     238           0 :         return true;
     239             :       }
     240             :     }
     241             :     // bottom right
     242           8 :     if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[eCornerBottomRightX] &&
     243           0 :         rect.YMost() > rr.mRect.YMost() - rr.mRadii[eCornerBottomRightY]) {
     244           0 :       if (!IsInsideEllipse(rr.mRadii[eCornerBottomRightX],
     245           0 :                            rr.mRect.XMost() - rr.mRadii[eCornerBottomRightX],
     246             :                            rect.XMost(),
     247           0 :                            rr.mRadii[eCornerBottomRightY],
     248           0 :                            rr.mRect.YMost() - rr.mRadii[eCornerBottomRightY],
     249             :                            rect.YMost())) {
     250           0 :         return true;
     251             :       }
     252             :     }
     253             :   }
     254           8 :   return false;
     255             : }
     256             : 
     257             : nsRect
     258        2230 : DisplayItemClip::NonRoundedIntersection() const
     259             : {
     260        2230 :   NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
     261        2230 :   nsRect result = mClipRect;
     262        2520 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     263        2520 :        i < iEnd; ++i) {
     264         290 :     result.IntersectRect(result, mRoundedClipRects[i].mRect);
     265             :   }
     266        2230 :   return result;
     267             : }
     268             : 
     269             : bool
     270         773 : DisplayItemClip::IsRectAffectedByClip(const nsRect& aRect) const
     271             : {
     272         773 :   if (mHaveClipRect && !mClipRect.Contains(aRect)) {
     273         350 :     return true;
     274             :   }
     275         425 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     276         425 :        i < iEnd; ++i) {
     277           5 :     const RoundedRect &rr = mRoundedClipRects[i];
     278           7 :     nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, aRect);
     279           5 :     if (!rgn.Contains(aRect)) {
     280           3 :       return true;
     281             :     }
     282             :   }
     283         420 :   return false;
     284             : }
     285             : 
     286             : bool
     287          26 : DisplayItemClip::IsRectAffectedByClip(const nsIntRect& aRect,
     288             :                                       float aXScale,
     289             :                                       float aYScale,
     290             :                                       int32_t A2D) const
     291             : {
     292          26 :   if (mHaveClipRect) {
     293          24 :     nsIntRect pixelClipRect = mClipRect.ScaleToNearestPixels(aXScale, aYScale, A2D);
     294          24 :     if (!pixelClipRect.Contains(aRect)) {
     295           0 :       return true;
     296             :     }
     297             :   }
     298             : 
     299             :   // Rounded rect clipping only snaps to user-space pixels, not device space.
     300          26 :   nsIntRect unscaled = aRect;
     301          26 :   unscaled.Scale(1/aXScale, 1/aYScale);
     302             : 
     303          26 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     304          26 :        i < iEnd; ++i) {
     305           0 :     const RoundedRect &rr = mRoundedClipRects[i];
     306             : 
     307           0 :     nsIntRect pixelRect = rr.mRect.ToNearestPixels(A2D);
     308             : 
     309           0 :     RectCornerRadii pixelRadii;
     310           0 :     nsCSSRendering::ComputePixelRadii(rr.mRadii, A2D, &pixelRadii);
     311             : 
     312           0 :     nsIntRegion rgn = nsLayoutUtils::RoundedRectIntersectIntRect(pixelRect, pixelRadii, unscaled);
     313           0 :     if (!rgn.Contains(unscaled)) {
     314           0 :       return true;
     315             :     }
     316             :   }
     317          26 :   return false;
     318             : }
     319             : 
     320             : nsRect
     321        6752 : DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const
     322             : {
     323        6752 :   if (!mHaveClipRect) {
     324        1158 :     return aRect;
     325             :   }
     326             : 
     327       11188 :   nsRect result = aRect.Intersect(mClipRect);
     328        5934 :   for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
     329        5934 :        i < iEnd; ++i) {
     330         340 :     result = result.Intersect(mRoundedClipRects[i].mRect);
     331             :   }
     332        5594 :   return result;
     333             : }
     334             : 
     335             : void
     336           8 : DisplayItemClip::RemoveRoundedCorners()
     337             : {
     338           8 :   if (mRoundedClipRects.IsEmpty())
     339           0 :     return;
     340             : 
     341           8 :   mClipRect = NonRoundedIntersection();
     342           8 :   mRoundedClipRects.Clear();
     343             : }
     344             : 
     345             : // Computes the difference between aR1 and aR2, limited to aBounds.
     346             : static void
     347         920 : AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, const nsRect& aBounds, nsRegion* aOut)
     348             : {
     349         920 :   if (aR1.IsEqualInterior(aR2))
     350         889 :     return;
     351          62 :   nsRegion r;
     352          31 :   r.Xor(aR1, aR2);
     353          31 :   r.And(r, aBounds);
     354          31 :   aOut->Or(*aOut, r);
     355             : }
     356             : 
     357             : void
     358        1109 : DisplayItemClip::AddOffsetAndComputeDifference(uint32_t aStart,
     359             :                                                const nsPoint& aOffset,
     360             :                                                const nsRect& aBounds,
     361             :                                                const DisplayItemClip& aOther,
     362             :                                                uint32_t aOtherStart,
     363             :                                                const nsRect& aOtherBounds,
     364             :                                                nsRegion* aDifference)
     365             : {
     366        3327 :   if (mHaveClipRect != aOther.mHaveClipRect ||
     367        2218 :       aStart != aOtherStart ||
     368        1109 :       mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
     369           0 :     aDifference->Or(*aDifference, aBounds);
     370           0 :     aDifference->Or(*aDifference, aOtherBounds);
     371           0 :     return;
     372             :   }
     373        1109 :   if (mHaveClipRect) {
     374        1840 :     AccumulateRectDifference(mClipRect + aOffset, aOther.mClipRect,
     375        1840 :                              aBounds.Union(aOtherBounds),
     376         920 :                              aDifference);
     377             :   }
     378        1218 :   for (uint32_t i = aStart; i < mRoundedClipRects.Length(); ++i) {
     379         109 :     if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
     380             :       // The corners make it tricky so we'll just add both rects here.
     381           6 :       aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
     382           6 :       aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
     383             :     }
     384             :   }
     385             : }
     386             : 
     387             : uint32_t
     388        1067 : DisplayItemClip::GetCommonRoundedRectCount(const DisplayItemClip& aOther,
     389             :                                            uint32_t aMax) const
     390             : {
     391        3201 :   uint32_t end = std::min(std::min(mRoundedClipRects.Length(), size_t(aMax)),
     392        4268 :                           aOther.mRoundedClipRects.Length());
     393        1067 :   uint32_t clipCount = 0;
     394        1067 :   for (; clipCount < end; ++clipCount) {
     395           0 :     if (mRoundedClipRects[clipCount] !=
     396           0 :         aOther.mRoundedClipRects[clipCount]) {
     397           0 :       return clipCount;
     398             :     }
     399             :   }
     400        1067 :   return clipCount;
     401             : }
     402             : 
     403             : void
     404           0 : DisplayItemClip::AppendRoundedRects(nsTArray<RoundedRect>* aArray, uint32_t aCount) const
     405             : {
     406           0 :   size_t count = std::min(mRoundedClipRects.Length(), size_t(aCount));
     407           0 :   aArray->AppendElements(mRoundedClipRects.Elements(), count);
     408           0 : }
     409             : 
     410             : bool
     411        1252 : DisplayItemClip::ComputeRegionInClips(DisplayItemClip* aOldClip,
     412             :                                       const nsPoint& aShift,
     413             :                                       nsRegion* aCombined) const
     414             : {
     415        1252 :   if (!mHaveClipRect || (aOldClip && !aOldClip->mHaveClipRect)) {
     416         332 :     return false;
     417             :   }
     418             : 
     419         920 :   if (aOldClip) {
     420         920 :     *aCombined = aOldClip->NonRoundedIntersection();
     421         920 :     aCombined->MoveBy(aShift);
     422         920 :     aCombined->Or(*aCombined, NonRoundedIntersection());
     423             :   } else {
     424           0 :     *aCombined = NonRoundedIntersection();
     425             :   }
     426         920 :   return true;
     427             : }
     428             : 
     429             : void
     430           0 : DisplayItemClip::MoveBy(nsPoint aPoint)
     431             : {
     432           0 :   if (!mHaveClipRect)
     433           0 :     return;
     434           0 :   mClipRect += aPoint;
     435           0 :   for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
     436           0 :     mRoundedClipRects[i].mRect += aPoint;
     437             :   }
     438             : }
     439             : 
     440             : static DisplayItemClip* gNoClip;
     441             : 
     442             : const DisplayItemClip&
     443        2084 : DisplayItemClip::NoClip()
     444             : {
     445        2084 :   if (!gNoClip) {
     446           2 :     gNoClip = new DisplayItemClip();
     447             :   }
     448        2084 :   return *gNoClip;
     449             : }
     450             : 
     451             : void
     452           0 : DisplayItemClip::Shutdown()
     453             : {
     454           0 :   delete gNoClip;
     455           0 :   gNoClip = nullptr;
     456           0 : }
     457             : 
     458             : nsCString
     459           0 : DisplayItemClip::ToString() const
     460             : {
     461           0 :   nsAutoCString str;
     462           0 :   if (mHaveClipRect) {
     463           0 :     str.AppendPrintf("%d,%d,%d,%d", mClipRect.x, mClipRect.y,
     464           0 :                      mClipRect.width, mClipRect.height);
     465           0 :     for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
     466           0 :       const RoundedRect& r = mRoundedClipRects[i];
     467             :       str.AppendPrintf(" [%d,%d,%d,%d corners %d,%d,%d,%d,%d,%d,%d,%d]",
     468           0 :                        r.mRect.x, r.mRect.y, r.mRect.width, r.mRect.height,
     469           0 :                        r.mRadii[0], r.mRadii[1], r.mRadii[2], r.mRadii[3],
     470           0 :                        r.mRadii[4], r.mRadii[5], r.mRadii[6], r.mRadii[7]);
     471             :     }
     472             :   }
     473           0 :   return str;
     474             : }
     475             : 
     476             : } // namespace mozilla

Generated by: LCOV version 1.13