blob: c382b09c71f00e74d1a5abfdf1839e3f582b1d8a [file] [log] [blame]
/*
* 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 HOLDER “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 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 "BasicShapeFunctions.h"
#include "BasicShapes.h"
#include "CSSBasicShapes.h"
#include "CSSPrimitiveValueMappings.h"
#include "CSSValuePool.h"
#include "Pair.h"
#include "RenderStyle.h"
namespace WebCore {
PassRefPtr<CSSPrimitiveValue> valueForBox(BasicShape::ReferenceBox box)
{
switch (box) {
case BasicShape::ContentBox:
return cssValuePool().createIdentifierValue(CSSValueContentBox);
case BasicShape::PaddingBox:
return cssValuePool().createIdentifierValue(CSSValuePaddingBox);
case BasicShape::BorderBox:
return cssValuePool().createIdentifierValue(CSSValueBorderBox);
case BasicShape::MarginBox:
return cssValuePool().createIdentifierValue(CSSValueMarginBox);
case BasicShape::None:
return nullptr;
}
ASSERT_NOT_REACHED();
return nullptr;
}
BasicShape::ReferenceBox boxForValue(const CSSPrimitiveValue* value)
{
if (!value)
return BasicShape::None;
switch (value->getValueID()) {
case CSSValueContentBox:
return BasicShape::ContentBox;
case CSSValuePaddingBox:
return BasicShape::PaddingBox;
case CSSValueBorderBox:
return BasicShape::BorderBox;
case CSSValueMarginBox:
return BasicShape::MarginBox;
default:
ASSERT_NOT_REACHED();
return BasicShape::None;
}
}
static PassRefPtr<CSSPrimitiveValue> valueForCenterCoordinate(CSSValuePool& pool, const RenderStyle* style, const BasicShapeCenterCoordinate& center)
{
CSSValueID keyword = CSSValueInvalid;
switch (center.keyword()) {
case BasicShapeCenterCoordinate::None:
return pool.createValue(center.length(), style);
case BasicShapeCenterCoordinate::Top:
keyword = CSSValueTop;
break;
case BasicShapeCenterCoordinate::Right:
keyword = CSSValueRight;
break;
case BasicShapeCenterCoordinate::Bottom:
keyword = CSSValueBottom;
break;
case BasicShapeCenterCoordinate::Left:
keyword = CSSValueLeft;
break;
}
return pool.createValue(Pair::create(pool.createIdentifierValue(keyword), pool.createValue(center.length(), style)));
}
static PassRefPtr<CSSPrimitiveValue> basicShapeRadiusToCSSValue(const RenderStyle* style, CSSValuePool& pool, const BasicShapeRadius& radius)
{
switch (radius.type()) {
case BasicShapeRadius::Value:
return pool.createValue(radius.value(), style);
case BasicShapeRadius::ClosestSide:
return pool.createIdentifierValue(CSSValueClosestSide);
case BasicShapeRadius::FarthestSide:
return pool.createIdentifierValue(CSSValueFarthestSide);
}
ASSERT_NOT_REACHED();
return 0;
}
PassRefPtr<CSSValue> valueForBasicShape(const RenderStyle* style, const BasicShape* basicShape)
{
CSSValuePool& pool = cssValuePool();
RefPtr<CSSBasicShape> basicShapeValue;
switch (basicShape->type()) {
case BasicShape::BasicShapeRectangleType: {
const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape);
RefPtr<CSSBasicShapeRectangle> rectangleValue = CSSBasicShapeRectangle::create();
rectangleValue->setX(pool.createValue(rectangle->x(), style));
rectangleValue->setY(pool.createValue(rectangle->y(), style));
rectangleValue->setWidth(pool.createValue(rectangle->width(), style));
rectangleValue->setHeight(pool.createValue(rectangle->height(), style));
rectangleValue->setRadiusX(pool.createValue(rectangle->cornerRadiusX(), style));
rectangleValue->setRadiusY(pool.createValue(rectangle->cornerRadiusY(), style));
basicShapeValue = rectangleValue.release();
break;
}
case BasicShape::DeprecatedBasicShapeCircleType: {
const DeprecatedBasicShapeCircle* circle = static_cast<const DeprecatedBasicShapeCircle*>(basicShape);
RefPtr<CSSDeprecatedBasicShapeCircle> circleValue = CSSDeprecatedBasicShapeCircle::create();
circleValue->setCenterX(pool.createValue(circle->centerX(), style));
circleValue->setCenterY(pool.createValue(circle->centerY(), style));
circleValue->setRadius(pool.createValue(circle->radius(), style));
basicShapeValue = circleValue.release();
break;
}
case BasicShape::BasicShapeCircleType: {
const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape);
RefPtr<CSSBasicShapeCircle> circleValue = CSSBasicShapeCircle::create();
circleValue->setCenterX(valueForCenterCoordinate(pool, style, circle->centerX()));
circleValue->setCenterY(valueForCenterCoordinate(pool, style, circle->centerY()));
circleValue->setRadius(basicShapeRadiusToCSSValue(style, pool, circle->radius()));
basicShapeValue = circleValue.release();
break;
}
case BasicShape::DeprecatedBasicShapeEllipseType: {
const DeprecatedBasicShapeEllipse* ellipse = static_cast<const DeprecatedBasicShapeEllipse*>(basicShape);
RefPtr<CSSDeprecatedBasicShapeEllipse> ellipseValue = CSSDeprecatedBasicShapeEllipse::create();
ellipseValue->setCenterX(pool.createValue(ellipse->centerX(), style));
ellipseValue->setCenterY(pool.createValue(ellipse->centerY(), style));
ellipseValue->setRadiusX(pool.createValue(ellipse->radiusX(), style));
ellipseValue->setRadiusY(pool.createValue(ellipse->radiusY(), style));
basicShapeValue = ellipseValue.release();
break;
}
case BasicShape::BasicShapeEllipseType: {
const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape);
RefPtr<CSSBasicShapeEllipse> ellipseValue = CSSBasicShapeEllipse::create();
ellipseValue->setCenterX(valueForCenterCoordinate(pool, style, ellipse->centerX()));
ellipseValue->setCenterY(valueForCenterCoordinate(pool, style, ellipse->centerY()));
ellipseValue->setRadiusX(basicShapeRadiusToCSSValue(style, pool, ellipse->radiusX()));
ellipseValue->setRadiusY(basicShapeRadiusToCSSValue(style, pool, ellipse->radiusY()));
basicShapeValue = ellipseValue.release();
break;
}
case BasicShape::BasicShapePolygonType: {
const BasicShapePolygon* polygon = static_cast<const BasicShapePolygon*>(basicShape);
RefPtr<CSSBasicShapePolygon> polygonValue = CSSBasicShapePolygon::create();
polygonValue->setWindRule(polygon->windRule());
const Vector<Length>& values = polygon->values();
for (unsigned i = 0; i < values.size(); i += 2)
polygonValue->appendPoint(pool.createValue(values.at(i), style), pool.createValue(values.at(i + 1), style));
basicShapeValue = polygonValue.release();
break;
}
case BasicShape::BasicShapeInsetRectangleType: {
const BasicShapeInsetRectangle* rectangle = static_cast<const BasicShapeInsetRectangle*>(basicShape);
RefPtr<CSSBasicShapeInsetRectangle> rectangleValue = CSSBasicShapeInsetRectangle::create();
rectangleValue->setTop(pool.createValue(rectangle->top(), style));
rectangleValue->setRight(pool.createValue(rectangle->right(), style));
rectangleValue->setBottom(pool.createValue(rectangle->bottom(), style));
rectangleValue->setLeft(pool.createValue(rectangle->left(), style));
rectangleValue->setRadiusX(pool.createValue(rectangle->cornerRadiusX(), style));
rectangleValue->setRadiusY(pool.createValue(rectangle->cornerRadiusY(), style));
basicShapeValue = rectangleValue.release();
break;
}
case BasicShape::BasicShapeInsetType: {
const BasicShapeInset* inset = static_cast<const BasicShapeInset*>(basicShape);
RefPtr<CSSBasicShapeInset> insetValue = CSSBasicShapeInset::create();
insetValue->setTop(pool.createValue(inset->top()));
insetValue->setRight(pool.createValue(inset->right()));
insetValue->setBottom(pool.createValue(inset->bottom()));
insetValue->setLeft(pool.createValue(inset->left()));
insetValue->setTopLeftRadius(pool.createValue(inset->topLeftRadius()));
insetValue->setTopRightRadius(pool.createValue(inset->topRightRadius()));
insetValue->setBottomRightRadius(pool.createValue(inset->bottomRightRadius()));
insetValue->setBottomLeftRadius(pool.createValue(inset->bottomLeftRadius()));
basicShapeValue = insetValue.release();
break;
}
default:
break;
}
basicShapeValue->setBox(valueForBox(basicShape->box()));
return pool.createValue(basicShapeValue.release());
}
static Length convertToLength(const RenderStyle* style, const RenderStyle* rootStyle, CSSPrimitiveValue* value)
{
return value->convertToLength<FixedIntegerConversion | FixedFloatConversion | PercentConversion | CalculatedConversion | ViewportPercentageConversion>(style, rootStyle, style->effectiveZoom());
}
static BasicShapeCenterCoordinate convertToCenterCoordinate(const RenderStyle* style, const RenderStyle* rootStyle, CSSPrimitiveValue* value)
{
if (Pair* pair = value->getPairValue()) {
BasicShapeCenterCoordinate::Keyword keyword = BasicShapeCenterCoordinate::None;
switch (pair->first()->getValueID()) {
case CSSValueTop:
keyword = BasicShapeCenterCoordinate::Top;
break;
case CSSValueRight:
keyword = BasicShapeCenterCoordinate::Right;
break;
case CSSValueBottom:
keyword = BasicShapeCenterCoordinate::Bottom;
break;
case CSSValueLeft:
keyword = BasicShapeCenterCoordinate::Left;
break;
default:
ASSERT_NOT_REACHED();
break;
}
return BasicShapeCenterCoordinate(keyword, convertToLength(style, rootStyle, pair->second()));
}
return BasicShapeCenterCoordinate(convertToLength(style, rootStyle, value));
}
static BasicShapeRadius cssValueToBasicShapeRadius(const RenderStyle* style, const RenderStyle* rootStyle, PassRefPtr<CSSPrimitiveValue> radius)
{
if (!radius)
return BasicShapeRadius(BasicShapeRadius::ClosestSide);
if (radius->isValueID()) {
switch (radius->getValueID()) {
case CSSValueClosestSide:
return BasicShapeRadius(BasicShapeRadius::ClosestSide);
case CSSValueFarthestSide:
return BasicShapeRadius(BasicShapeRadius::FarthestSide);
default:
ASSERT_NOT_REACHED();
break;
}
}
return BasicShapeRadius(convertToLength(style, rootStyle, radius.get()));
}
PassRefPtr<BasicShape> basicShapeForValue(const RenderStyle* style, const RenderStyle* rootStyle, const CSSBasicShape* basicShapeValue)
{
RefPtr<BasicShape> basicShape;
switch (basicShapeValue->type()) {
case CSSBasicShape::CSSBasicShapeRectangleType: {
const CSSBasicShapeRectangle* rectValue = static_cast<const CSSBasicShapeRectangle *>(basicShapeValue);
RefPtr<BasicShapeRectangle> rect = BasicShapeRectangle::create();
rect->setX(convertToLength(style, rootStyle, rectValue->x()));
rect->setY(convertToLength(style, rootStyle, rectValue->y()));
rect->setWidth(convertToLength(style, rootStyle, rectValue->width()));
rect->setHeight(convertToLength(style, rootStyle, rectValue->height()));
if (rectValue->radiusX()) {
Length radiusX = convertToLength(style, rootStyle, rectValue->radiusX());
rect->setCornerRadiusX(radiusX);
if (rectValue->radiusY())
rect->setCornerRadiusY(convertToLength(style, rootStyle, rectValue->radiusY()));
else
rect->setCornerRadiusY(radiusX);
} else {
rect->setCornerRadiusX(Length(0, Fixed));
rect->setCornerRadiusY(Length(0, Fixed));
}
basicShape = rect.release();
break;
}
case CSSBasicShape::CSSDeprecatedBasicShapeCircleType: {
const CSSDeprecatedBasicShapeCircle* circleValue = static_cast<const CSSDeprecatedBasicShapeCircle *>(basicShapeValue);
RefPtr<DeprecatedBasicShapeCircle> circle = DeprecatedBasicShapeCircle::create();
circle->setCenterX(convertToLength(style, rootStyle, circleValue->centerX()));
circle->setCenterY(convertToLength(style, rootStyle, circleValue->centerY()));
circle->setRadius(convertToLength(style, rootStyle, circleValue->radius()));
basicShape = circle.release();
break;
}
case CSSBasicShape::CSSBasicShapeCircleType: {
const CSSBasicShapeCircle* circleValue = static_cast<const CSSBasicShapeCircle *>(basicShapeValue);
RefPtr<BasicShapeCircle> circle = BasicShapeCircle::create();
if (circleValue->centerX() && circleValue->centerY()) {
circle->setCenterX(convertToCenterCoordinate(style, rootStyle, circleValue->centerX()));
circle->setCenterY(convertToCenterCoordinate(style, rootStyle, circleValue->centerY()));
} else {
circle->setCenterX(BasicShapeCenterCoordinate(Length(50, Percent)));
circle->setCenterY(BasicShapeCenterCoordinate(Length(50, Percent)));
}
circle->setRadius(cssValueToBasicShapeRadius(style, rootStyle, circleValue->radius()));
basicShape = circle.release();
break;
}
case CSSBasicShape::CSSDeprecatedBasicShapeEllipseType: {
const CSSDeprecatedBasicShapeEllipse* ellipseValue = static_cast<const CSSDeprecatedBasicShapeEllipse *>(basicShapeValue);
RefPtr<DeprecatedBasicShapeEllipse> ellipse = DeprecatedBasicShapeEllipse::create();
ellipse->setCenterX(convertToLength(style, rootStyle, ellipseValue->centerX()));
ellipse->setCenterY(convertToLength(style, rootStyle, ellipseValue->centerY()));
ellipse->setRadiusX(convertToLength(style, rootStyle, ellipseValue->radiusX()));
ellipse->setRadiusY(convertToLength(style, rootStyle, ellipseValue->radiusY()));
basicShape = ellipse.release();
break;
}
case CSSBasicShape::CSSBasicShapeEllipseType: {
const CSSBasicShapeEllipse* ellipseValue = static_cast<const CSSBasicShapeEllipse *>(basicShapeValue);
RefPtr<BasicShapeEllipse> ellipse = BasicShapeEllipse::create();
if (ellipseValue->centerX() && ellipseValue->centerY()) {
ellipse->setCenterX(convertToCenterCoordinate(style, rootStyle, ellipseValue->centerX()));
ellipse->setCenterY(convertToCenterCoordinate(style, rootStyle, ellipseValue->centerY()));
} else {
ellipse->setCenterX(BasicShapeCenterCoordinate(Length(50, Percent)));
ellipse->setCenterY(BasicShapeCenterCoordinate(Length(50, Percent)));
}
ellipse->setRadiusX(cssValueToBasicShapeRadius(style, rootStyle, ellipseValue->radiusX()));
ellipse->setRadiusY(cssValueToBasicShapeRadius(style, rootStyle, ellipseValue->radiusY()));
basicShape = ellipse.release();
break;
}
case CSSBasicShape::CSSBasicShapePolygonType: {
const CSSBasicShapePolygon* polygonValue = static_cast<const CSSBasicShapePolygon *>(basicShapeValue);
RefPtr<BasicShapePolygon> polygon = BasicShapePolygon::create();
polygon->setWindRule(polygonValue->windRule());
const Vector<RefPtr<CSSPrimitiveValue>>& values = polygonValue->values();
for (unsigned i = 0; i < values.size(); i += 2)
polygon->appendPoint(convertToLength(style, rootStyle, values.at(i).get()), convertToLength(style, rootStyle, values.at(i + 1).get()));
basicShape = polygon.release();
break;
}
case CSSBasicShape::CSSBasicShapeInsetRectangleType: {
const CSSBasicShapeInsetRectangle* rectValue = static_cast<const CSSBasicShapeInsetRectangle *>(basicShapeValue);
RefPtr<BasicShapeInsetRectangle> rect = BasicShapeInsetRectangle::create();
rect->setTop(convertToLength(style, rootStyle, rectValue->top()));
rect->setRight(convertToLength(style, rootStyle, rectValue->right()));
rect->setBottom(convertToLength(style, rootStyle, rectValue->bottom()));
rect->setLeft(convertToLength(style, rootStyle, rectValue->left()));
if (rectValue->radiusX()) {
Length radiusX = convertToLength(style, rootStyle, rectValue->radiusX());
rect->setCornerRadiusX(radiusX);
if (rectValue->radiusY())
rect->setCornerRadiusY(convertToLength(style, rootStyle, rectValue->radiusY()));
else
rect->setCornerRadiusY(radiusX);
} else {
rect->setCornerRadiusX(Length(0, Fixed));
rect->setCornerRadiusY(Length(0, Fixed));
}
basicShape = rect.release();
break;
}
case CSSBasicShape::CSSBasicShapeInsetType: {
const CSSBasicShapeInset* rectValue = static_cast<const CSSBasicShapeInset* >(basicShapeValue);
RefPtr<BasicShapeInset> rect = BasicShapeInset::create();
if (rectValue->left())
rect->setTop(convertToLength(style, rootStyle, rectValue->top()));
else
return rect;
if (rectValue->right())
rect->setRight(convertToLength(style, rootStyle, rectValue->right()));
if (rectValue->bottom())
rect->setBottom(convertToLength(style, rootStyle, rectValue->bottom()));
if (rectValue->left())
rect->setLeft(convertToLength(style, rootStyle, rectValue->left()));
if (rectValue->topLeftRadius()) {
Pair* topLeftRadius = rectValue->topLeftRadius()->getPairValue();
rect->setTopLeftRadius(LengthSize(convertToLength(style, rootStyle, topLeftRadius->first()), convertToLength(style, rootStyle, topLeftRadius->second())));
} else
rect->setTopLeftRadius(LengthSize(Length(0, Fixed), Length(0, Fixed)));
if (rectValue->topRightRadius()) {
Pair* topRightRadius = rectValue->topRightRadius()->getPairValue();
rect->setTopRightRadius(LengthSize(convertToLength(style, rootStyle, topRightRadius->first()), convertToLength(style, rootStyle, topRightRadius->second())));
} else
rect->setTopRightRadius(LengthSize(Length(0, Fixed), Length(0, Fixed)));
if (rectValue->bottomRightRadius()) {
Pair* bottomRightRadius = rectValue->bottomRightRadius()->getPairValue();
rect->setBottomRightRadius(LengthSize(convertToLength(style, rootStyle, bottomRightRadius->first()), convertToLength(style, rootStyle, bottomRightRadius->second())));
} else
rect->setBottomRightRadius(LengthSize(Length(0, Fixed), Length(0, Fixed)));
if (rectValue->topLeftRadius()) {
Pair* bottomLeftRadius = rectValue->bottomLeftRadius()->getPairValue();
rect->setBottomLeftRadius(LengthSize(convertToLength(style, rootStyle, bottomLeftRadius->first()), convertToLength(style, rootStyle, bottomLeftRadius->second())));
} else
rect->setBottomLeftRadius(LengthSize(Length(0, Fixed), Length(0, Fixed)));
basicShape = rect.release();
break;
}
default:
break;
}
basicShape->setBox(boxForValue(basicShapeValue->box()));
return basicShape.release();
}
float floatValueForCenterCoordinate(const BasicShapeCenterCoordinate& center, float boxDimension)
{
float offset = floatValueForLength(center.length(), boxDimension);
switch (center.keyword()) {
case BasicShapeCenterCoordinate::None:
return offset;
case BasicShapeCenterCoordinate::Top:
case BasicShapeCenterCoordinate::Left:
return offset;
case BasicShapeCenterCoordinate::Bottom:
case BasicShapeCenterCoordinate::Right:
return boxDimension - offset;
}
ASSERT_NOT_REACHED();
return 0;
}
}