From 4fe67b36e3bbe6ea5505ed7233c245be9374f0ce Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Sun, 2 Feb 2025 20:59:36 +0100 Subject: refactor selection --- lib/nes/app/graphics/CMakeLists.txt | 1 + lib/nes/app/graphics/renderer.cc | 95 ++++++++++++++++++++++++++------- lib/nes/app/graphics/renderer.hh | 35 ++++++++---- lib/nes/app/graphics/text-attributes.hh | 39 ++++++++++++++ 4 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 lib/nes/app/graphics/text-attributes.hh (limited to 'lib/nes/app/graphics') diff --git a/lib/nes/app/graphics/CMakeLists.txt b/lib/nes/app/graphics/CMakeLists.txt index 0f869b7..2415f4f 100644 --- a/lib/nes/app/graphics/CMakeLists.txt +++ b/lib/nes/app/graphics/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources( image-view.hh mask-tile.hh image-tile.hh + text-attributes.hh renderer.hh renderer.cc) diff --git a/lib/nes/app/graphics/renderer.cc b/lib/nes/app/graphics/renderer.cc index 6a45b6c..6429a52 100644 --- a/lib/nes/app/graphics/renderer.cc +++ b/lib/nes/app/graphics/renderer.cc @@ -3,6 +3,7 @@ #include "nes/app/graphics/mask-tile.hh" #include "nes/app/graphics/image-tile.hh" #include "nes/app/graphics/image-view.hh" +#include "nes/app/graphics/text-attributes.hh" #include "nes/app/graphics/tiles/characters.hh" #include "nes/common/display.hh" @@ -13,9 +14,9 @@ namespace nes::app { } - auto renderer::render_image_tile(u32 const x, u32 const y, image_tile const& tile) -> void + auto renderer::render_image_tile(i32 const x, i32 const y, image_tile const& tile) -> void { - if (x >= width || y >= height) { return; } + if (x < 0 || y < 0 || x >= static_cast(width) || y >= static_cast(height)) { return; } for (auto y_px = u32{ 0 }; y_px < 8; ++y_px) { @@ -24,16 +25,16 @@ namespace nes::app auto const c = tile.data[y_px * 8 + x_px]; if (c != color::transparent) { - display_.set(8 * x + x_px, 8 * y + y_px, resolve_color(c)); + display_.set(8 * static_cast(x) + x_px, 8 * static_cast(y) + y_px, resolve_color(c)); } } } } - auto renderer::render_mask_tile(u32 const x, u32 const y, mask_tile const tile, color const c) -> void + auto renderer::render_mask_tile(i32 const x, i32 const y, mask_tile const tile, color const c) -> void { if (c == color::transparent) { return; } - if (x >= width || y >= height) { return; } + if (x < 0 || y < 0 || x >= static_cast(width) || y >= static_cast(height)) { return; } auto const resolved = resolve_color(c); for (auto y_px = u32{ 0 }; y_px < 8; ++y_px) @@ -43,47 +44,103 @@ namespace nes::app { if (row & 0b10000000) { - display_.set(8 * x + x_px, 8 * y + y_px, resolved); + display_.set(8 * static_cast(x) + x_px, 8 * static_cast(y) + y_px, resolved); } row <<= 1; } } } - auto renderer::render_image(u32 const x, u32 const y, image_view const image) -> void + auto renderer::render_image(i32 const x, i32 const y, image_view const image) -> void { for (auto y_img = u32{ 0 }; y_img < image.get_height(); ++y_img) { for (auto x_img = u32{ 0 }; x_img < image.get_width(); ++x_img) { - render_image_tile(x + x_img, y + y_img, image.get(x_img, y_img)); + render_image_tile(x + static_cast(x_img), y + static_cast(y_img), image.get(x_img, y_img)); } } } - auto renderer::render_text(u32 const x, u32 const y, std::string_view const str, color const c) -> void + auto renderer::render_text( + i32 x, i32 const y, std::string_view const str, color const c, text_attributes const attributes) -> void { - for (auto i = u32{ 0 }; i < str.size(); ++i) + // Enforce max length + char buffer[width]; + auto const length = std::min(static_cast(str.length()), std::min(width, attributes.get_max_width())); + auto const ellipsize_dots = std::min(length, u32{ 3 }); + + // Enforce ellipsize mode + if (str.length() > length) + { + switch (attributes.get_ellipsize_mode()) + { + case ellipsize_mode::clip: + { + std::copy_n(str.begin(), length, buffer); + break; + } + case ellipsize_mode::end: + { + std::copy_n(str.begin(), length, buffer); + for (auto i = u32{ 0 }; i < ellipsize_dots; ++i) { buffer[length - 1 - i] = '.'; } + break; + } + case ellipsize_mode::start: + { + std::copy_n(str.begin() + str.length() - length, length, buffer); + for (auto i = u32{ 0 }; i < ellipsize_dots; ++i) { buffer[i] = '.'; } + break; + } + case ellipsize_mode::middle: + { + auto const visible = length - ellipsize_dots; + auto const start = visible / 2; + auto const end = visible - start; + std::copy_n(str.begin(), start, buffer); + for (auto i = u32{ 0 }; i < ellipsize_dots; ++i) { buffer[start + i] = '.'; } + std::copy_n(str.begin() + str.length() - end, end, buffer + start + ellipsize_dots); + break; + } + } + } + else + { + std::copy_n(str.begin(), length, buffer); + } + + // Enforce alignment + switch (attributes.get_alignment()) + { + case text_alignment::left: break; + case text_alignment::center: x -= length / 2; break; + case text_alignment::right: x -= length; break; + } + + // Draw the text + for (auto i = u32{ 0 }; i < length; ++i) { - auto const character = str[i]; - render_mask_tile(x + i, y, resolve_character(str[i]), c); + auto const character = static_cast(buffer[i]); + render_mask_tile(x + static_cast(i), y, resolve_character(character), c); } } auto renderer::render_rect( - u32 const x, u32 const y, u32 const width, u32 const height, color const c) -> void + i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void { if (c == color::transparent) { return; } - if (x >= width || y >= height) { return; } + if (x >= static_cast(width) || y >= static_cast(height)) { return; } auto const resolved = resolve_color(c); - auto const x_stop = std::min(x + width, renderer::width); - auto const y_stop = std::min(y + height, renderer::height); - for (auto y_px = u32{ y * 8 }; y_px < y_stop * 8; ++y_px) + auto const x_stop = std::min(x + static_cast(width), static_cast(renderer::width)); + auto const y_stop = std::min(y + static_cast(height), static_cast(renderer::height)); + auto const x_start = std::max(x, 0); + auto const y_start = std::max(y, 0); + for (auto y_px = i32{ y_start * 8 }; y_px < y_stop * 8; ++y_px) { - for (auto x_px = u32{ x * 8 }; x_px < x_stop * 8; ++x_px) + for (auto x_px = i32{ x_start * 8 }; x_px < x_stop * 8; ++x_px) { - display_.set(x_px, y_px, resolved); + display_.set(static_cast(x_px), static_cast(y_px), resolved); } } } diff --git a/lib/nes/app/graphics/renderer.hh b/lib/nes/app/graphics/renderer.hh index 08e4225..cc0b9af 100644 --- a/lib/nes/app/graphics/renderer.hh +++ b/lib/nes/app/graphics/renderer.hh @@ -1,6 +1,9 @@ #pragma once #include "nes/common/types.hh" +#include "nes/app/graphics/color.hh" +#include "nes/app/graphics/text-attributes.hh" +#include "nes/common/string-builder.hh" #include namespace nes @@ -14,7 +17,6 @@ namespace nes::app struct image_tile; struct mask_tile; class image_view; - enum class color : u8; /// Renders tiles and other primitive shapes onto a display. /// @@ -41,18 +43,33 @@ namespace nes::app auto set_border(u32 px, color) -> void; /// Render a colored tile at the given tile coordinates. - auto render_image_tile(u32 x, u32 y, image_tile const&) -> void; + auto render_image_tile(i32 x, i32 y, image_tile const&) -> void; /// Render an alpha-only tile at the given tile coordinates with the given color. - auto render_mask_tile(u32 x, u32 y, mask_tile, color) -> void; + auto render_mask_tile(i32 x, i32 y, mask_tile, color) -> void; /// Render an image at the given tile coordinates. - auto render_image(u32 x, u32 y, image_view) -> void; + auto render_image(i32 x, i32 y, image_view) -> void; /// Render a string of text at the given tile coordinates (one tile per character). - auto render_text(u32 x, u32 y, std::string_view, color) -> void; - /// Render a string of text at the given tile coordinates, limiting its length to the given width and - /// ellipsizing afterward. - auto render_text(u32 x, u32 y, std::string_view, u32 width, color) -> void; + auto render_text(i32 x, i32 y, std::string_view, color, text_attributes = text_attributes{}) -> void; + + template + auto render_text_format( + i32 const x, i32 const y, color const c, text_attributes const attrs, std::string_view const fmt, + Args... args) -> void + { + char buffer[width]; + auto const str = string_builder{ buffer, sizeof(buffer) }.append_format(fmt, args...).get_result(); + render_text(x, y, str, c, attrs); + } + + template + auto render_text_format( + i32 const x, i32 const y, color const c, std::string_view const fmt, Args... args) -> void + { + render_text_format(x, y, c, text_attributes{}, fmt, args...); + } + /// Render a rectangle at the given tile coordinates with a given size (measured in tiles). - auto render_rect(u32 x, u32 y, u32 width, u32 height, color) -> void; + auto render_rect(i32 x, i32 y, u32 width, u32 height, color) -> void; /// Completely fill the screen. auto render_fill(color) -> void; diff --git a/lib/nes/app/graphics/text-attributes.hh b/lib/nes/app/graphics/text-attributes.hh new file mode 100644 index 0000000..6c82311 --- /dev/null +++ b/lib/nes/app/graphics/text-attributes.hh @@ -0,0 +1,39 @@ +#pragma once + +#include "nes/common/types.hh" + +namespace nes::app +{ + enum class ellipsize_mode + { + clip, + start, + middle, + end, + }; + + enum class text_alignment + { + left, + center, + right, + }; + + class text_attributes + { + u32 max_width_{ 32 }; + ellipsize_mode ellipsize_mode_{ ellipsize_mode::clip }; + text_alignment alignment_{ text_alignment::left }; + + public: + explicit text_attributes() = default; + + auto get_max_width() const -> u32 { return max_width_; } + auto get_ellipsize_mode() const -> ellipsize_mode { return ellipsize_mode_; } + auto get_alignment() const -> text_alignment { return alignment_; } + + auto set_max_width(u32 const v) -> text_attributes& { max_width_ = v; return *this; } + auto set_ellipsize_mode(ellipsize_mode const v) -> text_attributes& { ellipsize_mode_ = v; return *this; } + auto set_alignment(text_alignment const v) -> text_attributes& { alignment_ = v; return *this; } + }; +} // namespace nes::app \ No newline at end of file -- cgit v1.3.1