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