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 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 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/Optional.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 | if (other.m_lastPlanarSecondaryQuad) |
43 | m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(*other.m_lastPlanarSecondaryQuad); |
44 | else |
45 | m_lastPlanarSecondaryQuad = nullptr; |
46 | } |
47 | m_accumulatingTransform = other.m_accumulatingTransform; |
48 | m_direction = other.m_direction; |
49 | |
50 | m_accumulatedTransform = nullptr; |
51 | |
52 | if (other.m_accumulatedTransform) |
53 | m_accumulatedTransform = std::make_unique<TransformationMatrix>(*other.m_accumulatedTransform); |
54 | |
55 | return *this; |
56 | } |
57 | |
58 | void TransformState::translateTransform(const LayoutSize& offset) |
59 | { |
60 | if (m_direction == ApplyTransformDirection) |
61 | m_accumulatedTransform->translateRight(offset.width(), offset.height()); |
62 | else |
63 | m_accumulatedTransform->translate(offset.width(), offset.height()); |
64 | } |
65 | |
66 | void TransformState::translateMappedCoordinates(const LayoutSize& offset) |
67 | { |
68 | LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset; |
69 | if (m_mapPoint) |
70 | m_lastPlanarPoint.move(adjustedOffset); |
71 | if (m_mapQuad) { |
72 | m_lastPlanarQuad.move(adjustedOffset); |
73 | if (m_lastPlanarSecondaryQuad) |
74 | m_lastPlanarSecondaryQuad->move(adjustedOffset); |
75 | } |
76 | } |
77 | |
78 | void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate) |
79 | { |
80 | if (accumulate == FlattenTransform && !m_accumulatedTransform) |
81 | m_accumulatedOffset += offset; |
82 | else { |
83 | applyAccumulatedOffset(); |
84 | if (m_accumulatingTransform && m_accumulatedTransform) { |
85 | // If we're accumulating into an existing transform, apply the translation. |
86 | translateTransform(offset); |
87 | |
88 | // Then flatten if necessary. |
89 | if (accumulate == FlattenTransform) |
90 | flatten(); |
91 | } else |
92 | // Just move the point and/or quad. |
93 | translateMappedCoordinates(offset); |
94 | } |
95 | m_accumulatingTransform = accumulate == AccumulateTransform; |
96 | } |
97 | |
98 | void TransformState::applyAccumulatedOffset() |
99 | { |
100 | LayoutSize offset = m_accumulatedOffset; |
101 | m_accumulatedOffset = LayoutSize(); |
102 | if (!offset.isZero()) { |
103 | if (m_accumulatedTransform) { |
104 | translateTransform(offset); |
105 | flatten(); |
106 | } else |
107 | translateMappedCoordinates(offset); |
108 | } |
109 | } |
110 | |
111 | // FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient. |
112 | void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) |
113 | { |
114 | applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped); |
115 | } |
116 | |
117 | void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) |
118 | { |
119 | if (wasClamped) |
120 | *wasClamped = false; |
121 | |
122 | if (transformFromContainer.isIntegerTranslation()) { |
123 | move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate); |
124 | return; |
125 | } |
126 | |
127 | applyAccumulatedOffset(); |
128 | |
129 | // If we have an accumulated transform from last time, multiply in this transform |
130 | if (m_accumulatedTransform) { |
131 | if (m_direction == ApplyTransformDirection) |
132 | m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer * *m_accumulatedTransform); |
133 | else |
134 | m_accumulatedTransform->multiply(transformFromContainer); |
135 | } else if (accumulate == AccumulateTransform) { |
136 | // Make one if we started to accumulate |
137 | m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer); |
138 | } |
139 | |
140 | if (accumulate == FlattenTransform) { |
141 | const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; |
142 | flattenWithTransform(*finalTransform, wasClamped); |
143 | } |
144 | m_accumulatingTransform = accumulate == AccumulateTransform; |
145 | } |
146 | |
147 | void TransformState::flatten(bool* wasClamped) |
148 | { |
149 | if (wasClamped) |
150 | *wasClamped = false; |
151 | |
152 | applyAccumulatedOffset(); |
153 | |
154 | if (!m_accumulatedTransform) { |
155 | m_accumulatingTransform = false; |
156 | return; |
157 | } |
158 | |
159 | flattenWithTransform(*m_accumulatedTransform, wasClamped); |
160 | } |
161 | |
162 | FloatPoint TransformState::mappedPoint(bool* wasClamped) const |
163 | { |
164 | if (wasClamped) |
165 | *wasClamped = false; |
166 | |
167 | FloatPoint point = m_lastPlanarPoint; |
168 | point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); |
169 | if (!m_accumulatedTransform) |
170 | return point; |
171 | |
172 | if (m_direction == ApplyTransformDirection) |
173 | return m_accumulatedTransform->mapPoint(point); |
174 | |
175 | return m_accumulatedTransform->inverse().valueOr(TransformationMatrix()).projectPoint(point, wasClamped); |
176 | } |
177 | |
178 | FloatQuad TransformState::mappedQuad(bool* wasClamped) const |
179 | { |
180 | if (wasClamped) |
181 | *wasClamped = false; |
182 | |
183 | FloatQuad quad = m_lastPlanarQuad; |
184 | mapQuad(quad, m_direction, wasClamped); |
185 | return quad; |
186 | } |
187 | |
188 | Optional<FloatQuad> TransformState::mappedSecondaryQuad(bool* wasClamped) const |
189 | { |
190 | if (wasClamped) |
191 | *wasClamped = false; |
192 | |
193 | if (!m_lastPlanarSecondaryQuad) |
194 | return Optional<FloatQuad>(); |
195 | |
196 | FloatQuad quad = *m_lastPlanarSecondaryQuad; |
197 | mapQuad(quad, m_direction, wasClamped); |
198 | return quad; |
199 | } |
200 | |
201 | void TransformState::setLastPlanarSecondaryQuad(const FloatQuad* quad) |
202 | { |
203 | if (!quad) { |
204 | m_lastPlanarSecondaryQuad = nullptr; |
205 | return; |
206 | } |
207 | |
208 | // Map the quad back through any transform or offset back into the last flattening coordinate space. |
209 | FloatQuad backMappedQuad(*quad); |
210 | mapQuad(backMappedQuad, inverseDirection()); |
211 | m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(backMappedQuad); |
212 | } |
213 | |
214 | void TransformState::mapQuad(FloatQuad& quad, TransformDirection direction, bool* wasClamped) const |
215 | { |
216 | quad.move((direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); |
217 | if (!m_accumulatedTransform) |
218 | return; |
219 | |
220 | if (direction == ApplyTransformDirection) |
221 | quad = m_accumulatedTransform->mapQuad(quad); |
222 | |
223 | quad = m_accumulatedTransform->inverse().valueOr(TransformationMatrix()).projectQuad(quad, wasClamped); |
224 | } |
225 | |
226 | void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) |
227 | { |
228 | if (m_direction == ApplyTransformDirection) { |
229 | if (m_mapPoint) |
230 | m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); |
231 | if (m_mapQuad) { |
232 | m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); |
233 | if (m_lastPlanarSecondaryQuad) |
234 | *m_lastPlanarSecondaryQuad = t.mapQuad(*m_lastPlanarSecondaryQuad); |
235 | } |
236 | |
237 | } else { |
238 | TransformationMatrix inverseTransform = t.inverse().valueOr(TransformationMatrix()); |
239 | if (m_mapPoint) |
240 | m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); |
241 | if (m_mapQuad) { |
242 | m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); |
243 | if (m_lastPlanarSecondaryQuad) |
244 | *m_lastPlanarSecondaryQuad = inverseTransform.projectQuad(*m_lastPlanarSecondaryQuad, wasClamped); |
245 | } |
246 | } |
247 | |
248 | // We could throw away m_accumulatedTransform if we wanted to here, but that |
249 | // would cause thrash when traversing hierarchies with alternating |
250 | // preserve-3d and flat elements. |
251 | if (m_accumulatedTransform) |
252 | m_accumulatedTransform->makeIdentity(); |
253 | m_accumulatingTransform = false; |
254 | } |
255 | |
256 | } // namespace WebCore |
257 | |