Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
rectangle_packer.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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
6
7#include <algorithm>
8#include <vector>
9
10namespace impeller {
11
12// Pack rectangles and track the current silhouette
13// Based, in part, on Jukka Jylanki's work at http://clb.demon.fi
14// and ported from Skia's implementation
15// https://github.com/google/skia/blob/b5de4b8ae95c877a9ecfad5eab0765bc22550301/src/gpu/RectanizerSkyline.cpp
17 public:
19 this->reset();
20 }
21
23
24 void reset() final {
25 area_so_far_ = 0;
26 skyline_.clear();
27 skyline_.push_back(SkylineSegment{0, 0, this->width()});
28 }
29
30 bool addRect(int w, int h, IPoint16* loc) final;
31
32 float percentFull() const final {
33 return area_so_far_ / ((float)this->width() * this->height());
34 }
35
36 private:
37 struct SkylineSegment {
38 int x_;
39 int y_;
40 int width_;
41 };
42
43 std::vector<SkylineSegment> skyline_;
44
45 int32_t area_so_far_;
46
47 // Can a width x height rectangle fit in the free space represented by
48 // the skyline segments >= 'skylineIndex'? If so, return true and fill in
49 // 'y' with the y-location at which it fits (the x location is pulled from
50 // 'skylineIndex's segment.
51 bool rectangleFits(int skylineIndex, int width, int height, int* y) const;
52 // Update the skyline structure to include a width x height rect located
53 // at x,y.
54 void addSkylineLevel(int skylineIndex, int x, int y, int width, int height);
55};
56
58 if ((unsigned)width > (unsigned)this->width() ||
59 (unsigned)height > (unsigned)this->height()) {
60 return false;
61 }
62
63 // find position for new rectangle
64 int bestWidth = this->width() + 1;
65 int bestX = 0;
66 int bestY = this->height() + 1;
67 int bestIndex = -1;
68 for (int i = 0; i < (int)skyline_.size(); ++i) {
69 int y;
70 if (this->rectangleFits(i, width, height, &y)) {
71 // minimize y position first, then width of skyline
72 if (y < bestY || (y == bestY && skyline_[i].width_ < bestWidth)) {
73 bestIndex = i;
74 bestWidth = skyline_[i].width_;
75 bestX = skyline_[i].x_;
76 bestY = y;
77 }
78 }
79 }
80
81 // add rectangle to skyline
82 if (-1 != bestIndex) {
83 this->addSkylineLevel(bestIndex, bestX, bestY, width, height);
84 loc->x_ = bestX;
85 loc->y_ = bestY;
86
87 area_so_far_ += width * height;
88 return true;
89 }
90
91 loc->x_ = 0;
92 loc->y_ = 0;
93 return false;
94}
95
96bool SkylineRectanglePacker::rectangleFits(int skylineIndex,
97 int width,
98 int height,
99 int* ypos) const {
100 int x = skyline_[skylineIndex].x_;
101 if (x + width > this->width()) {
102 return false;
103 }
104
105 int widthLeft = width;
106 int i = skylineIndex;
107 int y = skyline_[skylineIndex].y_;
108 while (widthLeft > 0 && i < (int)skyline_.size()) {
109 y = std::max(y, skyline_[i].y_);
110 if (y + height > this->height()) {
111 return false;
112 }
113 widthLeft -= skyline_[i].width_;
114 ++i;
115 }
116
117 *ypos = y;
118 return true;
119}
120
121void SkylineRectanglePacker::addSkylineLevel(int skylineIndex,
122 int x,
123 int y,
124 int width,
125 int height) {
126 SkylineSegment newSegment;
127 newSegment.x_ = x;
128 newSegment.y_ = y + height;
129 newSegment.width_ = width;
130 skyline_.insert(std::next(skyline_.begin(), skylineIndex), newSegment);
131
132 FML_DCHECK(newSegment.x_ + newSegment.width_ <= this->width());
133 FML_DCHECK(newSegment.y_ <= this->height());
134
135 // delete width of the new skyline segment from following ones
136 for (int i = skylineIndex + 1; i < (int)skyline_.size(); ++i) {
137 // The new segment subsumes all or part of skyline_[i]
138 FML_DCHECK(skyline_[i - 1].x_ <= skyline_[i].x_);
139
140 if (skyline_[i].x_ < skyline_[i - 1].x_ + skyline_[i - 1].width_) {
141 int shrink = skyline_[i - 1].x_ + skyline_[i - 1].width_ - skyline_[i].x_;
142
143 skyline_[i].x_ += shrink;
144 skyline_[i].width_ -= shrink;
145
146 if (skyline_[i].width_ <= 0) {
147 // fully consumed, remove item at index i
148 skyline_.erase(std::next(skyline_.begin(), i));
149 --i;
150 } else {
151 // only partially consumed
152 break;
153 }
154 } else {
155 break;
156 }
157 }
158
159 // merge skyline_s
160 for (int i = 0; i < ((int)skyline_.size()) - 1; ++i) {
161 if (skyline_[i].y_ == skyline_[i + 1].y_) {
162 skyline_[i].width_ += skyline_[i + 1].width_;
163 skyline_.erase(std::next(skyline_.begin(), i));
164 --i;
165 }
166 }
167}
168
169std::unique_ptr<RectanglePacker> RectanglePacker::Factory(int width,
170 int height) {
171 return std::make_unique<SkylineRectanglePacker>(width, height);
172}
173
174} // namespace impeller
Type::kYUV Type::kRGBA() int(0.7 *637)
Packs rectangles into a specified area without rotating them.
static std::unique_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
float percentFull() const final
Returns how much area has been filled with rectangles.
void reset() final
Empty out all previously added rectangles.
bool addRect(int w, int h, IPoint16 *loc) final
Attempt to add a rect without moving already placed rectangles.
#define FML_DCHECK(condition)
Definition logging.h:103
double y
double x
SkScalar w
SkScalar h
int32_t height
int32_t width