1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
6 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
7 
8 #include "third_party/blink/renderer/platform/platform_export.h"
9 
10 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
11 
12 namespace blink {
13 
14 class TransformPaintPropertyNode;
15 
16 // A GeometryMapperTransformCache hangs off a TransformPaintPropertyNode.
17 // It stores useful intermediate results such as screen matrix for geometry
18 // queries.
19 class PLATFORM_EXPORT GeometryMapperTransformCache {
20   USING_FAST_MALLOC(GeometryMapperTransformCache);
21  public:
22   GeometryMapperTransformCache() = default;
23 
24   static void ClearCache();
25   bool IsValid() const;
26 
UpdateIfNeeded(const TransformPaintPropertyNode & node)27   void UpdateIfNeeded(const TransformPaintPropertyNode& node) {
28     if (cache_generation_ != s_global_generation)
29       Update(node);
30     DCHECK_EQ(cache_generation_, s_global_generation);
31   }
32 
to_2d_translation_root()33   const FloatSize& to_2d_translation_root() const {
34     return to_2d_translation_root_;
35   }
root_of_2d_translation()36   const TransformPaintPropertyNode* root_of_2d_translation() const {
37     return root_of_2d_translation_;
38   }
39 
40   // As screen transform data are used rarely, they are updated only when
41   // needed. This method must be called before calling any screen transform
42   // related getters.
43   void UpdateScreenTransform(const TransformPaintPropertyNode&);
44 
45   // These getters must be called after UpdateScreenTransform() when screen
46   // transform data is really needed.
to_screen()47   const TransformationMatrix& to_screen() const {
48     DCHECK(screen_transform_);
49     return screen_transform_->to_screen;
50   }
projection_from_screen()51   const TransformationMatrix& projection_from_screen() const {
52     DCHECK(screen_transform_);
53     return screen_transform_->projection_from_screen;
54   }
projection_from_screen_is_valid()55   bool projection_from_screen_is_valid() const {
56 #if DCHECK_IS_ON()
57     CheckScreenTransformUpdated();
58 #endif
59     return LIKELY(!screen_transform_) ||
60            screen_transform_->projection_from_screen_is_valid;
61   }
ApplyToScreen(TransformationMatrix & m)62   void ApplyToScreen(TransformationMatrix& m) const {
63     if (UNLIKELY(screen_transform_))
64       m.Multiply(to_screen());
65     else
66       ApplyToPlaneRoot(m);
67   }
ApplyProjectionFromScreen(TransformationMatrix & m)68   void ApplyProjectionFromScreen(TransformationMatrix& m) const {
69     if (UNLIKELY(screen_transform_))
70       m.Multiply(projection_from_screen());
71     else
72       ApplyFromPlaneRoot(m);
73   }
has_animation_to_screen()74   bool has_animation_to_screen() const {
75 #if DCHECK_IS_ON()
76     CheckScreenTransformUpdated();
77 #endif
78     return UNLIKELY(screen_transform_) ? screen_transform_->has_animation
79                                        : has_animation_to_plane_root();
80   }
81 
to_plane_root()82   const TransformationMatrix& to_plane_root() const {
83     DCHECK(plane_root_transform_);
84     return plane_root_transform_->to_plane_root;
85   }
from_plane_root()86   const TransformationMatrix& from_plane_root() const {
87     DCHECK(plane_root_transform_);
88     return plane_root_transform_->from_plane_root;
89   }
ApplyToPlaneRoot(TransformationMatrix & m)90   void ApplyToPlaneRoot(TransformationMatrix& m) const {
91     if (UNLIKELY(plane_root_transform_)) {
92       m.Multiply(to_plane_root());
93     } else {
94       m.Translate(to_2d_translation_root_.Width(),
95                   to_2d_translation_root_.Height());
96     }
97   }
ApplyFromPlaneRoot(TransformationMatrix & m)98   void ApplyFromPlaneRoot(TransformationMatrix& m) const {
99     if (UNLIKELY(plane_root_transform_)) {
100       m.Multiply(from_plane_root());
101     } else {
102       m.Translate(-to_2d_translation_root_.Width(),
103                   -to_2d_translation_root_.Height());
104     }
105   }
plane_root()106   const TransformPaintPropertyNode* plane_root() const {
107     return UNLIKELY(plane_root_transform_) ? plane_root_transform_->plane_root
108                                            : root_of_2d_translation();
109   }
has_animation_to_plane_root()110   bool has_animation_to_plane_root() const {
111     return UNLIKELY(plane_root_transform_) &&
112            plane_root_transform_->has_animation;
113   }
114 
115  private:
116   friend class GeometryMapperTransformCacheTest;
117 
118 #if DCHECK_IS_ON()
119   void CheckScreenTransformUpdated() const;
120 #endif
121 
122   void Update(const TransformPaintPropertyNode&);
123 
124   static unsigned s_global_generation;
125 
126   // The accumulated 2d translation to root_of_2d_translation().
127   FloatSize to_2d_translation_root_;
128 
129   // The parent of the root of consecutive identity or 2d translations from the
130   // transform node, or the root of the tree if the whole path from the
131   // transform node to the root contains identity or 2d translations only.
132   const TransformPaintPropertyNode* root_of_2d_translation_;
133 
134   // The cached values here can be categorized in two logical groups:
135   //
136   // [ Screen Transform ]
137   // to_screen : The screen matrix of the node, as defined by:
138   //   to_screen = (flattens_inherited_transform ?
139   //       flatten(parent.to_screen) : parent.to_screen) * local
140   // projection_from_screen : Back projection from screen.
141   //   projection_from_screen = flatten(to_screen) ^ -1
142   //   Undefined if the inverse projection doesn't exist.
143   //   Guaranteed to be flat.
144   // projection_from_screen_is_valid : Whether projection_from_screen
145   //   is defined.
146   //
147   // [ Plane Root Transform ]
148   // plane_root : The oldest ancestor node such that every intermediate node
149   //   in the ancestor chain has a flat and invertible local matrix. In other
150   //   words, a node inherits its parent's plane_root if its local matrix is
151   //   flat and invertible. Otherwise, it becomes its own plane root.
152   //   For example:
153   //   <xfrm id="A" matrix="rotateY(10deg)">
154   //     <xfrm id="B" flatten_inherited matrix="translateX(10px)"/>
155   //     <xfrm id="C" matrix="scaleX(0)">
156   //       <xfrm id="D" matrix="scaleX(2)"/>
157   //       <xfrm id="E" matrix="rotate(30deg)"/>
158   //     </xfrm>
159   //     <xfrm id="F" matrix="scaleZ(0)"/>
160   //   </xfrm>
161   //   A is the plane root of itself because its local matrix is 3D.
162   //   B's plane root is A because its local matrix is flat.
163   //   C is the plane root of itself because its local matrix is non-invertible.
164   //   D and E's plane root is C because their local matrix is flat.
165   //   F is the plane root of itself because its local matrix is 3D and
166   //     non-invertible.
167   // to_plane_root : The accumulated matrix between this node and plane_root.
168   //   to_plane_root = (plane_root == this) ? I : parent.to_plane_root * local
169   //   Guaranteed to be flat.
170   // from_plane_root :
171   //   from_plane_root = to_plane_root ^ -1
172   //   Guaranteed to exist because each local matrices are invertible.
173   //   Guaranteed to be flat.
174   // An important invariant is that
175   //   flatten(to_screen) = flatten(plane_root.to_screen) * to_plane_root
176   //   Proof by induction:
177   //   If plane_root == this,
178   //     flatten(plane_root.to_screen) * to_plane_root = flatten(to_screen) * I
179   //     = flatten(to_screen)
180   //   Otherwise,
181   //     flatten(to_screen) = flatten((flattens_inherited_transform ?
182   //         flatten(parent.to_screen) : parent.to_screen) * local)
183   //     Because local is known to be flat,
184   //     = flatten((flattens_inherited_transform ?
185   //         flatten(parent.to_screen) : parent.to_screen) * flatten(local))
186   //     Then by flatten lemma (https://goo.gl/DNKyOc),
187   //     = flatten(parent.to_screen) * local
188   //     = flatten(parent.plane_root.to_screen) * parent.to_plane_root * local
189   //     = flatten(plane_root.to_screen) * to_plane_root
190   struct PlaneRootTransform {
191     TransformationMatrix to_plane_root;
192     TransformationMatrix from_plane_root;
193     const TransformPaintPropertyNode* plane_root;
194     bool has_animation;
195   };
196   std::unique_ptr<PlaneRootTransform> plane_root_transform_;
197 
198   struct ScreenTransform {
199     TransformationMatrix to_screen;
200     TransformationMatrix projection_from_screen;
201     bool projection_from_screen_is_valid;
202     bool has_animation;
203   };
204   std::unique_ptr<ScreenTransform> screen_transform_;
205 
206   unsigned cache_generation_ = s_global_generation - 1;
207   DISALLOW_COPY_AND_ASSIGN(GeometryMapperTransformCache);
208 };
209 
210 }  // namespace blink
211 
212 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
213