summaryrefslogtreecommitdiff
path: root/lib/nes/app/graphics
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-02-02 20:59:36 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-02-02 20:59:36 +0100
commit4fe67b36e3bbe6ea5505ed7233c245be9374f0ce (patch)
treef5cfe88f252ee2e9830162a1673441a704e6ea37 /lib/nes/app/graphics
parent8062afc25e895cb49e76f6e26e51a7d4a1ad2df2 (diff)
refactor selection
Diffstat (limited to 'lib/nes/app/graphics')
-rw-r--r--lib/nes/app/graphics/CMakeLists.txt1
-rw-r--r--lib/nes/app/graphics/renderer.cc95
-rw-r--r--lib/nes/app/graphics/renderer.hh35
-rw-r--r--lib/nes/app/graphics/text-attributes.hh39
4 files changed, 142 insertions, 28 deletions
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<i32>(width) || y >= static_cast<i32>(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<u32>(x) + x_px, 8 * static_cast<u32>(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<i32>(width) || y >= static_cast<i32>(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<u32>(x) + x_px, 8 * static_cast<u32>(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<i32>(x_img), y + static_cast<i32>(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<u32>(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<u32>(buffer[i]);
+ render_mask_tile(x + static_cast<i32>(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<i32>(width) || y >= static_cast<i32>(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<i32>(width), static_cast<i32>(renderer::width));
+ auto const y_stop = std::min(y + static_cast<i32>(height), static_cast<i32>(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<u32>(x_px), static_cast<u32>(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 <string_view>
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<typename... Args>
+ 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<typename... Args>
+ 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