srctree

Mikael Larsson parent 83b585cc 64e735fd
gfx: Change draw_border to draw_rect

This commit modifies the canvas api by changingthe draw_border function to draw_rect. This newfunction draws both the inner rectangle andthe border. This will make it easier to handlerounded rectangles and borders.

inlinesplit
gfx/canvas_command_saver.h added: 136, removed: 59, total 77
@@ -41,11 +41,12 @@ struct FillRectCmd {
[[nodiscard]] constexpr bool operator==(FillRectCmd const &) const = default;
};
 
struct DrawBorderCmd {
struct DrawRectCmd {
geom::Rect rect{};
Color color{};
Borders borders{};
 
[[nodiscard]] constexpr bool operator==(DrawBorderCmd const &) const = default;
[[nodiscard]] constexpr bool operator==(DrawRectCmd const &) const = default;
};
 
struct DrawTextCmd {
@@ -59,7 +60,7 @@ struct DrawTextCmd {
};
 
using CanvasCommand =
std::variant<SetViewportSizeCmd, SetScaleCmd, AddTranslationCmd, FillRectCmd, DrawBorderCmd, DrawTextCmd>;
std::variant<SetViewportSizeCmd, SetScaleCmd, AddTranslationCmd, FillRectCmd, DrawRectCmd, DrawTextCmd>;
 
class CanvasCommandSaver : public ICanvas {
public:
@@ -68,8 +69,8 @@ public:
void set_scale(int scale) override { cmds_.emplace_back(SetScaleCmd{scale}); }
void add_translation(int dx, int dy) override { cmds_.emplace_back(AddTranslationCmd{dx, dy}); }
void fill_rect(geom::Rect const &rect, Color color) override { cmds_.emplace_back(FillRectCmd{rect, color}); }
void draw_border(geom::Rect const &rect, Borders const &borders) override {
cmds_.emplace_back(DrawBorderCmd{rect, borders});
void draw_rect(geom::Rect const &rect, Color const &color, Borders const &borders) override {
cmds_.emplace_back(DrawRectCmd{rect, color, borders});
}
void draw_text(geom::Position position, std::string_view text, Font font, FontSize size, Color color) override {
cmds_.emplace_back(DrawTextCmd{position, std::string{text}, std::string{font.font}, size.px, color});
@@ -90,7 +91,7 @@ public:
constexpr void operator()(SetScaleCmd const &cmd) { canvas_.set_scale(cmd.scale); }
constexpr void operator()(AddTranslationCmd const &cmd) { canvas_.add_translation(cmd.dx, cmd.dy); }
constexpr void operator()(FillRectCmd const &cmd) { canvas_.fill_rect(cmd.rect, cmd.color); }
constexpr void operator()(DrawBorderCmd const &cmd) { canvas_.draw_border(cmd.rect, cmd.borders); }
constexpr void operator()(DrawRectCmd const &cmd) { canvas_.draw_rect(cmd.rect, cmd.color, cmd.borders); }
 
void operator()(DrawTextCmd const &cmd) {
canvas_.draw_text(cmd.position, cmd.text, {cmd.font}, {cmd.size}, cmd.color);
 
gfx/canvas_command_saver_test.cpp added: 136, removed: 59, total 77
@@ -68,8 +68,8 @@ int main() {
borders.bottom.color = Color::from_rgb(0xFF00FF);
borders.bottom.size = 10;
 
saver.draw_border({1, 2, 3, 4}, borders);
expect_eq(saver.take_commands(), CanvasCommands{DrawBorderCmd{{1, 2, 3, 4}, borders}});
saver.draw_rect({1, 2, 3, 4}, {0xFF, 0xAA, 0xFF}, borders);
expect_eq(saver.take_commands(), CanvasCommands{DrawRectCmd{{1, 2, 3, 4}, {0xFF, 0xAA, 0xFF}, borders}});
});
 
etest::test("CanvasCommandSaver::draw_text", [] {
@@ -86,7 +86,7 @@ int main() {
saver.set_scale(1);
saver.add_translation(1234, 5678);
saver.fill_rect({9, 9, 9, 9}, {0x12, 0x34, 0x56});
saver.draw_border({9, 9, 9, 9}, {});
saver.draw_rect({9, 9, 9, 9}, {0x10, 0x11, 0x12}, {});
saver.draw_text({10, 10}, "beep beep boop!"sv, {"helvetica"}, {42}, {3, 2, 1});
auto cmds = saver.take_commands();
 
 
gfx/icanvas.h added: 136, removed: 59, total 77
@@ -32,7 +32,7 @@ public:
virtual void set_scale(int scale) = 0;
virtual void add_translation(int dx, int dy) = 0;
virtual void fill_rect(geom::Rect const &, Color) = 0;
virtual void draw_border(geom::Rect const &, Borders const &) = 0;
virtual void draw_rect(geom::Rect const &, Color const &, Borders const &) = 0;
virtual void draw_text(geom::Position, std::string_view, Font, FontSize, Color) = 0;
};
 
 
gfx/opengl_canvas.h added: 136, removed: 59, total 77
@@ -22,7 +22,7 @@ public:
}
 
void fill_rect(geom::Rect const &, Color) override;
void draw_border(geom::Rect const &, Borders const &) override {}
void draw_rect(geom::Rect const &, Color const &, Borders const &) override {}
void draw_text(geom::Position, std::string_view, Font, FontSize, Color) override {}
 
private:
 
gfx/painter.h added: 136, removed: 59, total 77
@@ -15,7 +15,9 @@ public:
 
void fill_rect(geom::Rect const &rect, Color color) { canvas_.fill_rect(rect, color); }
 
void draw_border(geom::Rect const &rect, Borders const &borders) { canvas_.draw_border(rect, borders); }
void draw_rect(geom::Rect const &rect, Color const &color, Borders const &borders) {
canvas_.draw_rect(rect, color, borders);
}
 
void draw_text(geom::Position p, std::string_view text, Font font, FontSize size, Color color) {
canvas_.draw_text(p, text, font, size, color);
 
gfx/sfml_canvas.cpp added: 136, removed: 59, total 77
@@ -96,9 +96,12 @@ void SfmlCanvas::fill_rect(geom::Rect const &rect, Color color) {
target_.draw(drawable);
}
 
void SfmlCanvas::draw_border(geom::Rect const &rect, Borders const &borders) {
void SfmlCanvas::draw_rect(geom::Rect const &rect, Color const &color, Borders const &borders) {
auto translated{rect.translated(tx_, ty_)};
auto inner_rect{translated.scaled(scale_)};
 
fill_rect(rect, color);
 
auto outer_rect =
inner_rect.expanded({borders.left.size, borders.right.size, borders.top.size, borders.bottom.size});
 
 
gfx/sfml_canvas.h added: 136, removed: 59, total 77
@@ -32,7 +32,7 @@ public:
}
 
void fill_rect(geom::Rect const &, Color) override;
void draw_border(geom::Rect const &, Borders const &) override;
void draw_rect(geom::Rect const &, Color const &, Borders const &) override;
void draw_text(geom::Position, std::string_view, Font, FontSize, Color) override;
 
private:
 
render/render.cpp added: 136, removed: 59, total 77
@@ -25,7 +25,8 @@ using namespace std::literals;
namespace render {
namespace {
 
constexpr std::string_view kDefaultColor{"#000000"};
constexpr gfx::Color kDefaultColor{0x0, 0x0, 0x0};
constexpr gfx::Color kTransparentColor{0xFF, 0xFF, 0xFF, 0x0};
 
struct CaseInsensitiveLess {
using is_transparent = void;
@@ -63,8 +64,8 @@ bool looks_like_hex(std::string_view str) {
return str.starts_with('#') && (str.length() == 7 || str.length() == 4);
}
 
bool has_any_border(layout::LayoutBox const &layout) {
return layout.dimensions.border_box() != layout.dimensions.padding_box();
bool has_any_border(geom::EdgeSize const &border) {
return border != geom::EdgeSize{};
}
 
dom::Text const *try_get_text(layout::LayoutBox const &layout) {
@@ -99,46 +100,40 @@ gfx::Color parse_color(std::string_view str) {
return gfx::Color{0xFF, 0, 0};
}
 
std::optional<gfx::Color> try_get_color(layout::LayoutBox const &layout, std::string_view color) {
if (auto maybe_color = style::get_property(*layout.node, color)) {
return parse_color(*maybe_color);
}
return std::nullopt;
}
 
void render_text(gfx::Painter &painter, layout::LayoutBox const &layout, dom::Text const &text) {
// TODO(robinlinden):
// * We need to grab properties from the parent for this to work.
// * This shouldn't be done here.
auto font = gfx::Font{"arial"sv};
auto font_size = gfx::FontSize{.px = 10};
auto color = parse_color(style::get_property(*layout.node, "color"sv).value_or(kDefaultColor));
auto color = try_get_color(layout, "color"sv).value_or(kDefaultColor);
painter.draw_text(layout.dimensions.content.position(), text.text, font, font_size, color);
}
 
void render_borders(gfx::Painter &painter, layout::LayoutBox const &layout) {
// TODO(mkiael): Handle a lot more border styles
auto color = style::get_property(*layout.node, "color"sv).value_or(kDefaultColor);
void render_element(gfx::Painter &painter, layout::LayoutBox const &layout) {
auto background_color = try_get_color(layout, "background-color"sv).value_or(kTransparentColor);
auto const &border_size = layout.dimensions.border;
if (border_size.left > 0 || border_size.right > 0 || border_size.top > 0 || border_size.bottom > 0) {
gfx::BorderProperties left_prop{
parse_color(style::get_property(*layout.node, "border-left-color"sv).value_or(color)),
border_size.left,
};
gfx::BorderProperties right_prop{
parse_color(style::get_property(*layout.node, "border-right-color"sv).value_or(color)),
border_size.right,
};
gfx::BorderProperties top_prop{
parse_color(style::get_property(*layout.node, "border-top-color"sv).value_or(color)),
border_size.top,
};
gfx::BorderProperties bottom_prop{
parse_color(style::get_property(*layout.node, "border-bottom-color"sv).value_or(color)),
border_size.bottom,
};
if (has_any_border(border_size)) {
gfx::Borders borders{};
borders.left.color = try_get_color(layout, "border-left-color"sv).value_or(kDefaultColor);
borders.left.size = border_size.left;
borders.right.color = try_get_color(layout, "border-right-color"sv).value_or(kDefaultColor);
borders.right.size = border_size.right;
borders.top.color = try_get_color(layout, "border-top-color"sv).value_or(kDefaultColor);
borders.top.size = border_size.top;
borders.bottom.color = try_get_color(layout, "border-bottom-color"sv).value_or(kDefaultColor);
borders.bottom.size = border_size.bottom;
 
painter.draw_border(
layout.dimensions.padding_box(), gfx::Borders{left_prop, right_prop, top_prop, bottom_prop});
}
}
 
void render_background(gfx::Painter &painter, layout::LayoutBox const &layout) {
if (auto maybe_color = style::get_property(*layout.node, "background-color")) {
painter.fill_rect(layout.dimensions.padding_box(), parse_color(*maybe_color));
painter.draw_rect(layout.dimensions.padding_box(), background_color, borders);
} else if (background_color != kTransparentColor) {
painter.draw_rect(layout.dimensions.padding_box(), background_color, gfx::Borders{});
}
}
 
@@ -146,10 +141,7 @@ void do_render(gfx::Painter &painter, layout::LayoutBox const &layout) {
if (auto const *text = try_get_text(layout)) {
render_text(painter, layout, *text);
} else {
if (has_any_border(layout)) {
render_borders(painter, layout);
}
render_background(painter, layout);
render_element(painter, layout);
}
}
 
 
render/render_test.cpp added: 136, removed: 59, total 77
@@ -7,6 +7,8 @@
#include "dom/dom.h"
#include "etest/etest.h"
#include "gfx/canvas_command_saver.h"
#include "gfx/color.h"
#include "gfx/icanvas.h"
#include "layout/layout.h"
#include "style/styled_node.h"
 
@@ -39,5 +41,82 @@ int main() {
expect_eq(saver.take_commands(), CanvasCommands{gfx::DrawTextCmd{{0, 0}, "hello", "arial", 10}});
});
 
etest::test("render block with background-color", [] {
auto dom = dom::create_element_node("div", {}, {dom::create_element_node("first", {}, {})});
auto styled = style::StyledNode{
.node = dom,
.properties = {{"display", "block"}, {"background-color", "#0A0B0C"}},
};
 
auto layout = layout::LayoutBox{
.node = &styled,
.type = layout::LayoutType::Block,
.dimensions = {{10, 20, 100, 100}, {}, {}, {}},
};
 
gfx::CanvasCommandSaver saver;
gfx::Painter painter{saver};
render::render_layout(painter, layout);
 
geom::Rect expected_rect{10, 20, 100, 100};
gfx::Color expected_color{0xA, 0xB, 0xC};
 
expect_eq(saver.take_commands(), CanvasCommands{gfx::DrawRectCmd{expected_rect, expected_color, {}}});
});
 
etest::test("render block with borders, default color", [] {
auto dom = dom::create_element_node("div", {}, {dom::create_element_node("first", {}, {})});
auto styled = style::StyledNode{
.node = dom,
.properties = {{"display", "block"}, {"background-color", "#0A0B0C"}},
};
 
auto layout = layout::LayoutBox{
.node = &styled,
.type = layout::LayoutType::Block,
.dimensions = {{0, 0, 20, 40}, {}, {10, 10, 10, 10}, {}},
};
 
gfx::CanvasCommandSaver saver;
gfx::Painter painter{saver};
render::render_layout(painter, layout);
 
geom::Rect expected_rect{0, 0, 20, 40};
gfx::Color expected_color{0xA, 0xB, 0xC};
gfx::Borders expected_borders{{{}, 10}, {{}, 10}, {{}, 10}, {{}, 10}};
 
expect_eq(saver.take_commands(),
CanvasCommands{gfx::DrawRectCmd{expected_rect, expected_color, expected_borders}});
});
 
etest::test("render block with borders, custom color", [] {
auto dom = dom::create_element_node("div", {}, {dom::create_element_node("first", {}, {})});
auto styled = style::StyledNode{
.node = dom,
.properties = {{"display", "block"},
{"border-left-color", "#010101"},
{"border-right-color", "#020202"},
{"border-top-color", "#030303"},
{"border-bottom-color", "#040404"}},
};
 
auto layout = layout::LayoutBox{
.node = &styled,
.type = layout::LayoutType::Block,
.dimensions = {{0, 0, 20, 40}, {}, {2, 4, 6, 8}, {}},
};
 
gfx::CanvasCommandSaver saver;
gfx::Painter painter{saver};
render::render_layout(painter, layout);
 
geom::Rect expected_rect{0, 0, 20, 40};
gfx::Color expected_color{0xFF, 0xFF, 0xFF, 0x0};
gfx::Borders expected_borders{{{1, 1, 1}, 2}, {{2, 2, 2}, 4}, {{3, 3, 3}, 6}, {{4, 4, 4}, 8}};
 
expect_eq(saver.take_commands(),
CanvasCommands{gfx::DrawRectCmd{expected_rect, expected_color, expected_borders}});
});
 
return etest::run_all_tests();
}