2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
31 // A region class based on the paper "Scanline Coherent Shape Algebra"
32 // by Jonathan E. Steinhart from the book "Graphics Gems II".
34 // This implementation uses two vectors instead of linked list, and
35 // also compresses regions when possible.
43 Region::Region(const IntRect& rect)
49 Vector<IntRect> Region::rects() const
51 Vector<IntRect> rects;
53 for (Shape::SpanIterator span = m_shape.spans_begin(), end = m_shape.spans_end(); span != end && span + 1 != end; ++span) {
55 int height = (span + 1)->y - y;
57 for (Shape::SegmentIterator segment = m_shape.segments_begin(span), end = m_shape.segments_end(span); segment != end && segment + 1 != end; segment += 2) {
59 int width = *(segment + 1) - x;
61 rects.append(IntRect(x, y, width, height));
68 bool Region::contains(const Region& region) const
70 if (!m_bounds.contains(region.m_bounds))
73 return Shape::compareShapes<Shape::CompareContainsOperation>(m_shape, region.m_shape);
76 bool Region::contains(const IntPoint& point) const
78 if (!m_bounds.contains(point))
81 for (Shape::SpanIterator span = m_shape.spans_begin(), end = m_shape.spans_end(); span != end && span + 1 != end; ++span) {
83 int maxY = (span + 1)->y;
87 if (maxY <= point.y())
90 for (Shape::SegmentIterator segment = m_shape.segments_begin(span), end = m_shape.segments_end(span); segment != end && segment + 1 != end; segment += 2) {
92 int maxX = *(segment + 1);
104 bool Region::intersects(const Region& region) const
106 if (!m_bounds.intersects(region.m_bounds))
109 return Shape::compareShapes<Shape::CompareIntersectsOperation>(m_shape, region.m_shape);
112 unsigned Region::totalArea() const
114 Vector<IntRect> rects = this->rects();
115 size_t size = rects.size();
116 unsigned totalArea = 0;
118 for (size_t i = 0; i < size; ++i) {
119 IntRect rect = rects[i];
120 totalArea += (rect.width() * rect.height());
126 template<typename CompareOperation>
127 bool Region::Shape::compareShapes(const Shape& aShape, const Shape& bShape)
129 bool result = CompareOperation::defaultResult;
131 Shape::SpanIterator aSpan = aShape.spans_begin();
132 Shape::SpanIterator aSpanEnd = aShape.spans_end();
133 Shape::SpanIterator bSpan = bShape.spans_begin();
134 Shape::SpanIterator bSpanEnd = bShape.spans_end();
136 bool aHadSegmentInPreviousSpan = false;
137 bool bHadSegmentInPreviousSpan = false;
138 while (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && bSpan != bSpanEnd && bSpan + 1 != bSpanEnd) {
140 int aMaxY = (aSpan + 1)->y;
142 int bMaxY = (bSpan + 1)->y;
144 Shape::SegmentIterator aSegment = aShape.segments_begin(aSpan);
145 Shape::SegmentIterator aSegmentEnd = aShape.segments_end(aSpan);
146 Shape::SegmentIterator bSegment = bShape.segments_begin(bSpan);
147 Shape::SegmentIterator bSegmentEnd = bShape.segments_end(bSpan);
149 // Look for a non-overlapping part of the spans. If B had a segment in its previous span, then we already tested A against B within that span.
150 bool aHasSegmentInSpan = aSegment != aSegmentEnd;
151 bool bHasSegmentInSpan = bSegment != bSegmentEnd;
152 if (aY < bY && !bHadSegmentInPreviousSpan && aHasSegmentInSpan && CompareOperation::aOutsideB(result))
154 if (bY < aY && !aHadSegmentInPreviousSpan && bHasSegmentInSpan && CompareOperation::bOutsideA(result))
157 aHadSegmentInPreviousSpan = aHasSegmentInSpan;
158 bHadSegmentInPreviousSpan = bHasSegmentInSpan;
160 bool spansOverlap = bMaxY > aY && bY < aMaxY;
162 while (aSegment != aSegmentEnd && bSegment != bSegmentEnd) {
164 int aMaxX = *(aSegment + 1);
166 int bMaxX = *(bSegment + 1);
168 bool segmentsOverlap = bMaxX > aX && bX < aMaxX;
169 if (segmentsOverlap && CompareOperation::aOverlapsB(result))
171 if (aX < bX && CompareOperation::aOutsideB(result))
173 if (bX < aX && CompareOperation::bOutsideA(result))
178 else if (bMaxX < aMaxX)
186 if (aSegment != aSegmentEnd && CompareOperation::aOutsideB(result))
188 if (bSegment != bSegmentEnd && CompareOperation::bOutsideA(result))
194 else if (bMaxY < aMaxY)
202 if (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && CompareOperation::aOutsideB(result))
204 if (bSpan != bSpanEnd && bSpan + 1 != bSpanEnd && CompareOperation::bOutsideA(result))
210 struct Region::Shape::CompareContainsOperation {
211 const static bool defaultResult = true;
212 inline static bool aOutsideB(bool& /* result */) { return false; }
213 inline static bool bOutsideA(bool& result) { result = false; return true; }
214 inline static bool aOverlapsB(bool& /* result */) { return false; }
217 struct Region::Shape::CompareIntersectsOperation {
218 const static bool defaultResult = false;
219 inline static bool aOutsideB(bool& /* result */) { return false; }
220 inline static bool bOutsideA(bool& /* result */) { return false; }
221 inline static bool aOverlapsB(bool& result) { result = true; return true; }
224 Region::Shape::Shape()
228 Region::Shape::Shape(const IntRect& rect)
230 appendSpan(rect.y());
231 appendSegment(rect.x());
232 appendSegment(rect.maxX());
233 appendSpan(rect.maxY());
236 void Region::Shape::appendSpan(int y)
238 m_spans.append(Span(y, m_segments.size()));
241 bool Region::Shape::canCoalesce(SegmentIterator begin, SegmentIterator end)
243 if (m_spans.isEmpty())
246 SegmentIterator lastSpanBegin = m_segments.data() + m_spans.last().segmentIndex;
247 SegmentIterator lastSpanEnd = m_segments.data() + m_segments.size();
249 // Check if both spans have an equal number of segments.
250 if (lastSpanEnd - lastSpanBegin != end - begin)
253 // Check if both spans are equal.
254 if (!std::equal(begin, end, lastSpanBegin))
257 // Since the segments are equal the second segment can just be ignored.
261 void Region::Shape::appendSpan(int y, SegmentIterator begin, SegmentIterator end)
263 if (canCoalesce(begin, end))
267 m_segments.appendRange(begin, end);
270 void Region::Shape::appendSpans(const Shape& shape, SpanIterator begin, SpanIterator end)
272 for (SpanIterator it = begin; it != end; ++it)
273 appendSpan(it->y, shape.segments_begin(it), shape.segments_end(it));
276 void Region::Shape::appendSegment(int x)
278 m_segments.append(x);
281 Region::Shape::SpanIterator Region::Shape::spans_begin() const
283 return m_spans.data();
286 Region::Shape::SpanIterator Region::Shape::spans_end() const
288 return m_spans.data() + m_spans.size();
291 Region::Shape::SegmentIterator Region::Shape::segments_begin(SpanIterator it) const
293 ASSERT(it >= m_spans.data());
294 ASSERT(it < m_spans.data() + m_spans.size());
296 // Check if this span has any segments.
297 if (it->segmentIndex == m_segments.size())
300 return &m_segments[it->segmentIndex];
303 Region::Shape::SegmentIterator Region::Shape::segments_end(SpanIterator it) const
305 ASSERT(it >= m_spans.data());
306 ASSERT(it < m_spans.data() + m_spans.size());
308 // Check if this span has any segments.
309 if (it->segmentIndex == m_segments.size())
312 ASSERT(it + 1 < m_spans.data() + m_spans.size());
313 size_t segmentIndex = (it + 1)->segmentIndex;
315 ASSERT(segmentIndex <= m_segments.size());
316 return m_segments.data() + segmentIndex;
320 void Region::Shape::dump() const
322 for (Shape::SpanIterator span = spans_begin(), end = spans_end(); span != end; ++span) {
323 printf("%6d: (", span->y);
325 for (Shape::SegmentIterator segment = segments_begin(span), end = segments_end(span); segment != end; ++segment)
326 printf("%d ", *segment);
334 IntRect Region::Shape::bounds() const
339 SpanIterator span = spans_begin();
342 SpanIterator lastSpan = spans_end() - 1;
343 int maxY = lastSpan->y;
345 int minX = std::numeric_limits<int>::max();
346 int maxX = std::numeric_limits<int>::min();
348 while (span != lastSpan) {
349 SegmentIterator firstSegment = segments_begin(span);
350 SegmentIterator lastSegment = segments_end(span) - 1;
352 if (firstSegment && lastSegment) {
353 ASSERT(firstSegment != lastSegment);
355 if (*firstSegment < minX)
356 minX = *firstSegment;
358 if (*lastSegment > maxX)
365 ASSERT(minX <= maxX);
366 ASSERT(minY <= maxY);
368 return IntRect(minX, minY, maxX - minX, maxY - minY);
371 void Region::Shape::translate(const IntSize& offset)
373 for (size_t i = 0; i < m_segments.size(); ++i)
374 m_segments[i] += offset.width();
375 for (size_t i = 0; i < m_spans.size(); ++i)
376 m_spans[i].y += offset.height();
379 void Region::Shape::swap(Shape& other)
381 m_segments.swap(other.m_segments);
382 m_spans.swap(other.m_spans);
390 template<typename Operation>
391 Region::Shape Region::Shape::shapeOperation(const Shape& shape1, const Shape& shape2)
393 COMPILE_ASSERT(!(!Operation::shouldAddRemainingSegmentsFromSpan1 && Operation::shouldAddRemainingSegmentsFromSpan2), invalid_segment_combination);
394 COMPILE_ASSERT(!(!Operation::shouldAddRemainingSpansFromShape1 && Operation::shouldAddRemainingSpansFromShape2), invalid_span_combination);
397 if (Operation::trySimpleOperation(shape1, shape2, result))
400 SpanIterator spans1 = shape1.spans_begin();
401 SpanIterator spans1End = shape1.spans_end();
403 SpanIterator spans2 = shape2.spans_begin();
404 SpanIterator spans2End = shape2.spans_end();
406 SegmentIterator segments1 = 0;
407 SegmentIterator segments1End = 0;
409 SegmentIterator segments2 = 0;
410 SegmentIterator segments2End = 0;
412 // Iterate over all spans.
413 while (spans1 != spans1End && spans2 != spans2End) {
415 int test = spans1->y - spans2->y;
420 segments1 = shape1.segments_begin(spans1);
421 segments1End = shape1.segments_end(spans1);
427 segments2 = shape2.segments_begin(spans2);
428 segments2End = shape2.segments_end(spans2);
435 SegmentIterator s1 = segments1;
436 SegmentIterator s2 = segments2;
438 Vector<int, 32> segments;
440 // Now iterate over the segments in each span and construct a new vector of segments.
441 while (s1 != segments1End && s2 != segments2End) {
442 int test = *s1 - *s2;
456 if (flag == Operation::opCode || oldFlag == Operation::opCode)
462 // Add any remaining segments.
463 if (Operation::shouldAddRemainingSegmentsFromSpan1 && s1 != segments1End)
464 segments.appendRange(s1, segments1End);
465 else if (Operation::shouldAddRemainingSegmentsFromSpan2 && s2 != segments2End)
466 segments.appendRange(s2, segments2End);
469 if (!segments.isEmpty() || !result.isEmpty())
470 result.appendSpan(y, segments.data(), segments.data() + segments.size());
473 // Add any remaining spans.
474 if (Operation::shouldAddRemainingSpansFromShape1 && spans1 != spans1End)
475 result.appendSpans(shape1, spans1, spans1End);
476 else if (Operation::shouldAddRemainingSpansFromShape2 && spans2 != spans2End)
477 result.appendSpans(shape2, spans2, spans2End);
482 struct Region::Shape::UnionOperation {
483 static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result)
485 if (shape1.isEmpty()) {
493 static const int opCode = 0;
495 static const bool shouldAddRemainingSegmentsFromSpan1 = true;
496 static const bool shouldAddRemainingSegmentsFromSpan2 = true;
497 static const bool shouldAddRemainingSpansFromShape1 = true;
498 static const bool shouldAddRemainingSpansFromShape2 = true;
501 Region::Shape Region::Shape::unionShapes(const Shape& shape1, const Shape& shape2)
503 return shapeOperation<UnionOperation>(shape1, shape2);
506 struct Region::Shape::IntersectOperation {
507 static bool trySimpleOperation(const Shape&, const Shape&, Shape&)
512 static const int opCode = 3;
514 static const bool shouldAddRemainingSegmentsFromSpan1 = false;
515 static const bool shouldAddRemainingSegmentsFromSpan2 = false;
516 static const bool shouldAddRemainingSpansFromShape1 = false;
517 static const bool shouldAddRemainingSpansFromShape2 = false;
520 Region::Shape Region::Shape::intersectShapes(const Shape& shape1, const Shape& shape2)
522 return shapeOperation<IntersectOperation>(shape1, shape2);
525 struct Region::Shape::SubtractOperation {
526 static bool trySimpleOperation(const Shape&, const Shape&, Region::Shape&)
531 static const int opCode = 1;
533 static const bool shouldAddRemainingSegmentsFromSpan1 = true;
534 static const bool shouldAddRemainingSegmentsFromSpan2 = false;
535 static const bool shouldAddRemainingSpansFromShape1 = true;
536 static const bool shouldAddRemainingSpansFromShape2 = false;
539 Region::Shape Region::Shape::subtractShapes(const Shape& shape1, const Shape& shape2)
541 return shapeOperation<SubtractOperation>(shape1, shape2);
545 void Region::dump() const
547 printf("Bounds: (%d, %d, %d, %d)\n",
548 m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height());
553 void Region::intersect(const Region& region)
555 if (m_bounds.isEmpty())
557 if (!m_bounds.intersects(region.m_bounds)) {
559 m_bounds = IntRect();
563 Shape intersectedShape = Shape::intersectShapes(m_shape, region.m_shape);
565 m_shape.swap(intersectedShape);
566 m_bounds = m_shape.bounds();
569 void Region::unite(const Region& region)
571 if (region.isEmpty())
573 if (isRect() && m_bounds.contains(region.m_bounds))
575 if (region.isRect() && region.m_bounds.contains(m_bounds)) {
576 m_shape = region.m_shape;
577 m_bounds = region.m_bounds;
580 // FIXME: We may want another way to construct a Region without doing this test when we expect it to be false.
581 if (!isRect() && contains(region))
584 Shape unitedShape = Shape::unionShapes(m_shape, region.m_shape);
586 m_shape.swap(unitedShape);
587 m_bounds.unite(region.m_bounds);
590 void Region::subtract(const Region& region)
592 if (m_bounds.isEmpty())
594 if (region.isEmpty())
596 if (!m_bounds.intersects(region.m_bounds))
599 Shape subtractedShape = Shape::subtractShapes(m_shape, region.m_shape);
601 m_shape.swap(subtractedShape);
602 m_bounds = m_shape.bounds();
605 void Region::translate(const IntSize& offset)
607 m_bounds.move(offset);
608 m_shape.translate(offset);
611 } // namespace WebCore