blob: f1e5c6781a4e3094108f91274f36f0380eae61ca [file] [log] [blame]
/*
* Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
* 2010 Dirk Schulze <krit@webkit.org>
* Copyright (C) 2013 Google Inc. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/transforms/affine_transform.h"
#include "third_party/blink/renderer/platform/geometry/float_quad.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/int_rect.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
AffineTransform::AffineTransform() {
const Transform kIdentity = IDENTITY_TRANSFORM;
SetMatrix(kIdentity);
}
AffineTransform::AffineTransform(double a,
double b,
double c,
double d,
double e,
double f) {
SetMatrix(a, b, c, d, e, f);
}
void AffineTransform::MakeIdentity() {
SetMatrix(1, 0, 0, 1, 0, 0);
}
void AffineTransform::SetMatrix(double a,
double b,
double c,
double d,
double e,
double f) {
transform_[0] = a;
transform_[1] = b;
transform_[2] = c;
transform_[3] = d;
transform_[4] = e;
transform_[5] = f;
}
bool AffineTransform::IsIdentity() const {
return (transform_[0] == 1 && transform_[1] == 0 && transform_[2] == 0 &&
transform_[3] == 1 && transform_[4] == 0 && transform_[5] == 0);
}
double AffineTransform::XScaleSquared() const {
return transform_[0] * transform_[0] + transform_[1] * transform_[1];
}
double AffineTransform::XScale() const {
return sqrt(XScaleSquared());
}
double AffineTransform::YScaleSquared() const {
return transform_[2] * transform_[2] + transform_[3] * transform_[3];
}
double AffineTransform::YScale() const {
return sqrt(YScaleSquared());
}
double AffineTransform::Det() const {
return transform_[0] * transform_[3] - transform_[1] * transform_[2];
}
bool AffineTransform::IsInvertible() const {
return Det() != 0.0;
}
AffineTransform AffineTransform::Inverse() const {
double determinant = Det();
AffineTransform result;
if (determinant == 0.0)
return result;
if (IsIdentityOrTranslation()) {
result.transform_[4] = -transform_[4];
result.transform_[5] = -transform_[5];
return result;
}
result.transform_[0] = transform_[3] / determinant;
result.transform_[1] = -transform_[1] / determinant;
result.transform_[2] = -transform_[2] / determinant;
result.transform_[3] = transform_[0] / determinant;
result.transform_[4] =
(transform_[2] * transform_[5] - transform_[3] * transform_[4]) /
determinant;
result.transform_[5] =
(transform_[1] * transform_[4] - transform_[0] * transform_[5]) /
determinant;
return result;
}
namespace {
// res = t1 * t2
inline void DoMultiply(const AffineTransform& t1,
const AffineTransform& t2,
AffineTransform* res) {
res->SetA(t1.A() * t2.A() + t1.C() * t2.B());
res->SetB(t1.B() * t2.A() + t1.D() * t2.B());
res->SetC(t1.A() * t2.C() + t1.C() * t2.D());
res->SetD(t1.B() * t2.C() + t1.D() * t2.D());
res->SetE(t1.A() * t2.E() + t1.C() * t2.F() + t1.E());
res->SetF(t1.B() * t2.E() + t1.D() * t2.F() + t1.F());
}
} // anonymous namespace
AffineTransform& AffineTransform::Multiply(const AffineTransform& other) {
if (other.IsIdentityOrTranslation()) {
if (other.transform_[4] || other.transform_[5])
Translate(other.transform_[4], other.transform_[5]);
return *this;
}
AffineTransform trans;
DoMultiply(*this, other, &trans);
SetMatrix(trans.transform_);
return *this;
}
AffineTransform& AffineTransform::PreMultiply(const AffineTransform& other) {
if (other.IsIdentityOrTranslation()) {
if (other.transform_[4] || other.transform_[5]) {
transform_[4] += other.transform_[4];
transform_[5] += other.transform_[5];
}
return *this;
}
AffineTransform trans;
DoMultiply(other, *this, &trans);
SetMatrix(trans.transform_);
return *this;
}
AffineTransform& AffineTransform::Rotate(double a) {
// angle is in degree. Switch to radian
return RotateRadians(deg2rad(a));
}
AffineTransform& AffineTransform::RotateRadians(double a) {
double cos_angle = cos(a);
double sin_angle = sin(a);
AffineTransform rot(cos_angle, sin_angle, -sin_angle, cos_angle, 0, 0);
Multiply(rot);
return *this;
}
AffineTransform& AffineTransform::Scale(double s) {
return Scale(s, s);
}
AffineTransform& AffineTransform::Scale(double sx, double sy) {
transform_[0] *= sx;
transform_[1] *= sx;
transform_[2] *= sy;
transform_[3] *= sy;
return *this;
}
// *this = *this * translation
AffineTransform& AffineTransform::Translate(double tx, double ty) {
if (IsIdentityOrTranslation()) {
transform_[4] += tx;
transform_[5] += ty;
return *this;
}
transform_[4] += tx * transform_[0] + ty * transform_[2];
transform_[5] += tx * transform_[1] + ty * transform_[3];
return *this;
}
AffineTransform& AffineTransform::ScaleNonUniform(double sx, double sy) {
return Scale(sx, sy);
}
AffineTransform& AffineTransform::RotateFromVector(double x, double y) {
return RotateRadians(atan2(y, x));
}
AffineTransform& AffineTransform::FlipX() {
return Scale(-1, 1);
}
AffineTransform& AffineTransform::FlipY() {
return Scale(1, -1);
}
AffineTransform& AffineTransform::Shear(double sx, double sy) {
double a = transform_[0];
double b = transform_[1];
transform_[0] += sy * transform_[2];
transform_[1] += sy * transform_[3];
transform_[2] += sx * a;
transform_[3] += sx * b;
return *this;
}
AffineTransform& AffineTransform::Skew(double angle_x, double angle_y) {
return Shear(tan(deg2rad(angle_x)), tan(deg2rad(angle_y)));
}
AffineTransform& AffineTransform::SkewX(double angle) {
return Shear(tan(deg2rad(angle)), 0);
}
AffineTransform& AffineTransform::SkewY(double angle) {
return Shear(0, tan(deg2rad(angle)));
}
void AffineTransform::Map(double x, double y, double& x2, double& y2) const {
x2 = (transform_[0] * x + transform_[2] * y + transform_[4]);
y2 = (transform_[1] * x + transform_[3] * y + transform_[5]);
}
IntPoint AffineTransform::MapPoint(const IntPoint& point) const {
double x2, y2;
Map(point.X(), point.Y(), x2, y2);
// Round the point.
return IntPoint(static_cast<int>(lround(x2)), static_cast<int>(lround(y2)));
}
FloatPoint AffineTransform::MapPoint(const FloatPoint& point) const {
double x2, y2;
Map(point.X(), point.Y(), x2, y2);
return FloatPoint(clampTo<float>(x2), clampTo<float>(y2));
}
IntSize AffineTransform::MapSize(const IntSize& size) const {
double width2 = size.Width() * XScale();
double height2 = size.Height() * YScale();
return IntSize(static_cast<int>(lround(width2)),
static_cast<int>(lround(height2)));
}
FloatSize AffineTransform::MapSize(const FloatSize& size) const {
double width2 = size.Width() * XScale();
double height2 = size.Height() * YScale();
return FloatSize(clampTo<float>(width2), clampTo<float>(height2));
}
IntRect AffineTransform::MapRect(const IntRect& rect) const {
return EnclosingIntRect(MapRect(FloatRect(rect)));
}
FloatRect AffineTransform::MapRect(const FloatRect& rect) const {
if (IsIdentityOrTranslation()) {
if (!transform_[4] && !transform_[5])
return rect;
FloatRect mapped_rect(rect);
mapped_rect.Move(clampTo<float>(transform_[4]),
clampTo<float>(transform_[5]));
return mapped_rect;
}
FloatQuad result;
result.SetP1(MapPoint(rect.Location()));
result.SetP2(MapPoint(FloatPoint(rect.MaxX(), rect.Y())));
result.SetP3(MapPoint(FloatPoint(rect.MaxX(), rect.MaxY())));
result.SetP4(MapPoint(FloatPoint(rect.X(), rect.MaxY())));
return result.BoundingBox();
}
FloatQuad AffineTransform::MapQuad(const FloatQuad& q) const {
if (IsIdentityOrTranslation()) {
FloatQuad mapped_quad(q);
mapped_quad.Move(clampTo<float>(transform_[4]),
clampTo<float>(transform_[5]));
return mapped_quad;
}
FloatQuad result;
result.SetP1(MapPoint(q.P1()));
result.SetP2(MapPoint(q.P2()));
result.SetP3(MapPoint(q.P3()));
result.SetP4(MapPoint(q.P4()));
return result;
}
TransformationMatrix AffineTransform::ToTransformationMatrix() const {
return TransformationMatrix(transform_[0], transform_[1], transform_[2],
transform_[3], transform_[4], transform_[5]);
}
bool AffineTransform::Decompose(DecomposedType& decomp) const {
AffineTransform m(*this);
// Compute scaling factors
double sx = XScale();
double sy = YScale();
// Compute cross product of transformed unit vectors. If negative,
// one axis was flipped.
if (m.A() * m.D() - m.C() * m.B() < 0) {
// Flip axis with minimum unit vector dot product
if (m.A() < m.D())
sx = -sx;
else
sy = -sy;
}
// Remove scale from matrix
m.Scale(1 / sx, 1 / sy);
// Compute rotation
double angle = atan2(m.B(), m.A());
// Remove rotation from matrix
m.RotateRadians(-angle);
// Return results
decomp.scale_x = sx;
decomp.scale_y = sy;
decomp.angle = angle;
decomp.remainder_a = m.A();
decomp.remainder_b = m.B();
decomp.remainder_c = m.C();
decomp.remainder_d = m.D();
decomp.translate_x = m.E();
decomp.translate_y = m.F();
return true;
}
void AffineTransform::Recompose(const DecomposedType& decomp) {
this->SetA(decomp.remainder_a);
this->SetB(decomp.remainder_b);
this->SetC(decomp.remainder_c);
this->SetD(decomp.remainder_d);
this->SetE(decomp.translate_x);
this->SetF(decomp.translate_y);
this->RotateRadians(decomp.angle);
this->Scale(decomp.scale_x, decomp.scale_y);
}
String AffineTransform::ToString(bool as_matrix) const {
if (as_matrix) {
// Return as a matrix in row-major order.
return String::Format("[%lg,%lg,%lg,\n%lg,%lg,%lg]", A(), C(), E(), B(),
D(), F());
}
if (IsIdentity())
return "identity";
AffineTransform::DecomposedType decomposition;
Decompose(decomposition);
if (IsIdentityOrTranslation())
return String::Format("translation(%lg,%lg)", decomposition.translate_x,
decomposition.translate_y);
return String::Format(
"translation(%lg,%lg), scale(%lg,%lg), angle(%lgdeg), "
"remainder(%lg,%lg,%lg,%lg)",
decomposition.translate_x, decomposition.translate_y,
decomposition.scale_x, decomposition.scale_y,
rad2deg(decomposition.angle), decomposition.remainder_a,
decomposition.remainder_b, decomposition.remainder_c,
decomposition.remainder_d);
}
std::ostream& operator<<(std::ostream& ostream,
const AffineTransform& transform) {
return ostream << transform.ToString();
}
} // namespace blink