1/* 2 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following 13 * disclaimer in the documentation and/or other materials 14 * provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 27 * OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include "config.h" 31#include "BoxShape.h" 32 33#include "RenderBox.h" 34#include <wtf/MathExtras.h> 35 36namespace WebCore { 37 38static inline LayoutUnit adjustRadiusForMarginBoxShape(LayoutUnit radius, LayoutUnit margin) 39{ 40 // This algorithm is defined in the CSS Shapes specifcation 41 if (!margin) 42 return radius; 43 44 LayoutUnit ratio = radius / margin; 45 if (ratio < 1) 46 return radius + (margin * (1 + pow(ratio - 1, 3))); 47 48 return radius + margin; 49} 50 51static inline LayoutSize computeMarginBoxShapeRadius(const LayoutSize& radius, const LayoutSize& adjacentMargins) 52{ 53 return LayoutSize(adjustRadiusForMarginBoxShape(radius.width(), adjacentMargins.width()), 54 adjustRadiusForMarginBoxShape(radius.height(), adjacentMargins.height())); 55} 56 57static inline RoundedRect::Radii computeMarginBoxShapeRadii(const RoundedRect::Radii& radii, const RenderBox& renderer) 58{ 59 return RoundedRect::Radii(computeMarginBoxShapeRadius(radii.topLeft(), LayoutSize(renderer.marginLeft(), renderer.marginTop())), 60 computeMarginBoxShapeRadius(radii.topRight(), LayoutSize(renderer.marginRight(), renderer.marginTop())), 61 computeMarginBoxShapeRadius(radii.bottomLeft(), LayoutSize(renderer.marginLeft(), renderer.marginBottom())), 62 computeMarginBoxShapeRadius(radii.bottomRight(), LayoutSize(renderer.marginRight(), renderer.marginBottom()))); 63} 64 65RoundedRect computeRoundedRectForBoxShape(CSSBoxType box, const RenderBox& renderer) 66{ 67 const RenderStyle& style = renderer.style(); 68 switch (box) { 69 case MarginBox: { 70 if (!style.hasBorderRadius()) 71 return RoundedRect(renderer.marginBoxRect(), RoundedRect::Radii()); 72 73 LayoutRect marginBox = renderer.marginBoxRect(); 74 RoundedRect::Radii radii = computeMarginBoxShapeRadii(style.getRoundedBorderFor(renderer.borderBoxRect(), &(renderer.view())).radii(), renderer); 75 radii.scale(calcBorderRadiiConstraintScaleFor(marginBox, radii)); 76 return RoundedRect(marginBox, radii); 77 } 78 case PaddingBox: 79 return style.getRoundedInnerBorderFor(renderer.borderBoxRect()); 80 case ContentBox: 81 return style.getRoundedInnerBorderFor(renderer.borderBoxRect(), 82 renderer.paddingTop() + renderer.borderTop(), renderer.paddingBottom() + renderer.borderBottom(), 83 renderer.paddingLeft() + renderer.borderLeft(), renderer.paddingRight() + renderer.borderRight()); 84 // fill, stroke, view-box compute to border-box for HTML elements. 85 case BorderBox: 86 case Fill: 87 case Stroke: 88 case ViewBox: 89 case BoxMissing: 90 return style.getRoundedBorderFor(renderer.borderBoxRect(), &(renderer.view())); 91 } 92 93 ASSERT_NOT_REACHED(); 94 return style.getRoundedBorderFor(renderer.borderBoxRect(), &(renderer.view())); 95} 96 97LayoutRect BoxShape::shapeMarginLogicalBoundingBox() const 98{ 99 FloatRect marginBounds(m_bounds.rect()); 100 if (shapeMargin() > 0) 101 marginBounds.inflate(shapeMargin()); 102 return static_cast<LayoutRect>(marginBounds); 103} 104 105FloatRoundedRect BoxShape::shapeMarginBounds() const 106{ 107 FloatRoundedRect marginBounds(m_bounds); 108 if (shapeMargin() > 0) { 109 marginBounds.inflate(shapeMargin()); 110 marginBounds.expandRadii(shapeMargin()); 111 } 112 return marginBounds; 113} 114 115void BoxShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const 116{ 117 const FloatRoundedRect& marginBounds = shapeMarginBounds(); 118 if (marginBounds.isEmpty() || !lineOverlapsShapeMarginBounds(logicalTop, logicalHeight)) 119 return; 120 121 float y1 = logicalTop; 122 float y2 = logicalTop + logicalHeight; 123 const FloatRect& rect = marginBounds.rect(); 124 125 if (!marginBounds.isRounded()) { 126 result.append(LineSegment(rect.x(), rect.maxX())); 127 return; 128 } 129 130 float topCornerMaxY = std::max<float>(marginBounds.topLeftCorner().maxY(), marginBounds.topRightCorner().maxY()); 131 float bottomCornerMinY = std::min<float>(marginBounds.bottomLeftCorner().y(), marginBounds.bottomRightCorner().y()); 132 133 if (topCornerMaxY <= bottomCornerMinY && y1 <= topCornerMaxY && y2 >= bottomCornerMinY) { 134 result.append(LineSegment(rect.x(), rect.maxX())); 135 return; 136 } 137 138 float x1 = rect.maxX(); 139 float x2 = rect.x(); 140 float minXIntercept; 141 float maxXIntercept; 142 143 if (y1 <= marginBounds.topLeftCorner().maxY() && y2 >= marginBounds.bottomLeftCorner().y()) 144 x1 = rect.x(); 145 146 if (y1 <= marginBounds.topRightCorner().maxY() && y2 >= marginBounds.bottomRightCorner().y()) 147 x2 = rect.maxX(); 148 149 if (marginBounds.xInterceptsAtY(y1, minXIntercept, maxXIntercept)) { 150 x1 = std::min<float>(x1, minXIntercept); 151 x2 = std::max<float>(x2, maxXIntercept); 152 } 153 154 if (marginBounds.xInterceptsAtY(y2, minXIntercept, maxXIntercept)) { 155 x1 = std::min<float>(x1, minXIntercept); 156 x2 = std::max<float>(x2, maxXIntercept); 157 } 158 159 ASSERT(x2 >= x1); 160 result.append(LineSegment(x1, x2)); 161} 162 163void BoxShape::buildDisplayPaths(DisplayPaths& paths) const 164{ 165 paths.shape.addRoundedRect(m_bounds, Path::PreferBezierRoundedRect); 166 if (shapeMargin()) 167 paths.marginShape.addRoundedRect(shapeMarginBounds(), Path::PreferBezierRoundedRect); 168} 169 170} // namespace WebCore 171