diff options
Diffstat (limited to 'Source/WebCore/rendering/shapes')
17 files changed, 751 insertions, 2004 deletions
diff --git a/Source/WebCore/rendering/shapes/BoxShape.cpp b/Source/WebCore/rendering/shapes/BoxShape.cpp index 25c966875..dba19e76f 100644 --- a/Source/WebCore/rendering/shapes/BoxShape.cpp +++ b/Source/WebCore/rendering/shapes/BoxShape.cpp @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -30,10 +30,70 @@ #include "config.h" #include "BoxShape.h" +#include "RenderBox.h" #include <wtf/MathExtras.h> namespace WebCore { +static inline LayoutUnit adjustRadiusForMarginBoxShape(LayoutUnit radius, LayoutUnit margin) +{ + // This algorithm is defined in the CSS Shapes specifcation + if (!margin) + return radius; + + LayoutUnit ratio = radius / margin; + if (ratio < 1) + return radius + (margin * (1 + pow(ratio - 1, 3.0))); + + return radius + margin; +} + +static inline LayoutSize computeMarginBoxShapeRadius(const LayoutSize& radius, const LayoutSize& adjacentMargins) +{ + return LayoutSize(adjustRadiusForMarginBoxShape(radius.width(), adjacentMargins.width()), + adjustRadiusForMarginBoxShape(radius.height(), adjacentMargins.height())); +} + +static inline RoundedRect::Radii computeMarginBoxShapeRadii(const RoundedRect::Radii& radii, const RenderBox& renderer) +{ + return RoundedRect::Radii(computeMarginBoxShapeRadius(radii.topLeft(), LayoutSize(renderer.marginLeft(), renderer.marginTop())), + computeMarginBoxShapeRadius(radii.topRight(), LayoutSize(renderer.marginRight(), renderer.marginTop())), + computeMarginBoxShapeRadius(radii.bottomLeft(), LayoutSize(renderer.marginLeft(), renderer.marginBottom())), + computeMarginBoxShapeRadius(radii.bottomRight(), LayoutSize(renderer.marginRight(), renderer.marginBottom()))); +} + +RoundedRect computeRoundedRectForBoxShape(CSSBoxType box, const RenderBox& renderer) +{ + const RenderStyle& style = renderer.style(); + switch (box) { + case MarginBox: { + if (!style.hasBorderRadius()) + return RoundedRect(renderer.marginBoxRect(), RoundedRect::Radii()); + + LayoutRect marginBox = renderer.marginBoxRect(); + RoundedRect::Radii radii = computeMarginBoxShapeRadii(style.getRoundedBorderFor(renderer.borderBoxRect()).radii(), renderer); + radii.scale(calcBorderRadiiConstraintScaleFor(marginBox, radii)); + return RoundedRect(marginBox, radii); + } + case PaddingBox: + return style.getRoundedInnerBorderFor(renderer.borderBoxRect()); + case ContentBox: + return style.getRoundedInnerBorderFor(renderer.borderBoxRect(), + renderer.paddingTop() + renderer.borderTop(), renderer.paddingBottom() + renderer.borderBottom(), + renderer.paddingLeft() + renderer.borderLeft(), renderer.paddingRight() + renderer.borderRight()); + // fill, stroke, view-box compute to border-box for HTML elements. + case BorderBox: + case Fill: + case Stroke: + case ViewBox: + case BoxMissing: + return style.getRoundedBorderFor(renderer.borderBoxRect()); + } + + ASSERT_NOT_REACHED(); + return style.getRoundedBorderFor(renderer.borderBoxRect()); +} + LayoutRect BoxShape::shapeMarginLogicalBoundingBox() const { FloatRect marginBounds(m_bounds.rect()); @@ -42,14 +102,6 @@ LayoutRect BoxShape::shapeMarginLogicalBoundingBox() const return static_cast<LayoutRect>(marginBounds); } -LayoutRect BoxShape::shapePaddingLogicalBoundingBox() const -{ - FloatRect paddingBounds(m_bounds.rect()); - if (shapePadding() > 0) - paddingBounds.inflate(-shapePadding()); - return static_cast<LayoutRect>(paddingBounds); -} - FloatRoundedRect BoxShape::shapeMarginBounds() const { FloatRoundedRect marginBounds(m_bounds); @@ -60,44 +112,36 @@ FloatRoundedRect BoxShape::shapeMarginBounds() const return marginBounds; } -FloatRoundedRect BoxShape::shapePaddingBounds() const -{ - FloatRoundedRect paddingBounds(m_bounds); - if (shapePadding() > 0) { - paddingBounds.inflate(-shapePadding()); - paddingBounds.expandRadii(-shapePadding()); - } - return paddingBounds; -} - -void BoxShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +LineSegment BoxShape::getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const { const FloatRoundedRect& marginBounds = shapeMarginBounds(); if (marginBounds.isEmpty() || !lineOverlapsShapeMarginBounds(logicalTop, logicalHeight)) - return; + return LineSegment(); float y1 = logicalTop; float y2 = logicalTop + logicalHeight; const FloatRect& rect = marginBounds.rect(); - if (!marginBounds.isRounded()) { - result.append(LineSegment(rect.x(), rect.maxX())); - return; - } + if (!marginBounds.isRounded()) + return LineSegment(rect.x(), rect.maxX()); float topCornerMaxY = std::max<float>(marginBounds.topLeftCorner().maxY(), marginBounds.topRightCorner().maxY()); float bottomCornerMinY = std::min<float>(marginBounds.bottomLeftCorner().y(), marginBounds.bottomRightCorner().y()); - if (y1 <= topCornerMaxY && y2 >= bottomCornerMinY) { - result.append(LineSegment(rect.x(), rect.maxX())); - return; - } + if (topCornerMaxY <= bottomCornerMinY && y1 <= topCornerMaxY && y2 >= bottomCornerMinY) + return LineSegment(rect.x(), rect.maxX()); float x1 = rect.maxX(); float x2 = rect.x(); float minXIntercept; float maxXIntercept; + if (y1 <= marginBounds.topLeftCorner().maxY() && y2 >= marginBounds.bottomLeftCorner().y()) + x1 = rect.x(); + + if (y1 <= marginBounds.topRightCorner().maxY() && y2 >= marginBounds.bottomRightCorner().y()) + x2 = rect.maxX(); + if (marginBounds.xInterceptsAtY(y1, minXIntercept, maxXIntercept)) { x1 = std::min<float>(x1, minXIntercept); x2 = std::max<float>(x2, maxXIntercept); @@ -109,65 +153,14 @@ void BoxShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHei } ASSERT(x2 >= x1); - result.append(LineSegment(x1, x2)); -} - -void BoxShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const -{ - const FloatRoundedRect& paddingBounds = shapePaddingBounds(); - if (paddingBounds.isEmpty()) - return; - - const FloatRect& rect = paddingBounds.rect(); - float y1 = logicalTop; - float y2 = logicalTop + logicalHeight; - - if (y1 < rect.y() || y2 > rect.maxY()) - return; - - if (!paddingBounds.isRounded()) { - result.append(LineSegment(rect.x(), rect.maxX())); - return; - } - - float x1 = rect.x(); - float x2 = rect.maxX(); - float minXIntercept; - float maxXIntercept; - - if (paddingBounds.xInterceptsAtY(y1, minXIntercept, maxXIntercept)) { - x1 = std::max<float>(x1, minXIntercept); - x2 = std::min<float>(x2, maxXIntercept); - } - - if (paddingBounds.xInterceptsAtY(y2, minXIntercept, maxXIntercept)) { - x1 = std::max<float>(x1, minXIntercept); - x2 = std::min<float>(x2, maxXIntercept); - } - - result.append(LineSegment(x1, x2)); -} - -bool BoxShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize&, LayoutUnit& result) const -{ - // FIXME: this method is only a stub, https://bugs.webkit.org/show_bug.cgi?id=124606. - - result = minLogicalIntervalTop; - return true; -} - -static void addRoundedRect(Path& path, const FloatRect& rect, const FloatRoundedRect::Radii& radii) -{ - path.addRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight(), Path::PreferBezierRoundedRect); + return LineSegment(x1, x2); } void BoxShape::buildDisplayPaths(DisplayPaths& paths) const { - addRoundedRect(paths.shape, m_bounds.rect(), m_bounds.radii()); - if (shapeMargin()) { - const FloatRoundedRect& marginBounds = shapeMarginBounds(); - addRoundedRect(paths.marginShape, marginBounds.rect(), marginBounds.radii()); - } + paths.shape.addRoundedRect(m_bounds, Path::PreferBezierRoundedRect); + if (shapeMargin()) + paths.marginShape.addRoundedRect(shapeMarginBounds(), Path::PreferBezierRoundedRect); } } // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/BoxShape.h b/Source/WebCore/rendering/shapes/BoxShape.h index 975578d69..9f95fcab0 100644 --- a/Source/WebCore/rendering/shapes/BoxShape.h +++ b/Source/WebCore/rendering/shapes/BoxShape.h @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -27,37 +27,35 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BoxShape_h -#define BoxShape_h +#pragma once #include "FloatRoundedRect.h" +#include "RenderStyleConstants.h" #include "Shape.h" namespace WebCore { -class BoxShape : public Shape { +class RenderBox; + +RoundedRect computeRoundedRectForBoxShape(CSSBoxType, const RenderBox&); + +class BoxShape final : public Shape { public: BoxShape(const FloatRoundedRect& bounds) : m_bounds(bounds) { } - virtual LayoutRect shapeMarginLogicalBoundingBox() const override; - virtual LayoutRect shapePaddingLogicalBoundingBox() const override; - virtual bool isEmpty() const override { return m_bounds.isEmpty(); } - virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit&) const override; + LayoutRect shapeMarginLogicalBoundingBox() const override; + bool isEmpty() const override { return m_bounds.isEmpty(); } + LineSegment getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const override; - virtual void buildDisplayPaths(DisplayPaths&) const override; + void buildDisplayPaths(DisplayPaths&) const override; private: FloatRoundedRect shapeMarginBounds() const; - FloatRoundedRect shapePaddingBounds() const; FloatRoundedRect m_bounds; }; } // namespace WebCore - -#endif // BoxShape_h diff --git a/Source/WebCore/rendering/shapes/PolygonShape.cpp b/Source/WebCore/rendering/shapes/PolygonShape.cpp index 8e241eb14..65a5af94d 100644 --- a/Source/WebCore/rendering/shapes/PolygonShape.cpp +++ b/Source/WebCore/rendering/shapes/PolygonShape.cpp @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -30,69 +30,10 @@ #include "config.h" #include "PolygonShape.h" -#include "ShapeInterval.h" #include <wtf/MathExtras.h> namespace WebCore { -enum EdgeIntersectionType { - Normal, - VertexMinY, - VertexMaxY, - VertexYBoth -}; - -struct EdgeIntersection { - const FloatPolygonEdge* edge; - FloatPoint point; - EdgeIntersectionType type; -}; - -static inline float leftSide(const FloatPoint& vertex1, const FloatPoint& vertex2, const FloatPoint& point) -{ - return ((point.x() - vertex1.x()) * (vertex2.y() - vertex1.y())) - ((vertex2.x() - vertex1.x()) * (point.y() - vertex1.y())); -} - -static inline bool isReflexVertex(const FloatPoint& prevVertex, const FloatPoint& vertex, const FloatPoint& nextVertex) -{ - return leftSide(prevVertex, nextVertex, vertex) < 0; -} - -static bool computeXIntersection(const FloatPolygonEdge* edgePointer, float y, EdgeIntersection& result) -{ - const FloatPolygonEdge& edge = *edgePointer; - - if (edge.minY() > y || edge.maxY() < y) - return false; - - const FloatPoint& vertex1 = edge.vertex1(); - const FloatPoint& vertex2 = edge.vertex2(); - float dy = vertex2.y() - vertex1.y(); - - float intersectionX; - EdgeIntersectionType intersectionType; - - if (!dy) { - intersectionType = VertexYBoth; - intersectionX = edge.minX(); - } else if (y == edge.minY()) { - intersectionType = VertexMinY; - intersectionX = (vertex1.y() < vertex2.y()) ? vertex1.x() : vertex2.x(); - } else if (y == edge.maxY()) { - intersectionType = VertexMaxY; - intersectionX = (vertex1.y() > vertex2.y()) ? vertex1.x() : vertex2.x(); - } else { - intersectionType = Normal; - intersectionX = ((y - vertex1.y()) * (vertex2.x() - vertex1.x()) / dy) + vertex1.x(); - } - - result.edge = edgePointer; - result.type = intersectionType; - result.point.set(intersectionX, y); - - return true; -} - static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge) { FloatSize edgeDelta = edge.vertex2() - edge.vertex1(); @@ -109,438 +50,116 @@ static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge) return -inwardEdgeNormal(edge); } -static inline void appendArc(Vector<FloatPoint>& vertices, const FloatPoint& arcCenter, float arcRadius, const FloatPoint& startArcVertex, const FloatPoint& endArcVertex, bool padding) -{ - float startAngle = atan2(startArcVertex.y() - arcCenter.y(), startArcVertex.x() - arcCenter.x()); - float endAngle = atan2(endArcVertex.y() - arcCenter.y(), endArcVertex.x() - arcCenter.x()); - const float twoPI = piFloat * 2; - if (startAngle < 0) - startAngle += twoPI; - if (endAngle < 0) - endAngle += twoPI; - float angle = (startAngle > endAngle) ? (startAngle - endAngle) : (startAngle + twoPI - endAngle); - const float arcSegmentCount = 6; // An even number so that one arc vertex will be eactly arcRadius from arcCenter. - float arcSegmentAngle = ((padding) ? -angle : twoPI - angle) / arcSegmentCount; - - vertices.append(startArcVertex); - for (unsigned i = 1; i < arcSegmentCount; ++i) { - float angle = startAngle + arcSegmentAngle * i; - vertices.append(arcCenter + FloatPoint(cos(angle) * arcRadius, sin(angle) * arcRadius)); - } - vertices.append(endArcVertex); -} - -static inline void snapVerticesToLayoutUnitGrid(Vector<FloatPoint>& vertices) -{ - for (unsigned i = 0; i < vertices.size(); ++i) - vertices[i].set(LayoutUnit(vertices[i].x()).toFloat(), LayoutUnit(vertices[i].y()).toFloat()); -} - -static inline PassOwnPtr<FloatPolygon> computeShapePaddingBounds(const FloatPolygon& polygon, float padding, WindRule fillRule) -{ - OwnPtr<Vector<FloatPoint>> paddedVertices = adoptPtr(new Vector<FloatPoint>()); - FloatPoint intersection; - - for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) { - const FloatPolygonEdge& thisEdge = polygon.edgeAt(i); - const FloatPolygonEdge& prevEdge = thisEdge.previousEdge(); - OffsetPolygonEdge thisOffsetEdge(thisEdge, inwardEdgeNormal(thisEdge) * padding); - OffsetPolygonEdge prevOffsetEdge(prevEdge, inwardEdgeNormal(prevEdge) * padding); - - if (prevOffsetEdge.intersection(thisOffsetEdge, intersection)) - paddedVertices->append(intersection); - else if (isReflexVertex(prevEdge.vertex1(), thisEdge.vertex1(), thisEdge.vertex2())) - appendArc(*paddedVertices, thisEdge.vertex1(), padding, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1(), true); - } - - snapVerticesToLayoutUnitGrid(*paddedVertices); - return adoptPtr(new FloatPolygon(paddedVertices.release(), fillRule)); -} - -static inline PassOwnPtr<FloatPolygon> computeShapeMarginBounds(const FloatPolygon& polygon, float margin, WindRule fillRule) +float OffsetPolygonEdge::xIntercept(float y) const { - OwnPtr<Vector<FloatPoint>> marginVertices = adoptPtr(new Vector<FloatPoint>()); - FloatPoint intersection; + ASSERT(y >= minY() && y <= maxY()); - for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) { - const FloatPolygonEdge& thisEdge = polygon.edgeAt(i); - const FloatPolygonEdge& prevEdge = thisEdge.previousEdge(); - OffsetPolygonEdge thisOffsetEdge(thisEdge, outwardEdgeNormal(thisEdge) * margin); - OffsetPolygonEdge prevOffsetEdge(prevEdge, outwardEdgeNormal(prevEdge) * margin); + if (vertex1().y() == vertex2().y() || vertex1().x() == vertex2().x()) + return minX(); + if (y == minY()) + return vertex1().y() < vertex2().y() ? vertex1().x() : vertex2().x(); + if (y == maxY()) + return vertex1().y() > vertex2().y() ? vertex1().x() : vertex2().x(); - if (prevOffsetEdge.intersection(thisOffsetEdge, intersection)) - marginVertices->append(intersection); - else - appendArc(*marginVertices, thisEdge.vertex1(), margin, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1(), false); - } - - snapVerticesToLayoutUnitGrid(*marginVertices); - return adoptPtr(new FloatPolygon(marginVertices.release(), fillRule)); + return vertex1().x() + ((y - vertex1().y()) * (vertex2().x() - vertex1().x()) / (vertex2().y() - vertex1().y())); } -const FloatPolygon& PolygonShape::shapePaddingBounds() const +FloatShapeInterval OffsetPolygonEdge::clippedEdgeXRange(float y1, float y2) const { - ASSERT(shapePadding() >= 0); - if (!shapePadding() || m_polygon.isEmpty()) - return m_polygon; + if (!overlapsYRange(y1, y2) || (y1 == maxY() && minY() <= y1) || (y2 == minY() && maxY() >= y2)) + return FloatShapeInterval(); - if (!m_paddingBounds) - m_paddingBounds = computeShapePaddingBounds(m_polygon, shapePadding(), m_polygon.fillRule()); + if (isWithinYRange(y1, y2)) + return FloatShapeInterval(minX(), maxX()); - return *m_paddingBounds; -} + // Clip the edge line segment to the vertical range y1,y2 and then return + // the clipped line segment's horizontal range. -const FloatPolygon& PolygonShape::shapeMarginBounds() const -{ - ASSERT(shapeMargin() >= 0); - if (!shapeMargin() || m_polygon.isEmpty()) - return m_polygon; - - if (!m_marginBounds) - m_marginBounds = computeShapeMarginBounds(m_polygon, shapeMargin(), m_polygon.fillRule()); - - return *m_marginBounds; -} - -static inline bool getVertexIntersectionVertices(const EdgeIntersection& intersection, FloatPoint& prevVertex, FloatPoint& thisVertex, FloatPoint& nextVertex) -{ - if (intersection.type != VertexMinY && intersection.type != VertexMaxY) - return false; - - ASSERT(intersection.edge && intersection.edge->polygon()); - const FloatPolygon& polygon = *(intersection.edge->polygon()); - const FloatPolygonEdge& thisEdge = *(intersection.edge); - - if ((intersection.type == VertexMinY && (thisEdge.vertex1().y() < thisEdge.vertex2().y())) - || (intersection.type == VertexMaxY && (thisEdge.vertex1().y() > thisEdge.vertex2().y()))) { - prevVertex = polygon.vertexAt(thisEdge.previousEdge().vertexIndex1()); - thisVertex = polygon.vertexAt(thisEdge.vertexIndex1()); - nextVertex = polygon.vertexAt(thisEdge.vertexIndex2()); + FloatPoint minYVertex; + FloatPoint maxYVertex; + if (vertex1().y() < vertex2().y()) { + minYVertex = vertex1(); + maxYVertex = vertex2(); } else { - prevVertex = polygon.vertexAt(thisEdge.vertexIndex1()); - thisVertex = polygon.vertexAt(thisEdge.vertexIndex2()); - nextVertex = polygon.vertexAt(thisEdge.nextEdge().vertexIndex2()); + minYVertex = vertex2(); + maxYVertex = vertex1(); } - - return true; + float xForY1 = (minYVertex.y() < y1) ? xIntercept(y1) : minYVertex.x(); + float xForY2 = (maxYVertex.y() > y2) ? xIntercept(y2) : maxYVertex.x(); + return FloatShapeInterval(std::min(xForY1, xForY2), std::max(xForY1, xForY2)); } -static inline bool appendIntervalX(float x, bool inside, FloatShapeIntervals& result) +static float circleXIntercept(float y, float radius) { - if (!inside) - result.append(FloatShapeInterval(x, x)); - else - result.last().setX2(x); - - return !inside; + ASSERT(radius > 0); + return radius * sqrt(1 - (y * y) / (radius * radius)); } -static bool compareEdgeIntersectionX(const EdgeIntersection& intersection1, const EdgeIntersection& intersection2) +static FloatShapeInterval clippedCircleXRange(const FloatPoint& center, float radius, float y1, float y2) { - float x1 = intersection1.point.x(); - float x2 = intersection2.point.x(); - return (x1 == x2) ? intersection1.type < intersection2.type : x1 < x2; -} + if (y1 >= center.y() + radius || y2 <= center.y() - radius) + return FloatShapeInterval(); -static void computeXIntersections(const FloatPolygon& polygon, float y, bool isMinY, FloatShapeIntervals& result) -{ - Vector<const FloatPolygonEdge*> edges; - if (!polygon.overlappingEdges(y, y, edges)) - return; - - Vector<EdgeIntersection> intersections; - EdgeIntersection intersection; - for (unsigned i = 0; i < edges.size(); ++i) { - if (computeXIntersection(edges[i], y, intersection) && intersection.type != VertexYBoth) - intersections.append(intersection); - } - - if (intersections.size() < 2) - return; - - std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIntersectionX); - - unsigned index = 0; - int windCount = 0; - bool inside = false; - - while (index < intersections.size()) { - const EdgeIntersection& thisIntersection = intersections[index]; - if (index + 1 < intersections.size()) { - const EdgeIntersection& nextIntersection = intersections[index + 1]; - if ((thisIntersection.point.x() == nextIntersection.point.x()) && (thisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) { - if (thisIntersection.type == nextIntersection.type) { - // Skip pairs of intersections whose types are VertexMaxY,VertexMaxY and VertexMinY,VertexMinY. - index += 2; - } else { - // Replace pairs of intersections whose types are VertexMinY,VertexMaxY or VertexMaxY,VertexMinY with one intersection. - ++index; - } - continue; - } - } - - bool edgeCrossing = thisIntersection.type == Normal; - if (!edgeCrossing) { - FloatPoint prevVertex; - FloatPoint thisVertex; - FloatPoint nextVertex; - - if (getVertexIntersectionVertices(thisIntersection, prevVertex, thisVertex, nextVertex)) { - if (nextVertex.y() == y) - edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y() < y; - else if (prevVertex.y() == y) - edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y() < y; - else - edgeCrossing = true; - } - } + if (center.y() >= y1 && center.y() <= y2) + return FloatShapeInterval(center.x() - radius, center.x() + radius); - if (edgeCrossing && polygon.fillRule() == RULE_NONZERO) { - const FloatPolygonEdge& thisEdge = *thisIntersection.edge; - windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 : -1; - } - - if (edgeCrossing && (!inside || !windCount)) - inside = appendIntervalX(thisIntersection.point.x(), inside, result); + // Clip the circle to the vertical range y1,y2 and return the extent of the clipped circle's + // projection on the X axis - ++index; - } + float xi = circleXIntercept((y2 < center.y() ? y2 : y1) - center.y(), radius); + return FloatShapeInterval(center.x() - xi, center.x() + xi); } -static bool compareX1(const FloatShapeInterval a, const FloatShapeInterval& b) { return a.x1() < b.x1(); } - -static void sortAndMergeShapeIntervals(FloatShapeIntervals& intervals) +LayoutRect PolygonShape::shapeMarginLogicalBoundingBox() const { - std::sort(intervals.begin(), intervals.end(), compareX1); - - for (unsigned i = 1; i < intervals.size(); ) { - const FloatShapeInterval& thisInterval = intervals[i]; - FloatShapeInterval& previousInterval = intervals[i - 1]; - if (thisInterval.overlaps(previousInterval)) { - previousInterval.setX2(std::max<float>(previousInterval.x2(), thisInterval.x2())); - intervals.remove(i); - } else - ++i; - } + FloatRect box = m_polygon.boundingBox(); + box.inflate(shapeMargin()); + return LayoutRect(box); } -static void computeOverlappingEdgeXProjections(const FloatPolygon& polygon, float y1, float y2, FloatShapeIntervals& result) +LineSegment PolygonShape::getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const { - Vector<const FloatPolygonEdge*> edges; - if (!polygon.overlappingEdges(y1, y2, edges)) - return; - - EdgeIntersection intersection; - for (unsigned i = 0; i < edges.size(); ++i) { - const FloatPolygonEdge *edge = edges[i]; - float x1; - float x2; - - if (edge->minY() < y1) { - computeXIntersection(edge, y1, intersection); - x1 = intersection.point.x(); - } else - x1 = (edge->vertex1().y() < edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); - - if (edge->maxY() > y2) { - computeXIntersection(edge, y2, intersection); - x2 = intersection.point.x(); - } else - x2 = (edge->vertex1().y() > edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x(); - - if (x1 > x2) - std::swap(x1, x2); - - if (x2 > x1) - result.append(FloatShapeInterval(x1, x2)); - } - - sortAndMergeShapeIntervals(result); -} - -void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const -{ - const FloatPolygon& polygon = shapeMarginBounds(); - if (polygon.isEmpty()) - return; - float y1 = logicalTop; float y2 = logicalTop + logicalHeight; - FloatShapeIntervals y1XIntervals, y2XIntervals; - computeXIntersections(polygon, y1, true, y1XIntervals); - computeXIntersections(polygon, y2, false, y2XIntervals); - - FloatShapeIntervals mergedIntervals; - FloatShapeInterval::uniteShapeIntervals(y1XIntervals, y2XIntervals, mergedIntervals); - - FloatShapeIntervals edgeIntervals; - computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals); - - FloatShapeIntervals excludedIntervals; - FloatShapeInterval::uniteShapeIntervals(mergedIntervals, edgeIntervals, excludedIntervals); - - for (unsigned i = 0; i < excludedIntervals.size(); ++i) { - FloatShapeInterval interval = excludedIntervals[i]; - result.append(LineSegment(interval.x1(), interval.x2())); - } -} - -void PolygonShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const -{ - const FloatPolygon& polygon = shapePaddingBounds(); - if (polygon.isEmpty()) - return; - - float y1 = logicalTop; - float y2 = logicalTop + logicalHeight; - - FloatShapeIntervals y1XIntervals, y2XIntervals; - computeXIntersections(polygon, y1, true, y1XIntervals); - computeXIntersections(polygon, y2, false, y2XIntervals); - - FloatShapeIntervals commonIntervals; - FloatShapeInterval::intersectShapeIntervals(y1XIntervals, y2XIntervals, commonIntervals); - - FloatShapeIntervals edgeIntervals; - computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals); - - FloatShapeIntervals includedIntervals; - FloatShapeInterval::subtractShapeIntervals(commonIntervals, edgeIntervals, includedIntervals); - - for (unsigned i = 0; i < includedIntervals.size(); ++i) { - const FloatShapeInterval& interval = includedIntervals[i]; - result.append(LineSegment(interval.x1(), interval.x2())); - } -} - -static inline bool firstFitRectInPolygon(const FloatPolygon& polygon, const FloatRect& rect, unsigned offsetEdgeIndex1, unsigned offsetEdgeIndex2) -{ - Vector<const FloatPolygonEdge*> edges; - if (!polygon.overlappingEdges(rect.y(), rect.maxY(), edges)) - return true; - - for (unsigned i = 0; i < edges.size(); ++i) { - const FloatPolygonEdge* edge = edges[i]; - if (edge->edgeIndex() != offsetEdgeIndex1 && edge->edgeIndex() != offsetEdgeIndex2 && edge->overlapsRect(rect)) - return false; - } - - return true; -} - -static inline bool aboveOrToTheLeft(const FloatRect& r1, const FloatRect& r2) -{ - if (r1.y() < r2.y()) - return true; - if (r1.y() == r2.y()) - return r1.x() < r2.x(); - return false; -} - -bool PolygonShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit& result) const -{ - float minIntervalTop = minLogicalIntervalTop; - float minIntervalHeight = minLogicalIntervalSize.height(); - float minIntervalWidth = minLogicalIntervalSize.width(); - - const FloatPolygon& polygon = shapePaddingBounds(); - const FloatRect boundingBox = polygon.boundingBox(); - if (minIntervalWidth > boundingBox.width()) - return false; - - float minY = std::max(boundingBox.y(), minIntervalTop); - float maxY = minY + minIntervalHeight; - - if (maxY > boundingBox.maxY()) - return false; - - Vector<const FloatPolygonEdge*> edges; - polygon.overlappingEdges(minIntervalTop, boundingBox.maxY(), edges); - - float dx = minIntervalWidth / 2; - float dy = minIntervalHeight / 2; - Vector<OffsetPolygonEdge> offsetEdges; - - for (unsigned i = 0; i < edges.size(); ++i) { - const FloatPolygonEdge& edge = *(edges[i]); - const FloatPoint& vertex0 = edge.previousEdge().vertex1(); - const FloatPoint& vertex1 = edge.vertex1(); - const FloatPoint& vertex2 = edge.vertex2(); - Vector<OffsetPolygonEdge> offsetEdgeBuffer; - - if (vertex2.y() > vertex1.y() ? vertex2.x() >= vertex1.x() : vertex1.x() >= vertex2.x()) { - offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, -dy))); - offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, dy))); - } else { - offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, dy))); - offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, -dy))); - } - - if (isReflexVertex(vertex0, vertex1, vertex2)) { - if (vertex2.x() <= vertex1.x() && vertex0.x() <= vertex1.x()) - offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(dx, -dy), FloatSize(dx, dy))); - else if (vertex2.x() >= vertex1.x() && vertex0.x() >= vertex1.x()) - offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(-dx, dy))); - if (vertex2.y() <= vertex1.y() && vertex0.y() <= vertex1.y()) - offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, dy), FloatSize(dx, dy))); - else if (vertex2.y() >= vertex1.y() && vertex0.y() >= vertex1.y()) - offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(dx, -dy))); + if (m_polygon.isEmpty() || !m_polygon.boundingBox().overlapsYRange(y1 - shapeMargin(), y2 + shapeMargin())) + return LineSegment(); + + Vector<const FloatPolygonEdge*> overlappingEdges; + if (!m_polygon.overlappingEdges(y1 - shapeMargin(), y2 + shapeMargin(), overlappingEdges)) + return LineSegment(); + + FloatShapeInterval excludedInterval; + for (unsigned i = 0; i < overlappingEdges.size(); i++) { + const FloatPolygonEdge& edge = *(overlappingEdges[i]); + if (edge.maxY() == edge.minY()) + continue; + if (!shapeMargin()) + excludedInterval.unite(OffsetPolygonEdge(edge, FloatSize()).clippedEdgeXRange(y1, y2)); + else { + excludedInterval.unite(OffsetPolygonEdge(edge, outwardEdgeNormal(edge) * shapeMargin()).clippedEdgeXRange(y1, y2)); + excludedInterval.unite(OffsetPolygonEdge(edge, inwardEdgeNormal(edge) * shapeMargin()).clippedEdgeXRange(y1, y2)); + excludedInterval.unite(clippedCircleXRange(edge.vertex1(), shapeMargin(), y1, y2)); + excludedInterval.unite(clippedCircleXRange(edge.vertex2(), shapeMargin(), y1, y2)); } - - for (unsigned j = 0; j < offsetEdgeBuffer.size(); ++j) - if (offsetEdgeBuffer[j].maxY() >= minY) - offsetEdges.append(offsetEdgeBuffer[j]); } - offsetEdges.append(OffsetPolygonEdge(polygon, minIntervalTop, FloatSize(0, dy))); - - FloatPoint offsetEdgesIntersection; - FloatRect firstFitRect; - bool firstFitFound = false; - - for (unsigned i = 0; i < offsetEdges.size() - 1; ++i) { - for (unsigned j = i + 1; j < offsetEdges.size(); ++j) { - if (offsetEdges[i].intersection(offsetEdges[j], offsetEdgesIntersection)) { - FloatPoint potentialFirstFitLocation(offsetEdgesIntersection.x() - dx, offsetEdgesIntersection.y() - dy); - FloatRect potentialFirstFitRect(potentialFirstFitLocation, minLogicalIntervalSize); - if ((offsetEdges[i].basis() == OffsetPolygonEdge::LineTop - || offsetEdges[j].basis() == OffsetPolygonEdge::LineTop - || potentialFirstFitLocation.y() >= minIntervalTop) - && (!firstFitFound || aboveOrToTheLeft(potentialFirstFitRect, firstFitRect)) - && polygon.contains(offsetEdgesIntersection) - && firstFitRectInPolygon(polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) { - firstFitFound = true; - firstFitRect = potentialFirstFitRect; - } - } - } - } + if (excludedInterval.isEmpty()) + return LineSegment(); - if (firstFitFound) - result = ceiledLayoutUnit(firstFitRect.y()); - return firstFitFound; + return LineSegment(excludedInterval.x1(), excludedInterval.x2()); } -static void addPolygon(Path& path, const FloatPolygon& polygon) +void PolygonShape::buildDisplayPaths(DisplayPaths& paths) const { - if (!polygon.numberOfVertices()) + if (m_polygon.isEmpty()) return; - path.moveTo(polygon.vertexAt(0)); - - for (size_t i = 1; i < polygon.numberOfVertices(); i++) - path.addLineTo(polygon.vertexAt(i)); + paths.shape.moveTo(m_polygon.vertexAt(0)); + for (unsigned i = 1; i < m_polygon.numberOfVertices(); i++) + paths.shape.addLineTo(m_polygon.vertexAt(i)); - path.closeSubpath(); -} - -void PolygonShape::buildDisplayPaths(DisplayPaths& paths) const -{ - addPolygon(paths.shape, m_polygon); - if (shapeMargin()) - addPolygon(paths.marginShape, shapeMarginBounds()); + paths.shape.closeSubpath(); } } // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/PolygonShape.h b/Source/WebCore/rendering/shapes/PolygonShape.h index 09f714083..6d92c83d3 100644 --- a/Source/WebCore/rendering/shapes/PolygonShape.h +++ b/Source/WebCore/rendering/shapes/PolygonShape.h @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -27,86 +27,51 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PolygonShape_h -#define PolygonShape_h +#pragma once #include "FloatPolygon.h" #include "Shape.h" +#include "ShapeInterval.h" namespace WebCore { -class OffsetPolygonEdge : public VertexPair { +class OffsetPolygonEdge final : public VertexPair { public: - enum Basis { - Edge, - Vertex, - LineTop - }; - OffsetPolygonEdge(const FloatPolygonEdge& edge, const FloatSize& offset) : m_vertex1(edge.vertex1() + offset) , m_vertex2(edge.vertex2() + offset) - , m_edgeIndex(edge.edgeIndex()) - , m_basis(Edge) { } - OffsetPolygonEdge(const FloatPoint& reflexVertex, const FloatSize& offset1, const FloatSize& offset2) - : m_vertex1(reflexVertex + offset1) - , m_vertex2(reflexVertex + offset2) - , m_edgeIndex(-1) - , m_basis(Vertex) - { - } + const FloatPoint& vertex1() const override { return m_vertex1; } + const FloatPoint& vertex2() const override { return m_vertex2; } - OffsetPolygonEdge(const FloatPolygon& polygon, float minLogicalIntervalTop, const FloatSize& offset) - : m_vertex1(FloatPoint(polygon.boundingBox().x(), minLogicalIntervalTop) + offset) - , m_vertex2(FloatPoint(polygon.boundingBox().maxX(), minLogicalIntervalTop) + offset) - , m_edgeIndex(-1) - , m_basis(LineTop) - { - } - - virtual const FloatPoint& vertex1() const override { return m_vertex1; } - virtual const FloatPoint& vertex2() const override { return m_vertex2; } - int edgeIndex() const { return m_edgeIndex; } - Basis basis() const { return m_basis; } + bool isWithinYRange(float y1, float y2) const { return y1 <= minY() && y2 >= maxY(); } + bool overlapsYRange(float y1, float y2) const { return y2 >= minY() && y1 <= maxY(); } + float xIntercept(float y) const; + FloatShapeInterval clippedEdgeXRange(float y1, float y2) const; private: FloatPoint m_vertex1; FloatPoint m_vertex2; - int m_edgeIndex; - Basis m_basis; }; class PolygonShape : public Shape { WTF_MAKE_NONCOPYABLE(PolygonShape); public: - PolygonShape(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule) - : m_polygon(vertices, fillRule) - , m_marginBounds(nullptr) - , m_paddingBounds(nullptr) + PolygonShape(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule) + : m_polygon(WTFMove(vertices), fillRule) { } - virtual LayoutRect shapeMarginLogicalBoundingBox() const override { return static_cast<LayoutRect>(shapeMarginBounds().boundingBox()); } - virtual LayoutRect shapePaddingLogicalBoundingBox() const override { return static_cast<LayoutRect>(shapePaddingBounds().boundingBox()); } - virtual bool isEmpty() const override { return m_polygon.isEmpty(); } - virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit&) const override; + LayoutRect shapeMarginLogicalBoundingBox() const override; + bool isEmpty() const override { return m_polygon.isEmpty(); } + LineSegment getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const override; - virtual void buildDisplayPaths(DisplayPaths&) const override; + void buildDisplayPaths(DisplayPaths&) const override; private: - const FloatPolygon& shapeMarginBounds() const; - const FloatPolygon& shapePaddingBounds() const; - FloatPolygon m_polygon; - mutable OwnPtr<FloatPolygon> m_marginBounds; - mutable OwnPtr<FloatPolygon> m_paddingBounds; }; } // namespace WebCore - -#endif // PolygonShape_h diff --git a/Source/WebCore/rendering/shapes/RasterShape.cpp b/Source/WebCore/rendering/shapes/RasterShape.cpp index e6a30e98d..d8aff98bc 100644 --- a/Source/WebCore/rendering/shapes/RasterShape.cpp +++ b/Source/WebCore/rendering/shapes/RasterShape.cpp @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -73,195 +73,61 @@ IntShapeInterval MarginIntervalGenerator::intervalAt(int y) const return IntShapeInterval(m_x1 - dx, m_x2 + dx); } -void RasterShapeIntervals::appendInterval(int y, int x1, int x2) -{ - ASSERT(x2 > x1 && (intervalsAt(y).isEmpty() || x1 > intervalsAt(y).last().x2())); - m_bounds.unite(IntRect(x1, y, x2 - x1, 1)); - intervalsAt(y).append(IntShapeInterval(x1, x2)); -} - -void RasterShapeIntervals::uniteMarginInterval(int y, const IntShapeInterval& interval) -{ - ASSERT(intervalsAt(y).size() <= 1); // Each m_intervalLists entry has 0 or one interval. - - if (intervalsAt(y).isEmpty()) - intervalsAt(y).append(interval); - else { - IntShapeInterval& resultInterval = intervalsAt(y)[0]; - resultInterval.set(std::min(resultInterval.x1(), interval.x1()), std::max(resultInterval.x2(), interval.x2())); - } - - m_bounds.unite(IntRect(interval.x1(), y, interval.width(), 1)); -} - -static inline bool shapeIntervalsContain(const IntShapeIntervals& intervals, const IntShapeInterval& interval) -{ - for (unsigned i = 0; i < intervals.size(); i++) { - if (intervals[i].x1() > interval.x2()) - return false; - if (intervals[i].contains(interval)) - return true; - } - - return false; -} - -bool RasterShapeIntervals::contains(const IntRect& rect) const -{ - if (!bounds().contains(rect)) - return false; - - const IntShapeInterval& rectInterval = IntShapeInterval(rect.x(), rect.maxX()); - for (int y = rect.y(); y < rect.maxY(); y++) { - if (!shapeIntervalsContain(intervalsAt(y), rectInterval)) - return false; - } - - return true; -} - -static inline void appendX1Values(const IntShapeIntervals& intervals, int minIntervalWidth, Vector<int>& result) -{ - for (unsigned i = 0; i < intervals.size(); i++) - if (intervals[i].width() >= minIntervalWidth) - result.append(intervals[i].x1()); -} - -bool RasterShapeIntervals::getIntervalX1Values(int y1, int y2, int minIntervalWidth, Vector<int>& result) const -{ - ASSERT(y1 >= 0 && y2 > y1); - - for (int y = y1; y < y2; y++) { - if (intervalsAt(y).isEmpty()) - return false; - } - - appendX1Values(intervalsAt(y1), minIntervalWidth, result); - for (int y = y1 + 1; y < y2; y++) { - if (intervalsAt(y) != intervalsAt(y - 1)) - appendX1Values(intervalsAt(y), minIntervalWidth, result); - } - - return true; -} - -bool RasterShapeIntervals::firstIncludedIntervalY(int minY, const IntSize& minSize, LayoutUnit& result) const -{ - minY = std::max<int>(bounds().y(), minY); - - ASSERT(minY >= 0 && minY < size()); - - if (minSize.isEmpty() || minSize.width() > bounds().width()) - return false; - - for (int lineY = minY; lineY <= bounds().maxY() - minSize.height(); lineY++) { - Vector<int> intervalX1Values; - if (!getIntervalX1Values(lineY, lineY + minSize.height(), minSize.width(), intervalX1Values)) - continue; - - std::sort(intervalX1Values.begin(), intervalX1Values.end()); - - IntRect firstFitRect(IntPoint(0, 0), minSize); - for (unsigned i = 0; i < intervalX1Values.size(); i++) { - int lineX = intervalX1Values[i]; - if (i > 0 && lineX == intervalX1Values[i - 1]) - continue; - firstFitRect.setLocation(IntPoint(lineX, lineY)); - if (contains(firstFitRect)) { - result = lineY; - return true; - } - } - } - - return false; -} - -void RasterShapeIntervals::getIncludedIntervals(int y1, int y2, IntShapeIntervals& result) const -{ - ASSERT(y2 >= y1); - - if (y1 < bounds().y() || y2 > bounds().maxY()) - return; - - for (int y = y1; y < y2; y++) { - if (intervalsAt(y).isEmpty()) - return; - } - - result = intervalsAt(y1); - for (int y = y1 + 1; y < y2 && !result.isEmpty(); y++) { - IntShapeIntervals intervals; - IntShapeInterval::intersectShapeIntervals(result, intervalsAt(y), intervals); - result.swap(intervals); - } -} - -void RasterShapeIntervals::getExcludedIntervals(int y1, int y2, IntShapeIntervals& result) const -{ - ASSERT(y2 >= y1); - - if (y2 < bounds().y() || y1 >= bounds().maxY()) - return; - - y1 = std::max(y1, bounds().y()); - y2 = std::min(y2, bounds().maxY()); - - result = intervalsAt(y1); - for (int y = y1 + 1; y < y2; y++) { - IntShapeIntervals intervals; - IntShapeInterval::uniteShapeIntervals(result, intervalsAt(y), intervals); - result.swap(intervals); - } -} - -// Currently limited to computing the margin boundary for shape-outside for floats, see https://bugs.webkit.org/show_bug.cgi?id=116348. - -PassOwnPtr<RasterShapeIntervals> RasterShapeIntervals::computeShapeMarginIntervals(unsigned shapeMargin) const +std::unique_ptr<RasterShapeIntervals> RasterShapeIntervals::computeShapeMarginIntervals(int shapeMargin) const { - OwnPtr<RasterShapeIntervals> result = adoptPtr(new RasterShapeIntervals(size(), shapeMargin)); + int marginIntervalsSize = (offset() > shapeMargin) ? size() : size() - offset() * 2 + shapeMargin * 2; + auto result = std::make_unique<RasterShapeIntervals>(marginIntervalsSize, std::max(shapeMargin, offset())); MarginIntervalGenerator marginIntervalGenerator(shapeMargin); - int minY = bounds().y(); - int maxY = bounds().maxY(); - - for (int y = minY; y < maxY; ++y) { - const IntShapeInterval& intervalAtY = limitIntervalAt(y); + for (int y = bounds().y(); y < bounds().maxY(); ++y) { + const IntShapeInterval& intervalAtY = intervalAt(y); if (intervalAtY.isEmpty()) continue; marginIntervalGenerator.set(y, intervalAtY); - int marginY0 = y - clampToInteger(shapeMargin); - int marginY1 = y + clampToInteger(shapeMargin); + int marginY0 = std::max(minY(), y - shapeMargin); + int marginY1 = std::min(maxY(), y + shapeMargin + 1); for (int marginY = y - 1; marginY >= marginY0; --marginY) { - if (marginY > minY && limitIntervalAt(marginY).contains(intervalAtY)) + if (marginY > bounds().y() && intervalAt(marginY).contains(intervalAtY)) break; - result->uniteMarginInterval(marginY, marginIntervalGenerator.intervalAt(marginY)); + result->intervalAt(marginY).unite(marginIntervalGenerator.intervalAt(marginY)); } - result->uniteMarginInterval(y, marginIntervalGenerator.intervalAt(y)); + result->intervalAt(y).unite(marginIntervalGenerator.intervalAt(y)); - for (int marginY = y + 1; marginY <= marginY1; ++marginY) { - if (marginY < maxY && limitIntervalAt(marginY).contains(intervalAtY)) + for (int marginY = y + 1; marginY < marginY1; ++marginY) { + if (marginY < bounds().maxY() && intervalAt(marginY).contains(intervalAtY)) break; - result->uniteMarginInterval(marginY, marginIntervalGenerator.intervalAt(marginY)); + result->intervalAt(marginY).unite(marginIntervalGenerator.intervalAt(marginY)); } } - return result.release(); + result->initializeBounds(); + return result; +} + +void RasterShapeIntervals::initializeBounds() +{ + m_bounds = IntRect(); + for (int y = minY(); y < maxY(); ++y) { + const IntShapeInterval& intervalAtY = intervalAt(y); + if (intervalAtY.isEmpty()) + continue; + m_bounds.unite(IntRect(intervalAtY.x1(), y, intervalAtY.width(), 1)); + } } void RasterShapeIntervals::buildBoundsPath(Path& path) const { for (int y = bounds().y(); y < bounds().maxY(); y++) { - if (intervalsAt(y).isEmpty()) + if (intervalAt(y).isEmpty()) continue; - IntShapeInterval extent = limitIntervalAt(y); + IntShapeInterval extent = intervalAt(y); int endY = y + 1; for (; endY < bounds().maxY(); endY++) { - if (intervalsAt(endY).isEmpty() || limitIntervalAt(endY) != extent) + if (intervalAt(endY).isEmpty() || intervalAt(endY) != extent) break; } path.addRect(FloatRect(extent.x1(), y, extent.width(), endY - y)); @@ -275,58 +141,38 @@ const RasterShapeIntervals& RasterShape::marginIntervals() const if (!shapeMargin()) return *m_intervals; - unsigned marginBoundaryRadius = std::min(clampToUnsigned(ceil(shapeMargin())), std::max<unsigned>(m_imageSize.width(), m_imageSize.height())); + int shapeMarginInt = clampToPositiveInteger(ceil(shapeMargin())); + int maxShapeMarginInt = std::max(m_marginRectSize.width(), m_marginRectSize.height()) * sqrt(2); if (!m_marginIntervals) - m_marginIntervals = m_intervals->computeShapeMarginIntervals(marginBoundaryRadius); + m_marginIntervals = m_intervals->computeShapeMarginIntervals(std::min(shapeMarginInt, maxShapeMarginInt)); return *m_marginIntervals; } -const RasterShapeIntervals& RasterShape::paddingIntervals() const -{ - ASSERT(shapePadding() >= 0); - if (!shapePadding()) - return *m_intervals; - - // FIXME: Add support for non-zero padding, see https://bugs.webkit.org/show_bug.cgi?id=116348. - return *m_intervals; -} - -static inline void appendLineSegments(const IntShapeIntervals& intervals, SegmentList& result) -{ - for (unsigned i = 0; i < intervals.size(); i++) - result.append(LineSegment(intervals[i].x1(), intervals[i].x2() + 1)); -} - -void RasterShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +LineSegment RasterShape::getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const { const RasterShapeIntervals& intervals = marginIntervals(); if (intervals.isEmpty()) - return; + return LineSegment(); - IntShapeIntervals excludedIntervals; - intervals.getExcludedIntervals(logicalTop, logicalTop + logicalHeight, excludedIntervals); - appendLineSegments(excludedIntervals, result); -} + int y1 = logicalTop; + int y2 = logicalTop + logicalHeight; + ASSERT(y2 >= y1); + if (y2 < intervals.bounds().y() || y1 >= intervals.bounds().maxY()) + return LineSegment(); -void RasterShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const -{ - const RasterShapeIntervals& intervals = paddingIntervals(); - if (intervals.isEmpty()) - return; + y1 = std::max(y1, intervals.bounds().y()); + y2 = std::min(y2, intervals.bounds().maxY()); + IntShapeInterval excludedInterval; - IntShapeIntervals includedIntervals; - intervals.getIncludedIntervals(logicalTop, logicalTop + logicalHeight, includedIntervals); - appendLineSegments(includedIntervals, result); -} - -bool RasterShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit& result) const -{ - const RasterShapeIntervals& intervals = paddingIntervals(); - if (intervals.isEmpty()) - return false; + if (y1 == y2) + excludedInterval = intervals.intervalAt(y1); + else { + for (int y = y1; y < y2; y++) + excludedInterval.unite(intervals.intervalAt(y)); + } - return intervals.firstIncludedIntervalY(minLogicalIntervalTop.floor(), flooredIntSize(minLogicalIntervalSize), result); + return LineSegment(excludedInterval.x1(), excludedInterval.x2()); } } // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/RasterShape.h b/Source/WebCore/rendering/shapes/RasterShape.h index c04ef15ff..23ddaf4ff 100644 --- a/Source/WebCore/rendering/shapes/RasterShape.h +++ b/Source/WebCore/rendering/shapes/RasterShape.h @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -27,8 +27,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RasterShape_h -#define RasterShape_h +#pragma once #include "FloatRect.h" #include "Shape.h" @@ -39,70 +38,59 @@ namespace WebCore { class RasterShapeIntervals { + WTF_MAKE_FAST_ALLOCATED; public: - RasterShapeIntervals(unsigned size, unsigned shapeMargin = 0) - : m_shapeMargin(shapeMargin) + RasterShapeIntervals(unsigned size, int offset = 0) + : m_offset(offset) { - m_intervalLists.resize(size + shapeMargin * 2); + m_intervals.resize(clampTo<int>(size)); } + void initializeBounds(); const IntRect& bounds() const { return m_bounds; } bool isEmpty() const { return m_bounds.isEmpty(); } - void appendInterval(int y, int x1, int x2); - - void getIncludedIntervals(int y1, int y2, IntShapeIntervals& result) const; - void getExcludedIntervals(int y1, int y2, IntShapeIntervals& result) const; - bool firstIncludedIntervalY(int minY, const IntSize& minSize, LayoutUnit& result) const; - PassOwnPtr<RasterShapeIntervals> computeShapeMarginIntervals(unsigned shapeMargin) const; - - void buildBoundsPath(Path&) const; - -private: - int size() const { return m_intervalLists.size(); } - IntShapeIntervals& intervalsAt(int y) + IntShapeInterval& intervalAt(int y) { - ASSERT(static_cast<int>(y + m_shapeMargin) >= 0 && y + m_shapeMargin < m_intervalLists.size()); - return m_intervalLists[y + m_shapeMargin]; + ASSERT(y + m_offset >= 0 && static_cast<unsigned>(y + m_offset) < m_intervals.size()); + return m_intervals[y + m_offset]; } - const IntShapeIntervals& intervalsAt(int y) const + const IntShapeInterval& intervalAt(int y) const { - ASSERT(static_cast<int>(y + m_shapeMargin) >= 0 && y + m_shapeMargin < m_intervalLists.size()); - return m_intervalLists[y + m_shapeMargin]; + ASSERT(y + m_offset >= 0 && static_cast<unsigned>(y + m_offset) < m_intervals.size()); + return m_intervals[y + m_offset]; } - IntShapeInterval limitIntervalAt(int y) const - { - const IntShapeIntervals& intervals = intervalsAt(y); - return intervals.size() ? IntShapeInterval(intervals[0].x1(), intervals.last().x2()) : IntShapeInterval(); - } + std::unique_ptr<RasterShapeIntervals> computeShapeMarginIntervals(int shapeMargin) const; + void buildBoundsPath(Path&) const; + +private: + int size() const { return m_intervals.size(); } + int offset() const { return m_offset; } + int minY() const { return -m_offset; } + int maxY() const { return -m_offset + m_intervals.size(); } - bool contains(const IntRect&) const; - bool getIntervalX1Values(int minY, int maxY, int minIntervalWidth, Vector<int>& result) const; - void uniteMarginInterval(int y, const IntShapeInterval&); IntRect m_bounds; - Vector<IntShapeIntervals> m_intervalLists; - unsigned m_shapeMargin; + Vector<IntShapeInterval> m_intervals; + int m_offset; }; -class RasterShape : public Shape { +class RasterShape final : public Shape { WTF_MAKE_NONCOPYABLE(RasterShape); public: - RasterShape(PassOwnPtr<RasterShapeIntervals> intervals, const IntSize& imageSize) - : m_intervals(intervals) - , m_imageSize(imageSize) + RasterShape(std::unique_ptr<RasterShapeIntervals> intervals, const IntSize& marginRectSize) + : m_intervals(WTFMove(intervals)) + , m_marginRectSize(marginRectSize) { + m_intervals->initializeBounds(); } - virtual LayoutRect shapeMarginLogicalBoundingBox() const override { return static_cast<LayoutRect>(marginIntervals().bounds()); } - virtual LayoutRect shapePaddingLogicalBoundingBox() const override { return static_cast<LayoutRect>(paddingIntervals().bounds()); } - virtual bool isEmpty() const override { return m_intervals->isEmpty(); } - virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit&) const override; + LayoutRect shapeMarginLogicalBoundingBox() const override { return static_cast<LayoutRect>(marginIntervals().bounds()); } + bool isEmpty() const override { return m_intervals->isEmpty(); } + LineSegment getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const override; - virtual void buildDisplayPaths(DisplayPaths& paths) const override + void buildDisplayPaths(DisplayPaths& paths) const override { m_intervals->buildBoundsPath(paths.shape); if (shapeMargin()) @@ -111,13 +99,10 @@ public: private: const RasterShapeIntervals& marginIntervals() const; - const RasterShapeIntervals& paddingIntervals() const; - OwnPtr<RasterShapeIntervals> m_intervals; - mutable OwnPtr<RasterShapeIntervals> m_marginIntervals; - IntSize m_imageSize; + std::unique_ptr<RasterShapeIntervals> m_intervals; + mutable std::unique_ptr<RasterShapeIntervals> m_marginIntervals; + IntSize m_marginRectSize; }; } // namespace WebCore - -#endif // RasterShape_h diff --git a/Source/WebCore/rendering/shapes/RectangleShape.cpp b/Source/WebCore/rendering/shapes/RectangleShape.cpp index 83768cc43..f3619d6a5 100644 --- a/Source/WebCore/rendering/shapes/RectangleShape.cpp +++ b/Source/WebCore/rendering/shapes/RectangleShape.cpp @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -40,26 +40,6 @@ static inline float ellipseXIntercept(float y, float rx, float ry) return rx * sqrt(1 - (y * y) / (ry * ry)); } -static inline float ellipseYIntercept(float x, float rx, float ry) -{ - ASSERT(rx > 0); - return ry * sqrt(1 - (x * x) / (rx * rx)); -} - -FloatRect RectangleShape::shapePaddingBounds() const -{ - ASSERT(shapePadding() >= 0); - if (!shapePadding() || isEmpty()) - return m_bounds; - - float boundsX = x() + std::min(width() / 2, shapePadding()); - float boundsY = y() + std::min(height() / 2, shapePadding()); - float boundsWidth = std::max(0.0f, width() - shapePadding() * 2); - float boundsHeight = std::max(0.0f, height() - shapePadding() * 2); - - return FloatRect(boundsX, boundsY, boundsWidth, boundsHeight); -} - FloatRect RectangleShape::shapeMarginBounds() const { ASSERT(shapeMargin() >= 0); @@ -73,17 +53,17 @@ FloatRect RectangleShape::shapeMarginBounds() const return FloatRect(boundsX, boundsY, boundsWidth, boundsHeight); } -void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const +LineSegment RectangleShape::getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const { const FloatRect& bounds = shapeMarginBounds(); if (bounds.isEmpty()) - return; + return LineSegment(); float y1 = logicalTop; float y2 = logicalTop + logicalHeight; if (y2 < bounds.y() || y1 >= bounds.maxY()) - return; + return LineSegment(); float x1 = bounds.x(); float x2 = bounds.maxX(); @@ -105,115 +85,7 @@ void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logi } } - result.append(LineSegment(x1, x2)); -} - -void RectangleShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const -{ - const FloatRect& bounds = shapePaddingBounds(); - if (bounds.isEmpty()) - return; - - float y1 = logicalTop; - float y2 = logicalTop + logicalHeight; - - if (y1 < bounds.y() || y2 > bounds.maxY()) - return; - - float x1 = bounds.x(); - float x2 = bounds.maxX(); - - float paddingRadiusX = std::max(0.0f, rx() - shapePadding()); - float paddingRadiusY = std::max(0.0f, ry() - shapePadding()); - - if (paddingRadiusX > 0) { - bool y1InterceptsCorner = y1 < bounds.y() + paddingRadiusY; - bool y2InterceptsCorner = y2 > bounds.maxY() - paddingRadiusY; - float xi = 0; - - if (y1InterceptsCorner && y2InterceptsCorner) { - if (y1 < bounds.height() + 2 * bounds.y() - y2) { - float yi = y1 - bounds.y() - paddingRadiusY; - xi = ellipseXIntercept(yi, paddingRadiusX, paddingRadiusY); - } else { - float yi = y2 - (bounds.maxY() - paddingRadiusY); - xi = ellipseXIntercept(yi, paddingRadiusX, paddingRadiusY); - } - } else if (y1InterceptsCorner) { - float yi = y1 - bounds.y() - paddingRadiusY; - xi = ellipseXIntercept(yi, paddingRadiusX, paddingRadiusY); - } else if (y2InterceptsCorner) { - float yi = y2 - (bounds.maxY() - paddingRadiusY); - xi = ellipseXIntercept(yi, paddingRadiusX, paddingRadiusY); - } - - if (y1InterceptsCorner || y2InterceptsCorner) { - x1 = bounds.x() + paddingRadiusX - xi; - x2 = bounds.maxX() - paddingRadiusX + xi; - } - } - - result.append(LineSegment(x1, x2)); -} - -static FloatPoint cornerInterceptForWidth(float width, float widthAtIntercept, float rx, float ry) -{ - float xi = (width - widthAtIntercept) / 2; - float yi = ry - ellipseYIntercept(rx - xi, rx, ry); - return FloatPoint(xi, yi); -} - -bool RectangleShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit& result) const -{ - float minIntervalTop = minLogicalIntervalTop; - float minIntervalHeight = minLogicalIntervalSize.height(); - float minIntervalWidth = minLogicalIntervalSize.width(); - - const FloatRect& bounds = shapePaddingBounds(); - if (bounds.isEmpty() || minIntervalWidth > bounds.width()) - return false; - - float minY = std::max(bounds.y(), minIntervalTop); - float maxY = minY + minIntervalHeight; - - if (maxY > bounds.maxY()) - return false; - - float paddingRadiusX = std::max(0.0f, rx() - shapePadding()); - float paddingRadiusY = std::max(0.0f, ry() - shapePadding()); - - bool intervalOverlapsMinCorner = minY < bounds.y() + paddingRadiusY; - bool intervalOverlapsMaxCorner = maxY > bounds.maxY() - paddingRadiusY; - - if (!intervalOverlapsMinCorner && !intervalOverlapsMaxCorner) { - result = ceiledLayoutUnit(minY); - return true; - } - - float centerY = bounds.y() + bounds.height() / 2; - bool minCornerDefinesX = fabs(centerY - minY) > fabs(centerY - maxY); - bool intervalFitsWithinCorners = minIntervalWidth + 2 * paddingRadiusX <= bounds.width(); - FloatPoint cornerIntercept = cornerInterceptForWidth(bounds.width(), minIntervalWidth, paddingRadiusX, paddingRadiusY); - - if (intervalOverlapsMinCorner && (!intervalOverlapsMaxCorner || minCornerDefinesX)) { - if (intervalFitsWithinCorners || bounds.y() + cornerIntercept.y() < minY) { - result = ceiledLayoutUnit(minY); - return true; - } - if (minIntervalHeight < bounds.height() - (2 * cornerIntercept.y())) { - result = ceiledLayoutUnit(bounds.y() + cornerIntercept.y()); - return true; - } - } - - if (intervalOverlapsMaxCorner && (!intervalOverlapsMinCorner || !minCornerDefinesX)) { - if (intervalFitsWithinCorners || minY <= bounds.maxY() - cornerIntercept.y() - minIntervalHeight) { - result = ceiledLayoutUnit(minY); - return true; - } - } - - return false; + return LineSegment(x1, x2); } void RectangleShape::buildDisplayPaths(DisplayPaths& paths) const diff --git a/Source/WebCore/rendering/shapes/RectangleShape.h b/Source/WebCore/rendering/shapes/RectangleShape.h index 7fde5eeef..494806006 100644 --- a/Source/WebCore/rendering/shapes/RectangleShape.h +++ b/Source/WebCore/rendering/shapes/RectangleShape.h @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -27,19 +27,17 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RectangleShape_h -#define RectangleShape_h +#pragma once #include "FloatPoint.h" #include "FloatRect.h" #include "FloatSize.h" #include "Shape.h" #include <wtf/Assertions.h> -#include <wtf/Vector.h> namespace WebCore { -class RectangleShape : public Shape { +class RectangleShape final : public Shape { public: RectangleShape(const FloatRect& bounds, const FloatSize& radii) : m_bounds(bounds) @@ -47,18 +45,14 @@ public: { } - virtual LayoutRect shapeMarginLogicalBoundingBox() const override { return static_cast<LayoutRect>(shapeMarginBounds()); } - virtual LayoutRect shapePaddingLogicalBoundingBox() const override { return static_cast<LayoutRect>(shapePaddingBounds()); } - virtual bool isEmpty() const override { return m_bounds.isEmpty(); } - virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const override; - virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit&) const override; + LayoutRect shapeMarginLogicalBoundingBox() const override { return static_cast<LayoutRect>(shapeMarginBounds()); } + bool isEmpty() const override { return m_bounds.isEmpty(); } + LineSegment getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const override; - virtual void buildDisplayPaths(DisplayPaths&) const override; + void buildDisplayPaths(DisplayPaths&) const override; private: FloatRect shapeMarginBounds() const; - FloatRect shapePaddingBounds() const; float rx() const { return m_radii.width(); } float ry() const { return m_radii.height(); } @@ -72,5 +66,3 @@ private: }; } // namespace WebCore - -#endif // RectangleShape_h diff --git a/Source/WebCore/rendering/shapes/Shape.cpp b/Source/WebCore/rendering/shapes/Shape.cpp index 012324997..6832d09b8 100644 --- a/Source/WebCore/rendering/shapes/Shape.cpp +++ b/Source/WebCore/rendering/shapes/Shape.cpp @@ -31,55 +31,46 @@ #include "Shape.h" #include "BasicShapeFunctions.h" +#include "BasicShapes.h" #include "BoxShape.h" -#include "CachedImage.h" -#include "FloatSize.h" +#include "GraphicsContext.h" #include "ImageBuffer.h" #include "LengthFunctions.h" #include "PolygonShape.h" #include "RasterShape.h" #include "RectangleShape.h" #include "WindRule.h" -#include <wtf/MathExtras.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> namespace WebCore { -static PassOwnPtr<Shape> createInsetShape(const FloatRoundedRect& bounds) +static std::unique_ptr<Shape> createInsetShape(const FloatRoundedRect& bounds) { ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0); - return adoptPtr(new BoxShape(bounds)); + return std::make_unique<BoxShape>(bounds); } -static PassOwnPtr<Shape> createRectangleShape(const FloatRect& bounds, const FloatSize& radii) -{ - ASSERT(bounds.width() >= 0 && bounds.height() >= 0 && radii.width() >= 0 && radii.height() >= 0); - return adoptPtr(new RectangleShape(bounds, radii)); -} - -static PassOwnPtr<Shape> createCircleShape(const FloatPoint& center, float radius) +static std::unique_ptr<Shape> createCircleShape(const FloatPoint& center, float radius) { ASSERT(radius >= 0); - return adoptPtr(new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius))); + return std::make_unique<RectangleShape>(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius)); } -static PassOwnPtr<Shape> createEllipseShape(const FloatPoint& center, const FloatSize& radii) +static std::unique_ptr<Shape> createEllipseShape(const FloatPoint& center, const FloatSize& radii) { ASSERT(radii.width() >= 0 && radii.height() >= 0); - return adoptPtr(new RectangleShape(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii)); + return std::make_unique<RectangleShape>(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii); } -static PassOwnPtr<Shape> createPolygonShape(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule) +static std::unique_ptr<Shape> createPolygonShape(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule) { - return adoptPtr(new PolygonShape(vertices, fillRule)); + return std::make_unique<PolygonShape>(WTFMove(vertices), fillRule); } static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight, WritingMode writingMode) { if (isHorizontalWritingMode(writingMode)) return rect; - if (isFlippedBlocksWritingMode(writingMode)) + if (isFlippedWritingMode(writingMode)) return FloatRect(rect.y(), logicalBoxHeight - rect.maxX(), rect.height(), rect.width()); return rect.transposedRect(); } @@ -88,7 +79,7 @@ static inline FloatPoint physicalPointToLogical(const FloatPoint& point, float l { if (isHorizontalWritingMode(writingMode)) return point; - if (isFlippedBlocksWritingMode(writingMode)) + if (isFlippedWritingMode(writingMode)) return FloatPoint(point.y(), logicalBoxHeight - point.x()); return point.transposedPoint(); } @@ -100,86 +91,32 @@ static inline FloatSize physicalSizeToLogical(const FloatSize& size, WritingMode return size.transposedSize(); } -static inline void ensureRadiiDoNotOverlap(FloatRect &bounds, FloatSize &radii) -{ - float widthRatio = bounds.width() / (2 * radii.width()); - float heightRatio = bounds.height() / (2 * radii.height()); - float reductionRatio = std::min<float>(widthRatio, heightRatio); - if (reductionRatio < 1) { - radii.setWidth(reductionRatio * radii.width()); - radii.setHeight(reductionRatio * radii.height()); - } -} - -PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, Length margin, Length padding) +std::unique_ptr<Shape> Shape::createShape(const BasicShape& basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, float margin) { - ASSERT(basicShape); - bool horizontalWritingMode = isHorizontalWritingMode(writingMode); float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height(); float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width(); - OwnPtr<Shape> shape; - - switch (basicShape->type()) { - - case BasicShape::BasicShapeRectangleType: { - const BasicShapeRectangle& rectangle = *static_cast<const BasicShapeRectangle*>(basicShape); - FloatRect bounds( - floatValueForLength(rectangle.x(), boxWidth), - floatValueForLength(rectangle.y(), boxHeight), - floatValueForLength(rectangle.width(), boxWidth), - floatValueForLength(rectangle.height(), boxHeight)); - FloatSize cornerRadii( - floatValueForLength(rectangle.cornerRadiusX(), boxWidth), - floatValueForLength(rectangle.cornerRadiusY(), boxHeight)); - ensureRadiiDoNotOverlap(bounds, cornerRadii); - FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode); - - shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode)); - break; - } + std::unique_ptr<Shape> shape; - case BasicShape::DeprecatedBasicShapeCircleType: { - const DeprecatedBasicShapeCircle* circle = static_cast<const DeprecatedBasicShapeCircle*>(basicShape); - float centerX = floatValueForLength(circle->centerX(), boxWidth); - float centerY = floatValueForLength(circle->centerY(), boxHeight); - float radius = floatValueForLength(circle->radius(), sqrtf((boxWidth * boxWidth + boxHeight * boxHeight) / 2)); - FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); - - shape = createCircleShape(logicalCenter, radius); - break; - } + switch (basicShape.type()) { case BasicShape::BasicShapeCircleType: { - const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape); - float centerX = floatValueForCenterCoordinate(circle->centerX(), boxWidth); - float centerY = floatValueForCenterCoordinate(circle->centerY(), boxHeight); - float radius = circle->floatValueForRadiusInBox(boxWidth, boxHeight); + const auto& circle = downcast<BasicShapeCircle>(basicShape); + float centerX = floatValueForCenterCoordinate(circle.centerX(), boxWidth); + float centerY = floatValueForCenterCoordinate(circle.centerY(), boxHeight); + float radius = circle.floatValueForRadiusInBox(boxWidth, boxHeight); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); shape = createCircleShape(logicalCenter, radius); break; } - case BasicShape::DeprecatedBasicShapeEllipseType: { - const DeprecatedBasicShapeEllipse* ellipse = static_cast<const DeprecatedBasicShapeEllipse*>(basicShape); - float centerX = floatValueForLength(ellipse->centerX(), boxWidth); - float centerY = floatValueForLength(ellipse->centerY(), boxHeight); - float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth); - float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight); - FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); - FloatSize logicalRadii = physicalSizeToLogical(FloatSize(radiusX, radiusY), writingMode); - - shape = createEllipseShape(logicalCenter, logicalRadii); - break; - } - case BasicShape::BasicShapeEllipseType: { - const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape); - float centerX = floatValueForCenterCoordinate(ellipse->centerX(), boxWidth); - float centerY = floatValueForCenterCoordinate(ellipse->centerY(), boxHeight); - float radiusX = ellipse->floatValueForRadiusInBox(ellipse->radiusX(), centerX, boxWidth); - float radiusY = ellipse->floatValueForRadiusInBox(ellipse->radiusY(), centerY, boxHeight); + const auto& ellipse = downcast<BasicShapeEllipse>(basicShape); + float centerX = floatValueForCenterCoordinate(ellipse.centerX(), boxWidth); + float centerY = floatValueForCenterCoordinate(ellipse.centerY(), boxHeight); + float radiusX = ellipse.floatValueForRadiusInBox(ellipse.radiusX(), centerX, boxWidth); + float radiusY = ellipse.floatValueForRadiusInBox(ellipse.radiusY(), centerY, boxHeight); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY)); @@ -187,11 +124,11 @@ PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutS } case BasicShape::BasicShapePolygonType: { - const BasicShapePolygon& polygon = *static_cast<const BasicShapePolygon*>(basicShape); + const auto& polygon = downcast<BasicShapePolygon>(basicShape); const Vector<Length>& values = polygon.values(); size_t valuesSize = values.size(); ASSERT(!(valuesSize % 2)); - OwnPtr<Vector<FloatPoint>> vertices = adoptPtr(new Vector<FloatPoint>(valuesSize / 2)); + std::unique_ptr<Vector<FloatPoint>> vertices = std::make_unique<Vector<FloatPoint>>(valuesSize / 2); for (unsigned i = 0; i < valuesSize; i += 2) { FloatPoint vertex( floatValueForLength(values.at(i), boxWidth), @@ -199,44 +136,30 @@ PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutS (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height(), writingMode); } - shape = createPolygonShape(vertices.release(), polygon.windRule()); - break; - } - - case BasicShape::BasicShapeInsetRectangleType: { - const BasicShapeInsetRectangle& rectangle = *static_cast<const BasicShapeInsetRectangle*>(basicShape); - float left = floatValueForLength(rectangle.left(), boxWidth); - float top = floatValueForLength(rectangle.top(), boxHeight); - FloatRect bounds( - left, - top, - boxWidth - left - floatValueForLength(rectangle.right(), boxWidth), - boxHeight - top - floatValueForLength(rectangle.bottom(), boxHeight)); - FloatSize cornerRadii( - floatValueForLength(rectangle.cornerRadiusX(), boxWidth), - floatValueForLength(rectangle.cornerRadiusY(), boxHeight)); - ensureRadiiDoNotOverlap(bounds, cornerRadii); - FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode); - - shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode)); + shape = createPolygonShape(WTFMove(vertices), polygon.windRule()); break; } case BasicShape::BasicShapeInsetType: { - const BasicShapeInset& inset = *static_cast<const BasicShapeInset*>(basicShape); + const auto& inset = downcast<BasicShapeInset>(basicShape); float left = floatValueForLength(inset.left(), boxWidth); float top = floatValueForLength(inset.top(), boxHeight); FloatRect rect(left, top, - boxWidth - left - floatValueForLength(inset.right(), boxWidth), - boxHeight - top - floatValueForLength(inset.bottom(), boxHeight)); + std::max<float>(boxWidth - left - floatValueForLength(inset.right(), boxWidth), 0), + std::max<float>(boxHeight - top - floatValueForLength(inset.bottom(), boxHeight), 0)); FloatRect logicalRect = physicalRectToLogical(rect, logicalBoxSize.height(), writingMode); - shape = createInsetShape(FloatRoundedRect(logicalRect, - inset.topLeftRadius().floatSize(), - inset.topRightRadius().floatSize(), - inset.bottomLeftRadius().floatSize(), - inset.bottomRightRadius().floatSize())); + FloatSize boxSize(boxWidth, boxHeight); + FloatSize topLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topLeftRadius(), boxSize), writingMode); + FloatSize topRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topRightRadius(), boxSize), writingMode); + FloatSize bottomLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomLeftRadius(), boxSize), writingMode); + FloatSize bottomRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomRightRadius(), boxSize), writingMode); + FloatRoundedRect::Radii cornerRadii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + cornerRadii.scale(calcBorderRadiiConstraintScaleFor(logicalRect, cornerRadii)); + + shape = createInsetShape(FloatRoundedRect(logicalRect, cornerRadii)); break; } @@ -245,42 +168,48 @@ PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutS } shape->m_writingMode = writingMode; - shape->m_margin = floatValueForLength(margin, 0); - shape->m_padding = floatValueForLength(padding, 0); + shape->m_margin = margin; - return shape.release(); + return shape; } -PassOwnPtr<Shape> Shape::createRasterShape(const StyleImage& styleImage, float threshold, const LayoutRect& imageRect, const LayoutSize&, WritingMode writingMode, Length margin, Length padding) +std::unique_ptr<Shape> Shape::createRasterShape(Image* image, float threshold, const LayoutRect& imageR, const LayoutRect& marginR, WritingMode writingMode, float margin) { - ASSERT(styleImage.cachedImage()); - ASSERT(styleImage.cachedImage()->hasImage()); + ASSERT(marginR.height() >= 0); - IntRect toRect = pixelSnappedIntRect(imageRect); - IntSize bufferSize = toRect.size(); - IntSize rasterSize = IntSize(toRect.maxX(), toRect.maxY()); - OwnPtr<RasterShapeIntervals> intervals = adoptPtr(new RasterShapeIntervals(rasterSize.height())); - std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(bufferSize); + IntRect imageRect = snappedIntRect(imageR); + IntRect marginRect = snappedIntRect(marginR); + auto intervals = std::make_unique<RasterShapeIntervals>(marginRect.height(), -marginRect.y()); + // FIXME (149420): This buffer should not be unconditionally unaccelerated. + std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(imageRect.size(), Unaccelerated); if (imageBuffer) { - GraphicsContext* graphicsContext = imageBuffer->context(); - graphicsContext->drawImage(styleImage.cachedImage()->image(), ColorSpaceDeviceRGB, IntRect(IntPoint(), bufferSize)); + GraphicsContext& graphicsContext = imageBuffer->context(); + if (image) + graphicsContext.drawImage(*image, IntRect(IntPoint(), imageRect.size())); - RefPtr<Uint8ClampedArray> pixelArray = imageBuffer->getUnmultipliedImageData(IntRect(IntPoint(), bufferSize)); + RefPtr<Uint8ClampedArray> pixelArray = imageBuffer->getUnmultipliedImageData(IntRect(IntPoint(), imageRect.size())); unsigned pixelArrayLength = pixelArray->length(); unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. uint8_t alphaPixelThreshold = threshold * 255; - if (static_cast<unsigned>(bufferSize.width() * bufferSize.height() * 4) == pixelArrayLength) { // sanity check - for (int y = 0; y < bufferSize.height(); ++y) { + int minBufferY = std::max(0, marginRect.y() - imageRect.y()); + int maxBufferY = std::min(imageRect.height(), marginRect.maxY() - imageRect.y()); + + if ((imageRect.area() * 4).unsafeGet() == pixelArrayLength) { + for (int y = minBufferY; y < maxBufferY; ++y) { int startX = -1; - for (int x = 0; x < bufferSize.width(); ++x, pixelArrayOffset += 4) { + for (int x = 0; x < imageRect.width(); ++x, pixelArrayOffset += 4) { uint8_t alpha = pixelArray->item(pixelArrayOffset); bool alphaAboveThreshold = alpha > alphaPixelThreshold; if (startX == -1 && alphaAboveThreshold) { startX = x; - } else if (startX != -1 && (!alphaAboveThreshold || x == bufferSize.width() - 1)) { - intervals->appendInterval(y + toRect.y(), startX + toRect.x(), x + toRect.x()); + } else if (startX != -1 && (!alphaAboveThreshold || x == imageRect.width() - 1)) { + // We're creating "end-point exclusive" intervals here. The value of an interval's x1 is + // the first index of an above-threshold pixel for y, and the value of x2 is 1+ the index + // of the last above-threshold pixel. + int endX = alphaAboveThreshold ? x + 1 : x; + intervals->intervalAt(y + imageRect.y()).unite(IntShapeInterval(startX + imageRect.x(), endX + imageRect.x())); startX = -1; } } @@ -288,25 +217,23 @@ PassOwnPtr<Shape> Shape::createRasterShape(const StyleImage& styleImage, float t } } - OwnPtr<RasterShape> rasterShape = adoptPtr(new RasterShape(intervals.release(), rasterSize)); + auto rasterShape = std::make_unique<RasterShape>(WTFMove(intervals), marginRect.size()); rasterShape->m_writingMode = writingMode; - rasterShape->m_margin = floatValueForLength(margin, 0); - rasterShape->m_padding = floatValueForLength(padding, 0); - return rasterShape.release(); + rasterShape->m_margin = margin; + return WTFMove(rasterShape); } -PassOwnPtr<Shape> Shape::createLayoutBoxShape(const RoundedRect& roundedRect, WritingMode writingMode, Length margin, Length padding) +std::unique_ptr<Shape> Shape::createBoxShape(const RoundedRect& roundedRect, WritingMode writingMode, float margin) { ASSERT(roundedRect.rect().width() >= 0 && roundedRect.rect().height() >= 0); FloatRect rect(0, 0, roundedRect.rect().width(), roundedRect.rect().height()); FloatRoundedRect bounds(rect, roundedRect.radii()); - OwnPtr<Shape> shape = adoptPtr(new BoxShape(bounds)); + auto shape = std::make_unique<BoxShape>(bounds); shape->m_writingMode = writingMode; - shape->m_margin = floatValueForLength(margin, 0); - shape->m_padding = floatValueForLength(padding, 0); + shape->m_margin = margin; - return shape.release(); + return WTFMove(shape); } } // namespace WebCore diff --git a/Source/WebCore/rendering/shapes/Shape.h b/Source/WebCore/rendering/shapes/Shape.h index ab71d5568..f810ff5ee 100644 --- a/Source/WebCore/rendering/shapes/Shape.h +++ b/Source/WebCore/rendering/shapes/Shape.h @@ -12,7 +12,7 @@ * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS @@ -27,33 +27,37 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Shape_h -#define Shape_h +#pragma once -#include "BasicShapes.h" #include "LayoutRect.h" #include "Path.h" -#include "RoundedRect.h" -#include "StyleImage.h" #include "WritingMode.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> namespace WebCore { struct LineSegment { + LineSegment() + : logicalLeft(0) + , logicalRight(0) + , isValid(false) + { + } + LineSegment(float logicalLeft, float logicalRight) : logicalLeft(logicalLeft) , logicalRight(logicalRight) + , isValid(true) { } - LayoutUnit logicalLeft; - LayoutUnit logicalRight; + float logicalLeft; + float logicalRight; + bool isValid; }; -typedef Vector<LineSegment> SegmentList; - +class BasicShape; +class Image; +class RoundedRect; // A representation of a BasicShape that enables layout code to determine how to break a line up into segments // that will fit within or around a shape. The line is defined by a pair of logical Y coordinates and the @@ -61,32 +65,29 @@ typedef Vector<LineSegment> SegmentList; // physical coordinates. class Shape { + WTF_MAKE_FAST_ALLOCATED; public: struct DisplayPaths { Path shape; Path marginShape; }; - static PassOwnPtr<Shape> createShape(const BasicShape*, const LayoutSize& logicalBoxSize, WritingMode, Length margin, Length padding); - static PassOwnPtr<Shape> createRasterShape(const StyleImage&, float threshold, const LayoutRect& imageRect, const LayoutSize& logicalBoxSize, WritingMode, Length margin, Length padding); - static PassOwnPtr<Shape> createLayoutBoxShape(const RoundedRect&, WritingMode, Length margin, Length padding); + static std::unique_ptr<Shape> createShape(const BasicShape&, const LayoutSize& logicalBoxSize, WritingMode, float margin); + static std::unique_ptr<Shape> createRasterShape(Image*, float threshold, const LayoutRect& imageRect, const LayoutRect& marginRect, WritingMode, float margin); + static std::unique_ptr<Shape> createBoxShape(const RoundedRect&, WritingMode, float margin); virtual ~Shape() { } virtual LayoutRect shapeMarginLogicalBoundingBox() const = 0; - virtual LayoutRect shapePaddingLogicalBoundingBox() const = 0; virtual bool isEmpty() const = 0; - virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const = 0; - virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const = 0; - virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, LayoutUnit& result) const = 0; + virtual LineSegment getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const = 0; + bool lineOverlapsShapeMarginBounds(LayoutUnit lineTop, LayoutUnit lineHeight) const { return lineOverlapsBoundingBox(lineTop, lineHeight, shapeMarginLogicalBoundingBox()); } - bool lineOverlapsShapePaddingBounds(LayoutUnit lineTop, LayoutUnit lineHeight) const { return lineOverlapsBoundingBox(lineTop, lineHeight, shapePaddingLogicalBoundingBox()); } virtual void buildDisplayPaths(DisplayPaths&) const = 0; protected: float shapeMargin() const { return m_margin; } - float shapePadding() const { return m_padding; } private: bool lineOverlapsBoundingBox(LayoutUnit lineTop, LayoutUnit lineHeight, const LayoutRect& rect) const @@ -98,9 +99,6 @@ private: WritingMode m_writingMode; float m_margin; - float m_padding; }; } // namespace WebCore - -#endif // Shape_h diff --git a/Source/WebCore/rendering/shapes/ShapeInfo.cpp b/Source/WebCore/rendering/shapes/ShapeInfo.cpp deleted file mode 100644 index 5e2af92ad..000000000 --- a/Source/WebCore/rendering/shapes/ShapeInfo.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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 "config.h" -#include "ShapeInfo.h" - -#if ENABLE(CSS_SHAPES) - -#include "LengthFunctions.h" -#include "RenderBlock.h" -#include "RenderBox.h" -#include "RenderImage.h" -#include "RenderRegion.h" -#include "RenderStyle.h" -#include "Shape.h" - -namespace WebCore { - - -bool checkShapeImageOrigin(Document& document, CachedImage& cachedImage) -{ - if (cachedImage.isOriginClean(document.securityOrigin())) - return true; - - const URL& url = cachedImage.url(); - String urlString = url.isNull() ? "''" : url.stringCenterEllipsizedToLength(); - document.addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Unsafe attempt to load URL " + urlString + "."); - - return false; -} - -static LayoutRect getShapeImageRect(const StyleImage& styleImage, const RenderBox& renderBox) -{ - if (renderBox.isRenderImage()) { - const RenderImage& renderImage = *toRenderImage(&renderBox); - return renderImage.replacedContentRect(renderBox.intrinsicSize()); - } - - ASSERT(styleImage.cachedImage()); - ASSERT(styleImage.cachedImage()->hasImage()); - return LayoutRect(LayoutPoint(), styleImage.cachedImage()->image()->size()); -} - -template<class RenderType> -const Shape& ShapeInfo<RenderType>::computedShape() const -{ - if (Shape* shape = m_shape.get()) - return *shape; - - WritingMode writingMode = this->writingMode(); - Length margin = m_renderer.style().shapeMargin(); - Length padding = m_renderer.style().shapePadding(); - float shapeImageThreshold = m_renderer.style().shapeImageThreshold(); - const ShapeValue* shapeValue = this->shapeValue(); - ASSERT(shapeValue); - - switch (shapeValue->type()) { - case ShapeValue::Shape: - ASSERT(shapeValue->shape()); - m_shape = Shape::createShape(shapeValue->shape(), m_shapeLogicalSize, writingMode, margin, padding); - break; - case ShapeValue::Image: { - ASSERT(shapeValue->image()); - const StyleImage& styleImage = *(shapeValue->image()); - m_shape = Shape::createRasterShape(styleImage, shapeImageThreshold, getShapeImageRect(styleImage, m_renderer), m_shapeLogicalSize, writingMode, margin, padding); - break; - } - case ShapeValue::Box: { - const RoundedRect& shapeRect = m_renderer.style().getRoundedBorderFor(LayoutRect(LayoutPoint(), m_shapeLogicalSize), &(m_renderer.view())); - m_shape = Shape::createLayoutBoxShape(shapeRect, writingMode, margin, padding); - break; - } - case ShapeValue::Outside: - // Outside should have already resolved to a different shape value - ASSERT_NOT_REACHED(); - } - - ASSERT(m_shape); - return *m_shape; -} - -template<class RenderType> -SegmentList ShapeInfo<RenderType>::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) const -{ - ASSERT(lineHeight >= 0); - SegmentList segments; - - getIntervals((lineTop - logicalTopOffset()), std::min(lineHeight, shapeLogicalBottom() - lineTop), segments); - - for (size_t i = 0; i < segments.size(); i++) { - segments[i].logicalLeft += logicalLeftOffset(); - segments[i].logicalRight += logicalLeftOffset(); - } - - return segments; -} - -template class ShapeInfo<RenderBlock>; -template class ShapeInfo<RenderBox>; - -} -#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInfo.h b/Source/WebCore/rendering/shapes/ShapeInfo.h deleted file mode 100644 index f571f242e..000000000 --- a/Source/WebCore/rendering/shapes/ShapeInfo.h +++ /dev/null @@ -1,217 +0,0 @@ -/* -* Copyright (C) 2012 Adobe Systems Incorporated. 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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. -*/ - -#ifndef ShapeInfo_h -#define ShapeInfo_h - -#if ENABLE(CSS_SHAPES) - -#include "FloatRect.h" -#include "LayoutUnit.h" -#include "RenderStyle.h" -#include "Shape.h" -#include "ShapeValue.h" -#include <wtf/OwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -template<class KeyType, class InfoType> -class MappedInfo { -public: - static InfoType& ensureInfo(const KeyType& key) - { - InfoMap& infoMap = MappedInfo<KeyType, InfoType>::infoMap(); - if (InfoType* info = infoMap.get(&key)) - return *info; - typename InfoMap::AddResult result = infoMap.add(&key, std::make_unique<InfoType>(key)); - return *result.iterator->value; - } - static void removeInfo(const KeyType& key) { infoMap().remove(&key); } - static InfoType* info(const KeyType& key) { return infoMap().get(&key); } - -private: - typedef HashMap<const KeyType*, std::unique_ptr<InfoType>> InfoMap; - static InfoMap& infoMap() - { - DEFINE_STATIC_LOCAL(InfoMap, staticInfoMap, ()); - return staticInfoMap; - } -}; - -template<class RenderType> -class ShapeInfo { - WTF_MAKE_FAST_ALLOCATED; -public: - virtual ~ShapeInfo() { } - - void setShapeSize(LayoutUnit logicalWidth, LayoutUnit logicalHeight) - { - LayoutBox box = resolvedLayoutBox(); - switch (box) { - case MarginBox: - logicalHeight += m_renderer.marginLogicalHeight(); - logicalWidth += m_renderer.marginLogicalWidth(); - break; - case BorderBox: - break; - case PaddingBox: - logicalHeight -= m_renderer.borderLogicalHeight(); - logicalWidth -= m_renderer.borderLogicalWidth(); - break; - case ContentBox: - logicalHeight -= m_renderer.borderAndPaddingLogicalHeight(); - logicalWidth -= m_renderer.borderAndPaddingLogicalWidth(); - break; - case BoundingBox: - case BoxMissing: - ASSERT_NOT_REACHED(); - break; - } - - LayoutSize newLogicalSize(logicalWidth, logicalHeight); - if (m_shapeLogicalSize == newLogicalSize) - return; - dirtyShapeSize(); - m_shapeLogicalSize = newLogicalSize; - } - - SegmentList computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) const; - - LayoutUnit shapeLogicalTop() const { return computedShapeLogicalBoundingBox().y() + logicalTopOffset(); } - LayoutUnit shapeLogicalBottom() const { return computedShapeLogicalBoundingBox().maxY() + logicalTopOffset(); } - LayoutUnit shapeLogicalLeft() const { return computedShapeLogicalBoundingBox().x() + logicalLeftOffset(); } - LayoutUnit shapeLogicalRight() const { return computedShapeLogicalBoundingBox().maxX() + logicalLeftOffset(); } - LayoutUnit shapeLogicalWidth() const { return computedShapeLogicalBoundingBox().width(); } - LayoutUnit shapeLogicalHeight() const { return computedShapeLogicalBoundingBox().height(); } - - LayoutUnit logicalLineTop() const { return m_shapeLineTop + logicalTopOffset(); } - LayoutUnit logicalLineBottom() const { return m_shapeLineTop + m_lineHeight + logicalTopOffset(); } - LayoutUnit logicalLineBottom(LayoutUnit lineHeight) const { return m_shapeLineTop + lineHeight + logicalTopOffset(); } - - LayoutUnit shapeContainingBlockLogicalHeight() const { return (m_renderer.style().boxSizing() == CONTENT_BOX) ? (m_shapeLogicalSize.height() + m_renderer.borderAndPaddingLogicalHeight()) : m_shapeLogicalSize.height(); } - - virtual bool lineOverlapsShapeBounds() const = 0; - - void dirtyShapeSize() { m_shape.clear(); } - bool shapeSizeDirty() { return !m_shape.get(); } - const RenderType& owner() const { return m_renderer; } - LayoutSize shapeSize() const { return m_shapeLogicalSize; } - - LayoutRect computedShapePhysicalBoundingBox() const - { - LayoutRect physicalBoundingBox = computedShapeLogicalBoundingBox(); - physicalBoundingBox.setX(physicalBoundingBox.x() + logicalLeftOffset()); - physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset()); - if (m_renderer.style().isFlippedBlocksWritingMode()) - physicalBoundingBox.setY(m_renderer.logicalHeight() - physicalBoundingBox.maxY()); - if (!m_renderer.style().isHorizontalWritingMode()) - physicalBoundingBox = physicalBoundingBox.transposedRect(); - return physicalBoundingBox; - } - - FloatPoint shapeToRendererPoint(FloatPoint point) const - { - FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset()); - if (m_renderer.style().isFlippedBlocksWritingMode()) - result.setY(m_renderer.logicalHeight() - result.y()); - if (!m_renderer.style().isHorizontalWritingMode()) - result = result.transposedPoint(); - return result; - } - - FloatSize shapeToRendererSize(FloatSize size) const - { - if (!m_renderer.style().isHorizontalWritingMode()) - return size.transposedSize(); - return size; - } - - const Shape& computedShape() const; - -protected: - explicit ShapeInfo(const RenderType& renderer) - : m_renderer(renderer) - { - } - - virtual LayoutBox resolvedLayoutBox() const = 0; - virtual LayoutRect computedShapeLogicalBoundingBox() const = 0; - virtual ShapeValue* shapeValue() const = 0; - virtual void getIntervals(LayoutUnit, LayoutUnit, SegmentList&) const = 0; - - virtual WritingMode writingMode() const { return m_renderer.style().writingMode(); } - - LayoutUnit logicalTopOffset() const - { - LayoutBox box = resolvedLayoutBox(); - switch (box) { - case MarginBox: return -m_renderer.marginBefore(); - case BorderBox: return LayoutUnit(); - case PaddingBox: return m_renderer.borderBefore(); - case ContentBox: return m_renderer.borderAndPaddingBefore(); - case BoundingBox: break; - case BoxMissing: break; - } - ASSERT_NOT_REACHED(); - return LayoutUnit(); - } - - LayoutUnit logicalLeftOffset() const - { - if (m_renderer.isRenderRegion()) - return LayoutUnit(); - LayoutBox box = resolvedLayoutBox(); - switch (box) { - case MarginBox: return -m_renderer.marginStart(); - case BorderBox: return LayoutUnit(); - case PaddingBox: return m_renderer.borderStart(); - case ContentBox: return m_renderer.borderAndPaddingStart(); - case BoundingBox: break; - case BoxMissing: break; - } - ASSERT_NOT_REACHED(); - return LayoutUnit(); - } - - LayoutUnit m_shapeLineTop; - LayoutUnit m_lineHeight; - - const RenderType& m_renderer; - -private: - mutable OwnPtr<Shape> m_shape; - LayoutSize m_shapeLogicalSize; -}; - -bool checkShapeImageOrigin(Document&, CachedImage&); - -} -#endif -#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp b/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp deleted file mode 100644 index ff7e22bd9..000000000 --- a/Source/WebCore/rendering/shapes/ShapeInsideInfo.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. 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 THE COPYRIGHT HOLDER “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 THE COPYRIGHT HOLDER 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 "config.h" -#include "ShapeInsideInfo.h" - -#if ENABLE(CSS_SHAPES) - -#include "InlineIterator.h" -#include "RenderBlock.h" - -namespace WebCore { - -LineSegmentRange::LineSegmentRange(const InlineIterator& start, const InlineIterator& end) - : start(start.root(), start.renderer(), start.offset()) - , end(end.root(), end.renderer(), end.offset()) - { - } - -bool ShapeInsideInfo::isEnabledFor(const RenderBlock& renderer) -{ - ShapeValue* shapeValue = renderer.style().resolvedShapeInside(); - if (!shapeValue) - return false; - - switch (shapeValue->type()) { - case ShapeValue::Shape: - return shapeValue->shape() && shapeValue->shape()->type() != BasicShape::BasicShapeInsetRectangleType && shapeValue->shape()->type() != BasicShape::BasicShapeInsetType; - case ShapeValue::Image: - return shapeValue->isImageValid() && checkShapeImageOrigin(renderer.document(), *(shapeValue->image()->cachedImage())); - case ShapeValue::Box: - return true; - case ShapeValue::Outside: - // Outside value must already be resolved - break; - } - ASSERT_NOT_REACHED(); - return false; -} - -bool ShapeInsideInfo::updateSegmentsForLine(LayoutSize lineOffset, LayoutUnit lineHeight) -{ - m_segmentRanges.clear(); - bool result = updateSegmentsForLine(lineOffset.height(), lineHeight); - for (size_t i = 0; i < m_segments.size(); i++) { - m_segments[i].logicalLeft -= lineOffset.width(); - m_segments[i].logicalRight -= lineOffset.width(); - } - return result; -} - -bool ShapeInsideInfo::updateSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight) -{ - ASSERT(lineHeight >= 0); - m_shapeLineTop = lineTop - logicalTopOffset(); - m_lineHeight = lineHeight; - m_segments.clear(); - m_segmentRanges.clear(); - - if (lineOverlapsShapeBounds()) - m_segments = computeSegmentsForLine(lineTop, lineHeight); - - return m_segments.size(); -} - -bool ShapeInsideInfo::adjustLogicalLineTop(float minSegmentWidth) -{ - const Shape& shape = computedShape(); - if (m_lineHeight <= 0 || logicalLineTop() > shapeLogicalBottom()) - return false; - - LayoutUnit newLineTop; - if (shape.firstIncludedIntervalLogicalTop(m_shapeLineTop, FloatSize(minSegmentWidth, m_lineHeight), newLineTop)) { - if (newLineTop > m_shapeLineTop) { - m_shapeLineTop = newLineTop; - return true; - } - } - - return false; -} - -ShapeValue* ShapeInsideInfo::shapeValue() const -{ - return m_renderer.style().resolvedShapeInside(); -} - -LayoutUnit ShapeInsideInfo::computeFirstFitPositionForFloat(const FloatSize floatSize) const -{ - if (!floatSize.width() || shapeLogicalBottom() < logicalLineTop()) - return 0; - - LayoutUnit firstFitPosition = 0; - if (computedShape().firstIncludedIntervalLogicalTop(m_shapeLineTop, floatSize, firstFitPosition) && (m_shapeLineTop <= firstFitPosition)) - return firstFitPosition; - - return 0; -} - -} -#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInsideInfo.h b/Source/WebCore/rendering/shapes/ShapeInsideInfo.h deleted file mode 100644 index 77f4afa22..000000000 --- a/Source/WebCore/rendering/shapes/ShapeInsideInfo.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2012 Adobe Systems Incorporated. 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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. - */ - -#ifndef ShapeInsideInfo_h -#define ShapeInsideInfo_h - -#if ENABLE(CSS_SHAPES) - -#include "ShapeInfo.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class InlineIterator; -class RenderBlock; -class RenderElement; -class RenderObject; - -struct LineSegmentIterator { - RenderElement* root; - RenderObject* object; - unsigned offset; - LineSegmentIterator(RenderElement* root, RenderObject* object, unsigned offset) - : root(root) - , object(object) - , offset(offset) - { - } -}; - -struct LineSegmentRange { - LineSegmentIterator start; - LineSegmentIterator end; - LineSegmentRange(const InlineIterator& start, const InlineIterator& end); -}; - -typedef Vector<LineSegmentRange> SegmentRangeList; - -class ShapeInsideInfo final : public ShapeInfo<RenderBlock> { -public: - ShapeInsideInfo(const RenderBlock& renderer) - : ShapeInfo<RenderBlock>(renderer) - , m_needsLayout(false) - { - } - - static bool isEnabledFor(const RenderBlock& renderer); - - bool updateSegmentsForLine(LayoutSize lineOffset, LayoutUnit lineHeight); - bool updateSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight); - - bool hasSegments() const - { - return lineOverlapsShapeBounds() && m_segments.size(); - } - const SegmentList& segments() const - { - ASSERT(hasSegments()); - return m_segments; - } - SegmentRangeList& segmentRanges() { return m_segmentRanges; } - const SegmentRangeList& segmentRanges() const { return m_segmentRanges; } - const LineSegment* currentSegment() const - { - if (!hasSegments()) - return 0; - ASSERT(m_segmentRanges.size() < m_segments.size()); - return &m_segments[m_segmentRanges.size()]; - } - void clearSegments() { m_segments.clear(); } - bool adjustLogicalLineTop(float minSegmentWidth); - LayoutUnit computeFirstFitPositionForFloat(const FloatSize) const; - - void setNeedsLayout(bool value) { m_needsLayout = value; } - bool needsLayout() { return m_needsLayout; } - - virtual bool lineOverlapsShapeBounds() const override - { - return computedShape().lineOverlapsShapePaddingBounds(m_shapeLineTop, m_lineHeight); - } - -protected: - virtual LayoutBox resolvedLayoutBox() const override - { - if (shapeValue()->layoutBox() == BoxMissing) - return ContentBox; - - return shapeValue()->layoutBox(); - } - -private: - virtual LayoutRect computedShapeLogicalBoundingBox() const override { return computedShape().shapePaddingLogicalBoundingBox(); } - virtual ShapeValue* shapeValue() const override; - virtual void getIntervals(LayoutUnit lineTop, LayoutUnit lineHeight, SegmentList& segments) const override - { - return computedShape().getIncludedIntervals(lineTop, lineHeight, segments); - } - - SegmentRangeList m_segmentRanges; - bool m_needsLayout:1; - SegmentList m_segments; -}; - -} -#endif -#endif diff --git a/Source/WebCore/rendering/shapes/ShapeInterval.h b/Source/WebCore/rendering/shapes/ShapeInterval.h index 476f0ce79..6cc4dab49 100644 --- a/Source/WebCore/rendering/shapes/ShapeInterval.h +++ b/Source/WebCore/rendering/shapes/ShapeInterval.h @@ -27,8 +27,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ShapeInterval_h -#define ShapeInterval_h +#pragma once #include <wtf/Vector.h> @@ -38,29 +37,27 @@ template <typename T> class ShapeInterval { WTF_MAKE_FAST_ALLOCATED; public: - ShapeInterval(T x1 = 0, T x2 = 0) - : m_x1(x1) - , m_x2(x2) + ShapeInterval() + : m_x1(-1) + , m_x2(-2) { - ASSERT(x2 >= x1); + // The initial values of m_x1,x2 don't matter (unless you're looking + // at them in the debugger) so long as isUndefined() is true. + ASSERT(isUndefined()); } - T x1() const { return m_x1; } - T x2() const { return m_x2; } - T width() const { return m_x2 - m_x1; } - bool isEmpty() const { return m_x1 == m_x2; } - - void setX1(T x1) + ShapeInterval(T x1, T x2) + : m_x1(x1) + , m_x2(x2) { - ASSERT(m_x2 >= x1); - m_x1 = x1; + ASSERT(x2 >= x1); } - void setX2(T x2) - { - ASSERT(x2 >= m_x1); - m_x2 = x2; - } + bool isUndefined() const { return m_x2 < m_x1; } + T x1() const { return isUndefined() ? 0 : m_x1; } + T x2() const { return isUndefined() ? 0 : m_x2; } + T width() const { return isUndefined() ? 0 : m_x2 - m_x1; } + bool isEmpty() const { return isUndefined() ? true : m_x1 == m_x2; } void set(T x1, T x2) { @@ -71,135 +68,26 @@ public: bool overlaps(const ShapeInterval<T>& interval) const { + if (isUndefined() || interval.isUndefined()) + return false; return x2() >= interval.x1() && x1() <= interval.x2(); } bool contains(const ShapeInterval<T>& interval) const { + if (isUndefined() || interval.isUndefined()) + return false; return x1() <= interval.x1() && x2() >= interval.x2(); } - ShapeInterval<T> intersect(const ShapeInterval<T>& interval) const - { - ASSERT(overlaps(interval)); - return ShapeInterval<T>(std::max<T>(x1(), interval.x1()), std::min<T>(x2(), interval.x2())); - } - - typedef Vector<ShapeInterval<T>> ShapeIntervals; - typedef typename ShapeIntervals::const_iterator const_iterator; - typedef typename ShapeIntervals::iterator iterator; - - static void uniteShapeIntervals(const ShapeIntervals& a, const ShapeIntervals& b, ShapeIntervals& result) + void unite(const ShapeInterval<T>& interval) { - ASSERT(shapeIntervalsAreSortedAndDisjoint(a) && shapeIntervalsAreSortedAndDisjoint(b)); - - if (a.isEmpty() || a == b) { - result.appendRange(b.begin(), b.end()); - return; - } - - if (b.isEmpty()) { - result.appendRange(a.begin(), a.end()); + if (interval.isUndefined()) return; - } - - const_iterator aNext = a.begin(); - const_iterator bNext = b.begin(); - - while (aNext != a.end() || bNext != b.end()) { - const_iterator next = (bNext == b.end() || (aNext != a.end() && aNext->x1() < bNext->x1())) ? aNext++ : bNext++; - if (result.isEmpty() || !result.last().overlaps(*next)) - result.append(*next); - else - result.last().setX2(std::max<T>(result.last().x2(), next->x2())); - } - } - - static void intersectShapeIntervals(const ShapeIntervals& a, const ShapeIntervals& b, ShapeIntervals& result) - { - ASSERT(shapeIntervalsAreSortedAndDisjoint(a) && shapeIntervalsAreSortedAndDisjoint(b)); - - if (a.isEmpty() || b.isEmpty()) - return; - - if (a == b) { - result.appendRange(a.begin(), a.end()); - return; - } - - const_iterator aNext = a.begin(); - const_iterator bNext = b.begin(); - const_iterator working = aNext->x1() < bNext->x1() ? aNext++ : bNext++; - - while (aNext != a.end() || bNext != b.end()) { - const_iterator next = (bNext == b.end() || (aNext != a.end() && aNext->x1() < bNext->x1())) ? aNext++ : bNext++; - if (working->overlaps(*next)) { - result.append(working->intersect(*next)); - if (next->x2() > working->x2()) - working = next; - } else - working = next; - } - } - - static void subtractShapeIntervals(const ShapeIntervals& a, const ShapeIntervals& b, ShapeIntervals& result) - { - ASSERT(shapeIntervalsAreSortedAndDisjoint(a) && shapeIntervalsAreSortedAndDisjoint(b)); - - if (a.isEmpty() || a == b) - return; - - if (b.isEmpty()) { - result.appendRange(a.begin(), a.end()); - return; - } - - const_iterator aNext = a.begin(); - const_iterator bNext = b.begin(); - ShapeInterval<T> aValue = *aNext; - ShapeInterval<T> bValue = *bNext; - - do { - bool aIncrement = false; - bool bIncrement = false; - - if (bValue.contains(aValue)) { - aIncrement = true; - } else if (aValue.contains(bValue)) { - if (bValue.x1() > aValue.x1()) - result.append(ShapeInterval<T>(aValue.x1(), bValue.x1())); - if (aValue.x2() > bValue.x2()) - aValue.setX1(bValue.x2()); - else - aIncrement = true; - bIncrement = true; - } else if (aValue.overlaps(bValue)) { - if (aValue.x1() < bValue.x1()) { - result.append(ShapeInterval<T>(aValue.x1(), bValue.x1())); - aIncrement = true; - } else { - aValue.setX1(bValue.x2()); - bIncrement = true; - } - } else { - if (aValue.x1() < bValue.x1()) { - result.append(aValue); - aIncrement = true; - } else - bIncrement = true; - } - - if (aIncrement && ++aNext != a.end()) - aValue = *aNext; - if (bIncrement && ++bNext != b.end()) - bValue = *bNext; - - } while (aNext != a.end() && bNext != b.end()); - - if (aNext != a.end()) { - result.append(aValue); - result.appendRange(++aNext, a.end()); - } + if (isUndefined()) + set(interval.x1(), interval.x2()); + else + set(std::min<T>(x1(), interval.x1()), std::max<T>(x2(), interval.x2())); } bool operator==(const ShapeInterval<T>& other) const { return x1() == other.x1() && x2() == other.x2(); } @@ -208,15 +96,6 @@ public: private: T m_x1; T m_x2; - - static bool shapeIntervalsAreSortedAndDisjoint(const ShapeIntervals& intervals) - { - for (unsigned i = 1; i < intervals.size(); i++) - if (intervals[i - 1].x2() > intervals[i].x1()) - return false; - - return true; - } }; typedef ShapeInterval<int> IntShapeInterval; @@ -226,5 +105,3 @@ typedef Vector<IntShapeInterval> IntShapeIntervals; typedef Vector<FloatShapeInterval> FloatShapeIntervals; } // namespace WebCore - -#endif // ShapeInterval_h diff --git a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp index dcfa2bf33..9ce65155c 100644 --- a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp +++ b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.cpp @@ -29,16 +29,263 @@ #include "config.h" -#if ENABLE(CSS_SHAPES) - #include "ShapeOutsideInfo.h" +#include "BoxShape.h" #include "FloatingObjects.h" +#include "LengthFunctions.h" #include "RenderBlockFlow.h" #include "RenderBox.h" +#include "RenderImage.h" +#include "RenderRegion.h" namespace WebCore { +LayoutRect ShapeOutsideInfo::computedShapePhysicalBoundingBox() const +{ + LayoutRect physicalBoundingBox = computedShape().shapeMarginLogicalBoundingBox(); + physicalBoundingBox.setX(physicalBoundingBox.x() + logicalLeftOffset()); + physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset()); + if (m_renderer.style().isFlippedBlocksWritingMode()) + physicalBoundingBox.setY(m_renderer.logicalHeight() - physicalBoundingBox.maxY()); + if (!m_renderer.style().isHorizontalWritingMode()) + physicalBoundingBox = physicalBoundingBox.transposedRect(); + return physicalBoundingBox; +} + +FloatPoint ShapeOutsideInfo::shapeToRendererPoint(const FloatPoint& point) const +{ + FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset()); + if (m_renderer.style().isFlippedBlocksWritingMode()) + result.setY(m_renderer.logicalHeight() - result.y()); + if (!m_renderer.style().isHorizontalWritingMode()) + result = result.transposedPoint(); + return result; +} + +FloatSize ShapeOutsideInfo::shapeToRendererSize(const FloatSize& size) const +{ + if (!m_renderer.style().isHorizontalWritingMode()) + return size.transposedSize(); + return size; +} + +static inline CSSBoxType referenceBox(const ShapeValue& shapeValue) +{ + if (shapeValue.cssBox() == BoxMissing) { + if (shapeValue.type() == ShapeValue::Type::Image) + return ContentBox; + return MarginBox; + } + return shapeValue.cssBox(); +} + +void ShapeOutsideInfo::setReferenceBoxLogicalSize(LayoutSize newReferenceBoxLogicalSize) +{ + bool isHorizontalWritingMode = m_renderer.containingBlock()->style().isHorizontalWritingMode(); + switch (referenceBox(*m_renderer.style().shapeOutside())) { + case MarginBox: + if (isHorizontalWritingMode) + newReferenceBoxLogicalSize.expand(m_renderer.horizontalMarginExtent(), m_renderer.verticalMarginExtent()); + else + newReferenceBoxLogicalSize.expand(m_renderer.verticalMarginExtent(), m_renderer.horizontalMarginExtent()); + break; + case BorderBox: + break; + case PaddingBox: + if (isHorizontalWritingMode) + newReferenceBoxLogicalSize.shrink(m_renderer.horizontalBorderExtent(), m_renderer.verticalBorderExtent()); + else + newReferenceBoxLogicalSize.shrink(m_renderer.verticalBorderExtent(), m_renderer.horizontalBorderExtent()); + break; + case ContentBox: + if (isHorizontalWritingMode) + newReferenceBoxLogicalSize.shrink(m_renderer.horizontalBorderAndPaddingExtent(), m_renderer.verticalBorderAndPaddingExtent()); + else + newReferenceBoxLogicalSize.shrink(m_renderer.verticalBorderAndPaddingExtent(), m_renderer.horizontalBorderAndPaddingExtent()); + break; + case Fill: + case Stroke: + case ViewBox: + case BoxMissing: + ASSERT_NOT_REACHED(); + break; + } + + if (m_referenceBoxLogicalSize == newReferenceBoxLogicalSize) + return; + markShapeAsDirty(); + m_referenceBoxLogicalSize = newReferenceBoxLogicalSize; +} + +static inline bool checkShapeImageOrigin(Document& document, const StyleImage& styleImage) +{ + if (styleImage.isGeneratedImage()) + return true; + + ASSERT(styleImage.cachedImage()); + CachedImage& cachedImage = *(styleImage.cachedImage()); + if (cachedImage.isOriginClean(&document.securityOrigin())) + return true; + + const URL& url = cachedImage.url(); + String urlString = url.isNull() ? "''" : url.stringCenterEllipsizedToLength(); + document.addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Unsafe attempt to load URL " + urlString + "."); + + return false; +} + +static LayoutRect getShapeImageMarginRect(const RenderBox& renderBox, const LayoutSize& referenceBoxLogicalSize) +{ + LayoutPoint marginBoxOrigin(-renderBox.marginLogicalLeft() - renderBox.borderAndPaddingLogicalLeft(), -renderBox.marginBefore() - renderBox.borderBefore() - renderBox.paddingBefore()); + LayoutSize marginBoxSizeDelta(renderBox.marginLogicalWidth() + renderBox.borderAndPaddingLogicalWidth(), renderBox.marginLogicalHeight() + renderBox.borderAndPaddingLogicalHeight()); + LayoutSize marginRectSize(referenceBoxLogicalSize + marginBoxSizeDelta); + marginRectSize.clampNegativeToZero(); + return LayoutRect(marginBoxOrigin, marginRectSize); +} + +std::unique_ptr<Shape> ShapeOutsideInfo::createShapeForImage(StyleImage* styleImage, float shapeImageThreshold, WritingMode writingMode, float margin) const +{ + LayoutSize imageSize = m_renderer.calculateImageIntrinsicDimensions(styleImage, m_referenceBoxLogicalSize, RenderImage::ScaleByEffectiveZoom); + styleImage->setContainerSizeForRenderer(&m_renderer, imageSize, m_renderer.style().effectiveZoom()); + + const LayoutRect& marginRect = getShapeImageMarginRect(m_renderer, m_referenceBoxLogicalSize); + const LayoutRect& imageRect = is<RenderImage>(m_renderer) + ? downcast<RenderImage>(m_renderer).replacedContentRect(m_renderer.intrinsicSize()) + : LayoutRect(LayoutPoint(), imageSize); + + ASSERT(!styleImage->isPending()); + RefPtr<Image> image = styleImage->image(const_cast<RenderBox*>(&m_renderer), imageSize); + return Shape::createRasterShape(image.get(), shapeImageThreshold, imageRect, marginRect, writingMode, margin); +} + +const Shape& ShapeOutsideInfo::computedShape() const +{ + if (Shape* shape = m_shape.get()) + return *shape; + + const RenderStyle& style = m_renderer.style(); + ASSERT(m_renderer.containingBlock()); + const RenderStyle& containingBlockStyle = m_renderer.containingBlock()->style(); + + WritingMode writingMode = containingBlockStyle.writingMode(); + float margin = floatValueForLength(m_renderer.style().shapeMargin(), m_renderer.containingBlock() ? m_renderer.containingBlock()->contentWidth() : LayoutUnit()); + float shapeImageThreshold = style.shapeImageThreshold(); + const ShapeValue& shapeValue = *style.shapeOutside(); + + switch (shapeValue.type()) { + case ShapeValue::Type::Shape: + ASSERT(shapeValue.shape()); + m_shape = Shape::createShape(*shapeValue.shape(), m_referenceBoxLogicalSize, writingMode, margin); + break; + case ShapeValue::Type::Image: + ASSERT(shapeValue.isImageValid()); + m_shape = createShapeForImage(shapeValue.image(), shapeImageThreshold, writingMode, margin); + break; + case ShapeValue::Type::Box: { + RoundedRect shapeRect = computeRoundedRectForBoxShape(referenceBox(shapeValue), m_renderer); + if (!containingBlockStyle.isHorizontalWritingMode()) + shapeRect = shapeRect.transposedRect(); + m_shape = Shape::createBoxShape(shapeRect, writingMode, margin); + break; + } + } + + ASSERT(m_shape); + return *m_shape; +} + +static inline LayoutUnit borderBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode) +{ + switch (writingMode) { + case TopToBottomWritingMode: return renderer.borderTop(); + case BottomToTopWritingMode: return renderer.borderBottom(); + case LeftToRightWritingMode: return renderer.borderLeft(); + case RightToLeftWritingMode: return renderer.borderRight(); + } + + ASSERT_NOT_REACHED(); + return renderer.borderBefore(); +} + +static inline LayoutUnit borderAndPaddingBeforeInWritingMode(const RenderBox& renderer, WritingMode writingMode) +{ + switch (writingMode) { + case TopToBottomWritingMode: return renderer.borderTop() + renderer.paddingTop(); + case BottomToTopWritingMode: return renderer.borderBottom() + renderer.paddingBottom(); + case LeftToRightWritingMode: return renderer.borderLeft() + renderer.paddingLeft(); + case RightToLeftWritingMode: return renderer.borderRight() + renderer.paddingRight(); + } + + ASSERT_NOT_REACHED(); + return renderer.borderAndPaddingBefore(); +} + +LayoutUnit ShapeOutsideInfo::logicalTopOffset() const +{ + switch (referenceBox(*m_renderer.style().shapeOutside())) { + case MarginBox: return -m_renderer.marginBefore(&m_renderer.containingBlock()->style()); + case BorderBox: return LayoutUnit(); + case PaddingBox: return borderBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style().writingMode()); + case ContentBox: return borderAndPaddingBeforeInWritingMode(m_renderer, m_renderer.containingBlock()->style().writingMode()); + case Fill: break; + case Stroke: break; + case ViewBox: break; + case BoxMissing: break; + } + + ASSERT_NOT_REACHED(); + return LayoutUnit(); +} + +static inline LayoutUnit borderStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle& style) +{ + if (style.isHorizontalWritingMode()) { + if (style.isLeftToRightDirection()) + return renderer.borderLeft(); + + return renderer.borderRight(); + } + if (style.isLeftToRightDirection()) + return renderer.borderTop(); + + return renderer.borderBottom(); +} + +static inline LayoutUnit borderAndPaddingStartWithStyleForWritingMode(const RenderBox& renderer, const RenderStyle& style) +{ + if (style.isHorizontalWritingMode()) { + if (style.isLeftToRightDirection()) + return renderer.borderLeft() + renderer.paddingLeft(); + + return renderer.borderRight() + renderer.paddingRight(); + } + if (style.isLeftToRightDirection()) + return renderer.borderTop() + renderer.paddingTop(); + + return renderer.borderBottom() + renderer.paddingBottom(); +} + +LayoutUnit ShapeOutsideInfo::logicalLeftOffset() const +{ + if (m_renderer.isRenderRegion()) + return LayoutUnit(); + + switch (referenceBox(*m_renderer.style().shapeOutside())) { + case MarginBox: return -m_renderer.marginStart(&m_renderer.containingBlock()->style()); + case BorderBox: return LayoutUnit(); + case PaddingBox: return borderStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style()); + case ContentBox: return borderAndPaddingStartWithStyleForWritingMode(m_renderer, m_renderer.containingBlock()->style()); + case Fill: break; + case Stroke: break; + case ViewBox: break; + case BoxMissing: break; + } + + ASSERT_NOT_REACHED(); + return LayoutUnit(); +} + bool ShapeOutsideInfo::isEnabledFor(const RenderBox& box) { ShapeValue* shapeValue = box.style().shapeOutside(); @@ -46,64 +293,48 @@ bool ShapeOutsideInfo::isEnabledFor(const RenderBox& box) return false; switch (shapeValue->type()) { - case ShapeValue::Shape: - return shapeValue->shape(); - case ShapeValue::Image: - return shapeValue->isImageValid() && checkShapeImageOrigin(box.document(), *(shapeValue->image()->cachedImage())); - case ShapeValue::Box: - return true; - case ShapeValue::Outside: - break; + case ShapeValue::Type::Shape: return shapeValue->shape(); + case ShapeValue::Type::Image: return shapeValue->isImageValid() && checkShapeImageOrigin(box.document(), *(shapeValue->image())); + case ShapeValue::Type::Box: return true; } + ASSERT_NOT_REACHED(); return false; } -void ShapeOutsideInfo::updateDeltasForContainingBlockLine(const RenderBlockFlow& containingBlock, const FloatingObject& floatingObject, LayoutUnit lineTop, LayoutUnit lineHeight) +ShapeOutsideDeltas ShapeOutsideInfo::computeDeltasForContainingBlockLine(const RenderBlockFlow& containingBlock, const FloatingObject& floatingObject, LayoutUnit lineTop, LayoutUnit lineHeight) { - LayoutUnit shapeTop = containingBlock.logicalTopForFloat(&floatingObject) + std::max(LayoutUnit(), containingBlock.marginBeforeForChild(m_renderer)); - LayoutUnit floatRelativeLineTop = lineTop - shapeTop; + ASSERT(lineHeight >= 0); + LayoutUnit borderBoxTop = containingBlock.logicalTopForFloat(floatingObject) + containingBlock.marginBeforeForChild(m_renderer); + LayoutUnit borderBoxLineTop = lineTop - borderBoxTop; - if (shapeSizeDirty() || m_lineTop != floatRelativeLineTop || m_lineHeight != lineHeight) { - m_lineTop = floatRelativeLineTop; - m_shapeLineTop = floatRelativeLineTop - logicalTopOffset(); - m_lineHeight = lineHeight; + if (isShapeDirty() || !m_shapeOutsideDeltas.isForLine(borderBoxLineTop, lineHeight)) { + LayoutUnit referenceBoxLineTop = borderBoxLineTop - logicalTopOffset(); + LayoutUnit floatMarginBoxWidth = std::max<LayoutUnit>(LayoutUnit(), containingBlock.logicalWidthForFloat(floatingObject)); - LayoutUnit floatMarginBoxWidth = containingBlock.logicalWidthForFloat(&floatingObject); + if (computedShape().lineOverlapsShapeMarginBounds(referenceBoxLineTop, lineHeight)) { + LineSegment segment = computedShape().getExcludedInterval((borderBoxLineTop - logicalTopOffset()), std::min(lineHeight, shapeLogicalBottom() - borderBoxLineTop)); + if (segment.isValid) { + LayoutUnit logicalLeftMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginStartForChild(m_renderer) : containingBlock.marginEndForChild(m_renderer); + LayoutUnit rawLeftMarginBoxDelta = segment.logicalLeft + logicalLeftOffset() + logicalLeftMargin; + LayoutUnit leftMarginBoxDelta = clampTo<LayoutUnit>(rawLeftMarginBoxDelta, LayoutUnit(), floatMarginBoxWidth); - if (lineOverlapsShapeBounds()) { - SegmentList segments = computeSegmentsForLine(floatRelativeLineTop, lineHeight); - if (segments.size()) { - LayoutUnit rawLeftMarginBoxDelta = segments.first().logicalLeft + containingBlock.marginStartForChild(m_renderer); - m_leftMarginBoxDelta = clampTo<LayoutUnit>(rawLeftMarginBoxDelta, LayoutUnit(), floatMarginBoxWidth); + LayoutUnit logicalRightMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginEndForChild(m_renderer) : containingBlock.marginStartForChild(m_renderer); + LayoutUnit rawRightMarginBoxDelta = segment.logicalRight + logicalLeftOffset() - containingBlock.logicalWidthForChild(m_renderer) - logicalRightMargin; + LayoutUnit rightMarginBoxDelta = clampTo<LayoutUnit>(rawRightMarginBoxDelta, -floatMarginBoxWidth, LayoutUnit()); - LayoutUnit rawRightMarginBoxDelta = segments.last().logicalRight - containingBlock.logicalWidthForChild(m_renderer) - containingBlock.marginEndForChild(m_renderer); - m_rightMarginBoxDelta = clampTo<LayoutUnit>(rawRightMarginBoxDelta, -floatMarginBoxWidth, LayoutUnit()); - m_lineOverlapsShape = true; - return; + m_shapeOutsideDeltas = ShapeOutsideDeltas(leftMarginBoxDelta, rightMarginBoxDelta, true, borderBoxLineTop, lineHeight); + return m_shapeOutsideDeltas; } } // Lines that do not overlap the shape should act as if the float // wasn't there for layout purposes. So we set the deltas to remove the // entire width of the float - m_leftMarginBoxDelta = floatMarginBoxWidth; - m_rightMarginBoxDelta = -floatMarginBoxWidth; - m_lineOverlapsShape = false; + m_shapeOutsideDeltas = ShapeOutsideDeltas(floatMarginBoxWidth, -floatMarginBoxWidth, false, borderBoxLineTop, lineHeight); } -} -ShapeValue* ShapeOutsideInfo::shapeValue() const -{ - return m_renderer.style().shapeOutside(); + return m_shapeOutsideDeltas; } -WritingMode ShapeOutsideInfo::writingMode() const -{ - ASSERT(m_renderer.containingBlock()); - return m_renderer.containingBlock()->style().writingMode(); } - -} - -#endif diff --git a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h index 9bdf1a280..cd94f89c0 100644 --- a/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h +++ b/Source/WebCore/rendering/shapes/ShapeOutsideInfo.h @@ -27,68 +27,117 @@ * SUCH DAMAGE. */ -#ifndef ShapeOutsideInfo_h -#define ShapeOutsideInfo_h - -#if ENABLE(CSS_SHAPES) +#pragma once #include "LayoutSize.h" -#include "ShapeInfo.h" +#include "Shape.h" +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> namespace WebCore { class RenderBlockFlow; class RenderBox; +class StyleImage; class FloatingObject; -class ShapeOutsideInfo final : public ShapeInfo<RenderBox>, public MappedInfo<RenderBox, ShapeOutsideInfo> { +class ShapeOutsideDeltas final { public: - ShapeOutsideInfo(const RenderBox& renderer) - : ShapeInfo<RenderBox>(renderer) - , m_lineOverlapsShape(false) + ShapeOutsideDeltas() + : m_lineOverlapsShape(false) + , m_isValid(false) { } - static bool isEnabledFor(const RenderBox&); + ShapeOutsideDeltas(LayoutUnit leftMarginBoxDelta, LayoutUnit rightMarginBoxDelta, bool lineOverlapsShape, LayoutUnit borderBoxLineTop, LayoutUnit lineHeight) + : m_leftMarginBoxDelta(leftMarginBoxDelta) + , m_rightMarginBoxDelta(rightMarginBoxDelta) + , m_borderBoxLineTop(borderBoxLineTop) + , m_lineHeight(lineHeight) + , m_lineOverlapsShape(lineOverlapsShape) + , m_isValid(true) + { + } + + bool isForLine(LayoutUnit borderBoxLineTop, LayoutUnit lineHeight) + { + return m_isValid && m_borderBoxLineTop == borderBoxLineTop && m_lineHeight == lineHeight; + } - LayoutUnit leftMarginBoxDelta() const { return m_leftMarginBoxDelta; } - LayoutUnit rightMarginBoxDelta() const { return m_rightMarginBoxDelta; } - bool lineOverlapsShape() const { return m_lineOverlapsShape; } + bool isValid() { return m_isValid; } + LayoutUnit leftMarginBoxDelta() { ASSERT(m_isValid); return m_leftMarginBoxDelta; } + LayoutUnit rightMarginBoxDelta() { ASSERT(m_isValid); return m_rightMarginBoxDelta; } + bool lineOverlapsShape() { ASSERT(m_isValid); return m_lineOverlapsShape; } - void updateDeltasForContainingBlockLine(const RenderBlockFlow&, const FloatingObject&, LayoutUnit lineTop, LayoutUnit lineHeight); +private: + LayoutUnit m_leftMarginBoxDelta; + LayoutUnit m_rightMarginBoxDelta; + LayoutUnit m_borderBoxLineTop; + LayoutUnit m_lineHeight; + unsigned m_lineOverlapsShape : 1; + unsigned m_isValid : 1; +}; - virtual bool lineOverlapsShapeBounds() const override +class ShapeOutsideInfo final { + WTF_MAKE_FAST_ALLOCATED; +public: + ShapeOutsideInfo(const RenderBox& renderer) + : m_renderer(renderer) { - return computedShape().lineOverlapsShapeMarginBounds(m_shapeLineTop, m_lineHeight); } -protected: - virtual LayoutBox resolvedLayoutBox() const override + static bool isEnabledFor(const RenderBox&); + + ShapeOutsideDeltas computeDeltasForContainingBlockLine(const RenderBlockFlow&, const FloatingObject&, LayoutUnit lineTop, LayoutUnit lineHeight); + + void setReferenceBoxLogicalSize(LayoutSize); + + LayoutUnit shapeLogicalTop() const { return computedShape().shapeMarginLogicalBoundingBox().y() + logicalTopOffset(); } + LayoutUnit shapeLogicalBottom() const { return computedShape().shapeMarginLogicalBoundingBox().maxY() + logicalTopOffset(); } + LayoutUnit shapeLogicalLeft() const { return computedShape().shapeMarginLogicalBoundingBox().x() + logicalLeftOffset(); } + LayoutUnit shapeLogicalRight() const { return computedShape().shapeMarginLogicalBoundingBox().maxX() + logicalLeftOffset(); } + LayoutUnit shapeLogicalWidth() const { return computedShape().shapeMarginLogicalBoundingBox().width(); } + LayoutUnit shapeLogicalHeight() const { return computedShape().shapeMarginLogicalBoundingBox().height(); } + + void markShapeAsDirty() { m_shape = nullptr; } + bool isShapeDirty() { return !m_shape; } + + LayoutRect computedShapePhysicalBoundingBox() const; + FloatPoint shapeToRendererPoint(const FloatPoint&) const; + FloatSize shapeToRendererSize(const FloatSize&) const; + + const Shape& computedShape() const; + + static ShapeOutsideInfo& ensureInfo(const RenderBox& key) { - if (shapeValue()->layoutBox() == BoxMissing) { - if (shapeValue()->type() == ShapeValue::Image) - return ContentBox; - return MarginBox; - } - return shapeValue()->layoutBox(); + InfoMap& infoMap = ShapeOutsideInfo::infoMap(); + if (ShapeOutsideInfo* info = infoMap.get(&key)) + return *info; + auto result = infoMap.add(&key, std::make_unique<ShapeOutsideInfo>(key)); + return *result.iterator->value; } + static void removeInfo(const RenderBox& key) { infoMap().remove(&key); } + static ShapeOutsideInfo* info(const RenderBox& key) { return infoMap().get(&key); } private: - virtual LayoutRect computedShapeLogicalBoundingBox() const override { return computedShape().shapeMarginLogicalBoundingBox(); } - virtual ShapeValue* shapeValue() const override; - virtual void getIntervals(LayoutUnit lineTop, LayoutUnit lineHeight, SegmentList& segments) const override + std::unique_ptr<Shape> createShapeForImage(StyleImage*, float shapeImageThreshold, WritingMode, float margin) const; + + LayoutUnit logicalTopOffset() const; + LayoutUnit logicalLeftOffset() const; + + typedef HashMap<const RenderBox*, std::unique_ptr<ShapeOutsideInfo>> InfoMap; + static InfoMap& infoMap() { - return computedShape().getExcludedIntervals(lineTop, lineHeight, segments); + static NeverDestroyed<InfoMap> staticInfoMap; + return staticInfoMap; } - virtual WritingMode writingMode() const; + const RenderBox& m_renderer; - LayoutUnit m_leftMarginBoxDelta; - LayoutUnit m_rightMarginBoxDelta; - LayoutUnit m_lineTop; - bool m_lineOverlapsShape; + mutable std::unique_ptr<Shape> m_shape; + LayoutSize m_referenceBoxLogicalSize; + + ShapeOutsideDeltas m_shapeOutsideDeltas; }; } -#endif -#endif |