1 // Copyright 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "aemu/base/LayoutResolver.h"
16 
17 #include <algorithm>
18 #include <cmath>
19 #include <stdint.h>
20 #include <vector>
21 
22 namespace android {
23 namespace base {
24 
25 /*
26  *  An internal struct used for describing a rectangle that could contain a list
27  *of "children" rectangles that has to fit in the "parent" rectangle.
28  */
29 struct Rect {
30     uint32_t id;
31     uint32_t pos_x;
32     uint32_t pos_y;
33     uint32_t width;
34     uint32_t height;
35     // indicates that this rectangle is a child of some parent rectangle.
36     bool isChild;
37 
38     std::vector<uint32_t> children;
Rectandroid::base::Rect39     Rect(uint32_t i, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
40         : id(i), pos_x(x), pos_y(y), width(w), height(h), isChild(false) {}
41 };
42 
computeScore(const uint32_t combinedWidth,const uint32_t combinedHeight,const uint32_t firstRowWidth,const uint32_t secondRowWidth,const double monitorAspectRatio)43 static double computeScore(const uint32_t combinedWidth,
44                            const uint32_t combinedHeight,
45                            const uint32_t firstRowWidth,
46                            const uint32_t secondRowWidth,
47                            const double monitorAspectRatio) {
48     if (firstRowWidth == 0 || secondRowWidth == 0) {
49         return std::pow((double)combinedHeight / (double)combinedWidth -
50                                 monitorAspectRatio,
51                         2);
52     } else {
53         return std::pow((double)firstRowWidth / (double)secondRowWidth - 1.0,
54                         2) +
55                std::pow((double)combinedHeight / (double)combinedWidth -
56                                 monitorAspectRatio,
57                         2);
58     }
59 }
60 /*
61  * Compute the coordinates for each rectangle using the lower left corner as
62  * origin. Save the coordinates in @param retVal using id as the key.
63  */
64 static std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>>
computeCoordinatesPerRow(std::vector<Rect> & row,uint32_t yOffset,std::unordered_map<uint32_t,std::pair<uint32_t,uint32_t>> & displays)65 computeCoordinatesPerRow(
66         std::vector<Rect>& row,
67         uint32_t yOffset,
68         std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>>& displays) {
69     std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> retVal;
70     std::stable_sort(row.begin(), row.end(), [](const Rect& a, const Rect& b) {
71         return a.height > b.height || (a.height == b.height && a.id < b.id);
72     });
73     uint32_t width = 0;
74     for (const auto& iter : row) {
75         uint32_t displayId = iter.id;
76         retVal[displayId] = std::make_pair(width, yOffset);
77         uint32_t cumulativeHeight = displays[displayId].second + yOffset;
78         for (auto id : iter.children) {
79             retVal[id] = std::make_pair(width, cumulativeHeight);
80             cumulativeHeight += displays[id].second;
81         }
82         width += iter.width;
83     }
84     return retVal;
85 }
86 
resolveLayout(std::unordered_map<uint32_t,std::pair<uint32_t,uint32_t>> rect,const double monitorAspectRatio)87 std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> resolveLayout(
88         std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> rect,
89         const double monitorAspectRatio) {
90     // Combine smaller rectangles into bigger ones by trying every pair of
91     // rectangles but always maintain these two invariants. 1, Always start
92     // combining from rectangle with smaller width. 2, The height of combined
93     // rectangle should not exceed the max height of all the original
94     // rectangles. When two smaller rectangles form a new one, update the
95     // rectangle with bigger width as the newly-formed rectangle, the smaller
96     // one is marked as children.
97 
98     std::vector<Rect> rectangles;
99     uint32_t maxHeight = 0;
100     for (const auto& iter : rect) {
101         uint32_t width = iter.second.first;
102         uint32_t height = iter.second.second;
103         rectangles.emplace_back(iter.first, 0, 0, width, height);
104         maxHeight = std::max(maxHeight, height);
105     }
106 
107     std::stable_sort(
108             rectangles.begin(), rectangles.end(),
109             [](const Rect& a, const Rect& b) { return a.width < b.width || (a.width == b.width && a.id > b.id); });
110 
111     for (int i = 0; i < rectangles.size(); i++) {
112         for (int j = i + 1; j < rectangles.size(); j++) {
113             if (rectangles[i].height + rectangles[j].height <= maxHeight) {
114                 rectangles[i].isChild = true;
115                 rectangles[j].children.push_back(rectangles[i].id);
116                 if (!rectangles[i].children.empty()) {
117                     for (uint32_t it : rectangles[i].children) {
118                         rectangles[j].children.push_back(it);
119                     }
120                 }
121                 rectangles[j].height += rectangles[i].height;
122                 break;
123             }
124         }
125     }
126 
127     // Delete rectangles that are chidlren of others.
128     for (auto it = rectangles.begin(); it != rectangles.end();) {
129         if (it->isChild) {
130             it = rectangles.erase(it);
131         } else {
132             ++it;
133         }
134     }
135 
136     // Try every enumeration of placing a rectangle into either first or second
137     // row to find the best fit. We try to balance two factors: 1, the combined
138     // rectangle's aspect ratio will be close to the monitor screen's aspect
139     // ratio. 2, the combined width of the first row will be close to the
140     // combined width of the second row. After the rectangles are placed into
141     // two rows, in each row, the rectangles will be sorted based on height.
142     double bestScore = 0;
143     int bestScoreBitSet = -1;
144     for (int bitset = 0; bitset < 1 << (rectangles.size() - 1); bitset++) {
145         uint32_t firstRowWidth = 0;
146         uint32_t secondRowWidth = 0;
147         uint32_t firstRowHeight = 0;
148         uint32_t secondRowHeight = 0;
149         for (int idx = 0; idx < rectangles.size(); idx++) {
150             if ((1 << idx) & bitset) {
151                 firstRowWidth += rectangles[idx].width;
152                 firstRowHeight =
153                         std::max(firstRowHeight, rectangles[idx].height);
154             } else {
155                 secondRowWidth += rectangles[idx].width;
156                 secondRowHeight =
157                         std::max(secondRowHeight, rectangles[idx].height);
158             }
159         }
160         uint32_t combinedHeight = firstRowHeight + secondRowHeight;
161         uint32_t combinedWidth = std::max(firstRowWidth, secondRowWidth);
162         double score =
163                 computeScore(combinedWidth, combinedHeight, firstRowWidth,
164                              secondRowWidth, monitorAspectRatio);
165         if (bestScoreBitSet == -1 || score < bestScore) {
166             bestScoreBitSet = bitset;
167             bestScore = score;
168         }
169     }
170 
171     std::vector<Rect> firstRow, secondRow;
172     for (int idx = 0; idx < rectangles.size(); idx++) {
173         if ((1 << idx) & bestScoreBitSet) {
174             firstRow.push_back(std::move(rectangles[idx]));
175         } else {
176             secondRow.push_back(std::move(rectangles[idx]));
177         }
178     }
179 
180     std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> retVal;
181     uint32_t yOffset = 0;
182     auto resFirstRow = computeCoordinatesPerRow(firstRow, yOffset, rect);
183     retVal.insert(resFirstRow.begin(), resFirstRow.end());
184     uint32_t firstRowHeight = (firstRow.empty() ? 0 : firstRow[0].height);
185 
186     yOffset = firstRowHeight;
187     auto resSecRow = computeCoordinatesPerRow(secondRow, yOffset, rect);
188     retVal.insert(resSecRow.begin(), resSecRow.end());
189 
190     return retVal;
191 }
192 
193 }  // namespace base
194 }  // namespace android
195