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 "core/rendering/shapes/Shape.h"
32
33#include "core/fetch/ImageResource.h"
34#include "core/rendering/shapes/BoxShape.h"
35#include "core/rendering/shapes/PolygonShape.h"
36#include "core/rendering/shapes/RasterShape.h"
37#include "core/rendering/shapes/RectangleShape.h"
38#include "platform/LengthFunctions.h"
39#include "platform/geometry/FloatRoundedRect.h"
40#include "platform/geometry/FloatSize.h"
41#include "platform/graphics/ImageBuffer.h"
42#include "platform/graphics/WindRule.h"
43#include "wtf/MathExtras.h"
44#include "wtf/OwnPtr.h"
45
46namespace WebCore {
47
48static PassOwnPtr<Shape> createBoxShape(const FloatRoundedRect& bounds)
49{
50    ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0);
51    return adoptPtr(new BoxShape(bounds));
52}
53
54static PassOwnPtr<Shape> createRectangleShape(const FloatRect& bounds, const FloatSize& radii)
55{
56    ASSERT(bounds.width() >= 0 && bounds.height() >= 0 && radii.width() >= 0 && radii.height() >= 0);
57    return adoptPtr(new RectangleShape(bounds, radii));
58}
59
60static PassOwnPtr<Shape> createCircleShape(const FloatPoint& center, float radius)
61{
62    ASSERT(radius >= 0);
63    return adoptPtr(new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius)));
64}
65
66static PassOwnPtr<Shape> createEllipseShape(const FloatPoint& center, const FloatSize& radii)
67{
68    ASSERT(radii.width() >= 0 && radii.height() >= 0);
69    return adoptPtr(new RectangleShape(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii));
70}
71
72static PassOwnPtr<Shape> createPolygonShape(PassOwnPtr<Vector<FloatPoint> > vertices, WindRule fillRule)
73{
74    return adoptPtr(new PolygonShape(vertices, fillRule));
75}
76
77static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight, WritingMode writingMode)
78{
79    if (isHorizontalWritingMode(writingMode))
80        return rect;
81    if (isFlippedBlocksWritingMode(writingMode))
82        return FloatRect(rect.y(), logicalBoxHeight - rect.maxX(), rect.height(), rect.width());
83    return rect.transposedRect();
84}
85
86static inline FloatPoint physicalPointToLogical(const FloatPoint& point, float logicalBoxHeight, WritingMode writingMode)
87{
88    if (isHorizontalWritingMode(writingMode))
89        return point;
90    if (isFlippedBlocksWritingMode(writingMode))
91        return FloatPoint(point.y(), logicalBoxHeight - point.x());
92    return point.transposedPoint();
93}
94
95static inline FloatSize physicalSizeToLogical(const FloatSize& size, WritingMode writingMode)
96{
97    if (isHorizontalWritingMode(writingMode))
98        return size;
99    return size.transposedSize();
100}
101
102static inline void ensureRadiiDoNotOverlap(FloatRect &bounds, FloatSize &radii)
103{
104    float widthRatio = bounds.width() / (2 * radii.width());
105    float heightRatio = bounds.height() / (2 * radii.height());
106    float reductionRatio = std::min<float>(widthRatio, heightRatio);
107    if (reductionRatio < 1) {
108        radii.setWidth(reductionRatio * radii.width());
109        radii.setHeight(reductionRatio * radii.height());
110    }
111}
112
113PassOwnPtr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, Length margin, Length padding)
114{
115    ASSERT(basicShape);
116
117    bool horizontalWritingMode = isHorizontalWritingMode(writingMode);
118    float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height();
119    float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width();
120    OwnPtr<Shape> shape;
121
122    switch (basicShape->type()) {
123
124    case BasicShape::BasicShapeRectangleType: {
125        const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape);
126        FloatRect bounds(
127            floatValueForLength(rectangle->x(), boxWidth),
128            floatValueForLength(rectangle->y(), boxHeight),
129            floatValueForLength(rectangle->width(), boxWidth),
130            floatValueForLength(rectangle->height(), boxHeight));
131        FloatSize cornerRadii(
132            floatValueForLength(rectangle->cornerRadiusX(), boxWidth),
133            floatValueForLength(rectangle->cornerRadiusY(), boxHeight));
134        ensureRadiiDoNotOverlap(bounds, cornerRadii);
135        FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode);
136
137        shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode));
138        break;
139    }
140
141    case BasicShape::BasicShapeCircleType: {
142        const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape);
143        float centerX = floatValueForLength(circle->centerX(), boxWidth);
144        float centerY = floatValueForLength(circle->centerY(), boxHeight);
145        // This method of computing the radius is as defined in SVG
146        // (http://www.w3.org/TR/SVG/coords.html#Units). It bases the radius
147        // off of the diagonal of the box and ensures that if the box is
148        // square, the radius is equal to half the diagonal.
149        float radius = floatValueForLength(circle->radius(), sqrtf((boxWidth * boxWidth + boxHeight * boxHeight) / 2));
150        FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode);
151
152        shape = createCircleShape(logicalCenter, radius);
153        break;
154    }
155
156    case BasicShape::BasicShapeEllipseType: {
157        const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape);
158        float centerX = floatValueForLength(ellipse->centerX(), boxWidth);
159        float centerY = floatValueForLength(ellipse->centerY(), boxHeight);
160        float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth);
161        float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight);
162        FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode);
163        FloatSize logicalRadii = physicalSizeToLogical(FloatSize(radiusX, radiusY), writingMode);
164
165        shape = createEllipseShape(logicalCenter, logicalRadii);
166        break;
167    }
168
169    case BasicShape::BasicShapePolygonType: {
170        const BasicShapePolygon* polygon = static_cast<const BasicShapePolygon*>(basicShape);
171        const Vector<Length>& values = polygon->values();
172        size_t valuesSize = values.size();
173        ASSERT(!(valuesSize % 2));
174        OwnPtr<Vector<FloatPoint> > vertices = adoptPtr(new Vector<FloatPoint>(valuesSize / 2));
175        for (unsigned i = 0; i < valuesSize; i += 2) {
176            FloatPoint vertex(
177                floatValueForLength(values.at(i), boxWidth),
178                floatValueForLength(values.at(i + 1), boxHeight));
179            (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height(), writingMode);
180        }
181        shape = createPolygonShape(vertices.release(), polygon->windRule());
182        break;
183    }
184
185    case BasicShape::BasicShapeInsetRectangleType: {
186        const BasicShapeInsetRectangle* rectangle = static_cast<const BasicShapeInsetRectangle*>(basicShape);
187        float left = floatValueForLength(rectangle->left(), boxWidth);
188        float top = floatValueForLength(rectangle->top(), boxHeight);
189        FloatRect bounds(
190            left,
191            top,
192            boxWidth - left - floatValueForLength(rectangle->right(), boxWidth),
193            boxHeight - top - floatValueForLength(rectangle->bottom(), boxHeight));
194        FloatSize cornerRadii(
195            floatValueForLength(rectangle->cornerRadiusX(), boxWidth),
196            floatValueForLength(rectangle->cornerRadiusY(), boxHeight));
197        ensureRadiiDoNotOverlap(bounds, cornerRadii);
198        FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxSize.height(), writingMode);
199
200        shape = createRectangleShape(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode));
201        break;
202    }
203
204    default:
205        ASSERT_NOT_REACHED();
206    }
207
208    shape->m_writingMode = writingMode;
209    shape->m_margin = floatValueForLength(margin, 0);
210    shape->m_padding = floatValueForLength(padding, 0);
211
212    return shape.release();
213}
214
215PassOwnPtr<Shape> Shape::createShape(const StyleImage* styleImage, float threshold, const LayoutSize&, WritingMode writingMode, Length margin, Length padding)
216{
217    ASSERT(styleImage && styleImage->isImageResource() && styleImage->cachedImage() && styleImage->cachedImage()->image());
218
219    Image* image = styleImage->cachedImage()->image();
220    const IntSize& imageSize = image->size();
221    OwnPtr<RasterShapeIntervals> intervals = adoptPtr(new RasterShapeIntervals(imageSize.height()));
222    OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(imageSize);
223    if (imageBuffer) {
224        GraphicsContext* graphicsContext = imageBuffer->context();
225        graphicsContext->drawImage(image, IntPoint());
226
227        RefPtr<Uint8ClampedArray> pixelArray = imageBuffer->getUnmultipliedImageData(IntRect(IntPoint(), imageSize));
228        unsigned pixelArrayLength = pixelArray->length();
229        unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA.
230        uint8_t alphaPixelThreshold = threshold * 255;
231
232        ASSERT(static_cast<unsigned>(imageSize.width() * imageSize.height() * 4) == pixelArrayLength);
233
234        for (int y = 0; y < imageSize.height(); ++y) {
235            int startX = -1;
236            for (int x = 0; x < imageSize.width() && pixelArrayOffset < pixelArrayLength; ++x, pixelArrayOffset += 4) {
237                uint8_t alpha = pixelArray->item(pixelArrayOffset);
238                if ((startX == -1) && alpha > alphaPixelThreshold) {
239                    startX = x;
240                } else if (startX != -1 && (alpha <= alphaPixelThreshold || x == imageSize.width() - 1)) {
241                    intervals->appendInterval(y, startX, x);
242                    startX = -1;
243                }
244            }
245        }
246    }
247
248    OwnPtr<RasterShape> rasterShape = adoptPtr(new RasterShape(intervals.release(), imageSize));
249    rasterShape->m_writingMode = writingMode;
250    rasterShape->m_margin = floatValueForLength(margin, 0);
251    rasterShape->m_padding = floatValueForLength(padding, 0);
252    return rasterShape.release();
253}
254
255PassOwnPtr<Shape> Shape::createLayoutBoxShape(const LayoutSize& logicalSize, WritingMode writingMode, const Length& margin, const Length& padding)
256{
257    FloatRect rect(0, 0, logicalSize.width(), logicalSize.height());
258    FloatSize radii(0, 0);
259    FloatRoundedRect bounds(rect, radii, radii, radii, radii);
260    OwnPtr<Shape> shape = createBoxShape(bounds);
261    shape->m_writingMode = writingMode;
262    shape->m_margin = floatValueForLength(margin, 0);
263    shape->m_padding = floatValueForLength(padding, 0);
264
265    return shape.release();
266}
267
268} // namespace WebCore
269