|
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 "nsPresContext.h" |
|
10 #include "nsCSSRendering.h" |
|
11 #include "nsLayoutUtils.h" |
|
12 #include "nsRegion.h" |
|
13 |
|
14 namespace mozilla { |
|
15 |
|
16 void |
|
17 DisplayItemClip::SetTo(const nsRect& aRect) |
|
18 { |
|
19 mHaveClipRect = true; |
|
20 mClipRect = aRect; |
|
21 mRoundedClipRects.Clear(); |
|
22 } |
|
23 |
|
24 void |
|
25 DisplayItemClip::SetTo(const nsRect& aRect, const nscoord* aRadii) |
|
26 { |
|
27 mHaveClipRect = true; |
|
28 mClipRect = aRect; |
|
29 mRoundedClipRects.SetLength(1); |
|
30 mRoundedClipRects[0].mRect = aRect; |
|
31 memcpy(mRoundedClipRects[0].mRadii, aRadii, sizeof(nscoord)*8); |
|
32 } |
|
33 |
|
34 bool |
|
35 DisplayItemClip::MayIntersect(const nsRect& aRect) const |
|
36 { |
|
37 if (!mHaveClipRect) { |
|
38 return !aRect.IsEmpty(); |
|
39 } |
|
40 nsRect r = aRect.Intersect(mClipRect); |
|
41 if (r.IsEmpty()) { |
|
42 return false; |
|
43 } |
|
44 for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
|
45 const RoundedRect& rr = mRoundedClipRects[i]; |
|
46 if (!nsLayoutUtils::RoundedRectIntersectsRect(rr.mRect, rr.mRadii, r)) { |
|
47 return false; |
|
48 } |
|
49 } |
|
50 return true; |
|
51 } |
|
52 |
|
53 void |
|
54 DisplayItemClip::IntersectWith(const DisplayItemClip& aOther) |
|
55 { |
|
56 if (!aOther.mHaveClipRect) { |
|
57 return; |
|
58 } |
|
59 if (!mHaveClipRect) { |
|
60 *this = aOther; |
|
61 return; |
|
62 } |
|
63 if (!mClipRect.IntersectRect(mClipRect, aOther.mClipRect)) { |
|
64 mRoundedClipRects.Clear(); |
|
65 return; |
|
66 } |
|
67 mRoundedClipRects.AppendElements(aOther.mRoundedClipRects); |
|
68 } |
|
69 |
|
70 void |
|
71 DisplayItemClip::ApplyTo(gfxContext* aContext, |
|
72 nsPresContext* aPresContext, |
|
73 uint32_t aBegin, uint32_t aEnd) |
|
74 { |
|
75 int32_t A2D = aPresContext->AppUnitsPerDevPixel(); |
|
76 ApplyRectTo(aContext, A2D); |
|
77 ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd); |
|
78 } |
|
79 |
|
80 void |
|
81 DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const |
|
82 { |
|
83 aContext->NewPath(); |
|
84 gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D); |
|
85 aContext->Rectangle(clip, true); |
|
86 aContext->Clip(); |
|
87 } |
|
88 |
|
89 void |
|
90 DisplayItemClip::ApplyRoundedRectsTo(gfxContext* aContext, |
|
91 int32_t A2D, |
|
92 uint32_t aBegin, uint32_t aEnd) const |
|
93 { |
|
94 aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length()); |
|
95 |
|
96 for (uint32_t i = aBegin; i < aEnd; ++i) { |
|
97 AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]); |
|
98 aContext->Clip(); |
|
99 } |
|
100 } |
|
101 |
|
102 void |
|
103 DisplayItemClip::DrawRoundedRectsTo(gfxContext* aContext, |
|
104 int32_t A2D, |
|
105 uint32_t aBegin, uint32_t aEnd) const |
|
106 { |
|
107 aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length()); |
|
108 |
|
109 if (aEnd - aBegin == 0) |
|
110 return; |
|
111 |
|
112 // If there is just one rounded rect we can just fill it, if there are more then we |
|
113 // must clip the rest to get the intersection of clips |
|
114 ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1); |
|
115 AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]); |
|
116 aContext->Fill(); |
|
117 } |
|
118 |
|
119 void |
|
120 DisplayItemClip::AddRoundedRectPathTo(gfxContext* aContext, |
|
121 int32_t A2D, |
|
122 const RoundedRect &aRoundRect) const |
|
123 { |
|
124 gfxCornerSizes pixelRadii; |
|
125 nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii); |
|
126 |
|
127 gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D); |
|
128 clip.Round(); |
|
129 clip.Condition(); |
|
130 |
|
131 aContext->NewPath(); |
|
132 aContext->RoundedRectangle(clip, pixelRadii); |
|
133 } |
|
134 |
|
135 nsRect |
|
136 DisplayItemClip::ApproximateIntersectInward(const nsRect& aRect) const |
|
137 { |
|
138 nsRect r = aRect; |
|
139 if (mHaveClipRect) { |
|
140 r.IntersectRect(r, mClipRect); |
|
141 } |
|
142 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); |
|
143 i < iEnd; ++i) { |
|
144 const RoundedRect &rr = mRoundedClipRects[i]; |
|
145 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r); |
|
146 r = rgn.GetLargestRectangle(); |
|
147 } |
|
148 return r; |
|
149 } |
|
150 |
|
151 // Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter) |
|
152 // and radii aXRadius, aYRadius. |
|
153 bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint, |
|
154 nscoord aYRadius, nscoord aYCenter, nscoord aYPoint) |
|
155 { |
|
156 float scaledX = float(aXPoint - aXCenter) / float(aXRadius); |
|
157 float scaledY = float(aYPoint - aYCenter) / float(aYRadius); |
|
158 return scaledX * scaledX + scaledY * scaledY < 1.0f; |
|
159 } |
|
160 |
|
161 bool |
|
162 DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const |
|
163 { |
|
164 if (mRoundedClipRects.IsEmpty()) |
|
165 return false; |
|
166 |
|
167 nsRect rect; |
|
168 rect.IntersectRect(aRect, NonRoundedIntersection()); |
|
169 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); |
|
170 i < iEnd; ++i) { |
|
171 const RoundedRect &rr = mRoundedClipRects[i]; |
|
172 // top left |
|
173 if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] && |
|
174 rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) { |
|
175 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X], |
|
176 rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X], |
|
177 rect.x, |
|
178 rr.mRadii[NS_CORNER_TOP_LEFT_Y], |
|
179 rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y], |
|
180 rect.y)) { |
|
181 return true; |
|
182 } |
|
183 } |
|
184 // top right |
|
185 if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] && |
|
186 rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) { |
|
187 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X], |
|
188 rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X], |
|
189 rect.XMost(), |
|
190 rr.mRadii[NS_CORNER_TOP_RIGHT_Y], |
|
191 rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y], |
|
192 rect.y)) { |
|
193 return true; |
|
194 } |
|
195 } |
|
196 // bottom left |
|
197 if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] && |
|
198 rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) { |
|
199 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X], |
|
200 rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X], |
|
201 rect.x, |
|
202 rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y], |
|
203 rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y], |
|
204 rect.YMost())) { |
|
205 return true; |
|
206 } |
|
207 } |
|
208 // bottom right |
|
209 if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] && |
|
210 rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) { |
|
211 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X], |
|
212 rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X], |
|
213 rect.XMost(), |
|
214 rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y], |
|
215 rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y], |
|
216 rect.YMost())) { |
|
217 return true; |
|
218 } |
|
219 } |
|
220 } |
|
221 return false; |
|
222 } |
|
223 |
|
224 nsRect |
|
225 DisplayItemClip::NonRoundedIntersection() const |
|
226 { |
|
227 NS_ASSERTION(mHaveClipRect, "Must have a clip rect!"); |
|
228 nsRect result = mClipRect; |
|
229 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); |
|
230 i < iEnd; ++i) { |
|
231 result.IntersectRect(result, mRoundedClipRects[i].mRect); |
|
232 } |
|
233 return result; |
|
234 } |
|
235 |
|
236 bool |
|
237 DisplayItemClip::IsRectAffectedByClip(const nsRect& aRect) const |
|
238 { |
|
239 if (mHaveClipRect && !mClipRect.Contains(aRect)) { |
|
240 return true; |
|
241 } |
|
242 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); |
|
243 i < iEnd; ++i) { |
|
244 const RoundedRect &rr = mRoundedClipRects[i]; |
|
245 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, aRect); |
|
246 if (!rgn.Contains(aRect)) { |
|
247 return true; |
|
248 } |
|
249 } |
|
250 return false; |
|
251 } |
|
252 |
|
253 nsRect |
|
254 DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const |
|
255 { |
|
256 if (!mHaveClipRect) { |
|
257 return aRect; |
|
258 } |
|
259 |
|
260 nsRect result = aRect.Intersect(mClipRect); |
|
261 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length(); |
|
262 i < iEnd; ++i) { |
|
263 result.Intersect(mRoundedClipRects[i].mRect); |
|
264 } |
|
265 return result; |
|
266 } |
|
267 |
|
268 void |
|
269 DisplayItemClip::RemoveRoundedCorners() |
|
270 { |
|
271 if (mRoundedClipRects.IsEmpty()) |
|
272 return; |
|
273 |
|
274 mClipRect = NonRoundedIntersection(); |
|
275 mRoundedClipRects.Clear(); |
|
276 } |
|
277 |
|
278 static void |
|
279 AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut) |
|
280 { |
|
281 if (aR1.IsEqualInterior(aR2)) |
|
282 return; |
|
283 nsRegion r; |
|
284 r.Xor(aR1, aR2); |
|
285 aOut->Or(*aOut, r); |
|
286 } |
|
287 |
|
288 void |
|
289 DisplayItemClip::AddOffsetAndComputeDifference(const nsPoint& aOffset, |
|
290 const nsRect& aBounds, |
|
291 const DisplayItemClip& aOther, |
|
292 const nsRect& aOtherBounds, |
|
293 nsRegion* aDifference) |
|
294 { |
|
295 if (mHaveClipRect != aOther.mHaveClipRect || |
|
296 mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) { |
|
297 aDifference->Or(*aDifference, aBounds); |
|
298 aDifference->Or(*aDifference, aOtherBounds); |
|
299 return; |
|
300 } |
|
301 if (mHaveClipRect) { |
|
302 AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds), |
|
303 aOther.mClipRect.Intersect(aOtherBounds), |
|
304 aDifference); |
|
305 } |
|
306 for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
|
307 if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) { |
|
308 // The corners make it tricky so we'll just add both rects here. |
|
309 aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds)); |
|
310 aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds)); |
|
311 } |
|
312 } |
|
313 } |
|
314 |
|
315 uint32_t |
|
316 DisplayItemClip::GetCommonRoundedRectCount(const DisplayItemClip& aOther, |
|
317 uint32_t aMax) const |
|
318 { |
|
319 uint32_t end = std::min(std::min(mRoundedClipRects.Length(), aMax), |
|
320 aOther.mRoundedClipRects.Length()); |
|
321 uint32_t clipCount = 0; |
|
322 for (; clipCount < end; ++clipCount) { |
|
323 if (mRoundedClipRects[clipCount] != |
|
324 aOther.mRoundedClipRects[clipCount]) { |
|
325 return clipCount; |
|
326 } |
|
327 } |
|
328 return clipCount; |
|
329 } |
|
330 |
|
331 void |
|
332 DisplayItemClip::AppendRoundedRects(nsTArray<RoundedRect>* aArray, uint32_t aCount) const |
|
333 { |
|
334 uint32_t count = std::min(mRoundedClipRects.Length(), aCount); |
|
335 for (uint32_t i = 0; i < count; ++i) { |
|
336 *aArray->AppendElement() = mRoundedClipRects[i]; |
|
337 } |
|
338 } |
|
339 |
|
340 bool |
|
341 DisplayItemClip::ComputeRegionInClips(DisplayItemClip* aOldClip, |
|
342 const nsPoint& aShift, |
|
343 nsRegion* aCombined) const |
|
344 { |
|
345 if (!mHaveClipRect || (aOldClip && !aOldClip->mHaveClipRect)) { |
|
346 return false; |
|
347 } |
|
348 |
|
349 if (aOldClip) { |
|
350 *aCombined = aOldClip->NonRoundedIntersection(); |
|
351 aCombined->MoveBy(aShift); |
|
352 aCombined->Or(*aCombined, NonRoundedIntersection()); |
|
353 } else { |
|
354 *aCombined = NonRoundedIntersection(); |
|
355 } |
|
356 return true; |
|
357 } |
|
358 |
|
359 void |
|
360 DisplayItemClip::MoveBy(nsPoint aPoint) |
|
361 { |
|
362 if (!mHaveClipRect) |
|
363 return; |
|
364 mClipRect += aPoint; |
|
365 for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
|
366 mRoundedClipRects[i].mRect += aPoint; |
|
367 } |
|
368 } |
|
369 |
|
370 static DisplayItemClip* gNoClip; |
|
371 |
|
372 const DisplayItemClip& |
|
373 DisplayItemClip::NoClip() |
|
374 { |
|
375 if (!gNoClip) { |
|
376 gNoClip = new DisplayItemClip(); |
|
377 } |
|
378 return *gNoClip; |
|
379 } |
|
380 |
|
381 void |
|
382 DisplayItemClip::Shutdown() |
|
383 { |
|
384 delete gNoClip; |
|
385 gNoClip = nullptr; |
|
386 } |
|
387 |
|
388 #ifdef MOZ_DUMP_PAINTING |
|
389 nsCString |
|
390 DisplayItemClip::ToString() const |
|
391 { |
|
392 nsAutoCString str; |
|
393 if (mHaveClipRect) { |
|
394 str.AppendPrintf("%d,%d,%d,%d", mClipRect.x, mClipRect.y, |
|
395 mClipRect.width, mClipRect.height); |
|
396 for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) { |
|
397 const RoundedRect& r = mRoundedClipRects[i]; |
|
398 str.AppendPrintf(" [%d,%d,%d,%d corners %d,%d,%d,%d,%d,%d,%d,%d]", |
|
399 r.mRect.x, r.mRect.y, r.mRect.width, r.mRect.height, |
|
400 r.mRadii[0], r.mRadii[1], r.mRadii[2], r.mRadii[3], |
|
401 r.mRadii[4], r.mRadii[5], r.mRadii[6], r.mRadii[7]); |
|
402 } |
|
403 } |
|
404 return str; |
|
405 } |
|
406 #endif |
|
407 |
|
408 } |