1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <ostream>
20 
21 #include <android-base/stringprintf.h>
22 #include <ui/Rect.h>
23 #include <ui/Rotation.h>
24 #include <ui/Transform.h>
25 
26 namespace android {
27 namespace compositionengine {
28 
29 // Geometrical space to which content is projected.
30 // For example, this can be the layer space or the physical display space.
31 struct ProjectionSpace {
32     ProjectionSpace() = default;
ProjectionSpaceProjectionSpace33     ProjectionSpace(ui::Size size, Rect content)
34           : bounds(std::move(size)), content(std::move(content)) {}
35 
36     // Bounds of this space. Always starts at (0,0).
37     Rect bounds;
38 
39     // Rect onto which content is projected.
40     Rect content;
41 
42     // The orientation of this space. This value is meaningful only in relation to the rotation
43     // of another projection space and it's used to determine the rotating transformation when
44     // mapping between the two.
45     // As a convention when using this struct orientation = 0 for the "oriented*" projection
46     // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
47     // of the display space will become 90, while  the orientation of the layer stack space will
48     // remain the same.
49     ui::Rotation orientation = ui::ROTATION_0;
50 
51     // Returns a transform which maps this.content into destination.content
52     // and also rotates according to this.orientation and destination.orientation
getTransformProjectionSpace53     ui::Transform getTransform(const ProjectionSpace& destination) const {
54         ui::Rotation rotation = destination.orientation - orientation;
55 
56         // Compute a transformation which rotates the destination in a way it has the same
57         // orientation as us.
58         const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
59         ui::Transform inverseRotatingTransform;
60         inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
61                                      destination.bounds.height());
62         // The destination content rotated so it has the same orientation as us.
63         Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
64 
65         // Compute translation from the source content to (0, 0).
66         const float sourceX = content.left;
67         const float sourceY = content.top;
68         ui::Transform sourceTranslation;
69         sourceTranslation.set(-sourceX, -sourceY);
70 
71         // Compute scaling transform which maps source content to destination content, assuming
72         // they are both at (0, 0).
73         ui::Transform scale;
74         const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
75         const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
76         scale.set(scaleX, 0, 0, scaleY);
77 
78         // Compute translation from (0, 0) to the orientated destination content.
79         const float destX = orientedDestContent.left;
80         const float destY = orientedDestContent.top;
81         ui::Transform destTranslation;
82         destTranslation.set(destX, destY);
83 
84         // Compute rotation transform.
85         const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
86         auto orientedDestWidth = destination.bounds.width();
87         auto orientedDestHeight = destination.bounds.height();
88         if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
89             std::swap(orientedDestWidth, orientedDestHeight);
90         }
91         ui::Transform rotationTransform;
92         rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight);
93 
94         // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
95         // Apply the logical translation, scale to physical size, apply the
96         // physical translation and finally rotate to the physical orientation.
97         return rotationTransform * destTranslation * scale * sourceTranslation;
98     }
99 
100     bool operator==(const ProjectionSpace& other) const {
101         return bounds == other.bounds && content == other.content &&
102                 orientation == other.orientation;
103     }
104 };
105 
106 } // namespace compositionengine
107 
to_string(const android::compositionengine::ProjectionSpace & space)108 inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
109     return android::base::
110             StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
111                          to_string(space.bounds).c_str(), to_string(space.content).c_str(),
112                          toCString(space.orientation));
113 }
114 
115 // Defining PrintTo helps with Google Tests.
PrintTo(const android::compositionengine::ProjectionSpace & space,::std::ostream * os)116 inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) {
117     *os << to_string(space);
118 }
119 
120 } // namespace android