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