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