1 /*
2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/platform/geometry/region.h"
27
28 #include <stdio.h>
29
30 namespace blink {
31
32 Region::Region() = default;
33
Region(const IntRect & rect)34 Region::Region(const IntRect& rect) : bounds_(rect), shape_(rect) {}
35
Rects() const36 Vector<IntRect> Region::Rects() const {
37 Vector<IntRect> rects;
38
39 for (Shape::SpanIterator span = shape_.SpansBegin(), end = shape_.SpansEnd();
40 span != end && span + 1 != end; ++span) {
41 int y = span->y;
42 int height = (span + 1)->y - y;
43
44 for (Shape::SegmentIterator segment = shape_.SegmentsBegin(span),
45 segment_end = shape_.SegmentsEnd(span);
46 segment != segment_end && segment + 1 != segment_end; segment += 2) {
47 int x = *segment;
48 int width = *(segment + 1) - x;
49
50 rects.push_back(IntRect(x, y, width, height));
51 }
52 }
53
54 return rects;
55 }
56
Contains(const Region & region) const57 bool Region::Contains(const Region& region) const {
58 if (!bounds_.Contains(region.bounds_))
59 return false;
60
61 return Shape::CompareShapes<Shape::CompareContainsOperation>(shape_,
62 region.shape_);
63 }
64
Contains(const IntPoint & point) const65 bool Region::Contains(const IntPoint& point) const {
66 if (!bounds_.Contains(point))
67 return false;
68
69 for (Shape::SpanIterator span = shape_.SpansBegin(), end = shape_.SpansEnd();
70 span != end && span + 1 != end; ++span) {
71 int y = span->y;
72 int max_y = (span + 1)->y;
73
74 if (y > point.Y())
75 break;
76 if (max_y <= point.Y())
77 continue;
78
79 for (Shape::SegmentIterator segment = shape_.SegmentsBegin(span),
80 segment_end = shape_.SegmentsEnd(span);
81 segment != segment_end && segment + 1 != segment_end; segment += 2) {
82 int x = *segment;
83 int max_x = *(segment + 1);
84
85 if (x > point.X())
86 break;
87 if (max_x > point.X())
88 return true;
89 }
90 }
91
92 return false;
93 }
94
Intersects(const Region & region) const95 bool Region::Intersects(const Region& region) const {
96 if (!bounds_.Intersects(region.bounds_))
97 return false;
98
99 return Shape::CompareShapes<Shape::CompareIntersectsOperation>(shape_,
100 region.shape_);
101 }
102
Area() const103 uint64_t Region::Area() const {
104 uint64_t area = 0;
105 for (Shape::SpanIterator span = shape_.SpansBegin(), end = shape_.SpansEnd();
106 span != end && span + 1 != end; ++span) {
107 int height = (span + 1)->y - span->y;
108
109 for (Shape::SegmentIterator segment = shape_.SegmentsBegin(span),
110 segment_end = shape_.SegmentsEnd(span);
111 segment != segment_end && segment + 1 != segment_end; segment += 2) {
112 int width = *(segment + 1) - *segment;
113 area += (uint64_t)height * (uint64_t)width;
114 }
115 }
116 return area;
117 }
118
119 template <typename CompareOperation>
CompareShapes(const Shape & a_shape,const Shape & b_shape)120 bool Region::Shape::CompareShapes(const Shape& a_shape, const Shape& b_shape) {
121 bool result = CompareOperation::kDefaultResult;
122
123 Shape::SpanIterator a_span = a_shape.SpansBegin();
124 Shape::SpanIterator a_span_end = a_shape.SpansEnd();
125 Shape::SpanIterator b_span = b_shape.SpansBegin();
126 Shape::SpanIterator b_span_end = b_shape.SpansEnd();
127
128 bool a_had_segment_in_previous_span = false;
129 bool b_had_segment_in_previous_span = false;
130 while (a_span != a_span_end && a_span + 1 != a_span_end &&
131 b_span != b_span_end && b_span + 1 != b_span_end) {
132 int a_y = a_span->y;
133 int a_max_y = (a_span + 1)->y;
134 int b_y = b_span->y;
135 int b_max_y = (b_span + 1)->y;
136
137 Shape::SegmentIterator a_segment = a_shape.SegmentsBegin(a_span);
138 Shape::SegmentIterator a_segment_end = a_shape.SegmentsEnd(a_span);
139 Shape::SegmentIterator b_segment = b_shape.SegmentsBegin(b_span);
140 Shape::SegmentIterator b_segment_end = b_shape.SegmentsEnd(b_span);
141
142 // Look for a non-overlapping part of the spans. If B had a segment in its
143 // previous span, then we already tested A against B within that span.
144 bool a_has_segment_in_span = a_segment != a_segment_end;
145 bool b_has_segment_in_span = b_segment != b_segment_end;
146 if (a_y < b_y && !b_had_segment_in_previous_span && a_has_segment_in_span &&
147 CompareOperation::AOutsideB(result))
148 return result;
149 if (b_y < a_y && !a_had_segment_in_previous_span && b_has_segment_in_span &&
150 CompareOperation::BOutsideA(result))
151 return result;
152
153 a_had_segment_in_previous_span = a_has_segment_in_span;
154 b_had_segment_in_previous_span = b_has_segment_in_span;
155
156 bool spans_overlap = b_max_y > a_y && b_y < a_max_y;
157 if (spans_overlap) {
158 while (a_segment != a_segment_end && b_segment != b_segment_end) {
159 int a_x = *a_segment;
160 int a_max_x = *(a_segment + 1);
161 int b_x = *b_segment;
162 int b_max_x = *(b_segment + 1);
163
164 bool segments_overlap = b_max_x > a_x && b_x < a_max_x;
165 if (segments_overlap && CompareOperation::AOverlapsB(result))
166 return result;
167 if (a_x < b_x && CompareOperation::AOutsideB(result))
168 return result;
169 if (b_x < a_x && CompareOperation::BOutsideA(result))
170 return result;
171
172 if (a_max_x < b_max_x) {
173 a_segment += 2;
174 } else if (b_max_x < a_max_x) {
175 b_segment += 2;
176 } else {
177 a_segment += 2;
178 b_segment += 2;
179 }
180 }
181
182 if (a_segment != a_segment_end && CompareOperation::AOutsideB(result))
183 return result;
184 if (b_segment != b_segment_end && CompareOperation::BOutsideA(result))
185 return result;
186 }
187
188 if (a_max_y < b_max_y) {
189 a_span += 1;
190 } else if (b_max_y < a_max_y) {
191 b_span += 1;
192 } else {
193 a_span += 1;
194 b_span += 1;
195 }
196 }
197
198 if (a_span != a_span_end && a_span + 1 != a_span_end &&
199 CompareOperation::AOutsideB(result))
200 return result;
201 if (b_span != b_span_end && b_span + 1 != b_span_end &&
202 CompareOperation::BOutsideA(result))
203 return result;
204
205 return result;
206 }
207
TrimCapacities()208 void Region::Shape::TrimCapacities() {
209 segments_.ShrinkToReasonableCapacity();
210 spans_.ShrinkToReasonableCapacity();
211 }
212
213 struct Region::Shape::CompareContainsOperation {
214 STATIC_ONLY(CompareContainsOperation);
215 const static bool kDefaultResult = true;
AOutsideBblink::Region::Shape::CompareContainsOperation216 inline static bool AOutsideB(bool& /* result */) { return false; }
BOutsideAblink::Region::Shape::CompareContainsOperation217 inline static bool BOutsideA(bool& result) {
218 result = false;
219 return true;
220 }
AOverlapsBblink::Region::Shape::CompareContainsOperation221 inline static bool AOverlapsB(bool& /* result */) { return false; }
222 };
223
224 struct Region::Shape::CompareIntersectsOperation {
225 STATIC_ONLY(CompareIntersectsOperation);
226 const static bool kDefaultResult = false;
AOutsideBblink::Region::Shape::CompareIntersectsOperation227 inline static bool AOutsideB(bool& /* result */) { return false; }
BOutsideAblink::Region::Shape::CompareIntersectsOperation228 inline static bool BOutsideA(bool& /* result */) { return false; }
AOverlapsBblink::Region::Shape::CompareIntersectsOperation229 inline static bool AOverlapsB(bool& result) {
230 result = true;
231 return true;
232 }
233 };
234
235 Region::Shape::Shape() = default;
236
Shape(const IntRect & rect)237 Region::Shape::Shape(const IntRect& rect) {
238 AppendSpan(rect.Y());
239 AppendSegment(rect.X());
240 AppendSegment(rect.MaxX());
241 AppendSpan(rect.MaxY());
242 }
243
Shape(wtf_size_t segments_capacity,wtf_size_t spans_capacity)244 Region::Shape::Shape(wtf_size_t segments_capacity, wtf_size_t spans_capacity) {
245 segments_.ReserveCapacity(segments_capacity);
246 spans_.ReserveCapacity(spans_capacity);
247 }
248
AppendSpan(int y)249 void Region::Shape::AppendSpan(int y) {
250 spans_.push_back(Span(y, segments_.size()));
251 }
252
CanCoalesce(SegmentIterator begin,SegmentIterator end)253 bool Region::Shape::CanCoalesce(SegmentIterator begin, SegmentIterator end) {
254 if (spans_.IsEmpty())
255 return false;
256
257 SegmentIterator last_span_begin =
258 segments_.data() + spans_.back().segment_index;
259 SegmentIterator last_span_end = segments_.data() + segments_.size();
260
261 // Check if both spans have an equal number of segments.
262 if (last_span_end - last_span_begin != end - begin)
263 return false;
264
265 // Check if both spans are equal.
266 if (!std::equal(begin, end, last_span_begin))
267 return false;
268
269 // Since the segments are equal the second segment can just be ignored.
270 return true;
271 }
272
AppendSpan(int y,SegmentIterator begin,SegmentIterator end)273 void Region::Shape::AppendSpan(int y,
274 SegmentIterator begin,
275 SegmentIterator end) {
276 if (CanCoalesce(begin, end))
277 return;
278
279 AppendSpan(y);
280 segments_.AppendRange(begin, end);
281 }
282
AppendSpans(const Shape & shape,SpanIterator begin,SpanIterator end)283 void Region::Shape::AppendSpans(const Shape& shape,
284 SpanIterator begin,
285 SpanIterator end) {
286 for (SpanIterator it = begin; it != end; ++it)
287 AppendSpan(it->y, shape.SegmentsBegin(it), shape.SegmentsEnd(it));
288 }
289
AppendSegment(int x)290 void Region::Shape::AppendSegment(int x) {
291 segments_.push_back(x);
292 }
293
SpansBegin() const294 Region::Shape::SpanIterator Region::Shape::SpansBegin() const {
295 return spans_.data();
296 }
297
SpansEnd() const298 Region::Shape::SpanIterator Region::Shape::SpansEnd() const {
299 return spans_.data() + spans_.size();
300 }
301
SegmentsBegin(SpanIterator it) const302 Region::Shape::SegmentIterator Region::Shape::SegmentsBegin(
303 SpanIterator it) const {
304 DCHECK_GE(it, spans_.data());
305 DCHECK_LT(it, spans_.data() + spans_.size());
306
307 // Check if this span has any segments.
308 if (it->segment_index == segments_.size())
309 return nullptr;
310
311 return &segments_[it->segment_index];
312 }
313
SegmentsEnd(SpanIterator it) const314 Region::Shape::SegmentIterator Region::Shape::SegmentsEnd(
315 SpanIterator it) const {
316 DCHECK_GE(it, spans_.data());
317 DCHECK_LT(it, spans_.data() + spans_.size());
318
319 // Check if this span has any segments.
320 if (it->segment_index == segments_.size())
321 return nullptr;
322
323 DCHECK_LT(it + 1, spans_.data() + spans_.size());
324 wtf_size_t segment_index = (it + 1)->segment_index;
325
326 SECURITY_DCHECK(segment_index <= segments_.size());
327 return segments_.data() + segment_index;
328 }
329
330 #if DCHECK_IS_ON()
Dump() const331 void Region::Shape::Dump() const {
332 for (Shape::SpanIterator span = SpansBegin(), end = SpansEnd(); span != end;
333 ++span) {
334 printf("%6d: (", span->y);
335
336 for (Shape::SegmentIterator segment = SegmentsBegin(span),
337 segment_end = SegmentsEnd(span);
338 segment != segment_end; ++segment)
339 printf("%d ", *segment);
340 printf(")\n");
341 }
342
343 printf("\n");
344 }
345 #endif
346
Bounds() const347 IntRect Region::Shape::Bounds() const {
348 if (IsEmpty())
349 return IntRect();
350
351 SpanIterator span = SpansBegin();
352 int min_y = span->y;
353
354 SpanIterator last_span = SpansEnd() - 1;
355 int max_y = last_span->y;
356
357 int min_x = std::numeric_limits<int>::max();
358 int max_x = std::numeric_limits<int>::min();
359
360 while (span != last_span) {
361 SegmentIterator first_segment = SegmentsBegin(span);
362 SegmentIterator last_segment = SegmentsEnd(span) - 1;
363
364 if (first_segment && last_segment) {
365 DCHECK_NE(first_segment, last_segment);
366
367 if (*first_segment < min_x)
368 min_x = *first_segment;
369
370 if (*last_segment > max_x)
371 max_x = *last_segment;
372 }
373
374 ++span;
375 }
376
377 DCHECK_LE(min_x, max_x);
378 DCHECK_LE(min_y, max_y);
379
380 return IntRect(min_x, min_y, max_x - min_x, max_y - min_y);
381 }
382
Translate(const IntSize & offset)383 void Region::Shape::Translate(const IntSize& offset) {
384 for (wtf_size_t i = 0; i < segments_.size(); ++i)
385 segments_[i] += offset.Width();
386 for (wtf_size_t i = 0; i < spans_.size(); ++i)
387 spans_[i].y += offset.Height();
388 }
389
Swap(Shape & other)390 void Region::Shape::Swap(Shape& other) {
391 segments_.swap(other.segments_);
392 spans_.swap(other.spans_);
393 }
394
395 enum {
396 kShape1,
397 kShape2,
398 };
399
400 template <typename Operation>
ShapeOperation(const Shape & shape1,const Shape & shape2)401 Region::Shape Region::Shape::ShapeOperation(const Shape& shape1,
402 const Shape& shape2) {
403 static_assert(!(!Operation::kShouldAddRemainingSegmentsFromSpan1 &&
404 Operation::kShouldAddRemainingSegmentsFromSpan2),
405 "invalid segment combination");
406 static_assert(!(!Operation::kShouldAddRemainingSpansFromShape1 &&
407 Operation::kShouldAddRemainingSpansFromShape2),
408 "invalid span combination");
409
410 wtf_size_t segments_capacity = shape1.SegmentsSize() + shape2.SegmentsSize();
411 wtf_size_t spans_capacity = shape1.SpansSize() + shape2.SpansSize();
412 Shape result(segments_capacity, spans_capacity);
413 if (Operation::TrySimpleOperation(shape1, shape2, result))
414 return result;
415
416 SpanIterator spans1 = shape1.SpansBegin();
417 SpanIterator spans1_end = shape1.SpansEnd();
418
419 SpanIterator spans2 = shape2.SpansBegin();
420 SpanIterator spans2_end = shape2.SpansEnd();
421
422 SegmentIterator segments1 = nullptr;
423 SegmentIterator segments1_end = nullptr;
424
425 SegmentIterator segments2 = nullptr;
426 SegmentIterator segments2_end = nullptr;
427
428 Vector<int, 32> segments;
429 segments.ReserveCapacity(
430 std::max(shape1.SegmentsSize(), shape2.SegmentsSize()));
431
432 // Iterate over all spans.
433 while (spans1 != spans1_end && spans2 != spans2_end) {
434 int y = 0;
435 int y_diff = spans1->y - spans2->y;
436
437 if (y_diff <= 0) {
438 y = spans1->y;
439
440 segments1 = shape1.SegmentsBegin(spans1);
441 segments1_end = shape1.SegmentsEnd(spans1);
442 ++spans1;
443 }
444 if (y_diff >= 0) {
445 y = spans2->y;
446
447 segments2 = shape2.SegmentsBegin(spans2);
448 segments2_end = shape2.SegmentsEnd(spans2);
449 ++spans2;
450 }
451
452 int flag = 0;
453 int old_flag = 0;
454
455 SegmentIterator s1 = segments1;
456 SegmentIterator s2 = segments2;
457
458 // Clear vector without dropping capacity.
459 segments.resize(0);
460 DCHECK(segments.capacity());
461
462 // Now iterate over the segments in each span and construct a new vector of
463 // segments.
464 while (s1 != segments1_end && s2 != segments2_end) {
465 int s_diff = *s1 - *s2;
466 int x;
467
468 if (s_diff <= 0) {
469 x = *s1;
470 flag = flag ^ 1;
471 ++s1;
472 }
473 if (s_diff >= 0) {
474 x = *s2;
475 flag = flag ^ 2;
476 ++s2;
477 }
478
479 if (flag == Operation::kOpCode || old_flag == Operation::kOpCode)
480 segments.push_back(x);
481
482 old_flag = flag;
483 }
484
485 // Add any remaining segments.
486 if (Operation::kShouldAddRemainingSegmentsFromSpan1 && s1 != segments1_end)
487 segments.AppendRange(s1, segments1_end);
488 else if (Operation::kShouldAddRemainingSegmentsFromSpan2 &&
489 s2 != segments2_end)
490 segments.AppendRange(s2, segments2_end);
491
492 // Add the span.
493 if (!segments.IsEmpty() || !result.IsEmpty())
494 result.AppendSpan(y, segments.data(), segments.data() + segments.size());
495 }
496
497 // Add any remaining spans.
498 if (Operation::kShouldAddRemainingSpansFromShape1 && spans1 != spans1_end)
499 result.AppendSpans(shape1, spans1, spans1_end);
500 else if (Operation::kShouldAddRemainingSpansFromShape2 &&
501 spans2 != spans2_end)
502 result.AppendSpans(shape2, spans2, spans2_end);
503
504 result.TrimCapacities();
505
506 return result;
507 }
508
509 struct Region::Shape::UnionOperation {
510 STATIC_ONLY(UnionOperation);
TrySimpleOperationblink::Region::Shape::UnionOperation511 static bool TrySimpleOperation(const Shape& shape1,
512 const Shape& shape2,
513 Shape& result) {
514 if (shape1.IsEmpty()) {
515 result = shape2;
516 return true;
517 }
518
519 return false;
520 }
521
522 static const int kOpCode = 0;
523
524 static const bool kShouldAddRemainingSegmentsFromSpan1 = true;
525 static const bool kShouldAddRemainingSegmentsFromSpan2 = true;
526 static const bool kShouldAddRemainingSpansFromShape1 = true;
527 static const bool kShouldAddRemainingSpansFromShape2 = true;
528 };
529
UnionShapes(const Shape & shape1,const Shape & shape2)530 Region::Shape Region::Shape::UnionShapes(const Shape& shape1,
531 const Shape& shape2) {
532 return ShapeOperation<UnionOperation>(shape1, shape2);
533 }
534
535 struct Region::Shape::IntersectOperation {
536 STATIC_ONLY(IntersectOperation);
TrySimpleOperationblink::Region::Shape::IntersectOperation537 static bool TrySimpleOperation(const Shape&, const Shape&, Shape&) {
538 return false;
539 }
540
541 static const int kOpCode = 3;
542
543 static const bool kShouldAddRemainingSegmentsFromSpan1 = false;
544 static const bool kShouldAddRemainingSegmentsFromSpan2 = false;
545 static const bool kShouldAddRemainingSpansFromShape1 = false;
546 static const bool kShouldAddRemainingSpansFromShape2 = false;
547 };
548
IntersectShapes(const Shape & shape1,const Shape & shape2)549 Region::Shape Region::Shape::IntersectShapes(const Shape& shape1,
550 const Shape& shape2) {
551 return ShapeOperation<IntersectOperation>(shape1, shape2);
552 }
553
554 struct Region::Shape::SubtractOperation {
555 STATIC_ONLY(SubtractOperation);
TrySimpleOperationblink::Region::Shape::SubtractOperation556 static bool TrySimpleOperation(const Shape&, const Shape&, Region::Shape&) {
557 return false;
558 }
559
560 static const int kOpCode = 1;
561
562 static const bool kShouldAddRemainingSegmentsFromSpan1 = true;
563 static const bool kShouldAddRemainingSegmentsFromSpan2 = false;
564 static const bool kShouldAddRemainingSpansFromShape1 = true;
565 static const bool kShouldAddRemainingSpansFromShape2 = false;
566 };
567
SubtractShapes(const Shape & shape1,const Shape & shape2)568 Region::Shape Region::Shape::SubtractShapes(const Shape& shape1,
569 const Shape& shape2) {
570 return ShapeOperation<SubtractOperation>(shape1, shape2);
571 }
572
573 #if DCHECK_IS_ON()
Dump() const574 void Region::Dump() const {
575 printf("Bounds: (%d, %d, %d, %d)\n", bounds_.X(), bounds_.Y(),
576 bounds_.Width(), bounds_.Height());
577 shape_.Dump();
578 }
579 #endif
580
Intersect(const Region & region)581 void Region::Intersect(const Region& region) {
582 if (bounds_.IsEmpty())
583 return;
584 if (!bounds_.Intersects(region.bounds_)) {
585 shape_ = Shape();
586 bounds_ = IntRect();
587 return;
588 }
589
590 Shape intersected_shape = Shape::IntersectShapes(shape_, region.shape_);
591
592 shape_.Swap(intersected_shape);
593 bounds_ = shape_.Bounds();
594 }
595
Unite(const Region & region)596 void Region::Unite(const Region& region) {
597 if (region.IsEmpty())
598 return;
599 if (IsRect() && bounds_.Contains(region.bounds_))
600 return;
601 if (region.IsRect() && region.bounds_.Contains(bounds_)) {
602 shape_ = region.shape_;
603 bounds_ = region.bounds_;
604 return;
605 }
606 // FIXME: We may want another way to construct a Region without doing this
607 // test when we expect it to be false.
608 if (!IsRect() && Contains(region))
609 return;
610
611 Shape united_shape = Shape::UnionShapes(shape_, region.shape_);
612
613 shape_.Swap(united_shape);
614 bounds_.Unite(region.bounds_);
615 }
616
Subtract(const Region & region)617 void Region::Subtract(const Region& region) {
618 if (bounds_.IsEmpty())
619 return;
620 if (region.IsEmpty())
621 return;
622 if (!bounds_.Intersects(region.bounds_))
623 return;
624
625 Shape subtracted_shape = Shape::SubtractShapes(shape_, region.shape_);
626
627 shape_.Swap(subtracted_shape);
628 bounds_ = shape_.Bounds();
629 }
630
Translate(const IntSize & offset)631 void Region::Translate(const IntSize& offset) {
632 bounds_.Move(offset);
633 shape_.Translate(offset);
634 }
635
636 } // namespace blink
637