1/* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "TransformState.h" 28 29#include <wtf/PassOwnPtr.h> 30 31namespace WebCore { 32 33TransformState& TransformState::operator=(const TransformState& other) 34{ 35 m_accumulatedOffset = other.m_accumulatedOffset; 36 m_mapPoint = other.m_mapPoint; 37 m_mapQuad = other.m_mapQuad; 38 if (m_mapPoint) 39 m_lastPlanarPoint = other.m_lastPlanarPoint; 40 if (m_mapQuad) 41 m_lastPlanarQuad = other.m_lastPlanarQuad; 42 m_accumulatingTransform = other.m_accumulatingTransform; 43 m_direction = other.m_direction; 44 45 m_accumulatedTransform.clear(); 46 47 if (other.m_accumulatedTransform) 48 m_accumulatedTransform = adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform)); 49 50 return *this; 51} 52 53void TransformState::translateTransform(const LayoutSize& offset) 54{ 55 if (m_direction == ApplyTransformDirection) 56 m_accumulatedTransform->translateRight(offset.width(), offset.height()); 57 else 58 m_accumulatedTransform->translate(offset.width(), offset.height()); 59} 60 61void TransformState::translateMappedCoordinates(const LayoutSize& offset) 62{ 63 LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset; 64 if (m_mapPoint) 65 m_lastPlanarPoint.move(adjustedOffset); 66 if (m_mapQuad) 67 m_lastPlanarQuad.move(adjustedOffset); 68} 69 70void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate) 71{ 72 if (accumulate == FlattenTransform && !m_accumulatedTransform) 73 m_accumulatedOffset += offset; 74 else { 75 applyAccumulatedOffset(); 76 if (m_accumulatingTransform && m_accumulatedTransform) { 77 // If we're accumulating into an existing transform, apply the translation. 78 translateTransform(offset); 79 80 // Then flatten if necessary. 81 if (accumulate == FlattenTransform) 82 flatten(); 83 } else 84 // Just move the point and/or quad. 85 translateMappedCoordinates(offset); 86 } 87 m_accumulatingTransform = accumulate == AccumulateTransform; 88} 89 90void TransformState::applyAccumulatedOffset() 91{ 92 LayoutSize offset = m_accumulatedOffset; 93 m_accumulatedOffset = LayoutSize(); 94 if (!offset.isZero()) { 95 if (m_accumulatedTransform) { 96 translateTransform(offset); 97 flatten(); 98 } else 99 translateMappedCoordinates(offset); 100 } 101} 102 103// FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient. 104void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) 105{ 106 applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped); 107} 108 109void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) 110{ 111 if (wasClamped) 112 *wasClamped = false; 113 114 if (transformFromContainer.isIntegerTranslation()) { 115 move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate); 116 return; 117 } 118 119 applyAccumulatedOffset(); 120 121 // If we have an accumulated transform from last time, multiply in this transform 122 if (m_accumulatedTransform) { 123 if (m_direction == ApplyTransformDirection) 124 m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform)); 125 else 126 m_accumulatedTransform->multiply(transformFromContainer); 127 } else if (accumulate == AccumulateTransform) { 128 // Make one if we started to accumulate 129 m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer)); 130 } 131 132 if (accumulate == FlattenTransform) { 133 const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; 134 flattenWithTransform(*finalTransform, wasClamped); 135 } 136 m_accumulatingTransform = accumulate == AccumulateTransform; 137} 138 139void TransformState::flatten(bool* wasClamped) 140{ 141 if (wasClamped) 142 *wasClamped = false; 143 144 applyAccumulatedOffset(); 145 146 if (!m_accumulatedTransform) { 147 m_accumulatingTransform = false; 148 return; 149 } 150 151 flattenWithTransform(*m_accumulatedTransform, wasClamped); 152} 153 154FloatPoint TransformState::mappedPoint(bool* wasClamped) const 155{ 156 if (wasClamped) 157 *wasClamped = false; 158 159 FloatPoint point = m_lastPlanarPoint; 160 point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); 161 if (!m_accumulatedTransform) 162 return point; 163 164 if (m_direction == ApplyTransformDirection) 165 return m_accumulatedTransform->mapPoint(point); 166 167 return m_accumulatedTransform->inverse().projectPoint(point, wasClamped); 168} 169 170FloatQuad TransformState::mappedQuad(bool* wasClamped) const 171{ 172 if (wasClamped) 173 *wasClamped = false; 174 175 FloatQuad quad = m_lastPlanarQuad; 176 quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); 177 if (!m_accumulatedTransform) 178 return quad; 179 180 if (m_direction == ApplyTransformDirection) 181 return m_accumulatedTransform->mapQuad(quad); 182 183 return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); 184} 185 186void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) 187{ 188 if (m_direction == ApplyTransformDirection) { 189 if (m_mapPoint) 190 m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); 191 if (m_mapQuad) 192 m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); 193 } else { 194 TransformationMatrix inverseTransform = t.inverse(); 195 if (m_mapPoint) 196 m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); 197 if (m_mapQuad) 198 m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); 199 } 200 201 // We could throw away m_accumulatedTransform if we wanted to here, but that 202 // would cause thrash when traversing hierarchies with alternating 203 // preserve-3d and flat elements. 204 if (m_accumulatedTransform) 205 m_accumulatedTransform->makeIdentity(); 206 m_accumulatingTransform = false; 207} 208 209} // namespace WebCore 210