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 Region::Shape::Shape()
72 Region::Shape::Shape(const IntRect& rect)
75 appendSegment(rect.x());
76 appendSegment(rect.maxX());
77 appendSpan(rect.maxY());
80 void Region::Shape::appendSpan(int y)
82 m_spans.append(Span(y, m_segments.size()));
85 bool Region::Shape::canCoalesce(SegmentIterator begin, SegmentIterator end)
87 if (m_spans.isEmpty())
90 SegmentIterator lastSpanBegin = m_segments.data() + m_spans.last().segmentIndex;
91 SegmentIterator lastSpanEnd = m_segments.data() + m_segments.size();
93 // Check if both spans have an equal number of segments.
94 if (lastSpanEnd - lastSpanBegin != end - begin)
97 // Check if both spans are equal.
98 if (!std::equal(begin, end, lastSpanBegin))
101 // Since the segments are equal the second segment can just be ignored.
105 void Region::Shape::appendSpan(int y, SegmentIterator begin, SegmentIterator end)
107 if (canCoalesce(begin, end))
111 m_segments.appendRange(begin, end);
114 void Region::Shape::appendSpans(const Shape& shape, SpanIterator begin, SpanIterator end)
116 for (SpanIterator it = begin; it != end; ++it)
117 appendSpan(it->y, shape.segments_begin(it), shape.segments_end(it));
120 void Region::Shape::appendSegment(int x)
122 m_segments.append(x);
125 Region::Shape::SpanIterator Region::Shape::spans_begin() const
127 return m_spans.data();
130 Region::Shape::SpanIterator Region::Shape::spans_end() const
132 return m_spans.data() + m_spans.size();
135 Region::Shape::SegmentIterator Region::Shape::segments_begin(SpanIterator it) const
137 ASSERT(it >= m_spans.data());
138 ASSERT(it < m_spans.data() + m_spans.size());
140 // Check if this span has any segments.
141 if (it->segmentIndex == m_segments.size())
144 return &m_segments[it->segmentIndex];
147 Region::Shape::SegmentIterator Region::Shape::segments_end(SpanIterator it) const
149 ASSERT(it >= m_spans.data());
150 ASSERT(it < m_spans.data() + m_spans.size());
152 // Check if this span has any segments.
153 if (it->segmentIndex == m_segments.size())
156 ASSERT(it + 1 < m_spans.data() + m_spans.size());
157 size_t segmentIndex = (it + 1)->segmentIndex;
159 ASSERT(segmentIndex <= m_segments.size());
160 return m_segments.data() + segmentIndex;
164 void Region::Shape::dump() const
166 for (Shape::SpanIterator span = spans_begin(), end = spans_end(); span != end; ++span) {
167 printf("%6d: (", span->y);
169 for (Shape::SegmentIterator segment = segments_begin(span), end = segments_end(span); segment != end; ++segment)
170 printf("%d ", *segment);
178 IntRect Region::Shape::bounds() const
183 SpanIterator span = spans_begin();
186 SpanIterator lastSpan = spans_end() - 1;
187 int maxY = lastSpan->y;
189 int minX = std::numeric_limits<int>::max();
190 int maxX = std::numeric_limits<int>::min();
192 while (span != lastSpan) {
193 SegmentIterator firstSegment = segments_begin(span);
194 SegmentIterator lastSegment = segments_end(span) - 1;
196 if (firstSegment && lastSegment) {
197 ASSERT(firstSegment != lastSegment);
199 if (*firstSegment < minX)
200 minX = *firstSegment;
202 if (*lastSegment > maxX)
209 ASSERT(minX <= maxX);
210 ASSERT(minY <= maxY);
212 return IntRect(minX, minY, maxX - minX, maxY - minY);
215 void Region::Shape::translate(const IntSize& offset)
217 for (size_t i = 0; i < m_segments.size(); ++i)
218 m_segments[i] += offset.width();
219 for (size_t i = 0; i < m_spans.size(); ++i)
220 m_spans[i].y += offset.height();
223 void Region::Shape::swap(Shape& other)
225 m_segments.swap(other.m_segments);
226 m_spans.swap(other.m_spans);
234 template<typename Operation>
235 Region::Shape Region::Shape::shapeOperation(const Shape& shape1, const Shape& shape2)
237 COMPILE_ASSERT(!(!Operation::shouldAddRemainingSegmentsFromSpan1 && Operation::shouldAddRemainingSegmentsFromSpan2), invalid_segment_combination);
238 COMPILE_ASSERT(!(!Operation::shouldAddRemainingSpansFromShape1 && Operation::shouldAddRemainingSpansFromShape2), invalid_span_combination);
241 if (Operation::trySimpleOperation(shape1, shape2, result))
244 SpanIterator spans1 = shape1.spans_begin();
245 SpanIterator spans1End = shape1.spans_end();
247 SpanIterator spans2 = shape2.spans_begin();
248 SpanIterator spans2End = shape2.spans_end();
250 SegmentIterator segments1 = 0;
251 SegmentIterator segments1End = 0;
253 SegmentIterator segments2 = 0;
254 SegmentIterator segments2End = 0;
256 // Iterate over all spans.
257 while (spans1 != spans1End && spans2 != spans2End) {
259 int test = spans1->y - spans2->y;
264 segments1 = shape1.segments_begin(spans1);
265 segments1End = shape1.segments_end(spans1);
271 segments2 = shape2.segments_begin(spans2);
272 segments2End = shape2.segments_end(spans2);
279 SegmentIterator s1 = segments1;
280 SegmentIterator s2 = segments2;
282 Vector<int> segments;
284 // Now iterate over the segments in each span and construct a new vector of segments.
285 while (s1 != segments1End && s2 != segments2End) {
286 int test = *s1 - *s2;
300 if (flag == Operation::opCode || oldFlag == Operation::opCode)
306 // Add any remaining segments.
307 if (Operation::shouldAddRemainingSegmentsFromSpan1 && s1 != segments1End)
308 segments.appendRange(s1, segments1End);
309 else if (Operation::shouldAddRemainingSegmentsFromSpan2 && s2 != segments2End)
310 segments.appendRange(s2, segments2End);
313 if (!segments.isEmpty() || !result.isEmpty())
314 result.appendSpan(y, segments.data(), segments.data() + segments.size());
317 // Add any remaining spans.
318 if (Operation::shouldAddRemainingSpansFromShape1 && spans1 != spans1End)
319 result.appendSpans(shape1, spans1, spans1End);
320 else if (Operation::shouldAddRemainingSpansFromShape2 && spans2 != spans2End)
321 result.appendSpans(shape2, spans2, spans2End);
326 struct Region::Shape::UnionOperation {
327 static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result)
329 if (shape1.isEmpty()) {
334 if (shape2.isEmpty()) {
342 static const int opCode = 0;
344 static const bool shouldAddRemainingSegmentsFromSpan1 = true;
345 static const bool shouldAddRemainingSegmentsFromSpan2 = true;
346 static const bool shouldAddRemainingSpansFromShape1 = true;
347 static const bool shouldAddRemainingSpansFromShape2 = true;
350 Region::Shape Region::Shape::unionShapes(const Shape& shape1, const Shape& shape2)
352 return shapeOperation<UnionOperation>(shape1, shape2);
355 struct Region::Shape::IntersectOperation {
356 static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result)
358 if (shape1.isEmpty()) {
363 if (shape2.isEmpty()) {
371 static const int opCode = 3;
373 static const bool shouldAddRemainingSegmentsFromSpan1 = false;
374 static const bool shouldAddRemainingSegmentsFromSpan2 = false;
375 static const bool shouldAddRemainingSpansFromShape1 = false;
376 static const bool shouldAddRemainingSpansFromShape2 = false;
379 Region::Shape Region::Shape::intersectShapes(const Shape& shape1, const Shape& shape2)
381 return shapeOperation<IntersectOperation>(shape1, shape2);
384 struct Region::Shape::SubtractOperation {
385 static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Region::Shape& result)
388 if (shape1.isEmpty() || shape2.isEmpty()) {
396 static const int opCode = 1;
398 static const bool shouldAddRemainingSegmentsFromSpan1 = true;
399 static const bool shouldAddRemainingSegmentsFromSpan2 = false;
400 static const bool shouldAddRemainingSpansFromShape1 = true;
401 static const bool shouldAddRemainingSpansFromShape2 = false;
404 Region::Shape Region::Shape::subtractShapes(const Shape& shape1, const Shape& shape2)
406 return shapeOperation<SubtractOperation>(shape1, shape2);
410 void Region::dump() const
412 printf("Bounds: (%d, %d, %d, %d)\n",
413 m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height());
418 void Region::intersect(const Region& region)
420 if (!m_bounds.intersects(region.m_bounds)) {
422 m_bounds = IntRect();
426 Shape intersectedShape = Shape::intersectShapes(m_shape, region.m_shape);
428 m_shape.swap(intersectedShape);
429 m_bounds = m_shape.bounds();
432 void Region::unite(const Region& region)
434 Shape unitedShape = Shape::unionShapes(m_shape, region.m_shape);
436 m_shape.swap(unitedShape);
437 m_bounds.unite(region.m_bounds);
440 void Region::subtract(const Region& region)
442 Shape subtractedShape = Shape::subtractShapes(m_shape, region.m_shape);
444 m_shape.swap(subtractedShape);
445 m_bounds = m_shape.bounds();
448 void Region::translate(const IntSize& offset)
450 m_bounds.move(offset);
451 m_shape.translate(offset);
454 } // namespace WebCore