1 | /* |
2 | * Copyright (C) 2012 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 "Shape.h" |
32 | |
33 | #include "BasicShapeFunctions.h" |
34 | #include "BasicShapes.h" |
35 | #include "BoxShape.h" |
36 | #include "GraphicsContext.h" |
37 | #include "ImageBuffer.h" |
38 | #include "LengthFunctions.h" |
39 | #include "PolygonShape.h" |
40 | #include "RasterShape.h" |
41 | #include "RectangleShape.h" |
42 | #include "WindRule.h" |
43 | |
44 | namespace WebCore { |
45 | |
46 | static std::unique_ptr<Shape> createInsetShape(const FloatRoundedRect& bounds) |
47 | { |
48 | ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0); |
49 | return std::make_unique<BoxShape>(bounds); |
50 | } |
51 | |
52 | static std::unique_ptr<Shape> createCircleShape(const FloatPoint& center, float radius) |
53 | { |
54 | ASSERT(radius >= 0); |
55 | return std::make_unique<RectangleShape>(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius)); |
56 | } |
57 | |
58 | static std::unique_ptr<Shape> createEllipseShape(const FloatPoint& center, const FloatSize& radii) |
59 | { |
60 | ASSERT(radii.width() >= 0 && radii.height() >= 0); |
61 | return std::make_unique<RectangleShape>(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii); |
62 | } |
63 | |
64 | static std::unique_ptr<Shape> createPolygonShape(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule) |
65 | { |
66 | return std::make_unique<PolygonShape>(WTFMove(vertices), fillRule); |
67 | } |
68 | |
69 | static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight, WritingMode writingMode) |
70 | { |
71 | if (isHorizontalWritingMode(writingMode)) |
72 | return rect; |
73 | if (isFlippedWritingMode(writingMode)) |
74 | return FloatRect(rect.y(), logicalBoxHeight - rect.maxX(), rect.height(), rect.width()); |
75 | return rect.transposedRect(); |
76 | } |
77 | |
78 | static inline FloatPoint physicalPointToLogical(const FloatPoint& point, float logicalBoxHeight, WritingMode writingMode) |
79 | { |
80 | if (isHorizontalWritingMode(writingMode)) |
81 | return point; |
82 | if (isFlippedWritingMode(writingMode)) |
83 | return FloatPoint(point.y(), logicalBoxHeight - point.x()); |
84 | return point.transposedPoint(); |
85 | } |
86 | |
87 | static inline FloatSize physicalSizeToLogical(const FloatSize& size, WritingMode writingMode) |
88 | { |
89 | if (isHorizontalWritingMode(writingMode)) |
90 | return size; |
91 | return size.transposedSize(); |
92 | } |
93 | |
94 | std::unique_ptr<Shape> Shape::createShape(const BasicShape& basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, float margin) |
95 | { |
96 | bool horizontalWritingMode = isHorizontalWritingMode(writingMode); |
97 | float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height(); |
98 | float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width(); |
99 | std::unique_ptr<Shape> shape; |
100 | |
101 | switch (basicShape.type()) { |
102 | |
103 | case BasicShape::BasicShapeCircleType: { |
104 | const auto& circle = downcast<BasicShapeCircle>(basicShape); |
105 | float centerX = floatValueForCenterCoordinate(circle.centerX(), boxWidth); |
106 | float centerY = floatValueForCenterCoordinate(circle.centerY(), boxHeight); |
107 | float radius = circle.floatValueForRadiusInBox(boxWidth, boxHeight); |
108 | FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); |
109 | |
110 | shape = createCircleShape(logicalCenter, radius); |
111 | break; |
112 | } |
113 | |
114 | case BasicShape::BasicShapeEllipseType: { |
115 | const auto& ellipse = downcast<BasicShapeEllipse>(basicShape); |
116 | float centerX = floatValueForCenterCoordinate(ellipse.centerX(), boxWidth); |
117 | float centerY = floatValueForCenterCoordinate(ellipse.centerY(), boxHeight); |
118 | float radiusX = ellipse.floatValueForRadiusInBox(ellipse.radiusX(), centerX, boxWidth); |
119 | float radiusY = ellipse.floatValueForRadiusInBox(ellipse.radiusY(), centerY, boxHeight); |
120 | FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); |
121 | |
122 | shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY)); |
123 | break; |
124 | } |
125 | |
126 | case BasicShape::BasicShapePolygonType: { |
127 | const auto& polygon = downcast<BasicShapePolygon>(basicShape); |
128 | const Vector<Length>& values = polygon.values(); |
129 | size_t valuesSize = values.size(); |
130 | ASSERT(!(valuesSize % 2)); |
131 | std::unique_ptr<Vector<FloatPoint>> vertices = std::make_unique<Vector<FloatPoint>>(valuesSize / 2); |
132 | for (unsigned i = 0; i < valuesSize; i += 2) { |
133 | FloatPoint vertex( |
134 | floatValueForLength(values.at(i), boxWidth), |
135 | floatValueForLength(values.at(i + 1), boxHeight)); |
136 | (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height(), writingMode); |
137 | } |
138 | |
139 | shape = createPolygonShape(WTFMove(vertices), polygon.windRule()); |
140 | break; |
141 | } |
142 | |
143 | case BasicShape::BasicShapeInsetType: { |
144 | const auto& inset = downcast<BasicShapeInset>(basicShape); |
145 | float left = floatValueForLength(inset.left(), boxWidth); |
146 | float top = floatValueForLength(inset.top(), boxHeight); |
147 | FloatRect rect(left, |
148 | top, |
149 | std::max<float>(boxWidth - left - floatValueForLength(inset.right(), boxWidth), 0), |
150 | std::max<float>(boxHeight - top - floatValueForLength(inset.bottom(), boxHeight), 0)); |
151 | FloatRect logicalRect = physicalRectToLogical(rect, logicalBoxSize.height(), writingMode); |
152 | |
153 | FloatSize boxSize(boxWidth, boxHeight); |
154 | FloatSize topLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topLeftRadius(), boxSize), writingMode); |
155 | FloatSize topRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topRightRadius(), boxSize), writingMode); |
156 | FloatSize bottomLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomLeftRadius(), boxSize), writingMode); |
157 | FloatSize bottomRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomRightRadius(), boxSize), writingMode); |
158 | FloatRoundedRect::Radii cornerRadii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); |
159 | |
160 | cornerRadii.scale(calcBorderRadiiConstraintScaleFor(logicalRect, cornerRadii)); |
161 | |
162 | shape = createInsetShape(FloatRoundedRect(logicalRect, cornerRadii)); |
163 | break; |
164 | } |
165 | |
166 | default: |
167 | ASSERT_NOT_REACHED(); |
168 | } |
169 | |
170 | shape->m_writingMode = writingMode; |
171 | shape->m_margin = margin; |
172 | |
173 | return shape; |
174 | } |
175 | |
176 | std::unique_ptr<Shape> Shape::createRasterShape(Image* image, float threshold, const LayoutRect& imageR, const LayoutRect& marginR, WritingMode writingMode, float margin) |
177 | { |
178 | ASSERT(marginR.height() >= 0); |
179 | |
180 | IntRect imageRect = snappedIntRect(imageR); |
181 | IntRect marginRect = snappedIntRect(marginR); |
182 | auto intervals = std::make_unique<RasterShapeIntervals>(marginRect.height(), -marginRect.y()); |
183 | // FIXME (149420): This buffer should not be unconditionally unaccelerated. |
184 | std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(imageRect.size(), Unaccelerated); |
185 | |
186 | if (imageBuffer) { |
187 | GraphicsContext& graphicsContext = imageBuffer->context(); |
188 | if (image) |
189 | graphicsContext.drawImage(*image, IntRect(IntPoint(), imageRect.size())); |
190 | |
191 | RefPtr<Uint8ClampedArray> pixelArray = imageBuffer->getUnmultipliedImageData(IntRect(IntPoint(), imageRect.size())); |
192 | RELEASE_ASSERT(pixelArray); |
193 | unsigned pixelArrayLength = pixelArray->length(); |
194 | unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. |
195 | uint8_t alphaPixelThreshold = threshold * 255; |
196 | |
197 | int minBufferY = std::max(0, marginRect.y() - imageRect.y()); |
198 | int maxBufferY = std::min(imageRect.height(), marginRect.maxY() - imageRect.y()); |
199 | |
200 | if ((imageRect.area() * 4).unsafeGet() == pixelArrayLength) { |
201 | for (int y = minBufferY; y < maxBufferY; ++y) { |
202 | int startX = -1; |
203 | for (int x = 0; x < imageRect.width(); ++x, pixelArrayOffset += 4) { |
204 | uint8_t alpha = pixelArray->item(pixelArrayOffset); |
205 | bool alphaAboveThreshold = alpha > alphaPixelThreshold; |
206 | if (startX == -1 && alphaAboveThreshold) { |
207 | startX = x; |
208 | } else if (startX != -1 && (!alphaAboveThreshold || x == imageRect.width() - 1)) { |
209 | // We're creating "end-point exclusive" intervals here. The value of an interval's x1 is |
210 | // the first index of an above-threshold pixel for y, and the value of x2 is 1+ the index |
211 | // of the last above-threshold pixel. |
212 | int endX = alphaAboveThreshold ? x + 1 : x; |
213 | intervals->intervalAt(y + imageRect.y()).unite(IntShapeInterval(startX + imageRect.x(), endX + imageRect.x())); |
214 | startX = -1; |
215 | } |
216 | } |
217 | } |
218 | } |
219 | } |
220 | |
221 | auto rasterShape = std::make_unique<RasterShape>(WTFMove(intervals), marginRect.size()); |
222 | rasterShape->m_writingMode = writingMode; |
223 | rasterShape->m_margin = margin; |
224 | return rasterShape; |
225 | } |
226 | |
227 | std::unique_ptr<Shape> Shape::createBoxShape(const RoundedRect& roundedRect, WritingMode writingMode, float margin) |
228 | { |
229 | ASSERT(roundedRect.rect().width() >= 0 && roundedRect.rect().height() >= 0); |
230 | |
231 | FloatRect rect(0, 0, roundedRect.rect().width(), roundedRect.rect().height()); |
232 | FloatRoundedRect bounds(rect, roundedRect.radii()); |
233 | auto shape = std::make_unique<BoxShape>(bounds); |
234 | shape->m_writingMode = writingMode; |
235 | shape->m_margin = margin; |
236 | |
237 | return shape; |
238 | } |
239 | |
240 | } // namespace WebCore |
241 | |