summaryrefslogtreecommitdiffstats
path: root/Source/WebKit2/Platform/Region.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/Platform/Region.cpp')
-rw-r--r--Source/WebKit2/Platform/Region.cpp454
1 files changed, 454 insertions, 0 deletions
diff --git a/Source/WebKit2/Platform/Region.cpp b/Source/WebKit2/Platform/Region.cpp
new file mode 100644
index 0000000..a1cc24c
--- /dev/null
+++ b/Source/WebKit2/Platform/Region.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Region.h"
+
+// A region class based on the paper "Scanline Coherent Shape Algebra"
+// by Jonathan E. Steinhart from the book "Graphics Gems II".
+//
+// This implementation uses two vectors instead of linked list, and
+// also compresses regions when possible.
+
+using namespace WebCore;
+
+namespace WebKit {
+
+Region::Region()
+{
+}
+
+Region::Region(const IntRect& rect)
+ : m_bounds(rect)
+ , m_shape(rect)
+{
+}
+
+Vector<IntRect> Region::rects() const
+{
+ Vector<IntRect> rects;
+
+ for (Shape::SpanIterator span = m_shape.spans_begin(), end = m_shape.spans_end(); span != end && span + 1 != end; ++span) {
+ int y = span->y;
+ int height = (span + 1)->y - y;
+
+ for (Shape::SegmentIterator segment = m_shape.segments_begin(span), end = m_shape.segments_end(span); segment != end && segment + 1 != end; segment += 2) {
+ int x = *segment;
+ int width = *(segment + 1) - x;
+
+ rects.append(IntRect(x, y, width, height));
+ }
+ }
+
+ return rects;
+}
+
+Region::Shape::Shape()
+{
+}
+
+Region::Shape::Shape(const IntRect& rect)
+{
+ appendSpan(rect.y());
+ appendSegment(rect.x());
+ appendSegment(rect.right());
+ appendSpan(rect.bottom());
+}
+
+void Region::Shape::appendSpan(int y)
+{
+ m_spans.append(Span(y, m_segments.size()));
+}
+
+bool Region::Shape::canCoalesce(SegmentIterator begin, SegmentIterator end)
+{
+ if (m_spans.isEmpty())
+ return false;
+
+ SegmentIterator lastSpanBegin = m_segments.data() + m_spans.last().segmentIndex;
+ SegmentIterator lastSpanEnd = m_segments.data() + m_segments.size();
+
+ // Check if both spans have an equal number of segments.
+ if (lastSpanEnd - lastSpanBegin != end - begin)
+ return false;
+
+ // Check if both spans are equal.
+ if (!std::equal(begin, end, lastSpanBegin))
+ return false;
+
+ // Since the segments are equal the second segment can just be ignored.
+ return true;
+}
+
+void Region::Shape::appendSpan(int y, SegmentIterator begin, SegmentIterator end)
+{
+ if (canCoalesce(begin, end))
+ return;
+
+ appendSpan(y);
+ m_segments.appendRange(begin, end);
+}
+
+void Region::Shape::appendSpans(const Shape& shape, SpanIterator begin, SpanIterator end)
+{
+ for (SpanIterator it = begin; it != end; ++it)
+ appendSpan(it->y, shape.segments_begin(it), shape.segments_end(it));
+}
+
+void Region::Shape::appendSegment(int x)
+{
+ m_segments.append(x);
+}
+
+Region::Shape::SpanIterator Region::Shape::spans_begin() const
+{
+ return m_spans.data();
+}
+
+Region::Shape::SpanIterator Region::Shape::spans_end() const
+{
+ return m_spans.data() + m_spans.size();
+}
+
+Region::Shape::SegmentIterator Region::Shape::segments_begin(SpanIterator it) const
+{
+ ASSERT(it >= m_spans.data());
+ ASSERT(it < m_spans.data() + m_spans.size());
+
+ // Check if this span has any segments.
+ if (it->segmentIndex == m_segments.size())
+ return 0;
+
+ return &m_segments[it->segmentIndex];
+}
+
+Region::Shape::SegmentIterator Region::Shape::segments_end(SpanIterator it) const
+{
+ ASSERT(it >= m_spans.data());
+ ASSERT(it < m_spans.data() + m_spans.size());
+
+ // Check if this span has any segments.
+ if (it->segmentIndex == m_segments.size())
+ return 0;
+
+ ASSERT(it + 1 < m_spans.data() + m_spans.size());
+ size_t segmentIndex = (it + 1)->segmentIndex;
+
+ ASSERT(segmentIndex <= m_segments.size());
+ return m_segments.data() + segmentIndex;
+}
+
+#ifndef NDEBUG
+void Region::Shape::dump() const
+{
+ for (Shape::SpanIterator span = spans_begin(), end = spans_end(); span != end; ++span) {
+ printf("%6d: (", span->y);
+
+ for (Shape::SegmentIterator segment = segments_begin(span), end = segments_end(span); segment != end; ++segment)
+ printf("%d ", *segment);
+ printf(")\n");
+ }
+
+ printf("\n");
+}
+#endif
+
+IntRect Region::Shape::bounds() const
+{
+ if (isEmpty())
+ return IntRect();
+
+ SpanIterator span = spans_begin();
+ int minY = span->y;
+
+ SpanIterator lastSpan = spans_end() - 1;
+ int maxY = lastSpan->y;
+
+ int minX = std::numeric_limits<int>::max();
+ int maxX = std::numeric_limits<int>::min();
+
+ while (span != lastSpan) {
+ SegmentIterator firstSegment = segments_begin(span);
+ SegmentIterator lastSegment = segments_end(span) - 1;
+
+ if (firstSegment && lastSegment) {
+ ASSERT(firstSegment != lastSegment);
+
+ if (*firstSegment < minX)
+ minX = *firstSegment;
+
+ if (*lastSegment > maxX)
+ maxX = *lastSegment;
+ }
+
+ ++span;
+ }
+
+ ASSERT(minX <= maxX);
+ ASSERT(minY <= maxY);
+
+ return IntRect(minX, minY, maxX - minX, maxY - minY);
+}
+
+void Region::Shape::translate(const IntSize& offset)
+{
+ for (size_t i = 0; i < m_segments.size(); ++i)
+ m_segments[i] += offset.width();
+ for (size_t i = 0; i < m_spans.size(); ++i)
+ m_spans[i].y += offset.height();
+}
+
+void Region::Shape::swap(Shape& other)
+{
+ m_segments.swap(other.m_segments);
+ m_spans.swap(other.m_spans);
+}
+
+enum {
+ Shape1,
+ Shape2,
+};
+
+template<typename Operation>
+Region::Shape Region::Shape::shapeOperation(const Shape& shape1, const Shape& shape2)
+{
+ COMPILE_ASSERT(!(!Operation::shouldAddRemainingSegmentsFromSpan1 && Operation::shouldAddRemainingSegmentsFromSpan2), invalid_segment_combination);
+ COMPILE_ASSERT(!(!Operation::shouldAddRemainingSpansFromShape1 && Operation::shouldAddRemainingSpansFromShape2), invalid_span_combination);
+
+ Shape result;
+ if (Operation::trySimpleOperation(shape1, shape2, result))
+ return result;
+
+ SpanIterator spans1 = shape1.spans_begin();
+ SpanIterator spans1End = shape1.spans_end();
+
+ SpanIterator spans2 = shape2.spans_begin();
+ SpanIterator spans2End = shape2.spans_end();
+
+ SegmentIterator segments1 = 0;
+ SegmentIterator segments1End = 0;
+
+ SegmentIterator segments2 = 0;
+ SegmentIterator segments2End = 0;
+
+ // Iterate over all spans.
+ while (spans1 != spans1End && spans2 != spans2End) {
+ int y;
+ int test = spans1->y - spans2->y;
+
+ if (test <= 0) {
+ y = spans1->y;
+
+ segments1 = shape1.segments_begin(spans1);
+ segments1End = shape1.segments_end(spans1);
+ ++spans1;
+ }
+ if (test >= 0) {
+ y = spans2->y;
+
+ segments2 = shape2.segments_begin(spans2);
+ segments2End = shape2.segments_end(spans2);
+ ++spans2;
+ }
+
+ int flag = 0;
+ int oldFlag = 0;
+
+ SegmentIterator s1 = segments1;
+ SegmentIterator s2 = segments2;
+
+ Vector<int> segments;
+
+ // Now iterate over the segments in each span and construct a new vector of segments.
+ while (s1 != segments1End && s2 != segments2End) {
+ int test = *s1 - *s2;
+ int x;
+
+ if (test <= 0) {
+ x = *s1;
+ flag = flag ^ 1;
+ ++s1;
+ }
+ if (test >= 0) {
+ x = *s2;
+ flag = flag ^ 2;
+ ++s2;
+ }
+
+ if (flag == Operation::opCode || oldFlag == Operation::opCode)
+ segments.append(x);
+
+ oldFlag = flag;
+ }
+
+ // Add any remaining segments.
+ if (Operation::shouldAddRemainingSegmentsFromSpan1 && s1 != segments1End)
+ segments.appendRange(s1, segments1End);
+ else if (Operation::shouldAddRemainingSegmentsFromSpan2 && s2 != segments2End)
+ segments.appendRange(s2, segments2End);
+
+ // Add the span.
+ if (!segments.isEmpty() || !result.isEmpty())
+ result.appendSpan(y, segments.data(), segments.data() + segments.size());
+ }
+
+ // Add any remaining spans.
+ if (Operation::shouldAddRemainingSpansFromShape1 && spans1 != spans1End)
+ result.appendSpans(shape1, spans1, spans1End);
+ else if (Operation::shouldAddRemainingSpansFromShape2 && spans2 != spans2End)
+ result.appendSpans(shape2, spans2, spans2End);
+
+ return result;
+}
+
+struct Region::Shape::UnionOperation {
+ static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result)
+ {
+ if (shape1.isEmpty()) {
+ result = shape2;
+ return true;
+ }
+
+ if (shape2.isEmpty()) {
+ result = shape1;
+ return true;
+ }
+
+ return false;
+ }
+
+ static const int opCode = 0;
+
+ static const bool shouldAddRemainingSegmentsFromSpan1 = true;
+ static const bool shouldAddRemainingSegmentsFromSpan2 = true;
+ static const bool shouldAddRemainingSpansFromShape1 = true;
+ static const bool shouldAddRemainingSpansFromShape2 = true;
+};
+
+Region::Shape Region::Shape::unionShapes(const Shape& shape1, const Shape& shape2)
+{
+ return shapeOperation<UnionOperation>(shape1, shape2);
+}
+
+struct Region::Shape::IntersectOperation {
+ static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result)
+ {
+ if (shape1.isEmpty()) {
+ result = Shape();
+ return true;
+ }
+
+ if (shape2.isEmpty()) {
+ result = shape1;
+ return true;
+ }
+
+ return false;
+ }
+
+ static const int opCode = 3;
+
+ static const bool shouldAddRemainingSegmentsFromSpan1 = false;
+ static const bool shouldAddRemainingSegmentsFromSpan2 = false;
+ static const bool shouldAddRemainingSpansFromShape1 = false;
+ static const bool shouldAddRemainingSpansFromShape2 = false;
+};
+
+Region::Shape Region::Shape::intersectShapes(const Shape& shape1, const Shape& shape2)
+{
+ return shapeOperation<IntersectOperation>(shape1, shape2);
+}
+
+struct Region::Shape::SubtractOperation {
+ static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Region::Shape& result)
+ {
+
+ if (shape1.isEmpty() || shape2.isEmpty()) {
+ result = Shape();
+ return true;
+ }
+
+ return false;
+ }
+
+ static const int opCode = 1;
+
+ static const bool shouldAddRemainingSegmentsFromSpan1 = true;
+ static const bool shouldAddRemainingSegmentsFromSpan2 = false;
+ static const bool shouldAddRemainingSpansFromShape1 = true;
+ static const bool shouldAddRemainingSpansFromShape2 = false;
+};
+
+Region::Shape Region::Shape::subtractShapes(const Shape& shape1, const Shape& shape2)
+{
+ return shapeOperation<SubtractOperation>(shape1, shape2);
+}
+
+#ifndef NDEBUG
+void Region::dump() const
+{
+ printf("Bounds: (%d, %d, %d, %d)\n",
+ m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height());
+ m_shape.dump();
+}
+#endif
+
+void Region::intersect(const Region& region)
+{
+ if (!m_bounds.intersects(region.m_bounds)) {
+ m_shape = Shape();
+ m_bounds = IntRect();
+ return;
+ }
+
+ Shape intersectedShape = Shape::intersectShapes(m_shape, region.m_shape);
+
+ m_shape.swap(intersectedShape);
+ m_bounds = m_shape.bounds();
+}
+
+void Region::unite(const Region& region)
+{
+ Shape unitedShape = Shape::unionShapes(m_shape, region.m_shape);
+
+ m_shape.swap(unitedShape);
+ m_bounds.unite(region.m_bounds);
+}
+
+void Region::subtract(const Region& region)
+{
+ Shape subtractedShape = Shape::subtractShapes(m_shape, region.m_shape);
+
+ m_shape.swap(subtractedShape);
+ m_bounds = m_shape.bounds();
+}
+
+void Region::translate(const IntSize& offset)
+{
+ m_bounds.move(offset);
+ m_shape.translate(offset);
+}
+
+} // namespace WebKit
+