srctree

Robin Linden parent cee0c63f f7e00327
gfx: Switch to representing FontStyle as a struct

This is less code and plays nicer with static code analysis than the enum bitset thing we had previously.
gfx/canvas_command_saver.h added: 69, removed: 145, total 0
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2023 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022-2024 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
@@ -70,7 +70,7 @@ struct DrawTextWithFontOptionsCmd {
std::string text{};
std::vector<std::string> font_options{};
int size{};
FontStyle style{FontStyle::Normal};
FontStyle style{};
Color color{};
 
[[nodiscard]] bool operator==(DrawTextWithFontOptionsCmd const &) const = default;
@@ -81,7 +81,7 @@ struct DrawTextCmd {
std::string text{};
std::string font{};
int size{};
FontStyle style{FontStyle::Normal};
FontStyle style{};
Color color{};
 
[[nodiscard]] bool operator==(DrawTextCmd const &) const = default;
 
gfx/canvas_command_saver_test.cpp added: 69, removed: 145, total 0
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2023 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022-2024 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
@@ -92,14 +92,13 @@ int main() {
 
etest::test("CanvasCommandSaver::draw_text", [] {
CanvasCommandSaver saver;
saver.draw_text({1, 2}, "hello!"sv, {"comic sans"}, {11}, FontStyle::Normal, {1, 2, 3});
saver.draw_text({1, 2}, "hello!"sv, {"comic sans"}, {11}, {}, {1, 2, 3});
expect_eq(saver.take_commands(),
CanvasCommands{DrawTextCmd{{1, 2}, "hello!"s, {"comic sans"}, 11, FontStyle::Normal, {1, 2, 3}}});
CanvasCommands{DrawTextCmd{{1, 2}, "hello!"s, {"comic sans"}, 11, {}, {1, 2, 3}}});
 
saver.draw_text({1, 2}, "hello!"sv, std::vector<gfx::Font>{{"comic sans"}}, {11}, FontStyle::Normal, {1, 2, 3});
saver.draw_text({1, 2}, "hello!"sv, std::vector<gfx::Font>{{"comic sans"}}, {11}, {}, {1, 2, 3});
expect_eq(saver.take_commands(),
CanvasCommands{DrawTextWithFontOptionsCmd{
{1, 2}, "hello!"s, {{"comic sans"}}, 11, FontStyle::Normal, {1, 2, 3}}});
CanvasCommands{DrawTextWithFontOptionsCmd{{1, 2}, "hello!"s, {{"comic sans"}}, 11, {}, {1, 2, 3}}});
});
 
etest::test("CanvasCommandSaver::draw_pixels", [] {
@@ -118,8 +117,8 @@ int main() {
saver.add_translation(1234, 5678);
saver.fill_rect({9, 9, 9, 9}, {0x12, 0x34, 0x56});
saver.draw_rect({9, 9, 9, 9}, {0x10, 0x11, 0x12}, {}, {});
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.draw_text({10, 10}, "beep beep boop!"sv, {"helvetica"}, {42}, {.italic = true}, {3, 2, 1});
saver.draw_text({1, 5}, "hello?"sv, {{{"font1"}, {"font2"}}}, {42}, {}, {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();
 
gfx/font.h added: 69, removed: 145, total 0
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2023 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022-2024 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
@@ -6,7 +6,6 @@
#define GFX_FONT_H_
 
#include <string_view>
#include <utility>
 
namespace gfx {
 
@@ -18,45 +17,15 @@ struct FontSize {
int px{10};
};
 
enum class FontStyle {
Normal = 0,
Italic = 1 << 0,
Bold = 1 << 1,
Underlined = 1 << 2,
Strikethrough = 1 << 3,
struct FontStyle {
bool bold{false};
bool italic{false};
bool strikethrough{false};
bool underlined{false};
 
[[nodiscard]] constexpr bool operator==(FontStyle const &) const = default;
};
 
constexpr FontStyle operator|(FontStyle lhs, FontStyle rhs) {
return static_cast<FontStyle>(std::to_underlying(lhs) | std::to_underlying(rhs));
}
 
constexpr FontStyle &operator|=(FontStyle &lhs, FontStyle rhs) {
lhs = lhs | rhs;
return lhs;
}
 
constexpr FontStyle operator&(FontStyle lhs, FontStyle rhs) {
return static_cast<FontStyle>(std::to_underlying(lhs) & std::to_underlying(rhs));
}
 
constexpr FontStyle &operator&=(FontStyle &lhs, FontStyle rhs) {
lhs = lhs & rhs;
return lhs;
}
 
constexpr FontStyle operator^(FontStyle lhs, FontStyle rhs) {
return static_cast<FontStyle>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
}
 
constexpr FontStyle &operator^=(FontStyle &lhs, FontStyle rhs) {
lhs = lhs ^ rhs;
return lhs;
}
 
constexpr FontStyle operator~(FontStyle v) {
return static_cast<FontStyle>(~std::to_underlying(v));
}
 
} // namespace gfx
 
#endif
 
ev/null added: 69, removed: 145, total 0
@@ -1,37 +0,0 @@
// SPDX-FileCopyrightText: 2023 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#include "gfx/font.h"
 
#include "etest/etest.h"
 
using etest::expect;
using etest::expect_eq;
 
int main() {
etest::test("used as a bitset", [] {
auto style = gfx::FontStyle::Normal;
expect((style & gfx::FontStyle::Bold) != gfx::FontStyle::Bold);
expect((style & gfx::FontStyle::Italic) != gfx::FontStyle::Italic);
 
style |= gfx::FontStyle::Italic;
expect((style & gfx::FontStyle::Bold) != gfx::FontStyle::Bold);
expect((style & gfx::FontStyle::Italic) == gfx::FontStyle::Italic);
 
style |= gfx::FontStyle::Bold;
expect((style & gfx::FontStyle::Bold) == gfx::FontStyle::Bold);
expect((style & gfx::FontStyle::Italic) == gfx::FontStyle::Italic);
 
style &= ~gfx::FontStyle::Italic;
expect((style & gfx::FontStyle::Bold) == gfx::FontStyle::Bold);
expect((style & gfx::FontStyle::Italic) != gfx::FontStyle::Italic);
 
style ^= gfx::FontStyle::Bold;
expect((style & gfx::FontStyle::Bold) != gfx::FontStyle::Bold);
expect((style & gfx::FontStyle::Italic) != gfx::FontStyle::Italic);
expect_eq(style, gfx::FontStyle::Normal);
});
 
return etest::run_all_tests();
}
 
gfx/gfx_example.cpp added: 69, removed: 145, total 0
@@ -1,9 +1,8 @@
// SPDX-FileCopyrightText: 2021-2023 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2021-2024 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#include "gfx/color.h"
#include "gfx/font.h"
#include "gfx/icanvas.h"
#include "gfx/opengl_canvas.h"
#include "gfx/sfml_canvas.h"
@@ -65,26 +64,25 @@ int main(int argc, char **argv) {
 
canvas->draw_rect({400, 100, 50, 50}, gfx::Color{80, 80, 80}, {}, {.top_right{50, 50}, .bottom_left{25, 25}});
 
canvas->draw_text({100, 50}, "hello!"sv, {"arial"}, {16}, gfx::FontStyle::Normal, gfx::Color{});
canvas->draw_text({100, 80}, "hello, but fancy!"sv, {"arial"}, {16}, gfx::FontStyle::Italic, gfx::Color{});
canvas->draw_text({100, 50}, "hello!"sv, {"arial"}, {16}, {}, gfx::Color{});
canvas->draw_text({100, 80}, "hello, but fancy!"sv, {"arial"}, {16}, {.italic = true}, gfx::Color{});
canvas->draw_text({100, 110},
"hello, but *even fancier*!"sv,
{"arial"},
{32},
gfx::FontStyle::Italic | gfx::FontStyle::Bold,
{.bold = true, .italic = true},
gfx::Color{});
canvas->draw_text({120, 150},
"hmmmm"sv,
{"arial"},
{24},
gfx::FontStyle::Italic | gfx::FontStyle::Bold | gfx::FontStyle::Underlined,
{.bold = true, .italic = true, .underlined = true},
gfx::Color{});
canvas->draw_text({150, 200},
"oh no"sv,
{"arial"},
{24},
gfx::FontStyle::Italic | gfx::FontStyle::Bold | gfx::FontStyle::Underlined
| gfx::FontStyle::Strikethrough,
{.bold = true, .italic = true, .strikethrough = true, .underlined = true},
kHotPink);
auto px = std::to_array<std::uint8_t>(
{100, 100, 100, 0xff, 200, 200, 200, 0xff, 50, 50, 50, 0xff, 200, 0, 0, 0xff});
 
gfx/sfml_canvas.cpp added: 69, removed: 145, total 0
@@ -106,16 +106,22 @@ sf::Glsl::Vec4 to_vec4(Color const &color) {
 
sf::Text::Style to_sfml(FontStyle style) {
auto sf_style = sf::Text::Style::Regular;
auto transfer_enum_bit = [&]<FontStyle SourceBitT, sf::Text::Style TargetBitT> {
if ((style & SourceBitT) == SourceBitT) {
sf_style = static_cast<sf::Text::Style>(sf_style | TargetBitT);
}
};
if (style.bold) {
sf_style = static_cast<sf::Text::Style>(sf_style | sf::Text::Style::Bold);
}
 
if (style.italic) {
sf_style = static_cast<sf::Text::Style>(sf_style | sf::Text::Style::Italic);
}
 
if (style.underlined) {
sf_style = static_cast<sf::Text::Style>(sf_style | sf::Text::Style::Underlined);
}
 
if (style.strikethrough) {
sf_style = static_cast<sf::Text::Style>(sf_style | sf::Text::Style::StrikeThrough);
}
 
transfer_enum_bit.template operator()<FontStyle::Italic, sf::Text::Style::Italic>();
transfer_enum_bit.template operator()<FontStyle::Bold, sf::Text::Style::Bold>();
transfer_enum_bit.template operator()<FontStyle::Underlined, sf::Text::Style::Underlined>();
transfer_enum_bit.template operator()<FontStyle::Strikethrough, sf::Text::Style::StrikeThrough>();
return sf_style;
}
 
 
render/render.cpp added: 69, removed: 145, total 0
@@ -34,43 +34,35 @@ constexpr bool is_fully_transparent(gfx::Color const &c) {
return c.a == 0;
}
 
gfx::FontStyle to_gfx(style::FontStyle style) {
switch (style) {
case style::FontStyle::Italic:
case style::FontStyle::Oblique:
return gfx::FontStyle::Italic;
case style::FontStyle::Normal:
default:
return gfx::FontStyle::Normal;
}
}
 
gfx::FontStyle to_gfx(std::optional<style::FontWeight> style) {
if (!style || style->value < style::FontWeight::kBold) {
return gfx::FontStyle::Normal;
gfx::FontStyle to_gfx(style::FontStyle style,
std::optional<style::FontWeight> weight,
std::vector<style::TextDecorationLine> const &decorations) {
gfx::FontStyle gfx;
if (style == style::FontStyle::Italic || style == style::FontStyle::Oblique) {
gfx.italic = true;
}
 
return gfx::FontStyle::Bold;
}
if (weight && weight->value >= style::FontWeight::kBold) {
gfx.bold = true;
}
 
gfx::FontStyle to_gfx(std::vector<style::TextDecorationLine> const &decorations) {
gfx::FontStyle style{};
for (auto const &decoration : decorations) {
switch (decoration) {
case style::TextDecorationLine::None:
return {};
break;
case style::TextDecorationLine::Underline:
style |= gfx::FontStyle::Underlined;
gfx.underlined = true;
break;
case style::TextDecorationLine::LineThrough:
style |= gfx::FontStyle::Strikethrough;
gfx.strikethrough = true;
break;
default:
spdlog::warn("Unhandled text decoration line '{}'", std::to_underlying(decoration));
break;
}
}
return style;
 
return gfx;
}
 
void render_text(gfx::ICanvas &painter, layout::LayoutBox const &layout, std::string_view text) {
@@ -81,12 +73,10 @@ void render_text(gfx::ICanvas &painter, layout::LayoutBox const &layout, std::st
return fs;
}();
auto font_size = gfx::FontSize{.px = layout.get_property<css::PropertyId::FontSize>()};
auto style = to_gfx(layout.get_property<css::PropertyId::FontStyle>());
auto weight = to_gfx(layout.get_property<css::PropertyId::FontWeight>());
style |= weight;
auto style = to_gfx(layout.get_property<css::PropertyId::FontStyle>(),
layout.get_property<css::PropertyId::FontWeight>(),
layout.get_property<css::PropertyId::TextDecorationLine>());
auto color = layout.get_property<css::PropertyId::Color>();
auto text_decoration_line = to_gfx(layout.get_property<css::PropertyId::TextDecorationLine>());
style |= text_decoration_line;
painter.draw_text(layout.dimensions.content.position(), text, fonts, font_size, style, color);
}
 
 
render/render_test.cpp added: 69, removed: 145, total 0
@@ -10,7 +10,6 @@
#include "geom/geom.h"
#include "gfx/canvas_command_saver.h"
#include "gfx/color.h"
#include "gfx/font.h"
#include "gfx/icanvas.h"
#include "layout/layout_box.h"
#include "style/styled_node.h"
@@ -53,7 +52,7 @@ int main() {
 
expect_eq(saver.take_commands(),
CanvasCommands{gfx::ClearCmd{{0xFF, 0xFF, 0xFF}},
gfx::DrawTextWithFontOptionsCmd{{0, 0}, "hello", {"comic sans"}, 10, gfx::FontStyle::Italic}});
gfx::DrawTextWithFontOptionsCmd{{0, 0}, "hello", {"comic sans"}, 10, {.italic = true}}});
});
 
etest::test("render block with background-color", [] {
@@ -337,7 +336,7 @@ int main() {
"hello",
{"arial"},
16,
gfx::FontStyle::Strikethrough,
{.strikethrough = true},
gfx::Color::from_css_name("canvastext").value(),
},
});
@@ -354,7 +353,7 @@ int main() {
"hello",
{"arial"},
16,
gfx::FontStyle::Underlined | gfx::FontStyle::Italic,
{.italic = true, .underlined = true},
gfx::Color::from_css_name("canvastext").value(),
},
});
@@ -370,7 +369,7 @@ int main() {
"hello",
{"arial"},
16,
gfx::FontStyle::Italic,
{.italic = true},
gfx::Color::from_css_name("canvastext").value(),
},
});
@@ -386,7 +385,7 @@ int main() {
"hello",
{"arial"},
16,
gfx::FontStyle::Bold | gfx::FontStyle::Italic,
{.bold = true, .italic = true},
gfx::Color::from_css_name("canvastext").value(),
},
});