From 324b2818cc8fb04df3d5e6ebf5c3cf19c1dc3437 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Fri, 13 Oct 2023 21:40:25 +0200 Subject: [PATCH 1/3] gfx: Add ICanvas::draw_pixels --- gfx/canvas_command_saver.h | 16 +++++++++++++++- gfx/canvas_command_saver_test.cpp | 7 +++++++ gfx/icanvas.h | 1 + gfx/opengl_canvas.h | 1 + gfx/sfml_canvas.h | 1 + 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/gfx/canvas_command_saver.h b/gfx/canvas_command_saver.h index 334f4686..db63fc95 100644 --- a/gfx/canvas_command_saver.h +++ b/gfx/canvas_command_saver.h @@ -80,6 +80,13 @@ struct DrawTextCmd { [[nodiscard]] bool operator==(DrawTextCmd const &) const = default; }; +struct DrawPixelsCmd { + geom::Rect rect{}; + std::vector rgba_data{}; + + [[nodiscard]] bool operator==(DrawPixelsCmd const &) const = default; +}; + using CanvasCommand = std::variant; + DrawTextCmd, + DrawPixelsCmd>; class CanvasCommandSaver : public ICanvas { public: @@ -124,6 +132,10 @@ class CanvasCommandSaver : public ICanvas { cmds_.emplace_back(DrawTextCmd{position, std::string{text}, std::string{font.font}, size.px, style, color}); } + void draw_pixels(geom::Rect const &rect, std::span rgba_data) override { + cmds_.emplace_back(DrawPixelsCmd{rect, {rgba_data.begin(), rgba_data.end()}}); + } + // [[nodiscard]] std::vector take_commands() { return std::exchange(cmds_, {}); } @@ -156,6 +168,8 @@ class CanvasCommandVisitor { canvas_.draw_text(cmd.position, cmd.text, {cmd.font}, {cmd.size}, cmd.style, cmd.color); } + void operator()(DrawPixelsCmd const &cmd) { canvas_.draw_pixels(cmd.rect, cmd.rgba_data); } + private: ICanvas &canvas_; }; diff --git a/gfx/canvas_command_saver_test.cpp b/gfx/canvas_command_saver_test.cpp index 3c2901c5..a27104aa 100644 --- a/gfx/canvas_command_saver_test.cpp +++ b/gfx/canvas_command_saver_test.cpp @@ -97,6 +97,12 @@ int main() { {1, 2}, "hello!"s, {{"comic sans"}}, 11, FontStyle::Normal, {1, 2, 3}}}); }); + etest::test("CanvasCommandSaver::draw_pixels", [] { + CanvasCommandSaver saver; + saver.draw_pixels({1, 2, 3, 4}, {{0x12, 0x34, 0x56, 0x78}}); + expect_eq(saver.take_commands(), CanvasCommands{DrawPixelsCmd{{1, 2, 3, 4}, {0x12, 0x34, 0x56, 0x78}}}); + }); + etest::test("replay_commands", [] { CanvasCommandSaver saver; saver.clear(gfx::Color{}); @@ -110,6 +116,7 @@ int main() { saver.draw_text({10, 10}, "beep beep boop!"sv, {"helvetica"}, {42}, FontStyle::Italic, {3, 2, 1}); saver.draw_text({1, 5}, "hello?"sv, {{{"font1"}, {"font2"}}}, {42}, FontStyle::Normal, {1, 2, 3}); saver.clear(gfx::Color{1, 2, 3}); + saver.draw_pixels({1, 2, 3, 4}, {{0x12, 0x34, 0x56, 0x78}}); auto cmds = saver.take_commands(); CanvasCommandSaver replayed; diff --git a/gfx/icanvas.h b/gfx/icanvas.h index 7ceab8ba..fe767d2e 100644 --- a/gfx/icanvas.h +++ b/gfx/icanvas.h @@ -51,6 +51,7 @@ class ICanvas { virtual void draw_rect(geom::Rect const &, Color const &, Borders const &, Corners const &) = 0; virtual void draw_text(geom::Position, std::string_view, std::span, FontSize, FontStyle, Color) = 0; virtual void draw_text(geom::Position, std::string_view, Font, FontSize, FontStyle, Color) = 0; + virtual void draw_pixels(geom::Rect const &, std::span rgba_data) = 0; }; } // namespace gfx diff --git a/gfx/opengl_canvas.h b/gfx/opengl_canvas.h index bce1afaa..77684147 100644 --- a/gfx/opengl_canvas.h +++ b/gfx/opengl_canvas.h @@ -28,6 +28,7 @@ class OpenGLCanvas final : public ICanvas { void draw_rect(geom::Rect const &, Color const &, Borders const &, Corners const &) override; void draw_text(geom::Position, std::string_view, std::span, FontSize, FontStyle, Color) override {} void draw_text(geom::Position, std::string_view, Font, FontSize, FontStyle, Color) override {} + void draw_pixels(geom::Rect const &, std::span) override {} private: OpenGLShader border_shader_; diff --git a/gfx/sfml_canvas.h b/gfx/sfml_canvas.h index a896cfbf..e56a4817 100644 --- a/gfx/sfml_canvas.h +++ b/gfx/sfml_canvas.h @@ -36,6 +36,7 @@ class SfmlCanvas : public ICanvas { void draw_rect(geom::Rect const &, Color const &, Borders const &, Corners const &) override; void draw_text(geom::Position, std::string_view, std::span, FontSize, FontStyle, Color) override; void draw_text(geom::Position, std::string_view, Font, FontSize, FontStyle, Color) override; + void draw_pixels(geom::Rect const &, std::span) override {} private: sf::RenderTarget &target_; From 2695676036a78dbce6e5e66b99d9dc4162765b0f Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Fri, 13 Oct 2023 21:45:10 +0200 Subject: [PATCH 2/3] gfx/sfml: Implement pixel-drawing --- gfx/gfx_example.cpp | 4 ++++ gfx/sfml_canvas.cpp | 19 +++++++++++++++++++ gfx/sfml_canvas.h | 5 ++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/gfx/gfx_example.cpp b/gfx/gfx_example.cpp index 2ed00d71..76f44774 100644 --- a/gfx/gfx_example.cpp +++ b/gfx/gfx_example.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -78,6 +79,9 @@ int main(int argc, char **argv) { gfx::FontStyle::Italic | gfx::FontStyle::Bold | gfx::FontStyle::Underlined | gfx::FontStyle::Strikethrough, kHotPink); + auto px = std::to_array( + {100, 100, 100, 0xff, 200, 200, 200, 0xff, 50, 50, 50, 0xff, 200, 0, 0, 0xff}); + canvas->draw_pixels({1, 1, 2, 2}, px); window.display(); } diff --git a/gfx/sfml_canvas.cpp b/gfx/sfml_canvas.cpp index 89fe4767..0df939d1 100644 --- a/gfx/sfml_canvas.cpp +++ b/gfx/sfml_canvas.cpp @@ -10,11 +10,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -114,6 +116,7 @@ void SfmlCanvas::set_viewport_size(int width, int height) { void SfmlCanvas::clear(Color c) { target_.clear(sf::Color(c.as_rgba_u32())); + textures_.clear(); } void SfmlCanvas::fill_rect(geom::Rect const &rect, Color color) { @@ -238,4 +241,20 @@ void SfmlCanvas::draw_text( target_.draw(drawable); } +void SfmlCanvas::draw_pixels(geom::Rect const &rect, std::span rgba_data) { + assert(rgba_data.size() == static_cast(rect.width * rect.height * 4)); + sf::Image img; + // Textures need to be kept around while they're displayed. This will be + // cleared when the canvas is cleared. + sf::Texture &texture = textures_.emplace_back(); + texture.create(static_cast(rect.width), static_cast(rect.height)); + texture.update(rgba_data.data()); + sf::Sprite sprite{texture}; + sprite.setPosition(static_cast(rect.x), static_cast(rect.y)); + target_.draw(sprite); + sf::RectangleShape shape{{static_cast(rect.width), static_cast(rect.height)}}; + shape.setTexture(&texture); + target_.draw(shape); +} + } // namespace gfx diff --git a/gfx/sfml_canvas.h b/gfx/sfml_canvas.h index e56a4817..7e9eda69 100644 --- a/gfx/sfml_canvas.h +++ b/gfx/sfml_canvas.h @@ -8,9 +8,11 @@ #include "gfx/icanvas.h" #include +#include #include #include +#include namespace sf { class Font; @@ -36,12 +38,13 @@ class SfmlCanvas : public ICanvas { void draw_rect(geom::Rect const &, Color const &, Borders const &, Corners const &) override; void draw_text(geom::Position, std::string_view, std::span, FontSize, FontStyle, Color) override; void draw_text(geom::Position, std::string_view, Font, FontSize, FontStyle, Color) override; - void draw_pixels(geom::Rect const &, std::span) override {} + void draw_pixels(geom::Rect const &, std::span rgba_data) override; private: sf::RenderTarget &target_; sf::Shader border_shader_{}; std::map, std::less<>> font_cache_; + std::vector textures_; int scale_{1}; int tx_{0}; From 5826739e2caa98774da97c67eccb38a07b7be472 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sat, 21 Oct 2023 14:41:45 +0200 Subject: [PATCH 3/3] img: Use our own gfx-abstractions to render images in the example --- img/BUILD | 1 + img/img_example.cpp | 19 ++++++------------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/img/BUILD b/img/BUILD index e6a6b0b9..761cb495 100644 --- a/img/BUILD +++ b/img/BUILD @@ -74,6 +74,7 @@ cc_binary( ":gif", ":png", ":qoi", + "//gfx:sfml", "@sfml//:graphics", "@sfml//:window", ], diff --git a/img/img_example.cpp b/img/img_example.cpp index 8a02e6ee..5597d209 100644 --- a/img/img_example.cpp +++ b/img/img_example.cpp @@ -6,10 +6,9 @@ #include "img/png.h" #include "img/qoi.h" -#include +#include "gfx/sfml_canvas.h" + #include -#include -#include #include #include @@ -110,12 +109,7 @@ int main(int argc, char **argv) { window.setVerticalSyncEnabled(true); window.setActive(true); - sf::Image sf_image{}; - sf_image.create(width, height, bytes.data()); - sf::Texture texture{}; - texture.loadFromImage(sf_image); - sf::Sprite sprite{}; - sprite.setTexture(texture); + gfx::SfmlCanvas canvas{window}; bool running = true; while (running) { @@ -126,16 +120,15 @@ int main(int argc, char **argv) { running = false; break; case sf::Event::Resized: - window.setView(sf::View{sf::FloatRect{ - 0, 0, static_cast(event.size.width), static_cast(event.size.height)}}); + canvas.set_viewport_size(event.size.width, event.size.height); break; default: break; } } - window.clear(); - window.draw(sprite); + canvas.clear(gfx::Color{}); + canvas.draw_pixels({0, 0, static_cast(width), static_cast(height)}, bytes); window.display(); } }