lxgui
Loading...
Searching...
No Matches
gui_atlas.cpp
1#include "lxgui/gui_atlas.hpp"
2
3#include "lxgui/gui_exception.hpp"
4#include "lxgui/gui_font.hpp"
5#include "lxgui/gui_out.hpp"
6#include "lxgui/gui_renderer.hpp"
7#include "lxgui/gui_vertex.hpp"
8#include "lxgui/utils_string.hpp"
9
10namespace lxgui::gui {
11
13
14std::shared_ptr<material> atlas_page::fetch_material(const std::string& file_name) const {
15 auto iter = texture_list_.find(file_name);
16 if (iter != texture_list_.end()) {
17 if (std::shared_ptr<gui::material> lock = iter->second.lock())
18 return lock;
19 }
20
21 return nullptr;
22}
23
24std::shared_ptr<gui::material>
25atlas_page::add_material(const std::string& file_name, const material& mat) {
26 try {
27 const auto rect = mat.get_rect();
28 const auto location = find_location_(rect.width(), rect.height());
29 if (!location.has_value())
30 return nullptr;
31
32 std::shared_ptr<gui::material> tex = add_material_(mat, location.value());
33 texture_list_[file_name] = tex;
34 return tex;
35 } catch (const std::exception& e) {
36 gui::out << gui::warning << e.what() << std::endl;
37 return nullptr;
38 }
39}
40
41std::shared_ptr<font> atlas_page::fetch_font(const std::string& font_name) const {
42 auto iter = font_list_.find(font_name);
43 if (iter != font_list_.end()) {
44 if (std::shared_ptr<gui::font> lock = iter->second.lock())
45 return lock;
46 }
47
48 return nullptr;
49}
50
51bool atlas_page::add_font(const std::string& font_name, std::shared_ptr<gui::font> fnt) {
52 try {
53 if (const auto mat = fnt->get_texture().lock()) {
54 const auto rect = mat->get_rect();
55 const auto location = find_location_(rect.width(), rect.height());
56 if (!location.has_value())
57 return false;
58
59 std::shared_ptr<gui::material> tex = add_material_(*mat, location.value());
60 fnt->update_texture(tex);
61
62 font_list_[font_name] = std::move(fnt);
63 return true;
64 } else
65 return false;
66 } catch (const std::exception& e) {
67 gui::out << gui::warning << e.what() << std::endl;
68 return false;
69 }
70}
71
72bool atlas_page::empty() const {
73 for (const auto& mat : texture_list_) {
74 if (std::shared_ptr<gui::material> lock = mat.second.lock())
75 return false;
76 }
77
78 for (const auto& fnt : font_list_) {
79 if (std::shared_ptr<gui::font> lock = fnt.second.lock())
80 return false;
81 }
82
83 return true;
84}
85
86std::optional<bounds2f> atlas_page::find_location_(float width, float height) const {
87 constexpr float padding = 1.0f; // pixels
88
89 bounds2f start_quad(0, width, 0, height);
90 if (empty())
91 return start_quad;
92
93 const float atlas_width = get_width_();
94 const float atlast_height = get_height_();
95
96 std::vector<bounds2f> occupied_space;
97 occupied_space.reserve(texture_list_.size());
98
99 float max_width = 0.0f;
100 float max_height = 0.0f;
101
102 auto apply_padding = [&](bounds2f rect) {
103 rect.right += padding;
104 rect.bottom += padding;
105 return rect;
106 };
107
108 for (const auto& mat : texture_list_) {
109 if (std::shared_ptr<gui::material> lock = mat.second.lock()) {
110 occupied_space.push_back(apply_padding(lock->get_rect()));
111 max_width = std::max(max_width, occupied_space.back().right);
112 max_height = std::max(max_height, occupied_space.back().bottom);
113 }
114 }
115
116 for (const auto& fnt : font_list_) {
117 if (std::shared_ptr<gui::font> lock = fnt.second.lock()) {
118 occupied_space.push_back(apply_padding(lock->get_texture().lock()->get_rect()));
119 max_width = std::max(max_width, occupied_space.back().right);
120 max_height = std::max(max_height, occupied_space.back().bottom);
121 }
122 }
123
124 float best_area = std::numeric_limits<float>::infinity();
125 bounds2f best_quad;
126
127 for (const auto& rect_source : occupied_space) {
128 auto test_position = [&](const vector2f& pos) {
129 const bounds2f test_quad = start_quad + pos;
130 if (test_quad.right > atlas_width || test_quad.bottom > atlast_height)
131 return;
132
133 const float new_max_width = std::max(max_width, test_quad.right);
134 const float new_max_height = std::max(max_height, test_quad.bottom);
135 const float new_area = new_max_width * new_max_height;
136
137 if (new_area >= best_area)
138 return;
139
140 for (const auto& rect_other : occupied_space) {
141 if (test_quad.overlaps(rect_other))
142 return;
143 }
144
145 best_area = new_area;
146 best_quad = test_quad;
147 };
148
149 test_position(rect_source.top_right());
150 test_position(rect_source.bottom_left());
151 }
152
153 if (std::isfinite(best_area))
154 return best_quad;
155 else
156 return std::nullopt;
157}
158
159atlas::atlas(renderer& rdr, material::filter filt) : renderer_(rdr), filter_(filt) {}
160
161std::shared_ptr<gui::material> atlas::fetch_material(const std::string& file_name) const {
162 for (const auto& item : page_list_) {
163 auto tex = item.page->fetch_material(file_name);
164 if (tex)
165 return tex;
166 }
167
168 return nullptr;
169}
170
171std::shared_ptr<gui::material>
172atlas::add_material(const std::string& file_name, const material& mat) {
173 try {
174 for (const auto& item : page_list_) {
175 auto tex = item.page->add_material(file_name, mat);
176 if (tex)
177 return tex;
178
179 if (item.page->empty()) {
180 gui::out << gui::warning << "Could not fit texture '" << file_name
181 << "' on any atlas page." << std::endl;
182 return nullptr;
183 }
184 }
185
186 add_page_();
187 auto tex = page_list_.back().page->add_material(file_name, mat);
188 if (tex)
189 return tex;
190
191 return nullptr;
192 } catch (const std::exception& e) {
193 gui::out << gui::warning << e.what() << std::endl;
194 return nullptr;
195 }
196}
197
198std::shared_ptr<gui::font> atlas::fetch_font(const std::string& font_name) const {
199 for (const auto& item : page_list_) {
200 auto fnt = item.page->fetch_font(font_name);
201 if (fnt)
202 return fnt;
203 }
204
205 return nullptr;
206}
207
208bool atlas::add_font(const std::string& font_name, std::shared_ptr<gui::font> fnt) {
209 try {
210 for (const auto& item : page_list_) {
211 if (item.page->add_font(font_name, fnt))
212 return true;
213
214 if (item.page->empty()) {
215 gui::out << gui::warning << "Could not fit font '" << font_name
216 << "' on any atlas page." << std::endl;
217 return false;
218 }
219 }
220
221 add_page_();
222
223 return page_list_.back().page->add_font(font_name, std::move(fnt));
224 } catch (const std::exception& e) {
225 gui::out << gui::warning << e.what() << std::endl;
226 return false;
227 }
228}
229
230std::size_t atlas::get_page_count() const {
231 return page_list_.size();
232}
233
234void atlas::add_page_() {
235 page_item item;
236 item.page = create_page_();
237
238 // Add a white pixel as the first material in the atlas.
239 // This can be used for optimizing quad batching, to render
240 // quads with no texture.
241 color32 pixel{255, 255, 255, 255};
242 auto tex = renderer_.create_material(vector2ui(1u, 1u), &pixel);
243 item.no_texture_mat = item.page->add_material("", *tex);
244
245 page_list_.push_back(std::move(item));
246}
247
248} // namespace lxgui::gui
atlas_page(material::filter filt)
Constructor.
Definition gui_atlas.cpp:12
std::shared_ptr< material > add_material(const std::string &file_name, const material &mat)
Creates a new material from a texture file.
Definition gui_atlas.cpp:25
virtual std::shared_ptr< material > add_material_(const material &mat, const bounds2f &location)=0
Adds a new material to this page, at the provided location.
std::shared_ptr< font > fetch_font(const std::string &font_name) const
Find a font in this page (nullptr if not found).
Definition gui_atlas.cpp:41
bool add_font(const std::string &font_name, std::shared_ptr< gui::font > fnt)
Creates a new font from a texture file.
Definition gui_atlas.cpp:51
virtual float get_height_() const =0
Return the height of this page (in pixels).
std::shared_ptr< material > fetch_material(const std::string &file_name) const
Find a material in this page (nullptr if not found).
Definition gui_atlas.cpp:14
virtual float get_width_() const =0
Return the width of this page (in pixels).
bool empty() const
Checks if this page is empty (contains no materials).
Definition gui_atlas.cpp:72
std::size_t get_page_count() const
Return the number of pages in this atlas.
std::shared_ptr< material > add_material(const std::string &file_name, const material &mat)
Add a new material to the atlas.
bool add_font(const std::string &font_name, std::shared_ptr< gui::font > fnt)
Add a new font to the atlas.
std::shared_ptr< material > fetch_material(const std::string &file_name) const
Find a material in this atlas (nullptr if not found).
virtual std::unique_ptr< atlas_page > create_page_()=0
Create a new page in this atlas.
atlas(renderer &rdr, material::filter filt)
Constructor.
renderer & renderer_
std::shared_ptr< font > fetch_font(const std::string &font_name) const
Find a font in this atlas (nullptr if not found).
A class that holds rendering data.
virtual bounds2f get_rect() const =0
Returns the pixel rect in pixels of the canvas containing this texture (if any).
Abstract type for implementation specific management.
std::shared_ptr< material > create_material(const std::string &file_name, material::filter filt=material::filter::none)
Creates a new material from a texture file.
vector2< float > vector2f
Holds 2D coordinates (as floats)
bounds2< float > bounds2f
Holds 2D bounds of a region (as floats).
vector2< std::size_t > vector2ui
Holds 2D coordinates (as unsigned integers)
std::ostream out
const std::string warning
Definition gui_out.cpp:6
Holds a single color (byte RGBA, 32 bits)
Definition gui_color.hpp:76