blob: 1001365a511eb9f8cbd44c4f93087b228b3a53a5 [file] [log] [blame]
/*
* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2012, 2013 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 "TransformFunctions.h"
#include "CSSFunctionValue.h"
#include "CSSPrimitiveValueMappings.h"
#include "CSSValueList.h"
#include "Matrix3DTransformOperation.h"
#include "MatrixTransformOperation.h"
#include "PerspectiveTransformOperation.h"
#include "RotateTransformOperation.h"
#include "ScaleTransformOperation.h"
#include "SkewTransformOperation.h"
#include "TranslateTransformOperation.h"
namespace WebCore {
static TransformOperation::OperationType transformOperationType(CSSValueID type)
{
switch (type) {
case CSSValueScale:
return TransformOperation::SCALE;
case CSSValueScaleX:
return TransformOperation::SCALE_X;
case CSSValueScaleY:
return TransformOperation::SCALE_Y;
case CSSValueScaleZ:
return TransformOperation::SCALE_Z;
case CSSValueScale3d:
return TransformOperation::SCALE_3D;
case CSSValueTranslate:
return TransformOperation::TRANSLATE;
case CSSValueTranslateX:
return TransformOperation::TRANSLATE_X;
case CSSValueTranslateY:
return TransformOperation::TRANSLATE_Y;
case CSSValueTranslateZ:
return TransformOperation::TRANSLATE_Z;
case CSSValueTranslate3d:
return TransformOperation::TRANSLATE_3D;
case CSSValueRotate:
return TransformOperation::ROTATE;
case CSSValueRotateX:
return TransformOperation::ROTATE_X;
case CSSValueRotateY:
return TransformOperation::ROTATE_Y;
case CSSValueRotateZ:
return TransformOperation::ROTATE_Z;
case CSSValueRotate3d:
return TransformOperation::ROTATE_3D;
case CSSValueSkew:
return TransformOperation::SKEW;
case CSSValueSkewX:
return TransformOperation::SKEW_X;
case CSSValueSkewY:
return TransformOperation::SKEW_Y;
case CSSValueMatrix:
return TransformOperation::MATRIX;
case CSSValueMatrix3d:
return TransformOperation::MATRIX_3D;
case CSSValuePerspective:
return TransformOperation::PERSPECTIVE;
default:
break;
}
return TransformOperation::NONE;
}
Length convertToFloatLength(const CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
{
return primitiveValue ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData) : Length(Undefined);
}
bool transformsForValue(const CSSValue& value, const CSSToLengthConversionData& conversionData, TransformOperations& outOperations)
{
if (!is<CSSValueList>(value)) {
outOperations.clear();
return false;
}
TransformOperations operations;
for (auto& currentValue : downcast<CSSValueList>(value)) {
if (!is<CSSFunctionValue>(currentValue))
continue;
auto& transformValue = downcast<CSSFunctionValue>(currentValue.get());
if (!transformValue.length())
continue;
bool haveNonPrimitiveValue = false;
for (unsigned j = 0; j < transformValue.length(); ++j) {
if (!is<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(j))) {
haveNonPrimitiveValue = true;
break;
}
}
if (haveNonPrimitiveValue)
continue;
auto& firstValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0));
switch (transformValue.name()) {
case CSSValueScale:
case CSSValueScaleX:
case CSSValueScaleY: {
double sx = 1.0;
double sy = 1.0;
if (transformValue.name() == CSSValueScaleY)
sy = firstValue.doubleValue();
else {
sx = firstValue.doubleValue();
if (transformValue.name() != CSSValueScaleX) {
if (transformValue.length() > 1) {
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
sy = secondValue.doubleValue();
} else
sy = sx;
}
}
operations.operations().append(ScaleTransformOperation::create(sx, sy, 1.0, transformOperationType(transformValue.name())));
break;
}
case CSSValueScaleZ:
case CSSValueScale3d: {
double sx = 1.0;
double sy = 1.0;
double sz = 1.0;
if (transformValue.name() == CSSValueScaleZ)
sz = firstValue.doubleValue();
else if (transformValue.name() == CSSValueScaleY)
sy = firstValue.doubleValue();
else {
sx = firstValue.doubleValue();
if (transformValue.name() != CSSValueScaleX) {
if (transformValue.length() > 2) {
auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
sz = thirdValue.doubleValue();
}
if (transformValue.length() > 1) {
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
sy = secondValue.doubleValue();
} else
sy = sx;
}
}
operations.operations().append(ScaleTransformOperation::create(sx, sy, sz, transformOperationType(transformValue.name())));
break;
}
case CSSValueTranslate:
case CSSValueTranslateX:
case CSSValueTranslateY: {
Length tx = Length(0, Fixed);
Length ty = Length(0, Fixed);
if (transformValue.name() == CSSValueTranslateY)
ty = convertToFloatLength(&firstValue, conversionData);
else {
tx = convertToFloatLength(&firstValue, conversionData);
if (transformValue.name() != CSSValueTranslateX) {
if (transformValue.length() > 1) {
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
ty = convertToFloatLength(&secondValue, conversionData);
}
}
}
if (tx.isUndefined() || ty.isUndefined())
return false;
operations.operations().append(TranslateTransformOperation::create(tx, ty, Length(0, Fixed), transformOperationType(transformValue.name())));
break;
}
case CSSValueTranslateZ:
case CSSValueTranslate3d: {
Length tx = Length(0, Fixed);
Length ty = Length(0, Fixed);
Length tz = Length(0, Fixed);
if (transformValue.name() == CSSValueTranslateZ)
tz = convertToFloatLength(&firstValue, conversionData);
else if (transformValue.name() == CSSValueTranslateY)
ty = convertToFloatLength(&firstValue, conversionData);
else {
tx = convertToFloatLength(&firstValue, conversionData);
if (transformValue.name() != CSSValueTranslateX) {
if (transformValue.length() > 2) {
auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
tz = convertToFloatLength(&thirdValue, conversionData);
}
if (transformValue.length() > 1) {
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
ty = convertToFloatLength(&secondValue, conversionData);
}
}
}
if (tx.isUndefined() || ty.isUndefined() || tz.isUndefined())
return false;
operations.operations().append(TranslateTransformOperation::create(tx, ty, tz, transformOperationType(transformValue.name())));
break;
}
case CSSValueRotate: {
double angle = firstValue.computeDegrees();
operations.operations().append(RotateTransformOperation::create(0, 0, 1, angle, transformOperationType(transformValue.name())));
break;
}
case CSSValueRotateX:
case CSSValueRotateY:
case CSSValueRotateZ: {
double x = 0;
double y = 0;
double z = 0;
double angle = firstValue.computeDegrees();
if (transformValue.name() == CSSValueRotateX)
x = 1;
else if (transformValue.name() == CSSValueRotateY)
y = 1;
else
z = 1;
operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
break;
}
case CSSValueRotate3d: {
if (transformValue.length() < 4)
break;
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
auto& thirdValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2));
auto& fourthValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3));
double x = firstValue.doubleValue();
double y = secondValue.doubleValue();
double z = thirdValue.doubleValue();
double angle = fourthValue.computeDegrees();
operations.operations().append(RotateTransformOperation::create(x, y, z, angle, transformOperationType(transformValue.name())));
break;
}
case CSSValueSkew:
case CSSValueSkewX:
case CSSValueSkewY: {
double angleX = 0;
double angleY = 0;
double angle = firstValue.computeDegrees();
if (transformValue.name() == CSSValueSkewY)
angleY = angle;
else {
angleX = angle;
if (transformValue.name() == CSSValueSkew) {
if (transformValue.length() > 1) {
auto& secondValue = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1));
angleY = secondValue.computeDegrees();
}
}
}
operations.operations().append(SkewTransformOperation::create(angleX, angleY, transformOperationType(transformValue.name())));
break;
}
case CSSValueMatrix: {
if (transformValue.length() < 6)
break;
double a = firstValue.doubleValue();
double b = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue();
double c = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue();
double d = downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue();
double e = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue();
double f = conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue();
operations.operations().append(MatrixTransformOperation::create(a, b, c, d, e, f));
break;
}
case CSSValueMatrix3d: {
if (transformValue.length() < 16)
break;
TransformationMatrix matrix(downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(0)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(1)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(2)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(3)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(4)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(5)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(6)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(7)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(8)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(9)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(10)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(11)).doubleValue(),
conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(12)).doubleValue(),
conversionData.zoom() * downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(13)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(14)).doubleValue(),
downcast<CSSPrimitiveValue>(*transformValue.itemWithoutBoundsCheck(15)).doubleValue());
operations.operations().append(Matrix3DTransformOperation::create(matrix));
break;
}
case CSSValuePerspective: {
Length p = Length(0, Fixed);
if (firstValue.isLength())
p = convertToFloatLength(&firstValue, conversionData);
else {
// This is a quirk that should go away when 3d transforms are finalized.
double val = firstValue.doubleValue();
p = val >= 0 ? Length(clampToPositiveInteger(val), Fixed) : Length(Undefined);
}
if (p.isUndefined())
return false;
operations.operations().append(PerspectiveTransformOperation::create(p));
break;
}
default:
ASSERT_NOT_REACHED();
break;
}
}
outOperations = operations;
return true;
}
}