/src/mozilla-central/layout/painting/DisplayItemClip.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DisplayItemClip.h" |
8 | | |
9 | | #include "gfxContext.h" |
10 | | #include "gfxUtils.h" |
11 | | #include "mozilla/gfx/2D.h" |
12 | | #include "mozilla/gfx/PathHelpers.h" |
13 | | #include "mozilla/layers/StackingContextHelper.h" |
14 | | #include "mozilla/webrender/WebRenderTypes.h" |
15 | | #include "nsPresContext.h" |
16 | | #include "nsCSSRendering.h" |
17 | | #include "nsLayoutUtils.h" |
18 | | #include "nsRegion.h" |
19 | | |
20 | | using namespace mozilla::gfx; |
21 | | |
22 | | namespace mozilla { |
23 | | |
24 | | void |
25 | | DisplayItemClip::SetTo(const nsRect& aRect) |
26 | 0 | { |
27 | 0 | SetTo(aRect, nullptr); |
28 | 0 | } |
29 | | |
30 | | void |
31 | | DisplayItemClip::SetTo(const nsRect& aRect, const nscoord* aRadii) |
32 | 0 | { |
33 | 0 | mHaveClipRect = true; |
34 | 0 | mClipRect = aRect; |
35 | 0 | if (aRadii) { |
36 | 0 | mRoundedClipRects.SetLength(1); |
37 | 0 | mRoundedClipRects[0].mRect = aRect; |
38 | 0 | memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord) * 8); |
39 | 0 | } else { |
40 | 0 | mRoundedClipRects.Clear(); |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | void |
45 | | DisplayItemClip::SetTo(const nsRect& aRect, |
46 | | const nsRect& aRoundedRect, |
47 | | const nscoord* aRadii) |
48 | 0 | { |
49 | 0 | mHaveClipRect = true; |
50 | 0 | mClipRect = aRect; |
51 | 0 | mRoundedClipRects.SetLength(1); |
52 | 0 | mRoundedClipRects[0].mRect = aRoundedRect; |
53 | 0 | memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord) * 8); |
54 | 0 | } |
55 | | |
56 | | bool |
57 | | DisplayItemClip::MayIntersect(const nsRect& aRect) const |
58 | 0 | { |
59 | 0 | if (!mHaveClipRect) { |
60 | 0 | return !aRect.IsEmpty(); |
61 | 0 | } |
62 | 0 | nsRect r = aRect.Intersect(mClipRect); |
63 | 0 | if (r.IsEmpty()) { |
64 | 0 | return false; |
65 | 0 | } |
66 | 0 | for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
67 | 0 | const RoundedRect& rr = mRoundedClipRects[i]; |
68 | 0 | if (!nsLayoutUtils::RoundedRectIntersectsRect(rr.mRect, rr.mRadii, r)) { |
69 | 0 | return false; |
70 | 0 | } |
71 | 0 | } |
72 | 0 | return true; |
73 | 0 | } |
74 | | |
75 | | void |
76 | | DisplayItemClip::IntersectWith(const DisplayItemClip& aOther) |
77 | 0 | { |
78 | 0 | if (!aOther.mHaveClipRect) { |
79 | 0 | return; |
80 | 0 | } |
81 | 0 | if (!mHaveClipRect) { |
82 | 0 | *this = aOther; |
83 | 0 | return; |
84 | 0 | } |
85 | 0 | if (!mClipRect.IntersectRect(mClipRect, aOther.mClipRect)) { |
86 | 0 | mRoundedClipRects.Clear(); |
87 | 0 | return; |
88 | 0 | } |
89 | 0 | mRoundedClipRects.AppendElements(aOther.mRoundedClipRects); |
90 | 0 | } |
91 | | |
92 | | void |
93 | | DisplayItemClip::ApplyTo(gfxContext* aContext, int32_t A2D) const |
94 | 0 | { |
95 | 0 | ApplyRectTo(aContext, A2D); |
96 | 0 | ApplyRoundedRectClipsTo(aContext, A2D, 0, mRoundedClipRects.Length()); |
97 | 0 | } |
98 | | |
99 | | void |
100 | | DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const |
101 | 0 | { |
102 | 0 | aContext->NewPath(); |
103 | 0 | gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D); |
104 | 0 | aContext->Rectangle(clip, true); |
105 | 0 | aContext->Clip(); |
106 | 0 | } |
107 | | |
108 | | void |
109 | | DisplayItemClip::ApplyRoundedRectClipsTo(gfxContext* aContext, |
110 | | int32_t A2D, |
111 | | uint32_t aBegin, |
112 | | uint32_t aEnd) const |
113 | 0 | { |
114 | 0 | DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); |
115 | 0 |
|
116 | 0 | aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length()); |
117 | 0 |
|
118 | 0 | for (uint32_t i = aBegin; i < aEnd; ++i) { |
119 | 0 | RefPtr<Path> roundedRect = |
120 | 0 | MakeRoundedRectPath(aDrawTarget, A2D, mRoundedClipRects[i]); |
121 | 0 | aContext->Clip(roundedRect); |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | void |
126 | | DisplayItemClip::FillIntersectionOfRoundedRectClips( |
127 | | gfxContext* aContext, |
128 | | const Color& aColor, |
129 | | int32_t aAppUnitsPerDevPixel) const |
130 | 0 | { |
131 | 0 | DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); |
132 | 0 |
|
133 | 0 | uint32_t end = mRoundedClipRects.Length(); |
134 | 0 | if (!end) { |
135 | 0 | return; |
136 | 0 | } |
137 | 0 | |
138 | 0 | // Push clips for any rects that come BEFORE the rect at |aEnd - 1|, if any: |
139 | 0 | ApplyRoundedRectClipsTo(aContext, aAppUnitsPerDevPixel, 0, end - 1); |
140 | 0 |
|
141 | 0 | // Now fill the rect at |aEnd - 1|: |
142 | 0 | RefPtr<Path> roundedRect = MakeRoundedRectPath( |
143 | 0 | aDrawTarget, aAppUnitsPerDevPixel, mRoundedClipRects[end - 1]); |
144 | 0 | ColorPattern color(ToDeviceColor(aColor)); |
145 | 0 | aDrawTarget.Fill(roundedRect, color); |
146 | 0 |
|
147 | 0 | // Finally, pop any clips that we may have pushed: |
148 | 0 | for (uint32_t i = 0; i < end - 1; ++i) { |
149 | 0 | aContext->PopClip(); |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | already_AddRefed<Path> |
154 | | DisplayItemClip::MakeRoundedRectPath(DrawTarget& aDrawTarget, |
155 | | int32_t A2D, |
156 | | const RoundedRect& aRoundRect) const |
157 | 0 | { |
158 | 0 | RectCornerRadii pixelRadii; |
159 | 0 | nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii); |
160 | 0 |
|
161 | 0 | Rect rect = NSRectToSnappedRect(aRoundRect.mRect, A2D, aDrawTarget); |
162 | 0 |
|
163 | 0 | return MakePathForRoundedRect(aDrawTarget, rect, pixelRadii); |
164 | 0 | } |
165 | | |
166 | | nsRect |
167 | | DisplayItemClip::ApproximateIntersectInward(const nsRect& aRect) const |
168 | 0 | { |
169 | 0 | nsRect r = aRect; |
170 | 0 | if (mHaveClipRect) { |
171 | 0 | r.IntersectRect(r, mClipRect); |
172 | 0 | } |
173 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
174 | 0 | const RoundedRect& rr = mRoundedClipRects[i]; |
175 | 0 | nsRegion rgn = |
176 | 0 | nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r); |
177 | 0 | r = rgn.GetLargestRectangle(); |
178 | 0 | } |
179 | 0 | return r; |
180 | 0 | } |
181 | | |
182 | | // Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter) |
183 | | // and radii aXRadius, aYRadius. |
184 | | static bool |
185 | | IsInsideEllipse(nscoord aXRadius, |
186 | | nscoord aXCenter, |
187 | | nscoord aXPoint, |
188 | | nscoord aYRadius, |
189 | | nscoord aYCenter, |
190 | | nscoord aYPoint) |
191 | 0 | { |
192 | 0 | float scaledX = float(aXPoint - aXCenter) / float(aXRadius); |
193 | 0 | float scaledY = float(aYPoint - aYCenter) / float(aYRadius); |
194 | 0 | return scaledX * scaledX + scaledY * scaledY < 1.0f; |
195 | 0 | } |
196 | | |
197 | | bool |
198 | | DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const |
199 | 0 | { |
200 | 0 | if (mRoundedClipRects.IsEmpty()) |
201 | 0 | return false; |
202 | 0 | |
203 | 0 | nsRect rect; |
204 | 0 | rect.IntersectRect(aRect, NonRoundedIntersection()); |
205 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
206 | 0 | const RoundedRect& rr = mRoundedClipRects[i]; |
207 | 0 | // top left |
208 | 0 | if (rect.x < rr.mRect.x + rr.mRadii[eCornerTopLeftX] && |
209 | 0 | rect.y < rr.mRect.y + rr.mRadii[eCornerTopLeftY]) { |
210 | 0 | if (!IsInsideEllipse(rr.mRadii[eCornerTopLeftX], |
211 | 0 | rr.mRect.x + rr.mRadii[eCornerTopLeftX], |
212 | 0 | rect.x, |
213 | 0 | rr.mRadii[eCornerTopLeftY], |
214 | 0 | rr.mRect.y + rr.mRadii[eCornerTopLeftY], |
215 | 0 | rect.y)) { |
216 | 0 | return true; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | // top right |
220 | 0 | if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[eCornerTopRightX] && |
221 | 0 | rect.y < rr.mRect.y + rr.mRadii[eCornerTopRightY]) { |
222 | 0 | if (!IsInsideEllipse(rr.mRadii[eCornerTopRightX], |
223 | 0 | rr.mRect.XMost() - rr.mRadii[eCornerTopRightX], |
224 | 0 | rect.XMost(), |
225 | 0 | rr.mRadii[eCornerTopRightY], |
226 | 0 | rr.mRect.y + rr.mRadii[eCornerTopRightY], |
227 | 0 | rect.y)) { |
228 | 0 | return true; |
229 | 0 | } |
230 | 0 | } |
231 | 0 | // bottom left |
232 | 0 | if (rect.x < rr.mRect.x + rr.mRadii[eCornerBottomLeftX] && |
233 | 0 | rect.YMost() > rr.mRect.YMost() - rr.mRadii[eCornerBottomLeftY]) { |
234 | 0 | if (!IsInsideEllipse(rr.mRadii[eCornerBottomLeftX], |
235 | 0 | rr.mRect.x + rr.mRadii[eCornerBottomLeftX], |
236 | 0 | rect.x, |
237 | 0 | rr.mRadii[eCornerBottomLeftY], |
238 | 0 | rr.mRect.YMost() - rr.mRadii[eCornerBottomLeftY], |
239 | 0 | rect.YMost())) { |
240 | 0 | return true; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | // bottom right |
244 | 0 | if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[eCornerBottomRightX] && |
245 | 0 | rect.YMost() > rr.mRect.YMost() - rr.mRadii[eCornerBottomRightY]) { |
246 | 0 | if (!IsInsideEllipse(rr.mRadii[eCornerBottomRightX], |
247 | 0 | rr.mRect.XMost() - rr.mRadii[eCornerBottomRightX], |
248 | 0 | rect.XMost(), |
249 | 0 | rr.mRadii[eCornerBottomRightY], |
250 | 0 | rr.mRect.YMost() - rr.mRadii[eCornerBottomRightY], |
251 | 0 | rect.YMost())) { |
252 | 0 | return true; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | } |
256 | 0 | return false; |
257 | 0 | } |
258 | | |
259 | | nsRect |
260 | | DisplayItemClip::NonRoundedIntersection() const |
261 | 0 | { |
262 | 0 | NS_ASSERTION(mHaveClipRect, "Must have a clip rect!"); |
263 | 0 | nsRect result = mClipRect; |
264 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
265 | 0 | result.IntersectRect(result, mRoundedClipRects[i].mRect); |
266 | 0 | } |
267 | 0 | return result; |
268 | 0 | } |
269 | | |
270 | | bool |
271 | | DisplayItemClip::IsRectAffectedByClip(const nsRect& aRect) const |
272 | 0 | { |
273 | 0 | if (mHaveClipRect && !mClipRect.Contains(aRect)) { |
274 | 0 | return true; |
275 | 0 | } |
276 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
277 | 0 | const RoundedRect& rr = mRoundedClipRects[i]; |
278 | 0 | nsRegion rgn = |
279 | 0 | nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, aRect); |
280 | 0 | if (!rgn.Contains(aRect)) { |
281 | 0 | return true; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | return false; |
285 | 0 | } |
286 | | |
287 | | bool |
288 | | DisplayItemClip::IsRectAffectedByClip(const nsIntRect& aRect, |
289 | | float aXScale, |
290 | | float aYScale, |
291 | | int32_t A2D) const |
292 | 0 | { |
293 | 0 | if (mHaveClipRect) { |
294 | 0 | nsIntRect pixelClipRect = |
295 | 0 | mClipRect.ScaleToNearestPixels(aXScale, aYScale, A2D); |
296 | 0 | if (!pixelClipRect.Contains(aRect)) { |
297 | 0 | return true; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | |
301 | 0 | // Rounded rect clipping only snaps to user-space pixels, not device space. |
302 | 0 | nsIntRect unscaled = aRect; |
303 | 0 | unscaled.Scale(1 / aXScale, 1 / aYScale); |
304 | 0 |
|
305 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
306 | 0 | const RoundedRect& rr = mRoundedClipRects[i]; |
307 | 0 |
|
308 | 0 | nsIntRect pixelRect = rr.mRect.ToNearestPixels(A2D); |
309 | 0 |
|
310 | 0 | RectCornerRadii pixelRadii; |
311 | 0 | nsCSSRendering::ComputePixelRadii(rr.mRadii, A2D, &pixelRadii); |
312 | 0 |
|
313 | 0 | nsIntRegion rgn = nsLayoutUtils::RoundedRectIntersectIntRect( |
314 | 0 | pixelRect, pixelRadii, unscaled); |
315 | 0 | if (!rgn.Contains(unscaled)) { |
316 | 0 | return true; |
317 | 0 | } |
318 | 0 | } |
319 | 0 | return false; |
320 | 0 | } |
321 | | |
322 | | nsRect |
323 | | DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const |
324 | 0 | { |
325 | 0 | if (!mHaveClipRect) { |
326 | 0 | return aRect; |
327 | 0 | } |
328 | 0 | |
329 | 0 | nsRect result = aRect.Intersect(mClipRect); |
330 | 0 | for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { |
331 | 0 | result = result.Intersect(mRoundedClipRects[i].mRect); |
332 | 0 | } |
333 | 0 | return result; |
334 | 0 | } |
335 | | |
336 | | void |
337 | | DisplayItemClip::RemoveRoundedCorners() |
338 | 0 | { |
339 | 0 | if (mRoundedClipRects.IsEmpty()) |
340 | 0 | return; |
341 | 0 | |
342 | 0 | mClipRect = NonRoundedIntersection(); |
343 | 0 | mRoundedClipRects.Clear(); |
344 | 0 | } |
345 | | |
346 | | // Computes the difference between aR1 and aR2, limited to aBounds. |
347 | | static void |
348 | | AccumulateRectDifference(const nsRect& aR1, |
349 | | const nsRect& aR2, |
350 | | const nsRect& aBounds, |
351 | | nsRegion* aOut) |
352 | 0 | { |
353 | 0 | if (aR1.IsEqualInterior(aR2)) |
354 | 0 | return; |
355 | 0 | nsRegion r; |
356 | 0 | r.Xor(aR1, aR2); |
357 | 0 | r.And(r, aBounds); |
358 | 0 | aOut->Or(*aOut, r); |
359 | 0 | } |
360 | | |
361 | | static void |
362 | | AccumulateRoundedRectDifference(const DisplayItemClip::RoundedRect& aR1, |
363 | | const DisplayItemClip::RoundedRect& aR2, |
364 | | const nsRect& aBounds, |
365 | | const nsRect& aOtherBounds, |
366 | | nsRegion* aOut) |
367 | 0 | { |
368 | 0 | const nsRect& rect1 = aR1.mRect; |
369 | 0 | const nsRect& rect2 = aR2.mRect; |
370 | 0 |
|
371 | 0 | // If the two rectangles are totally disjoint, just add them both - otherwise |
372 | 0 | // we'd end up adding one big enclosing rect |
373 | 0 | if (!rect1.Intersects(rect2) || |
374 | 0 | memcmp(aR1.mRadii, aR2.mRadii, sizeof(aR1.mRadii))) { |
375 | 0 | aOut->Or(*aOut, rect1.Intersect(aBounds)); |
376 | 0 | aOut->Or(*aOut, rect2.Intersect(aOtherBounds)); |
377 | 0 | return; |
378 | 0 | } |
379 | 0 | |
380 | 0 | nscoord lowestBottom = std::max(rect1.YMost(), rect2.YMost()); |
381 | 0 | nscoord highestTop = std::min(rect1.Y(), rect2.Y()); |
382 | 0 | nscoord maxRight = std::max(rect1.XMost(), rect2.XMost()); |
383 | 0 | nscoord minLeft = std::min(rect1.X(), rect2.X()); |
384 | 0 |
|
385 | 0 | // At this point, we know that the radii haven't changed, and that the bounds |
386 | 0 | // are different in some way. To explain how this works, consider the case |
387 | 0 | // where the rounded rect has just been translated along the X direction. |
388 | 0 | // | ______________________ _ _ _ _ _ _ | |
389 | 0 | // | / / \ \ | |
390 | 0 | // | | | | |
391 | 0 | // | | aR1 | | aR2 | | |
392 | 0 | // | | | | |
393 | 0 | // | \ __________\___________ / _ _ _ _ _ / | |
394 | 0 | // | | |
395 | 0 | // The invalidation region will be as if we lopped off the left rounded part |
396 | 0 | // of aR2, and the right rounded part of aR1, and XOR'd them: |
397 | 0 | // | ______________________ _ _ _ _ _ _ | |
398 | 0 | // | -/-----------/- -\-----------\- | |
399 | 0 | // | |-------------- --|------------ | |
400 | 0 | // | |-----aR1---|-- --|-----aR2---| | |
401 | 0 | // | |-------------- --|------------ | |
402 | 0 | // | -\ __________\-__________-/ _ _ _ _ _ /- | |
403 | 0 | // | | |
404 | 0 | // The logic below just implements this idea, but generalized to both the |
405 | 0 | // X and Y dimensions. The "(...)Adjusted(...)" values represent the lopped |
406 | 0 | // off sides. |
407 | 0 | nscoord highestAdjustedBottom = std::min( |
408 | 0 | rect1.YMost() - aR1.mRadii[eCornerBottomLeftY], |
409 | 0 | std::min(rect1.YMost() - aR1.mRadii[eCornerBottomRightY], |
410 | 0 | std::min(rect2.YMost() - aR2.mRadii[eCornerBottomLeftY], |
411 | 0 | rect2.YMost() - aR2.mRadii[eCornerBottomRightY]))); |
412 | 0 | nscoord lowestAdjustedTop = |
413 | 0 | std::max(rect1.Y() + aR1.mRadii[eCornerTopLeftY], |
414 | 0 | std::max(rect1.Y() + aR1.mRadii[eCornerTopRightY], |
415 | 0 | std::max(rect2.Y() + aR2.mRadii[eCornerTopLeftY], |
416 | 0 | rect2.Y() + aR2.mRadii[eCornerTopRightY]))); |
417 | 0 |
|
418 | 0 | nscoord minAdjustedRight = std::min( |
419 | 0 | rect1.XMost() - aR1.mRadii[eCornerTopRightX], |
420 | 0 | std::min(rect1.XMost() - aR1.mRadii[eCornerBottomRightX], |
421 | 0 | std::min(rect2.XMost() - aR2.mRadii[eCornerTopRightX], |
422 | 0 | rect2.XMost() - aR2.mRadii[eCornerBottomRightX]))); |
423 | 0 | nscoord maxAdjustedLeft = |
424 | 0 | std::max(rect1.X() + aR1.mRadii[eCornerTopLeftX], |
425 | 0 | std::max(rect1.X() + aR1.mRadii[eCornerBottomLeftX], |
426 | 0 | std::max(rect2.X() + aR2.mRadii[eCornerTopLeftX], |
427 | 0 | rect2.X() + aR2.mRadii[eCornerBottomLeftX]))); |
428 | 0 |
|
429 | 0 | // We only want to add an invalidation rect if the bounds have changed. If we |
430 | 0 | // always added all of the 4 rects below, we would always be invalidating a |
431 | 0 | // border around the rects, even in cases where we just translated along the X |
432 | 0 | // or Y axis. |
433 | 0 | nsRegion r; |
434 | 0 | // First, or with the Y delta rects, wide along the X axis |
435 | 0 | if (rect1.Y() != rect2.Y()) { |
436 | 0 | r.Or(r, |
437 | 0 | nsRect(minLeft, |
438 | 0 | highestTop, |
439 | 0 | maxRight - minLeft, |
440 | 0 | lowestAdjustedTop - highestTop)); |
441 | 0 | } |
442 | 0 | if (rect1.YMost() != rect2.YMost()) { |
443 | 0 | r.Or(r, |
444 | 0 | nsRect(minLeft, |
445 | 0 | highestAdjustedBottom, |
446 | 0 | maxRight - minLeft, |
447 | 0 | lowestBottom - highestAdjustedBottom)); |
448 | 0 | } |
449 | 0 | // Then, or with the X delta rects, narrow along the Y axis |
450 | 0 | if (rect1.X() != rect2.X()) { |
451 | 0 | r.Or(r, |
452 | 0 | nsRect(minLeft, |
453 | 0 | lowestAdjustedTop, |
454 | 0 | maxAdjustedLeft - minLeft, |
455 | 0 | highestAdjustedBottom - lowestAdjustedTop)); |
456 | 0 | } |
457 | 0 | if (rect1.XMost() != rect2.XMost()) { |
458 | 0 | r.Or(r, |
459 | 0 | nsRect(minAdjustedRight, |
460 | 0 | lowestAdjustedTop, |
461 | 0 | maxRight - minAdjustedRight, |
462 | 0 | highestAdjustedBottom - lowestAdjustedTop)); |
463 | 0 | } |
464 | 0 |
|
465 | 0 | r.And(r, aBounds.Union(aOtherBounds)); |
466 | 0 | aOut->Or(*aOut, r); |
467 | 0 | } |
468 | | |
469 | | void |
470 | | DisplayItemClip::AddOffsetAndComputeDifference(const nsPoint& aOffset, |
471 | | const nsRect& aBounds, |
472 | | const DisplayItemClip& aOther, |
473 | | const nsRect& aOtherBounds, |
474 | | nsRegion* aDifference) |
475 | 0 | { |
476 | 0 | if (mHaveClipRect != aOther.mHaveClipRect || |
477 | 0 | mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) { |
478 | 0 | aDifference->Or(*aDifference, aBounds); |
479 | 0 | aDifference->Or(*aDifference, aOtherBounds); |
480 | 0 | return; |
481 | 0 | } |
482 | 0 | if (mHaveClipRect) { |
483 | 0 | AccumulateRectDifference(mClipRect + aOffset, |
484 | 0 | aOther.mClipRect, |
485 | 0 | aBounds.Union(aOtherBounds), |
486 | 0 | aDifference); |
487 | 0 | } |
488 | 0 | for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
489 | 0 | if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) { |
490 | 0 | AccumulateRoundedRectDifference(mRoundedClipRects[i] + aOffset, |
491 | 0 | aOther.mRoundedClipRects[i], |
492 | 0 | aBounds, |
493 | 0 | aOtherBounds, |
494 | 0 | aDifference); |
495 | 0 | } |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | | void |
500 | | DisplayItemClip::AppendRoundedRects(nsTArray<RoundedRect>* aArray) const |
501 | 0 | { |
502 | 0 | aArray->AppendElements(mRoundedClipRects.Elements(), |
503 | 0 | mRoundedClipRects.Length()); |
504 | 0 | } |
505 | | |
506 | | bool |
507 | | DisplayItemClip::ComputeRegionInClips(const DisplayItemClip* aOldClip, |
508 | | const nsPoint& aShift, |
509 | | nsRegion* aCombined) const |
510 | 0 | { |
511 | 0 | if (!mHaveClipRect || (aOldClip && !aOldClip->mHaveClipRect)) { |
512 | 0 | return false; |
513 | 0 | } |
514 | 0 | |
515 | 0 | if (aOldClip) { |
516 | 0 | *aCombined = aOldClip->NonRoundedIntersection(); |
517 | 0 | aCombined->MoveBy(aShift); |
518 | 0 | aCombined->Or(*aCombined, NonRoundedIntersection()); |
519 | 0 | } else { |
520 | 0 | *aCombined = NonRoundedIntersection(); |
521 | 0 | } |
522 | 0 | return true; |
523 | 0 | } |
524 | | |
525 | | void |
526 | | DisplayItemClip::MoveBy(const nsPoint& aPoint) |
527 | 0 | { |
528 | 0 | if (!mHaveClipRect) |
529 | 0 | return; |
530 | 0 | mClipRect += aPoint; |
531 | 0 | for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
532 | 0 | mRoundedClipRects[i].mRect += aPoint; |
533 | 0 | } |
534 | 0 | } |
535 | | |
536 | | static DisplayItemClip* gNoClip; |
537 | | |
538 | | const DisplayItemClip& |
539 | | DisplayItemClip::NoClip() |
540 | 0 | { |
541 | 0 | if (!gNoClip) { |
542 | 0 | gNoClip = new DisplayItemClip(); |
543 | 0 | } |
544 | 0 | return *gNoClip; |
545 | 0 | } |
546 | | |
547 | | void |
548 | | DisplayItemClip::Shutdown() |
549 | 0 | { |
550 | 0 | delete gNoClip; |
551 | 0 | gNoClip = nullptr; |
552 | 0 | } |
553 | | |
554 | | nsCString |
555 | | DisplayItemClip::ToString() const |
556 | 0 | { |
557 | 0 | nsAutoCString str; |
558 | 0 | if (mHaveClipRect) { |
559 | 0 | str.AppendPrintf("%d,%d,%d,%d", |
560 | 0 | mClipRect.x, |
561 | 0 | mClipRect.y, |
562 | 0 | mClipRect.width, |
563 | 0 | mClipRect.height); |
564 | 0 | for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
565 | 0 | const RoundedRect& r = mRoundedClipRects[i]; |
566 | 0 | str.AppendPrintf(" [%d,%d,%d,%d corners %d,%d,%d,%d,%d,%d,%d,%d]", |
567 | 0 | r.mRect.x, |
568 | 0 | r.mRect.y, |
569 | 0 | r.mRect.width, |
570 | 0 | r.mRect.height, |
571 | 0 | r.mRadii[0], |
572 | 0 | r.mRadii[1], |
573 | 0 | r.mRadii[2], |
574 | 0 | r.mRadii[3], |
575 | 0 | r.mRadii[4], |
576 | 0 | r.mRadii[5], |
577 | 0 | r.mRadii[6], |
578 | 0 | r.mRadii[7]); |
579 | 0 | } |
580 | 0 | } |
581 | 0 | return std::move(str); |
582 | 0 | } |
583 | | |
584 | | void |
585 | | DisplayItemClip::ToComplexClipRegions( |
586 | | int32_t aAppUnitsPerDevPixel, |
587 | | const layers::StackingContextHelper& aSc, |
588 | | nsTArray<wr::ComplexClipRegion>& aOutArray) const |
589 | 0 | { |
590 | 0 | for (uint32_t i = 0; i < mRoundedClipRects.Length(); i++) { |
591 | 0 | wr::ComplexClipRegion* region = aOutArray.AppendElement(); |
592 | 0 | region->rect = wr::ToRoundedLayoutRect(LayoutDeviceRect::FromAppUnits( |
593 | 0 | mRoundedClipRects[i].mRect, aAppUnitsPerDevPixel)); |
594 | 0 | const nscoord* radii = mRoundedClipRects[i].mRadii; |
595 | 0 | region->radii = wr::ToBorderRadius( |
596 | 0 | LayoutDeviceSize::FromAppUnits( |
597 | 0 | nsSize(radii[eCornerTopLeftX], radii[eCornerTopLeftY]), |
598 | 0 | aAppUnitsPerDevPixel), |
599 | 0 | LayoutDeviceSize::FromAppUnits( |
600 | 0 | nsSize(radii[eCornerTopRightX], radii[eCornerTopRightY]), |
601 | 0 | aAppUnitsPerDevPixel), |
602 | 0 | LayoutDeviceSize::FromAppUnits( |
603 | 0 | nsSize(radii[eCornerBottomLeftX], radii[eCornerBottomLeftY]), |
604 | 0 | aAppUnitsPerDevPixel), |
605 | 0 | LayoutDeviceSize::FromAppUnits( |
606 | 0 | nsSize(radii[eCornerBottomRightX], radii[eCornerBottomRightY]), |
607 | 0 | aAppUnitsPerDevPixel)); |
608 | 0 | region->mode = wr::ClipMode::Clip; |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | } // namespace mozilla |