1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/DisplayItemClip.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,408 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "DisplayItemClip.h" 1.10 + 1.11 +#include "gfxContext.h" 1.12 +#include "nsPresContext.h" 1.13 +#include "nsCSSRendering.h" 1.14 +#include "nsLayoutUtils.h" 1.15 +#include "nsRegion.h" 1.16 + 1.17 +namespace mozilla { 1.18 + 1.19 +void 1.20 +DisplayItemClip::SetTo(const nsRect& aRect) 1.21 +{ 1.22 + mHaveClipRect = true; 1.23 + mClipRect = aRect; 1.24 + mRoundedClipRects.Clear(); 1.25 +} 1.26 + 1.27 +void 1.28 +DisplayItemClip::SetTo(const nsRect& aRect, const nscoord* aRadii) 1.29 +{ 1.30 + mHaveClipRect = true; 1.31 + mClipRect = aRect; 1.32 + mRoundedClipRects.SetLength(1); 1.33 + mRoundedClipRects[0].mRect = aRect; 1.34 + memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord)*8); 1.35 +} 1.36 + 1.37 +bool 1.38 +DisplayItemClip::MayIntersect(const nsRect& aRect) const 1.39 +{ 1.40 + if (!mHaveClipRect) { 1.41 + return !aRect.IsEmpty(); 1.42 + } 1.43 + nsRect r = aRect.Intersect(mClipRect); 1.44 + if (r.IsEmpty()) { 1.45 + return false; 1.46 + } 1.47 + for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { 1.48 + const RoundedRect& rr = mRoundedClipRects[i]; 1.49 + if (!nsLayoutUtils::RoundedRectIntersectsRect(rr.mRect, rr.mRadii, r)) { 1.50 + return false; 1.51 + } 1.52 + } 1.53 + return true; 1.54 +} 1.55 + 1.56 +void 1.57 +DisplayItemClip::IntersectWith(const DisplayItemClip& aOther) 1.58 +{ 1.59 + if (!aOther.mHaveClipRect) { 1.60 + return; 1.61 + } 1.62 + if (!mHaveClipRect) { 1.63 + *this = aOther; 1.64 + return; 1.65 + } 1.66 + if (!mClipRect.IntersectRect(mClipRect, aOther.mClipRect)) { 1.67 + mRoundedClipRects.Clear(); 1.68 + return; 1.69 + } 1.70 + mRoundedClipRects.AppendElements(aOther.mRoundedClipRects); 1.71 +} 1.72 + 1.73 +void 1.74 +DisplayItemClip::ApplyTo(gfxContext* aContext, 1.75 + nsPresContext* aPresContext, 1.76 + uint32_t aBegin, uint32_t aEnd) 1.77 +{ 1.78 + int32_t A2D = aPresContext->AppUnitsPerDevPixel(); 1.79 + ApplyRectTo(aContext, A2D); 1.80 + ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd); 1.81 +} 1.82 + 1.83 +void 1.84 +DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const 1.85 +{ 1.86 + aContext->NewPath(); 1.87 + gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D); 1.88 + aContext->Rectangle(clip, true); 1.89 + aContext->Clip(); 1.90 +} 1.91 + 1.92 +void 1.93 +DisplayItemClip::ApplyRoundedRectsTo(gfxContext* aContext, 1.94 + int32_t A2D, 1.95 + uint32_t aBegin, uint32_t aEnd) const 1.96 +{ 1.97 + aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length()); 1.98 + 1.99 + for (uint32_t i = aBegin; i < aEnd; ++i) { 1.100 + AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]); 1.101 + aContext->Clip(); 1.102 + } 1.103 +} 1.104 + 1.105 +void 1.106 +DisplayItemClip::DrawRoundedRectsTo(gfxContext* aContext, 1.107 + int32_t A2D, 1.108 + uint32_t aBegin, uint32_t aEnd) const 1.109 +{ 1.110 + aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length()); 1.111 + 1.112 + if (aEnd - aBegin == 0) 1.113 + return; 1.114 + 1.115 + // If there is just one rounded rect we can just fill it, if there are more then we 1.116 + // must clip the rest to get the intersection of clips 1.117 + ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1); 1.118 + AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]); 1.119 + aContext->Fill(); 1.120 +} 1.121 + 1.122 +void 1.123 +DisplayItemClip::AddRoundedRectPathTo(gfxContext* aContext, 1.124 + int32_t A2D, 1.125 + const RoundedRect &aRoundRect) const 1.126 +{ 1.127 + gfxCornerSizes pixelRadii; 1.128 + nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii); 1.129 + 1.130 + gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D); 1.131 + clip.Round(); 1.132 + clip.Condition(); 1.133 + 1.134 + aContext->NewPath(); 1.135 + aContext->RoundedRectangle(clip, pixelRadii); 1.136 +} 1.137 + 1.138 +nsRect 1.139 +DisplayItemClip::ApproximateIntersectInward(const nsRect& aRect) const 1.140 +{ 1.141 + nsRect r = aRect; 1.142 + if (mHaveClipRect) { 1.143 + r.IntersectRect(r, mClipRect); 1.144 + } 1.145 + for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); 1.146 + i < iEnd; ++i) { 1.147 + const RoundedRect &rr = mRoundedClipRects[i]; 1.148 + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r); 1.149 + r = rgn.GetLargestRectangle(); 1.150 + } 1.151 + return r; 1.152 +} 1.153 + 1.154 +// Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter) 1.155 +// and radii aXRadius, aYRadius. 1.156 +bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint, 1.157 + nscoord aYRadius, nscoord aYCenter, nscoord aYPoint) 1.158 +{ 1.159 + float scaledX = float(aXPoint - aXCenter) / float(aXRadius); 1.160 + float scaledY = float(aYPoint - aYCenter) / float(aYRadius); 1.161 + return scaledX * scaledX + scaledY * scaledY < 1.0f; 1.162 +} 1.163 + 1.164 +bool 1.165 +DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const 1.166 +{ 1.167 + if (mRoundedClipRects.IsEmpty()) 1.168 + return false; 1.169 + 1.170 + nsRect rect; 1.171 + rect.IntersectRect(aRect, NonRoundedIntersection()); 1.172 + for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); 1.173 + i < iEnd; ++i) { 1.174 + const RoundedRect &rr = mRoundedClipRects[i]; 1.175 + // top left 1.176 + if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] && 1.177 + rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) { 1.178 + if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X], 1.179 + rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X], 1.180 + rect.x, 1.181 + rr.mRadii[NS_CORNER_TOP_LEFT_Y], 1.182 + rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y], 1.183 + rect.y)) { 1.184 + return true; 1.185 + } 1.186 + } 1.187 + // top right 1.188 + if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] && 1.189 + rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) { 1.190 + if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X], 1.191 + rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X], 1.192 + rect.XMost(), 1.193 + rr.mRadii[NS_CORNER_TOP_RIGHT_Y], 1.194 + rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y], 1.195 + rect.y)) { 1.196 + return true; 1.197 + } 1.198 + } 1.199 + // bottom left 1.200 + if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] && 1.201 + rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) { 1.202 + if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X], 1.203 + rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X], 1.204 + rect.x, 1.205 + rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y], 1.206 + rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y], 1.207 + rect.YMost())) { 1.208 + return true; 1.209 + } 1.210 + } 1.211 + // bottom right 1.212 + if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] && 1.213 + rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) { 1.214 + if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X], 1.215 + rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X], 1.216 + rect.XMost(), 1.217 + rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y], 1.218 + rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y], 1.219 + rect.YMost())) { 1.220 + return true; 1.221 + } 1.222 + } 1.223 + } 1.224 + return false; 1.225 +} 1.226 + 1.227 +nsRect 1.228 +DisplayItemClip::NonRoundedIntersection() const 1.229 +{ 1.230 + NS_ASSERTION(mHaveClipRect, "Must have a clip rect!"); 1.231 + nsRect result = mClipRect; 1.232 + for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); 1.233 + i < iEnd; ++i) { 1.234 + result.IntersectRect(result, mRoundedClipRects[i].mRect); 1.235 + } 1.236 + return result; 1.237 +} 1.238 + 1.239 +bool 1.240 +DisplayItemClip::IsRectAffectedByClip(const nsRect& aRect) const 1.241 +{ 1.242 + if (mHaveClipRect && !mClipRect.Contains(aRect)) { 1.243 + return true; 1.244 + } 1.245 + for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); 1.246 + i < iEnd; ++i) { 1.247 + const RoundedRect &rr = mRoundedClipRects[i]; 1.248 + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, aRect); 1.249 + if (!rgn.Contains(aRect)) { 1.250 + return true; 1.251 + } 1.252 + } 1.253 + return false; 1.254 +} 1.255 + 1.256 +nsRect 1.257 +DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const 1.258 +{ 1.259 + if (!mHaveClipRect) { 1.260 + return aRect; 1.261 + } 1.262 + 1.263 + nsRect result = aRect.Intersect(mClipRect); 1.264 + for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); 1.265 + i < iEnd; ++i) { 1.266 + result.Intersect(mRoundedClipRects[i].mRect); 1.267 + } 1.268 + return result; 1.269 +} 1.270 + 1.271 +void 1.272 +DisplayItemClip::RemoveRoundedCorners() 1.273 +{ 1.274 + if (mRoundedClipRects.IsEmpty()) 1.275 + return; 1.276 + 1.277 + mClipRect = NonRoundedIntersection(); 1.278 + mRoundedClipRects.Clear(); 1.279 +} 1.280 + 1.281 +static void 1.282 +AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut) 1.283 +{ 1.284 + if (aR1.IsEqualInterior(aR2)) 1.285 + return; 1.286 + nsRegion r; 1.287 + r.Xor(aR1, aR2); 1.288 + aOut->Or(*aOut, r); 1.289 +} 1.290 + 1.291 +void 1.292 +DisplayItemClip::AddOffsetAndComputeDifference(const nsPoint& aOffset, 1.293 + const nsRect& aBounds, 1.294 + const DisplayItemClip& aOther, 1.295 + const nsRect& aOtherBounds, 1.296 + nsRegion* aDifference) 1.297 +{ 1.298 + if (mHaveClipRect != aOther.mHaveClipRect || 1.299 + mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) { 1.300 + aDifference->Or(*aDifference, aBounds); 1.301 + aDifference->Or(*aDifference, aOtherBounds); 1.302 + return; 1.303 + } 1.304 + if (mHaveClipRect) { 1.305 + AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds), 1.306 + aOther.mClipRect.Intersect(aOtherBounds), 1.307 + aDifference); 1.308 + } 1.309 + for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { 1.310 + if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) { 1.311 + // The corners make it tricky so we'll just add both rects here. 1.312 + aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds)); 1.313 + aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds)); 1.314 + } 1.315 + } 1.316 +} 1.317 + 1.318 +uint32_t 1.319 +DisplayItemClip::GetCommonRoundedRectCount(const DisplayItemClip& aOther, 1.320 + uint32_t aMax) const 1.321 +{ 1.322 + uint32_t end = std::min(std::min(mRoundedClipRects.Length(), aMax), 1.323 + aOther.mRoundedClipRects.Length()); 1.324 + uint32_t clipCount = 0; 1.325 + for (; clipCount < end; ++clipCount) { 1.326 + if (mRoundedClipRects[clipCount] != 1.327 + aOther.mRoundedClipRects[clipCount]) { 1.328 + return clipCount; 1.329 + } 1.330 + } 1.331 + return clipCount; 1.332 +} 1.333 + 1.334 +void 1.335 +DisplayItemClip::AppendRoundedRects(nsTArray<RoundedRect>* aArray, uint32_t aCount) const 1.336 +{ 1.337 + uint32_t count = std::min(mRoundedClipRects.Length(), aCount); 1.338 + for (uint32_t i = 0; i < count; ++i) { 1.339 + *aArray->AppendElement() = mRoundedClipRects[i]; 1.340 + } 1.341 +} 1.342 + 1.343 +bool 1.344 +DisplayItemClip::ComputeRegionInClips(DisplayItemClip* aOldClip, 1.345 + const nsPoint& aShift, 1.346 + nsRegion* aCombined) const 1.347 +{ 1.348 + if (!mHaveClipRect || (aOldClip && !aOldClip->mHaveClipRect)) { 1.349 + return false; 1.350 + } 1.351 + 1.352 + if (aOldClip) { 1.353 + *aCombined = aOldClip->NonRoundedIntersection(); 1.354 + aCombined->MoveBy(aShift); 1.355 + aCombined->Or(*aCombined, NonRoundedIntersection()); 1.356 + } else { 1.357 + *aCombined = NonRoundedIntersection(); 1.358 + } 1.359 + return true; 1.360 +} 1.361 + 1.362 +void 1.363 +DisplayItemClip::MoveBy(nsPoint aPoint) 1.364 +{ 1.365 + if (!mHaveClipRect) 1.366 + return; 1.367 + mClipRect += aPoint; 1.368 + for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { 1.369 + mRoundedClipRects[i].mRect += aPoint; 1.370 + } 1.371 +} 1.372 + 1.373 +static DisplayItemClip* gNoClip; 1.374 + 1.375 +const DisplayItemClip& 1.376 +DisplayItemClip::NoClip() 1.377 +{ 1.378 + if (!gNoClip) { 1.379 + gNoClip = new DisplayItemClip(); 1.380 + } 1.381 + return *gNoClip; 1.382 +} 1.383 + 1.384 +void 1.385 +DisplayItemClip::Shutdown() 1.386 +{ 1.387 + delete gNoClip; 1.388 + gNoClip = nullptr; 1.389 +} 1.390 + 1.391 +#ifdef MOZ_DUMP_PAINTING 1.392 +nsCString 1.393 +DisplayItemClip::ToString() const 1.394 +{ 1.395 + nsAutoCString str; 1.396 + if (mHaveClipRect) { 1.397 + str.AppendPrintf("%d,%d,%d,%d", mClipRect.x, mClipRect.y, 1.398 + mClipRect.width, mClipRect.height); 1.399 + for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { 1.400 + const RoundedRect& r = mRoundedClipRects[i]; 1.401 + str.AppendPrintf(" [%d,%d,%d,%d corners %d,%d,%d,%d,%d,%d,%d,%d]", 1.402 + r.mRect.x, r.mRect.y, r.mRect.width, r.mRect.height, 1.403 + r.mRadii[0], r.mRadii[1], r.mRadii[2], r.mRadii[3], 1.404 + r.mRadii[4], r.mRadii[5], r.mRadii[6], r.mRadii[7]); 1.405 + } 1.406 + } 1.407 + return str; 1.408 +} 1.409 +#endif 1.410 + 1.411 +}