Porytiles
Loading...
Searching...
No Matches
ascii_tile_printer.cpp
Go to the documentation of this file.
2
3#include <set>
4#include <sstream>
5#include <string>
6#include <utility>
7#include <vector>
8
14
15namespace {
16
17using namespace porytiles;
18
19void push_to_stream(std::stringstream &ss, const std::string_view s, const std::size_t n)
20{
21 for (std::size_t i = 0; i < n; i++) {
22 ss << s;
23 }
24}
25
26void push_to_stream(
27 std::stringstream &ss, const TextFormatter &format, const std::string_view s, Style style, const std::size_t n)
28{
29 for (std::size_t i = 0; i < n; i++) {
30 ss << format.format("{}", FormatParam{s, style});
31 }
32}
33
34void reset_stream(std::stringstream &ss)
35{
36 ss.clear();
37 ss.str(std::string{});
38}
39
40Style rgba_to_fg_style(const Rgba32 &color)
41{
42 return rgb_fg_style(color.red(), color.green(), color.blue());
43}
44
45Style rgba_to_bg_style(const Rgba32 &color)
46{
47 return rgb_bg_style(color.red(), color.green(), color.blue());
48}
49
64std::vector<std::string> render_tile_with_highlights(
65 const PixelTile<Rgba32> &tile,
66 const std::set<std::pair<std::size_t, std::size_t>> &highlight_coords,
67 const Rgba32 &extrinsic_transparency,
68 const TextFormatter *format)
69{
70 std::vector<std::string> result{};
71 std::stringstream ss{};
72
73 // Insert a blank line
74 result.emplace_back();
75
76 for (std::size_t row = 0; row < tile::side_length_pix; row++) {
77 for (std::size_t col = 0; col < tile::side_length_pix; col++) {
78 auto pixel_color = tile.at(row, col);
79 if (pixel_color.is_intrinsically_transparent()) {
80 pixel_color = extrinsic_transparency;
81 }
82 const auto color_style_bg = rgba_to_bg_style(pixel_color);
83 const auto color_style_fg = rgba_to_fg_style(pixel_color);
84 if (highlight_coords.contains({row, col})) {
85 ss << format->format("{}", FormatParam{"XX", Style::bold | Style::blink | color_style_fg});
86 }
87 else {
88 ss << format->format("{}", FormatParam{" ", Style::bold | color_style_bg});
89 }
90 }
91 result.push_back(ss.str());
92 reset_stream(ss);
93 }
94
95 // Insert a blank line
96 result.emplace_back();
97
98 return result;
99}
100
112const PixelTile<Rgba32> &
113get_tile_from_metatile(const Metatile<Rgba32> &metatile, metatile::Layer layer, metatile::Subtile subtile)
114{
115 const auto subtile_index = static_cast<std::size_t>(subtile);
116 switch (layer) {
117 case metatile::Layer::bottom:
118 return metatile.bottom(subtile_index);
119 case metatile::Layer::middle:
120 return metatile.middle(subtile_index);
121 case metatile::Layer::top:
122 return metatile.top(subtile_index);
123 }
124 panic("invalid layer value");
125}
126
145std::vector<std::string> render_metatile_with_highlights(
146 const Metatile<Rgba32> &metatile,
147 metatile::Layer layer,
148 metatile::Subtile subtile,
149 const std::set<std::pair<std::size_t, std::size_t>> &highlight_coords,
150 const Rgba32 &extrinsic_transparency,
151 const TextFormatter *format,
152 bool highlight_subtile)
153{
154 std::vector<std::string> result{};
155 std::stringstream ss{};
156
157 // Insert a blank line
158 result.emplace_back();
159
160 if (highlight_subtile && subtile == metatile::Subtile::northwest) {
161 push_to_stream(ss, *format, "↓", Style::bold | Style::yellow, 16);
162 result.push_back(ss.str());
163 reset_stream(ss);
164 }
165 if (highlight_subtile && subtile == metatile::Subtile::northeast) {
166 push_to_stream(ss, " ", 16 + 1); // +1 to account for center space
167 push_to_stream(ss, *format, "↓", Style::bold | Style::yellow, 16);
168 result.push_back(ss.str());
169 reset_stream(ss);
170 }
171
172 for (std::size_t i = 0; i < metatile::side_length_pix; i++) {
173 for (std::size_t j = 0; j < metatile::side_length_pix; j++) {
174 // Determine which subtile (i, j) is in and compute subtile-local coordinates
175 metatile::Subtile current_subtile{};
176 std::size_t subtile_row = 0;
177 std::size_t subtile_col = 0;
178
179 if (i < 8 && j < 8) {
180 current_subtile = metatile::Subtile::northwest;
181 subtile_row = i;
182 subtile_col = j;
183 }
184 else if (i < 8 && j >= 8) {
185 current_subtile = metatile::Subtile::northeast;
186 subtile_row = i;
187 subtile_col = j - 8;
188 }
189 else if (i >= 8 && j < 8) {
190 current_subtile = metatile::Subtile::southwest;
191 subtile_row = i - 8;
192 subtile_col = j;
193 }
194 else {
195 current_subtile = metatile::Subtile::southeast;
196 subtile_row = i - 8;
197 subtile_col = j - 8;
198 }
199
200 const bool is_in_target_subtile = current_subtile == subtile;
201 const auto &current_tile = get_tile_from_metatile(metatile, layer, current_subtile);
202 auto pixel_color = current_tile.at(subtile_row, subtile_col);
203 if (pixel_color.is_intrinsically_transparent()) {
204 pixel_color = extrinsic_transparency;
205 }
206 const auto color_style_bg = rgba_to_bg_style(pixel_color);
207 const auto color_style_fg = rgba_to_fg_style(pixel_color);
208
209 if (is_in_target_subtile) {
210 // In target subtile: show pixel highlight if requested, highlight subtile if requested
211 if (highlight_coords.contains({subtile_row, subtile_col})) {
212 ss << format->format("{}", FormatParam{"XX", Style::bold | Style::blink | color_style_fg});
213 }
214 else {
215 ss << format->format("{}", FormatParam{" ", Style::bold | color_style_bg});
216 }
217 }
218 else {
219 // In non-target subtile: show styled blank with the pixel's RGB color (non-bold)
220 const auto styled_star = format->format("{}", FormatParam{" ", color_style_bg});
221 ss << styled_star;
222 }
223
224 // If we're at the midpoint cell, add an extra space.
225 if (j == 7) {
226 ss << " ";
227 }
228
229 // Reset once this row is exhausted
230 if (j == 15) {
231 result.push_back(ss.str());
232 reset_stream(ss);
233 }
234 }
235
236 // Insert a spacer line between top and bottom tiles
237 if (i == 7) {
238 result.push_back(ss.str());
239 reset_stream(ss);
240 }
241 }
242
243 if (highlight_subtile && subtile == metatile::Subtile::southwest) {
244 push_to_stream(ss, *format, "↑", Style::bold | Style::yellow, 16);
245 result.push_back(ss.str());
246 reset_stream(ss);
247 }
248 if (highlight_subtile && subtile == metatile::Subtile::southeast) {
249 push_to_stream(ss, " ", 16 + 1); // +1 to account for center space
250 push_to_stream(ss, *format, "↑", Style::bold | Style::yellow, 16);
251 result.push_back(ss.str());
252 reset_stream(ss);
253 }
254
255 // Insert a blank line
256 result.emplace_back();
257
258 return result;
259}
260
261} // namespace
262
263namespace porytiles {
264
265std::vector<std::string> AsciiTilePrinter::print_metatile(
266 const Metatile<Rgba32> &metatile, metatile::Layer layer, const Rgba32 &extrinsic_transparency) const
267{
268 return render_metatile_with_highlights(
269 metatile, layer, metatile::Subtile::northeast, {}, extrinsic_transparency, format_, false);
270}
271
273 const Metatile<Rgba32> &metatile,
274 metatile::Layer layer,
275 metatile::Subtile subtile,
276 const Rgba32 &extrinsic_transparency) const
277{
278 return render_metatile_with_highlights(metatile, layer, subtile, {}, extrinsic_transparency, format_, true);
279}
280
282 const Metatile<Rgba32> &metatile,
283 metatile::Layer layer,
284 metatile::Subtile subtile,
285 std::size_t row,
286 std::size_t col,
287 const Rgba32 &extrinsic_transparency) const
288{
289 std::set<std::pair<std::size_t, std::size_t>> coords{{row, col}};
290 return render_metatile_with_highlights(metatile, layer, subtile, coords, extrinsic_transparency, format_, true);
291}
292
294 const Metatile<Rgba32> &metatile,
295 metatile::Layer layer,
296 metatile::Subtile subtile,
297 const std::vector<std::size_t> &indexes,
298 const Rgba32 &extrinsic_transparency) const
299{
300 std::set<std::pair<std::size_t, std::size_t>> coords{};
301 for (const auto index : indexes) {
302 coords.insert(tile::index_to_row_col(index));
303 }
304 return render_metatile_with_highlights(metatile, layer, subtile, coords, extrinsic_transparency, format_, true);
305}
306
307std::vector<std::string>
308AsciiTilePrinter::print_tile(const PixelTile<Rgba32> &tile, const Rgba32 &extrinsic_transparency) const
309{
310 return render_tile_with_highlights(tile, {}, extrinsic_transparency, format_);
311}
312
313std::vector<std::string>
314AsciiTilePrinter::print_tile(const PixelTile<IndexPixel> &tile, const Rgba32 &extrinsic_transparency) const
315{
316 const auto greyscale_pal = standard_greyscale_pal();
317
318 std::vector<std::string> result{};
319 std::stringstream ss{};
320
321 // Insert a blank line
322 result.emplace_back();
323
324 for (std::size_t row = 0; row < tile::side_length_pix; row++) {
325 for (std::size_t col = 0; col < tile::side_length_pix; col++) {
326 const auto index_pixel = tile.at(row, col);
327 // Use color_index() to extract the lower 4 bits, which is always in range [0, 15].
328 // This handles both standard 4-bit pixels and true-color encoded 8-bit pixels correctly.
329 const Rgba32 pixel_color = greyscale_pal[index_pixel.color_index()];
330 const auto color_style_bg = rgba_to_bg_style(pixel_color);
331 ss << format_->format("{}", FormatParam{" ", Style::bold | color_style_bg});
332 }
333 result.push_back(ss.str());
334 reset_stream(ss);
335 }
336
337 // Insert a blank line
338 result.emplace_back();
339
340 return result;
341}
342
344 const PixelTile<Rgba32> &tile, std::size_t row, std::size_t col, const Rgba32 &extrinsic_transparency) const
345{
346 std::set<std::pair<std::size_t, std::size_t>> coords{{row, col}};
347 return render_tile_with_highlights(tile, coords, extrinsic_transparency, format_);
348}
349
351 const PixelTile<Rgba32> &tile, const std::vector<std::size_t> &indexes, const Rgba32 &extrinsic_transparency) const
352{
353 std::set<std::pair<std::size_t, std::size_t>> coords{};
354 for (const auto index : indexes) {
355 coords.insert(tile::index_to_row_col(index));
356 }
357 return render_tile_with_highlights(tile, coords, extrinsic_transparency, format_);
358}
359
360} // namespace porytiles
std::vector< std::string > print_metatile_pixel_highlight(const Metatile< Rgba32 > &metatile, metatile::Layer layer, metatile::Subtile subtile, std::size_t row, std::size_t col, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_metatile_tile_highlight(const Metatile< Rgba32 > &metatile, metatile::Layer layer, metatile::Subtile subtile, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_tile_pixel_highlight(const PixelTile< Rgba32 > &tile, std::size_t row, std::size_t col, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_tile_pixel_highlights(const PixelTile< Rgba32 > &tile, const std::vector< std::size_t > &indexes, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_metatile_pixel_highlights(const Metatile< Rgba32 > &metatile, metatile::Layer layer, metatile::Subtile subtile, const std::vector< std::size_t > &indexes, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_metatile(const Metatile< Rgba32 > &metatile, metatile::Layer layer, const Rgba32 &extrinsic_transparency) const override
std::vector< std::string > print_tile(const PixelTile< Rgba32 > &tile, const Rgba32 &extrinsic_transparency) const override
A text parameter with associated styling for formatted output.
The core tileset entity - a 2x2 grid of PixelTile objects arranged into three layers.
Definition metatile.hpp:186
const PixelTile< PixelType > & top(std::size_t i) const
Get a constant reference to a PixelTile from the top layer.
Definition metatile.hpp:430
const PixelTile< PixelType > & bottom(std::size_t i) const
Get a constant reference to a PixelTile from the bottom layer.
Definition metatile.hpp:336
const PixelTile< PixelType > & middle(std::size_t i) const
Get a constant reference to a PixelTile from the middle layer.
Definition metatile.hpp:383
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
PixelType at(std::size_t i) const
Represents a 32-bit RGBA color.
Definition rgba32.hpp:23
std::uint8_t red() const
Definition rgba32.hpp:81
std::uint8_t blue() const
Definition rgba32.hpp:91
std::uint8_t green() const
Definition rgba32.hpp:86
Text styling class supporting formatting, foreground colors, and background colors.
static const Style yellow
Yellow foreground color.
static const Style bold
Bold text formatting.
static const Style blink
Blink text formatting.
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.
constexpr std::size_t side_length_pix
Definition metatile.hpp:19
constexpr std::size_t side_length_pix
constexpr std::pair< std::size_t, std::size_t > index_to_row_col(std::size_t index)
Converts a linear index to row and column coordinates.
std::vector< Rgba32 > standard_greyscale_pal()
Returns the standard 16-color greyscale palette used for indexed tile output.
Definition rgba32.hpp:152
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
constexpr Style rgb_fg_style(std::uint8_t r, std::uint8_t g, std::uint8_t b)
Creates a Style value with a custom RGB foreground color.
constexpr Style rgb_bg_style(std::uint8_t r, std::uint8_t g, std::uint8_t b)
Creates a Style value with a custom RGB background color.