/* * 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 "Shape.h" #include "BasicShapeFunctions.h" #include "BoxShape.h" #include "GraphicsContext.h" #include "ImageBuffer.h" #include "LengthFunctions.h" #include "PolygonShape.h" #include "RasterShape.h" #include "RectangleShape.h" #include "WindRule.h" namespace WebCore { static std::unique_ptr createInsetShape(const FloatRoundedRect& bounds) { ASSERT(bounds.rect().width() >= 0 && bounds.rect().height() >= 0); return std::make_unique(bounds); } static std::unique_ptr createCircleShape(const FloatPoint& center, float radius) { ASSERT(radius >= 0); return std::make_unique(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius)); } static std::unique_ptr createEllipseShape(const FloatPoint& center, const FloatSize& radii) { ASSERT(radii.width() >= 0 && radii.height() >= 0); return std::make_unique(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii); } static std::unique_ptr createPolygonShape(std::unique_ptr> vertices, WindRule fillRule) { return std::make_unique(WTFMove(vertices), fillRule); } static inline FloatRect physicalRectToLogical(const FloatRect& rect, float logicalBoxHeight, WritingMode writingMode) { if (isHorizontalWritingMode(writingMode)) return rect; if (isFlippedWritingMode(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 (isFlippedWritingMode(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::createShape(const BasicShape& basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, float margin) { bool horizontalWritingMode = isHorizontalWritingMode(writingMode); float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height(); float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width(); std::unique_ptr shape; switch (basicShape.type()) { case BasicShape::BasicShapeCircleType: { const auto& circle = downcast(basicShape); float centerX = floatValueForCenterCoordinate(circle.centerX(), boxWidth); float centerY = floatValueForCenterCoordinate(circle.centerY(), boxHeight); float radius = circle.floatValueForRadiusInBox(boxWidth, boxHeight); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); shape = createCircleShape(logicalCenter, radius); break; } case BasicShape::BasicShapeEllipseType: { const auto& ellipse = downcast(basicShape); float centerX = floatValueForCenterCoordinate(ellipse.centerX(), boxWidth); float centerY = floatValueForCenterCoordinate(ellipse.centerY(), boxHeight); float radiusX = ellipse.floatValueForRadiusInBox(ellipse.radiusX(), centerX, boxWidth); float radiusY = ellipse.floatValueForRadiusInBox(ellipse.radiusY(), centerY, boxHeight); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode); shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY)); break; } case BasicShape::BasicShapePolygonType: { const auto& polygon = downcast(basicShape); const Vector& values = polygon.values(); size_t valuesSize = values.size(); ASSERT(!(valuesSize % 2)); std::unique_ptr> vertices = std::make_unique>(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(), writingMode); } shape = createPolygonShape(WTFMove(vertices), polygon.windRule()); break; } case BasicShape::BasicShapeInsetType: { const auto& inset = downcast(basicShape); float left = floatValueForLength(inset.left(), boxWidth); float top = floatValueForLength(inset.top(), boxHeight); FloatRect rect(left, top, std::max(boxWidth - left - floatValueForLength(inset.right(), boxWidth), 0), std::max(boxHeight - top - floatValueForLength(inset.bottom(), boxHeight), 0)); FloatRect logicalRect = physicalRectToLogical(rect, logicalBoxSize.height(), 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); cornerRadii.scale(calcBorderRadiiConstraintScaleFor(logicalRect, cornerRadii)); shape = createInsetShape(FloatRoundedRect(logicalRect, cornerRadii)); break; } default: ASSERT_NOT_REACHED(); } shape->m_writingMode = writingMode; shape->m_margin = margin; return shape; } std::unique_ptr Shape::createRasterShape(Image* image, float threshold, const LayoutRect& imageR, const LayoutRect& marginR, WritingMode writingMode, float margin) { ASSERT(marginR.height() >= 0); IntRect imageRect = snappedIntRect(imageR); IntRect marginRect = snappedIntRect(marginR); auto intervals = std::make_unique(marginRect.height(), -marginRect.y()); // FIXME (149420): This buffer should not be unconditionally unaccelerated. std::unique_ptr imageBuffer = ImageBuffer::create(imageRect.size(), Unaccelerated); if (imageBuffer) { GraphicsContext& graphicsContext = imageBuffer->context(); if (image) graphicsContext.drawImage(*image, IntRect(IntPoint(), imageRect.size())); RefPtr pixelArray = imageBuffer->getUnmultipliedImageData(IntRect(IntPoint(), imageRect.size())); unsigned pixelArrayLength = pixelArray->length(); unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. uint8_t alphaPixelThreshold = threshold * 255; int minBufferY = std::max(0, marginRect.y() - imageRect.y()); int maxBufferY = std::min(imageRect.height(), marginRect.maxY() - imageRect.y()); if (static_cast(imageRect.width() * imageRect.height() * 4) == pixelArrayLength) { 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)) { // We're creating "end-point exclusive" intervals here. The value of an interval's x1 is // the first index of an above-threshold pixel for y, and the value of x2 is 1+ the index // of the last above-threshold pixel. int endX = alphaAboveThreshold ? x + 1 : x; intervals->intervalAt(y + imageRect.y()).unite(IntShapeInterval(startX + imageRect.x(), endX + imageRect.x())); startX = -1; } } } } } auto rasterShape = std::make_unique(WTFMove(intervals), marginRect.size()); rasterShape->m_writingMode = writingMode; rasterShape->m_margin = margin; return WTFMove(rasterShape); } std::unique_ptr Shape::createBoxShape(const RoundedRect& roundedRect, WritingMode writingMode, float margin) { ASSERT(roundedRect.rect().width() >= 0 && roundedRect.rect().height() >= 0); FloatRect rect(0, 0, roundedRect.rect().width(), roundedRect.rect().height()); FloatRoundedRect bounds(rect, roundedRect.radii()); auto shape = std::make_unique(bounds); shape->m_writingMode = writingMode; shape->m_margin = margin; return WTFMove(shape); } } // namespace WebCore