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
62std::vector<std::string> render_tile_with_highlights(
63 const PixelTile<Rgba32> &tile,
64 const std::set<std::pair<std::size_t, std::size_t>> &highlight_coords,
65 const Rgba32 &extrinsic_transparency,
66 const TextFormatter *format)
67{
68 std::vector<std::string> result{};
69 std::stringstream ss{};
70
71 // Insert a blank line
72 result.emplace_back();
73
74 for (std::size_t row = 0; row < tile::side_length_pix; row++) {
75 for (std::size_t col = 0; col < tile::side_length_pix; col++) {
76 auto pixel_color = tile.at(row, col);
77 if (pixel_color.is_intrinsically_transparent()) {
78 pixel_color = extrinsic_transparency;
79 }
80 const auto color_style_bg = rgba_to_bg_style(pixel_color);
81 const auto color_style_fg = rgba_to_fg_style(pixel_color);
82 if (highlight_coords.contains({row, col})) {
83 ss << format->format("{}", FormatParam{"XX", Style::bold | Style::blink | color_style_fg});
84 }
85 else {
86 ss << format->format("{}", FormatParam{" ", Style::bold | color_style_bg});
87 }
88 }
89 result.push_back(ss.str());
90 reset_stream(ss);
91 }
92
93 // Insert a blank line
94 result.emplace_back();
95
96 return result;
97}
98
108const PixelTile<Rgba32> &
109get_tile_from_metatile(const Metatile<Rgba32> &metatile, metatile::Layer layer, metatile::Subtile subtile)
110{
111 const auto subtile_index = static_cast<std::size_t>(subtile);
112 switch (layer) {
113 case metatile::Layer::bottom:
114 return metatile.bottom(subtile_index);
115 case metatile::Layer::middle:
116 return metatile.middle(subtile_index);
117 case metatile::Layer::top:
118 return metatile.top(subtile_index);
119 }
120 panic("invalid layer value");
121}
122
139std::vector<std::string> render_metatile_with_highlights(
140 const Metatile<Rgba32> &metatile,
141 metatile::Layer layer,
142 metatile::Subtile subtile,
143 const std::set<std::pair<std::size_t, std::size_t>> &highlight_coords,
144 const Rgba32 &extrinsic_transparency,
145 const TextFormatter *format,
146 bool highlight_subtile)
147{
148 std::vector<std::string> result{};
149 std::stringstream ss{};
150
151 // Insert a blank line
152 result.emplace_back();
153
154 if (highlight_subtile && subtile == metatile::Subtile::northwest) {
155 push_to_stream(ss, *format, "↓", Style::bold | Style::yellow, 16);
156 result.push_back(ss.str());
157 reset_stream(ss);
158 }
159 if (highlight_subtile && subtile == metatile::Subtile::northeast) {
160 push_to_stream(ss, " ", 16 + 1); // +1 to account for center space
161 push_to_stream(ss, *format, "↓", Style::bold | Style::yellow, 16);
162 result.push_back(ss.str());
163 reset_stream(ss);
164 }
165
166 for (std::size_t i = 0; i < metatile::side_length_pix; i++) {
167 for (std::size_t j = 0; j < metatile::side_length_pix; j++) {
168 // Determine which subtile (i, j) is in and compute subtile-local coordinates
169 metatile::Subtile current_subtile{};
170 std::size_t subtile_row = 0;
171 std::size_t subtile_col = 0;
172
173 if (i < 8 && j < 8) {
174 current_subtile = metatile::Subtile::northwest;
175 subtile_row = i;
176 subtile_col = j;
177 }
178 else if (i < 8 && j >= 8) {
179 current_subtile = metatile::Subtile::northeast;
180 subtile_row = i;
181 subtile_col = j - 8;
182 }
183 else if (i >= 8 && j < 8) {
184 current_subtile = metatile::Subtile::southwest;
185 subtile_row = i - 8;
186 subtile_col = j;
187 }
188 else {
189 current_subtile = metatile::Subtile::southeast;
190 subtile_row = i - 8;
191 subtile_col = j - 8;
192 }
193
194 const bool is_in_target_subtile = current_subtile == subtile;
195 const auto &current_tile = get_tile_from_metatile(metatile, layer, current_subtile);
196 auto pixel_color = current_tile.at(subtile_row, subtile_col);
197 if (pixel_color.is_intrinsically_transparent()) {
198 pixel_color = extrinsic_transparency;
199 }
200 const auto color_style_bg = rgba_to_bg_style(pixel_color);
201 const auto color_style_fg = rgba_to_fg_style(pixel_color);
202
203 if (is_in_target_subtile) {
204 // In target subtile: show pixel highlight if requested, highlight subtile if requested
205 if (highlight_coords.contains({subtile_row, subtile_col})) {
206 ss << format->format("{}", FormatParam{"XX", Style::bold | Style::blink | color_style_fg});
207 }
208 else {
209 ss << format->format("{}", FormatParam{" ", Style::bold | color_style_bg});
210 }
211 }
212 else {
213 // In non-target subtile: show styled blank with the pixel's RGB color (non-bold)
214 const auto styled_star = format->format("{}", FormatParam{" ", color_style_bg});
215 ss << styled_star;
216 }
217
218 // If we're at the midpoint cell, add an extra space.
219 if (j == 7) {
220 ss << " ";
221 }
222
223 // Reset once this row is exhausted
224 if (j == 15) {
225 result.push_back(ss.str());
226 reset_stream(ss);
227 }
228 }
229
230 // Insert a spacer line between top and bottom tiles
231 if (i == 7) {
232 result.push_back(ss.str());
233 reset_stream(ss);
234 }
235 }
236
237 if (highlight_subtile && subtile == metatile::Subtile::southwest) {
238 push_to_stream(ss, *format, "↑", Style::bold | Style::yellow, 16);
239 result.push_back(ss.str());
240 reset_stream(ss);
241 }
242 if (highlight_subtile && subtile == metatile::Subtile::southeast) {
243 push_to_stream(ss, " ", 16 + 1); // +1 to account for center space
244 push_to_stream(ss, *format, "↑", Style::bold | Style::yellow, 16);
245 result.push_back(ss.str());
246 reset_stream(ss);
247 }
248
249 // Insert a blank line
250 result.emplace_back();
251
252 return result;
253}
254
255} // namespace
256
257namespace porytiles {
258
259std::vector<std::string> AsciiTilePrinter::print_metatile(
260 const Metatile<Rgba32> &metatile, metatile::Layer layer, const Rgba32 &extrinsic_transparency) const
261{
262 return render_metatile_with_highlights(
263 metatile, layer, metatile::Subtile::northeast, {}, extrinsic_transparency, format_, false);
264}
265
267 const Metatile<Rgba32> &metatile,
268 metatile::Layer layer,
269 metatile::Subtile subtile,
270 const Rgba32 &extrinsic_transparency) const
271{
272 return render_metatile_with_highlights(metatile, layer, subtile, {}, extrinsic_transparency, format_, true);
273}
274
276 const Metatile<Rgba32> &metatile,
277 metatile::Layer layer,
278 metatile::Subtile subtile,
279 std::size_t row,
280 std::size_t col,
281 const Rgba32 &extrinsic_transparency) const
282{
283 std::set<std::pair<std::size_t, std::size_t>> coords{{row, col}};
284 return render_metatile_with_highlights(metatile, layer, subtile, coords, extrinsic_transparency, format_, true);
285}
286
288 const Metatile<Rgba32> &metatile,
289 metatile::Layer layer,
290 metatile::Subtile subtile,
291 const std::vector<std::size_t> &indexes,
292 const Rgba32 &extrinsic_transparency) const
293{
294 std::set<std::pair<std::size_t, std::size_t>> coords{};
295 for (const auto index : indexes) {
296 coords.insert(tile::index_to_row_col(index));
297 }
298 return render_metatile_with_highlights(metatile, layer, subtile, coords, extrinsic_transparency, format_, true);
299}
300
301std::vector<std::string>
302AsciiTilePrinter::print_tile(const PixelTile<Rgba32> &tile, const Rgba32 &extrinsic_transparency) const
303{
304 return render_tile_with_highlights(tile, {}, extrinsic_transparency, format_);
305}
306
307std::vector<std::string>
308AsciiTilePrinter::print_tile(const PixelTile<IndexPixel> &tile, const Rgba32 &extrinsic_transparency) const
309{
310 const auto greyscale_palette = standard_greyscale_palette();
311
312 std::vector<std::string> result{};
313 std::stringstream ss{};
314
315 // Insert a blank line
316 result.emplace_back();
317
318 for (std::size_t row = 0; row < tile::side_length_pix; row++) {
319 for (std::size_t col = 0; col < tile::side_length_pix; col++) {
320 const auto index_pixel = tile.at(row, col);
321 // Use color_index() to extract the lower 4 bits, which is always in range [0, 15].
322 // This handles both standard 4-bit pixels and true-color encoded 8-bit pixels correctly.
323 const Rgba32 pixel_color = greyscale_palette[index_pixel.color_index()];
324 const auto color_style_bg = rgba_to_bg_style(pixel_color);
325 ss << format_->format("{}", FormatParam{" ", Style::bold | color_style_bg});
326 }
327 result.push_back(ss.str());
328 reset_stream(ss);
329 }
330
331 // Insert a blank line
332 result.emplace_back();
333
334 return result;
335}
336
338 const PixelTile<Rgba32> &tile, std::size_t row, std::size_t col, const Rgba32 &extrinsic_transparency) const
339{
340 std::set<std::pair<std::size_t, std::size_t>> coords{{row, col}};
341 return render_tile_with_highlights(tile, coords, extrinsic_transparency, format_);
342}
343
345 const PixelTile<Rgba32> &tile, const std::vector<std::size_t> &indexes, const Rgba32 &extrinsic_transparency) const
346{
347 std::set<std::pair<std::size_t, std::size_t>> coords{};
348 for (const auto index : indexes) {
349 coords.insert(tile::index_to_row_col(index));
350 }
351 return render_tile_with_highlights(tile, coords, extrinsic_transparency, format_);
352}
353
354} // 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:224
const PixelTile< PixelType > & top(std::size_t i) const
Get a constant reference to a PixelTile from the top layer.
Definition metatile.hpp:440
const PixelTile< PixelType > & bottom(std::size_t i) const
Get a constant reference to a PixelTile from the bottom layer.
Definition metatile.hpp:358
const PixelTile< PixelType > & middle(std::size_t i) const
Get a constant reference to a PixelTile from the middle layer.
Definition metatile.hpp:399
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:21
std::uint8_t red() const
Definition rgba32.hpp:73
std::uint8_t blue() const
Definition rgba32.hpp:83
std::uint8_t green() const
Definition rgba32.hpp:78
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:24
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.
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
std::vector< Rgba32 > standard_greyscale_palette()
Returns the standard 16-color greyscale palette used for indexed tile output.
Definition rgba32.hpp:140
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.