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