| /* |
| * 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 "core/layout/shapes/Shape.h" |
| |
| #include "core/css/BasicShapeFunctions.h" |
| #include "core/dom/DOMArrayBuffer.h" |
| #include "core/dom/DOMTypedArray.h" |
| #include "core/fetch/ImageResource.h" |
| #include "core/layout/shapes/BoxShape.h" |
| #include "core/layout/shapes/PolygonShape.h" |
| #include "core/layout/shapes/RasterShape.h" |
| #include "core/layout/shapes/RectangleShape.h" |
| #include "core/style/ComputedStyle.h" |
| #include "core/svg/graphics/SVGImage.h" |
| #include "platform/LengthFunctions.h" |
| #include "platform/geometry/FloatRoundedRect.h" |
| #include "platform/geometry/FloatSize.h" |
| #include "platform/graphics/GraphicsTypes.h" |
| #include "platform/graphics/ImageBuffer.h" |
| #include "wtf/MathExtras.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/typed_arrays/ArrayBufferContents.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| static std::unique_ptr<Shape> createInsetShape(const FloatRoundedRect& bounds) { |
| ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0); |
| return wrapUnique(new BoxShape(bounds)); |
| } |
| |
| static std::unique_ptr<Shape> createCircleShape(const FloatPoint& center, |
| float radius) { |
| ASSERT(radius >= 0); |
| return wrapUnique( |
| new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, |
| radius * 2, radius * 2), |
| FloatSize(radius, radius))); |
| } |
| |
| static std::unique_ptr<Shape> createEllipseShape(const FloatPoint& center, |
| const FloatSize& radii) { |
| ASSERT(radii.width() >= 0 && radii.height() >= 0); |
| return wrapUnique(new RectangleShape( |
| FloatRect(center.x() - radii.width(), center.y() - radii.height(), |
| radii.width() * 2, radii.height() * 2), |
| radii)); |
| } |
| |
| static std::unique_ptr<Shape> createPolygonShape( |
| std::unique_ptr<Vector<FloatPoint>> vertices, |
| WindRule fillRule) { |
| return wrapUnique(new PolygonShape(std::move(vertices), fillRule)); |
| } |
| |
| static inline FloatRect physicalRectToLogical(const FloatRect& rect, |
| float logicalBoxHeight, |
| WritingMode writingMode) { |
| if (isHorizontalWritingMode(writingMode)) |
| return rect; |
| if (isFlippedBlocksWritingMode(writingMode)) |
| return FloatRect(rect.y(), logicalBoxHeight - rect.maxX(), rect.height(), |
| rect.width()); |
| return rect.transposedRect(); |
| } |
| |
| static inline FloatPoint physicalPointToLogical(const FloatPoint& point, |
| float logicalBoxHeight, |
| WritingMode writingMode) { |
| if (isHorizontalWritingMode(writingMode)) |
| return point; |
| if (isFlippedBlocksWritingMode(writingMode)) |
| return FloatPoint(point.y(), logicalBoxHeight - point.x()); |
| return point.transposedPoint(); |
| } |
| |
| static inline FloatSize physicalSizeToLogical(const FloatSize& size, |
| WritingMode writingMode) { |
| if (isHorizontalWritingMode(writingMode)) |
| return size; |
| return size.transposedSize(); |
| } |
| |
| std::unique_ptr<Shape> Shape::createShape(const BasicShape* basicShape, |
| const LayoutSize& logicalBoxSize, |
| WritingMode writingMode, |
| float margin) { |
| ASSERT(basicShape); |
| |
| bool horizontalWritingMode = isHorizontalWritingMode(writingMode); |
| float boxWidth = horizontalWritingMode ? logicalBoxSize.width().toFloat() |
| : logicalBoxSize.height().toFloat(); |
| float boxHeight = horizontalWritingMode ? logicalBoxSize.height().toFloat() |
| : logicalBoxSize.width().toFloat(); |
| std::unique_ptr<Shape> 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(), writingMode); |
| |
| 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(), writingMode); |
| |
| shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY)); |
| break; |
| } |
| |
| case BasicShape::BasicShapePolygonType: { |
| const BasicShapePolygon* polygon = toBasicShapePolygon(basicShape); |
| const Vector<Length>& values = polygon->values(); |
| size_t valuesSize = values.size(); |
| ASSERT(!(valuesSize % 2)); |
| std::unique_ptr<Vector<FloatPoint>> vertices = |
| wrapUnique(new Vector<FloatPoint>(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(), writingMode); |
| } |
| shape = createPolygonShape(std::move(vertices), polygon->getWindRule()); |
| 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<float>(boxWidth - left - right, 0), |
| std::max<float>(boxHeight - top - bottom, 0)); |
| FloatRect logicalRect = physicalRectToLogical( |
| rect, logicalBoxSize.height().toFloat(), writingMode); |
| |
| FloatSize boxSize(boxWidth, boxHeight); |
| FloatSize topLeftRadius = physicalSizeToLogical( |
| floatSizeForLengthSize(inset.topLeftRadius(), boxSize), writingMode); |
| FloatSize topRightRadius = physicalSizeToLogical( |
| floatSizeForLengthSize(inset.topRightRadius(), boxSize), writingMode); |
| FloatSize bottomLeftRadius = physicalSizeToLogical( |
| floatSizeForLengthSize(inset.bottomLeftRadius(), boxSize), |
| writingMode); |
| FloatSize bottomRightRadius = physicalSizeToLogical( |
| floatSizeForLengthSize(inset.bottomRightRadius(), boxSize), |
| writingMode); |
| FloatRoundedRect::Radii cornerRadii(topLeftRadius, topRightRadius, |
| bottomLeftRadius, bottomRightRadius); |
| |
| FloatRoundedRect finalRect(logicalRect, cornerRadii); |
| finalRect.constrainRadii(); |
| |
| shape = createInsetShape(finalRect); |
| break; |
| } |
| |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| shape->m_writingMode = writingMode; |
| shape->m_margin = margin; |
| |
| return shape; |
| } |
| |
| std::unique_ptr<Shape> Shape::createEmptyRasterShape(WritingMode writingMode, |
| float margin) { |
| std::unique_ptr<RasterShapeIntervals> intervals = |
| wrapUnique(new RasterShapeIntervals(0, 0)); |
| std::unique_ptr<RasterShape> rasterShape = |
| wrapUnique(new RasterShape(std::move(intervals), IntSize())); |
| rasterShape->m_writingMode = writingMode; |
| rasterShape->m_margin = margin; |
| return std::move(rasterShape); |
| } |
| |
| std::unique_ptr<Shape> Shape::createRasterShape(Image* image, |
| float threshold, |
| const LayoutRect& imageR, |
| const LayoutRect& marginR, |
| WritingMode writingMode, |
| float margin) { |
| IntRect imageRect = pixelSnappedIntRect(imageR); |
| IntRect marginRect = pixelSnappedIntRect(marginR); |
| |
| std::unique_ptr<RasterShapeIntervals> intervals = wrapUnique( |
| new RasterShapeIntervals(marginRect.height(), -marginRect.y())); |
| std::unique_ptr<ImageBuffer> imageBuffer = |
| ImageBuffer::create(imageRect.size()); |
| |
| if (image && imageBuffer) { |
| // FIXME: This is not totally correct but it is needed to prevent shapes |
| // that loads SVG Images during paint invalidations to mark layoutObjects |
| // for layout, which is not allowed. See https://crbug.com/429346 |
| ImageObserverDisabler disabler(image); |
| SkPaint paint; |
| IntRect imageSourceRect(IntPoint(), image->size()); |
| IntRect imageDestRect(IntPoint(), imageRect.size()); |
| image->draw(imageBuffer->canvas(), paint, imageDestRect, imageSourceRect, |
| DoNotRespectImageOrientation, |
| Image::DoNotClampImageToSourceRect); |
| |
| WTF::ArrayBufferContents contents; |
| imageBuffer->getImageData(Unmultiplied, |
| IntRect(IntPoint(), imageRect.size()), contents); |
| DOMArrayBuffer* arrayBuffer = DOMArrayBuffer::create(contents); |
| DOMUint8ClampedArray* pixelArray = |
| DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()); |
| unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. |
| uint8_t alphaPixelThreshold = threshold * 255; |
| |
| ASSERT(static_cast<unsigned>(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; |
| } |
| } |
| } |
| } |
| |
| std::unique_ptr<RasterShape> rasterShape = |
| wrapUnique(new RasterShape(std::move(intervals), marginRect.size())); |
| rasterShape->m_writingMode = writingMode; |
| rasterShape->m_margin = margin; |
| return std::move(rasterShape); |
| } |
| |
| std::unique_ptr<Shape> Shape::createLayoutBoxShape( |
| const FloatRoundedRect& roundedRect, |
| WritingMode writingMode, |
| float margin) { |
| FloatRect rect(0, 0, roundedRect.rect().width(), roundedRect.rect().height()); |
| FloatRoundedRect bounds(rect, roundedRect.getRadii()); |
| std::unique_ptr<Shape> shape = createInsetShape(bounds); |
| shape->m_writingMode = writingMode; |
| shape->m_margin = margin; |
| |
| return shape; |
| } |
| |
| } // namespace blink |