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 "platform/geometry/TransformState.h" 28 29 #include "wtf/PassOwnPtr.h" 30 31 namespace WebCore { 32 33 TransformState& 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 53 void 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 61 void 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 70 void 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 } 88 m_accumulatingTransform = accumulate == AccumulateTransform; 89 } 90 91 void TransformState::applyAccumulatedOffset() 92 { 93 LayoutSize offset = m_accumulatedOffset; 94 m_accumulatedOffset = LayoutSize(); 95 if (!offset.isZero()) { 96 if (m_accumulatedTransform) { 97 translateTransform(offset); 98 flatten(); 99 } else { 100 translateMappedCoordinates(offset); 101 } 102 } 103 } 104 105 // FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient. 106 void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) 107 { 108 applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped); 109 } 110 111 void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) 112 { 113 if (wasClamped) 114 *wasClamped = false; 115 116 if (transformFromContainer.isIntegerTranslation()) { 117 move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate); 118 return; 119 } 120 121 applyAccumulatedOffset(); 122 123 // If we have an accumulated transform from last time, multiply in this transform 124 if (m_accumulatedTransform) { 125 if (m_direction == ApplyTransformDirection) 126 m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform)); 127 else 128 m_accumulatedTransform->multiply(transformFromContainer); 129 } else if (accumulate == AccumulateTransform) { 130 // Make one if we started to accumulate 131 m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer)); 132 } 133 134 if (accumulate == FlattenTransform) { 135 const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; 136 flattenWithTransform(*finalTransform, wasClamped); 137 } 138 m_accumulatingTransform = accumulate == AccumulateTransform; 139 } 140 141 void TransformState::flatten(bool* wasClamped) 142 { 143 if (wasClamped) 144 *wasClamped = false; 145 146 applyAccumulatedOffset(); 147 148 if (!m_accumulatedTransform) { 149 m_accumulatingTransform = false; 150 return; 151 } 152 153 flattenWithTransform(*m_accumulatedTransform, wasClamped); 154 } 155 156 FloatPoint TransformState::mappedPoint(bool* wasClamped) const 157 { 158 if (wasClamped) 159 *wasClamped = false; 160 161 FloatPoint point = m_lastPlanarPoint; 162 point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); 163 if (!m_accumulatedTransform) 164 return point; 165 166 if (m_direction == ApplyTransformDirection) 167 return m_accumulatedTransform->mapPoint(point); 168 169 return m_accumulatedTransform->inverse().projectPoint(point, wasClamped); 170 } 171 172 FloatQuad TransformState::mappedQuad(bool* wasClamped) const 173 { 174 if (wasClamped) 175 *wasClamped = false; 176 177 FloatQuad quad = m_lastPlanarQuad; 178 quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); 179 if (!m_accumulatedTransform) 180 return quad; 181 182 if (m_direction == ApplyTransformDirection) 183 return m_accumulatedTransform->mapQuad(quad); 184 185 return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); 186 } 187 188 void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) 189 { 190 if (m_direction == ApplyTransformDirection) { 191 if (m_mapPoint) 192 m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); 193 if (m_mapQuad) 194 m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); 195 } else { 196 TransformationMatrix inverseTransform = t.inverse(); 197 if (m_mapPoint) 198 m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); 199 if (m_mapQuad) 200 m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); 201 } 202 203 // We could throw away m_accumulatedTransform if we wanted to here, but that 204 // would cause thrash when traversing hierarchies with alternating 205 // preserve-3d and flat elements. 206 if (m_accumulatedTransform) 207 m_accumulatedTransform->makeIdentity(); 208 m_accumulatingTransform = false; 209 } 210 211 } // namespace WebCore 212