blob: f367b0d6027367210b18736edf536f8c352f22c1 [file] [log] [blame]
/*
* Copyright (C) 2011 Apple 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/core/layout/geometry/transform_state.h"
namespace blink {
TransformState& TransformState::operator=(const TransformState& other) {
accumulated_offset_ = other.accumulated_offset_;
map_point_ = other.map_point_;
map_quad_ = other.map_quad_;
if (map_point_)
last_planar_point_ = other.last_planar_point_;
if (map_quad_)
last_planar_quad_ = other.last_planar_quad_;
accumulating_transform_ = other.accumulating_transform_;
force_accumulating_transform_ = other.force_accumulating_transform_;
direction_ = other.direction_;
accumulated_transform_.reset();
if (other.accumulated_transform_) {
accumulated_transform_ =
std::make_unique<TransformationMatrix>(*other.accumulated_transform_);
}
return *this;
}
void TransformState::TranslateTransform(const PhysicalOffset& offset) {
if (direction_ == kApplyTransformDirection) {
accumulated_transform_->PostTranslate(offset.left.ToDouble(),
offset.top.ToDouble());
} else {
accumulated_transform_->Translate(offset.left.ToDouble(),
offset.top.ToDouble());
}
}
void TransformState::TranslateMappedCoordinates(const PhysicalOffset& offset) {
FloatSize adjusted_offset((direction_ == kApplyTransformDirection) ? offset
: -offset);
if (map_point_)
last_planar_point_.Move(adjusted_offset);
if (map_quad_)
last_planar_quad_.Move(adjusted_offset);
}
void TransformState::Move(const PhysicalOffset& offset,
TransformAccumulation accumulate) {
if (force_accumulating_transform_)
accumulate = kAccumulateTransform;
if (accumulate == kFlattenTransform || !accumulated_transform_) {
accumulated_offset_ += offset;
} else {
ApplyAccumulatedOffset();
if (accumulating_transform_ && accumulated_transform_) {
// If we're accumulating into an existing transform, apply the
// translation.
TranslateTransform(offset);
} else {
// Just move the point and/or quad.
TranslateMappedCoordinates(offset);
}
}
accumulating_transform_ = accumulate == kAccumulateTransform;
}
void TransformState::ApplyAccumulatedOffset() {
PhysicalOffset offset = accumulated_offset_;
accumulated_offset_ = PhysicalOffset();
if (!offset.IsZero()) {
if (accumulated_transform_) {
TranslateTransform(offset);
Flatten();
} else {
TranslateMappedCoordinates(offset);
}
}
}
// FIXME: We transform AffineTransform to TransformationMatrix. This is rather
// inefficient.
void TransformState::ApplyTransform(
const AffineTransform& transform_from_container,
TransformAccumulation accumulate) {
ApplyTransform(transform_from_container.ToTransformationMatrix(), accumulate);
}
void TransformState::ApplyTransform(
const TransformationMatrix& transform_from_container,
TransformAccumulation accumulate) {
if (transform_from_container.IsIntegerTranslation()) {
Move(PhysicalOffset::FromFloatSizeRound(
transform_from_container.To2DTranslation()),
accumulate);
return;
}
ApplyAccumulatedOffset();
// If we have an accumulated transform from last time, multiply in this
// transform
if (accumulated_transform_) {
if (direction_ == kApplyTransformDirection)
accumulated_transform_ = std::make_unique<TransformationMatrix>(
transform_from_container * *accumulated_transform_);
else
accumulated_transform_->Multiply(transform_from_container);
} else if (accumulate == kAccumulateTransform) {
// Make one if we started to accumulate
accumulated_transform_ =
std::make_unique<TransformationMatrix>(transform_from_container);
}
if (accumulate == kFlattenTransform) {
if (force_accumulating_transform_) {
accumulated_transform_->FlattenTo2d();
} else {
const TransformationMatrix* final_transform =
accumulated_transform_ ? accumulated_transform_.get()
: &transform_from_container;
FlattenWithTransform(*final_transform);
}
}
accumulating_transform_ =
accumulate == kAccumulateTransform || force_accumulating_transform_;
}
void TransformState::Flatten() {
DCHECK(!force_accumulating_transform_);
ApplyAccumulatedOffset();
if (!accumulated_transform_) {
accumulating_transform_ = false;
return;
}
FlattenWithTransform(*accumulated_transform_);
}
PhysicalOffset TransformState::MappedPoint() const {
FloatPoint point = last_planar_point_;
point.Move(FloatSize(direction_ == kApplyTransformDirection
? accumulated_offset_
: -accumulated_offset_));
if (accumulated_transform_) {
point = direction_ == kApplyTransformDirection
? accumulated_transform_->MapPoint(point)
: accumulated_transform_->Inverse().ProjectPoint(point);
}
return PhysicalOffset::FromFloatPointRound(point);
}
FloatQuad TransformState::MappedQuad() const {
FloatQuad quad = last_planar_quad_;
quad.Move(FloatSize((direction_ == kApplyTransformDirection)
? accumulated_offset_
: -accumulated_offset_));
if (!accumulated_transform_)
return quad;
if (direction_ == kApplyTransformDirection)
return accumulated_transform_->MapQuad(quad);
return accumulated_transform_->Inverse().ProjectQuad(quad);
}
const TransformationMatrix& TransformState::AccumulatedTransform() const {
DCHECK(force_accumulating_transform_);
DCHECK(accumulating_transform_);
return *accumulated_transform_;
}
void TransformState::FlattenWithTransform(const TransformationMatrix& t) {
if (direction_ == kApplyTransformDirection) {
if (map_point_)
last_planar_point_ = t.MapPoint(last_planar_point_);
if (map_quad_)
last_planar_quad_ = t.MapQuad(last_planar_quad_);
} else {
TransformationMatrix inverse_transform = t.Inverse();
if (map_point_)
last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
if (map_quad_) {
last_planar_quad_ = inverse_transform.ProjectQuad(last_planar_quad_);
}
}
// We could throw away m_accumulatedTransform if we wanted to here, but that
// would cause thrash when traversing hierarchies with alternating
// preserve-3d and flat elements.
if (accumulated_transform_)
accumulated_transform_->MakeIdentity();
accumulating_transform_ = false;
}
} // namespace blink