/* * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "core/rendering/shapes/Shape.h" #include "core/css/BasicShapeFunctions.h" #include "core/fetch/ImageResource.h" #include "core/rendering/shapes/BoxShape.h" #include "core/rendering/shapes/PolygonShape.h" #include "core/rendering/shapes/RasterShape.h" #include "core/rendering/shapes/RectangleShape.h" #include "core/rendering/style/RenderStyle.h" #include "platform/LengthFunctions.h" #include "platform/geometry/FloatSize.h" #include "platform/graphics/GraphicsContext.h" #include "platform/graphics/GraphicsTypes.h" #include "platform/graphics/ImageBuffer.h" #include "wtf/MathExtras.h" #include "wtf/OwnPtr.h" namespace blink { static PassOwnPtr createInsetShape(const FloatRoundedRect& bounds) { ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0); return adoptPtr(new BoxShape(bounds)); } static PassOwnPtr createCircleShape(const FloatPoint& center, float radius) { ASSERT(radius >= 0); return adoptPtr(new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius))); } static PassOwnPtr createEllipseShape(const FloatPoint& center, const FloatSize& radii) { ASSERT(radii.width() >= 0 && radii.height() >= 0); return adoptPtr(new RectangleShape(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii)); } static PassOwnPtr createPolygonShape(PassOwnPtr > vertices, WindRule fillRule) { return adoptPtr(new PolygonShape(vertices, fillRule)); } static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight) { return rect; } static inline FloatPoint physicalPointToLogical(const FloatPoint& point, float logicalBoxHeight) { return point; } static inline FloatSize physicalSizeToLogical(const FloatSize& size) { return size; } PassOwnPtr Shape::createShape(const BasicShape* basicShape, const LayoutSize& logicalBoxSize, float margin) { ASSERT(basicShape); float boxWidth = logicalBoxSize.width().toFloat(); float boxHeight = logicalBoxSize.height().toFloat(); OwnPtr shape; switch (basicShape->type()) { case BasicShape::BasicShapeCircleType: { const BasicShapeCircle* circle = toBasicShapeCircle(basicShape); FloatPoint center = floatPointForCenterCoordinate(circle->centerX(), circle->centerY(), FloatSize(boxWidth, boxHeight)); float radius = circle->floatValueForRadiusInBox(FloatSize(boxWidth, boxHeight)); FloatPoint logicalCenter = physicalPointToLogical(center, logicalBoxSize.height().toFloat()); shape = createCircleShape(logicalCenter, radius); break; } case BasicShape::BasicShapeEllipseType: { const BasicShapeEllipse* ellipse = toBasicShapeEllipse(basicShape); FloatPoint center = floatPointForCenterCoordinate(ellipse->centerX(), ellipse->centerY(), FloatSize(boxWidth, boxHeight)); float radiusX = ellipse->floatValueForRadiusInBox(ellipse->radiusX(), center.x(), boxWidth); float radiusY = ellipse->floatValueForRadiusInBox(ellipse->radiusY(), center.y(), boxHeight); FloatPoint logicalCenter = physicalPointToLogical(center, logicalBoxSize.height().toFloat()); shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY)); break; } case BasicShape::BasicShapePolygonType: { const BasicShapePolygon* polygon = toBasicShapePolygon(basicShape); const Vector& values = polygon->values(); size_t valuesSize = values.size(); ASSERT(!(valuesSize % 2)); OwnPtr > vertices = adoptPtr(new Vector(valuesSize / 2)); for (unsigned i = 0; i < valuesSize; i += 2) { FloatPoint vertex( floatValueForLength(values.at(i), boxWidth), floatValueForLength(values.at(i + 1), boxHeight)); (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height().toFloat()); } shape = createPolygonShape(vertices.release(), polygon->windRule()); break; } case BasicShape::BasicShapeInsetType: { const BasicShapeInset& inset = *toBasicShapeInset(basicShape); float left = floatValueForLength(inset.left(), boxWidth); float top = floatValueForLength(inset.top(), boxHeight); float right = floatValueForLength(inset.right(), boxWidth); float bottom = floatValueForLength(inset.bottom(), boxHeight); FloatRect rect(left, top, std::max(boxWidth - left - right, 0), std::max(boxHeight - top - bottom, 0)); FloatRect logicalRect = physicalRectToLogical(rect, logicalBoxSize.height().toFloat()); FloatSize boxSize(boxWidth, boxHeight); FloatSize topLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topLeftRadius(), boxSize)); FloatSize topRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topRightRadius(), boxSize)); FloatSize bottomLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomLeftRadius(), boxSize)); FloatSize bottomRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomRightRadius(), boxSize)); FloatRoundedRect::Radii cornerRadii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); cornerRadii.scale(calcBorderRadiiConstraintScaleFor(logicalRect, cornerRadii)); shape = createInsetShape(FloatRoundedRect(logicalRect, cornerRadii)); break; } default: ASSERT_NOT_REACHED(); } shape->m_margin = margin; return shape.release(); } PassOwnPtr Shape::createEmptyRasterShape(float margin) { OwnPtr intervals = adoptPtr(new RasterShapeIntervals(0, 0)); OwnPtr rasterShape = adoptPtr(new RasterShape(intervals.release(), IntSize())); rasterShape->m_margin = margin; return rasterShape.release(); } PassOwnPtr Shape::createRasterShape(Image* image, float threshold, const LayoutRect& imageR, const LayoutRect& marginR, float margin) { IntRect imageRect = pixelSnappedIntRect(imageR); IntRect marginRect = pixelSnappedIntRect(marginR); OwnPtr intervals = adoptPtr(new RasterShapeIntervals(marginRect.height(), -marginRect.y())); OwnPtr imageBuffer = ImageBuffer::create(imageRect.size()); if (imageBuffer) { GraphicsContext* graphicsContext = imageBuffer->context(); graphicsContext->drawImage(image, IntRect(IntPoint(), imageRect.size())); RefPtr pixelArray = imageBuffer->getImageData(Unmultiplied, IntRect(IntPoint(), imageRect.size())); unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. uint8_t alphaPixelThreshold = threshold * 255; ASSERT(static_cast(imageRect.width() * imageRect.height() * 4) == pixelArray->length()); int minBufferY = std::max(0, marginRect.y() - imageRect.y()); int maxBufferY = std::min(imageRect.height(), marginRect.maxY() - imageRect.y()); for (int y = minBufferY; y < maxBufferY; ++y) { int startX = -1; for (int x = 0; x < imageRect.width(); ++x, pixelArrayOffset += 4) { uint8_t alpha = pixelArray->item(pixelArrayOffset); bool alphaAboveThreshold = alpha > alphaPixelThreshold; if (startX == -1 && alphaAboveThreshold) { startX = x; } else if (startX != -1 && (!alphaAboveThreshold || x == imageRect.width() - 1)) { int endX = alphaAboveThreshold ? x + 1 : x; intervals->intervalAt(y + imageRect.y()).unite(IntShapeInterval(startX + imageRect.x(), endX + imageRect.x())); startX = -1; } } } } OwnPtr rasterShape = adoptPtr(new RasterShape(intervals.release(), marginRect.size())); rasterShape->m_margin = margin; return rasterShape.release(); } PassOwnPtr Shape::createLayoutBoxShape(const RoundedRect& roundedRect, float margin) { FloatRect rect(0, 0, roundedRect.rect().width(), roundedRect.rect().height()); FloatRoundedRect bounds(rect, roundedRect.radii()); OwnPtr shape = createInsetShape(bounds); shape->m_margin = margin; return shape.release(); } } // namespace blink