blob: d2bfb9520c47e10e1b8e68863205d15482ef720b [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc.
* All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
* Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
* (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/css/resolver/transform_builder.h"
#include "third_party/blink/renderer/core/css/css_function_value.h"
#include "third_party/blink/renderer/core/css/css_math_expression_node.h"
#include "third_party/blink/renderer/core/css/css_math_function_value.h"
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
#include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
namespace blink {
static Length ConvertToFloatLength(
const CSSPrimitiveValue& primitive_value,
const CSSToLengthConversionData& conversion_data) {
return primitive_value.ConvertToLength(conversion_data);
}
static TransformOperation::OperationType GetTransformOperationType(
CSSValueID type) {
switch (type) {
default:
NOTREACHED();
FALLTHROUGH;
case CSSValueID::kScale:
return TransformOperation::kScale;
case CSSValueID::kScaleX:
return TransformOperation::kScaleX;
case CSSValueID::kScaleY:
return TransformOperation::kScaleY;
case CSSValueID::kScaleZ:
return TransformOperation::kScaleZ;
case CSSValueID::kScale3d:
return TransformOperation::kScale3D;
case CSSValueID::kTranslate:
return TransformOperation::kTranslate;
case CSSValueID::kTranslateX:
return TransformOperation::kTranslateX;
case CSSValueID::kTranslateY:
return TransformOperation::kTranslateY;
case CSSValueID::kTranslateZ:
return TransformOperation::kTranslateZ;
case CSSValueID::kTranslate3d:
return TransformOperation::kTranslate3D;
case CSSValueID::kRotate:
return TransformOperation::kRotate;
case CSSValueID::kRotateX:
return TransformOperation::kRotateX;
case CSSValueID::kRotateY:
return TransformOperation::kRotateY;
case CSSValueID::kRotateZ:
return TransformOperation::kRotateZ;
case CSSValueID::kRotate3d:
return TransformOperation::kRotate3D;
case CSSValueID::kSkew:
return TransformOperation::kSkew;
case CSSValueID::kSkewX:
return TransformOperation::kSkewX;
case CSSValueID::kSkewY:
return TransformOperation::kSkewY;
case CSSValueID::kMatrix:
return TransformOperation::kMatrix;
case CSSValueID::kMatrix3d:
return TransformOperation::kMatrix3D;
case CSSValueID::kPerspective:
return TransformOperation::kPerspective;
}
}
bool TransformBuilder::HasRelativeLengths(const CSSValueList& value_list) {
for (auto& value : value_list) {
const auto* transform_value = To<CSSFunctionValue>(value.Get());
for (const CSSValue* item : *transform_value) {
const auto& primitive_value = To<CSSPrimitiveValue>(*item);
if (primitive_value.IsCalculated()) {
if (To<CSSMathFunctionValue>(primitive_value).MayHaveRelativeUnit())
return true;
} else {
CSSPrimitiveValue::UnitType unit_type =
To<CSSNumericLiteralValue>(primitive_value).GetType();
if (CSSPrimitiveValue::IsRelativeUnit(unit_type))
return true;
}
}
}
return false;
}
TransformOperations TransformBuilder::CreateTransformOperations(
const CSSValue& in_value,
const CSSToLengthConversionData& conversion_data) {
TransformOperations operations;
auto* in_value_list = DynamicTo<CSSValueList>(in_value);
if (!in_value_list) {
DCHECK_EQ(To<CSSIdentifierValue>(in_value).GetValueID(), CSSValueID::kNone);
return operations;
}
float zoom_factor = conversion_data.Zoom();
for (auto& value : *in_value_list) {
const auto* transform_value = To<CSSFunctionValue>(value.Get());
TransformOperation::OperationType transform_type =
GetTransformOperationType(transform_value->FunctionType());
const auto& first_value = To<CSSPrimitiveValue>(transform_value->Item(0));
switch (transform_type) {
case TransformOperation::kScale:
case TransformOperation::kScaleX:
case TransformOperation::kScaleY: {
double sx = 1.0;
double sy = 1.0;
if (transform_type == TransformOperation::kScaleY) {
sy = first_value.GetDoubleValue();
} else {
sx = first_value.GetDoubleValue();
if (transform_type != TransformOperation::kScaleX) {
if (transform_value->length() > 1) {
const auto& second_value =
To<CSSPrimitiveValue>(transform_value->Item(1));
sy = second_value.GetDoubleValue();
} else {
sy = sx;
}
}
}
operations.Operations().push_back(
ScaleTransformOperation::Create(sx, sy, 1.0, transform_type));
break;
}
case TransformOperation::kScaleZ:
case TransformOperation::kScale3D: {
double sx = 1.0;
double sy = 1.0;
double sz = 1.0;
if (transform_type == TransformOperation::kScaleZ) {
sz = first_value.GetDoubleValue();
} else {
sx = first_value.GetDoubleValue();
sy = To<CSSPrimitiveValue>(transform_value->Item(1)).GetDoubleValue();
sz = To<CSSPrimitiveValue>(transform_value->Item(2)).GetDoubleValue();
}
operations.Operations().push_back(
ScaleTransformOperation::Create(sx, sy, sz, transform_type));
break;
}
case TransformOperation::kTranslate:
case TransformOperation::kTranslateX:
case TransformOperation::kTranslateY: {
Length tx = Length::Fixed(0);
Length ty = Length::Fixed(0);
if (transform_type == TransformOperation::kTranslateY)
ty = ConvertToFloatLength(first_value, conversion_data);
else {
tx = ConvertToFloatLength(first_value, conversion_data);
if (transform_type != TransformOperation::kTranslateX) {
if (transform_value->length() > 1) {
const auto& second_value =
To<CSSPrimitiveValue>(transform_value->Item(1));
ty = ConvertToFloatLength(second_value, conversion_data);
}
}
}
operations.Operations().push_back(
TranslateTransformOperation::Create(tx, ty, 0, transform_type));
break;
}
case TransformOperation::kTranslateZ:
case TransformOperation::kTranslate3D: {
Length tx = Length::Fixed(0);
Length ty = Length::Fixed(0);
double tz = 0;
if (transform_type == TransformOperation::kTranslateZ) {
tz = first_value.ComputeLength<double>(conversion_data);
} else {
tx = ConvertToFloatLength(first_value, conversion_data);
ty = ConvertToFloatLength(
To<CSSPrimitiveValue>(transform_value->Item(1)), conversion_data);
tz = To<CSSPrimitiveValue>(transform_value->Item(2))
.ComputeLength<double>(conversion_data);
}
operations.Operations().push_back(
TranslateTransformOperation::Create(tx, ty, tz, transform_type));
break;
}
case TransformOperation::kRotateX:
case TransformOperation::kRotateY:
case TransformOperation::kRotateZ: {
double angle = first_value.ComputeDegrees();
if (transform_value->length() == 1) {
double x = transform_type == TransformOperation::kRotateX;
double y = transform_type == TransformOperation::kRotateY;
double z = transform_type == TransformOperation::kRotateZ;
operations.Operations().push_back(
RotateTransformOperation::Create(x, y, z, angle, transform_type));
} else {
// For SVG 'transform' attributes we generate 3-argument rotate()
// functions.
DCHECK_EQ(transform_value->length(), 3u);
const auto& second_value =
To<CSSPrimitiveValue>(transform_value->Item(1));
const CSSPrimitiveValue& third_value =
To<CSSPrimitiveValue>(transform_value->Item(2));
operations.Operations().push_back(
RotateAroundOriginTransformOperation::Create(
angle, second_value.ComputeLength<double>(conversion_data),
third_value.ComputeLength<double>(conversion_data)));
}
break;
}
case TransformOperation::kRotate3D: {
const auto& second_value =
To<CSSPrimitiveValue>(transform_value->Item(1));
const auto& third_value =
To<CSSPrimitiveValue>(transform_value->Item(2));
const auto& fourth_value =
To<CSSPrimitiveValue>(transform_value->Item(3));
double x = first_value.GetDoubleValue();
double y = second_value.GetDoubleValue();
double z = third_value.GetDoubleValue();
double angle = fourth_value.ComputeDegrees();
operations.Operations().push_back(
RotateTransformOperation::Create(x, y, z, angle, transform_type));
break;
}
case TransformOperation::kSkew:
case TransformOperation::kSkewX:
case TransformOperation::kSkewY: {
double angle_x = 0;
double angle_y = 0;
double angle = first_value.ComputeDegrees();
if (transform_type == TransformOperation::kSkewY)
angle_y = angle;
else {
angle_x = angle;
if (transform_type == TransformOperation::kSkew) {
if (transform_value->length() > 1) {
const auto& second_value =
To<CSSPrimitiveValue>(transform_value->Item(1));
angle_y = second_value.ComputeDegrees();
}
}
}
operations.Operations().push_back(
SkewTransformOperation::Create(angle_x, angle_y, transform_type));
break;
}
case TransformOperation::kMatrix: {
double a = first_value.GetDoubleValue();
double b =
To<CSSPrimitiveValue>(transform_value->Item(1)).GetDoubleValue();
double c =
To<CSSPrimitiveValue>(transform_value->Item(2)).GetDoubleValue();
double d =
To<CSSPrimitiveValue>(transform_value->Item(3)).GetDoubleValue();
double e =
zoom_factor *
To<CSSPrimitiveValue>(transform_value->Item(4)).GetDoubleValue();
double f =
zoom_factor *
To<CSSPrimitiveValue>(transform_value->Item(5)).GetDoubleValue();
operations.Operations().push_back(
MatrixTransformOperation::Create(a, b, c, d, e, f));
break;
}
case TransformOperation::kMatrix3D: {
TransformationMatrix matrix(
To<CSSPrimitiveValue>(transform_value->Item(0)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(1)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(2)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(3)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(4)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(5)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(6)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(7)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(8)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(9)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(10)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(11)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(12)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(13)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(14)).GetDoubleValue(),
To<CSSPrimitiveValue>(transform_value->Item(15)).GetDoubleValue());
matrix.Zoom(zoom_factor);
operations.Operations().push_back(
Matrix3DTransformOperation::Create(matrix));
break;
}
case TransformOperation::kPerspective: {
double p = first_value.ComputeLength<double>(conversion_data);
DCHECK_GE(p, 0);
operations.Operations().push_back(
PerspectiveTransformOperation::Create(p));
break;
}
default:
NOTREACHED();
break;
}
}
return operations;
}
} // namespace blink