Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

We <img> now #1145

Merged
merged 5 commits into from
Jan 5, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions browser/gui/app.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021-2024 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2021-2025 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause

@@ -832,7 +832,15 @@ void App::render_layout() {
std::optional{geom::Rect{0,
-scroll_offset_y_,
static_cast<int>(window_.getSize().x),
static_cast<int>(window_.getSize().y)}});
static_cast<int>(window_.getSize().y)}},
[this](std::string_view id) -> std::optional<render::ImageView> {
auto it = images_.find(id);
if (it == end(images_)) {
return std::nullopt;
}

return render::ImageView{it->second.width, it->second.height, it->second.rgba_bytes};
});
}
}

16 changes: 7 additions & 9 deletions gfx/sfml_canvas.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2024 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022-2025 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022 Mikael Larsson <c.mikael.larsson@gmail.com>
//
// SPDX-License-Identifier: BSD-2-Clause
@@ -14,7 +14,6 @@

#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Glsl.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
@@ -37,8 +36,6 @@
#include <system_error>
#include <tuple>

using namespace std::literals;

namespace gfx {
namespace {

@@ -213,7 +210,10 @@ void SfmlCanvas::draw_text(

void SfmlCanvas::draw_pixels(geom::Rect const &rect, std::span<std::uint8_t const> rgba_data) {
assert(rgba_data.size() == static_cast<std::size_t>(rect.width * rect.height * 4));
sf::Image img;

auto translated = rect.translated(tx_, ty_);
auto scaled = translated.scaled(scale_);

// 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();
@@ -224,11 +224,9 @@ void SfmlCanvas::draw_pixels(geom::Rect const &rect, std::span<std::uint8_t cons

texture.update(rgba_data.data());
sf::Sprite sprite{texture};
sprite.setPosition({static_cast<float>(rect.x), static_cast<float>(rect.y)});
sprite.setPosition({static_cast<float>(scaled.x), static_cast<float>(scaled.y)});
sprite.setScale({static_cast<float>(scale_), static_cast<float>(scale_)});
target_.draw(sprite);
sf::RectangleShape shape{{static_cast<float>(rect.width), static_cast<float>(rect.height)}};
shape.setTexture(&texture);
target_.draw(shape);
}

} // namespace gfx
46 changes: 39 additions & 7 deletions render/render.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: 2021-2024 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2021-2025 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022 Mikael Larsson <c.mikael.larsson@gmail.com>
//
// SPDX-License-Identifier: BSD-2-Clause

#include "render/render.h"

#include "css/property_id.h"
#include "dom/dom.h"
#include "dom/xpath.h"
#include "geom/geom.h"
#include "gfx/color.h"
@@ -19,6 +20,7 @@
#include <iterator>
#include <optional>
#include <string_view>
#include <variant>
#include <vector>

namespace render {
@@ -109,9 +111,33 @@ void render_element(gfx::ICanvas &painter, layout::LayoutBox const &layout) {
}
}

void do_render(gfx::ICanvas &painter, layout::LayoutBox const &layout) {
void render_image(gfx::ICanvas &painter, layout::LayoutBox const &layout, ImageView const &image) {
// TODO(robinlinden): Handle image scaling. image.{width,height} are unused
// right now, but should be used to scale the image to work with the content
// size.
painter.draw_pixels(layout.dimensions.content, image.rgba_data);
}

std::optional<std::string_view> get_image_id(layout::LayoutBox const &layout) {
assert(!layout.is_anonymous_block());
auto const *img = std::get_if<dom::Element>(&layout.node->node);
if (img == nullptr || img->name != "img") {
return {};
}

auto src = img->attributes.find("src");
if (src == img->attributes.end()) {
return {};
}

return src->second;
}

void do_render(gfx::ICanvas &painter, layout::LayoutBox const &layout, ImageLookupFn const &image_lookup) {
if (auto text = layout.text()) {
render_text(painter, layout, *text);
} else if (auto img = get_image_id(layout).and_then(image_lookup); img.has_value()) {
render_image(painter, layout, *img);
} else {
render_element(painter, layout);
}
@@ -122,25 +148,31 @@ bool should_render(layout::LayoutBox const &layout) {
}

// NOLINTNEXTLINE(misc-no-recursion)
void render_layout_impl(gfx::ICanvas &painter, layout::LayoutBox const &layout, std::optional<geom::Rect> const &clip) {
void render_layout_impl(gfx::ICanvas &painter,
layout::LayoutBox const &layout,
std::optional<geom::Rect> const &clip,
ImageLookupFn const &image_lookup) {
if (clip && clip->intersected(layout.dimensions.border_box()).empty()) {
return;
}

if (should_render(layout)) {
// display: none'd elements aren't part of the layout tree, so they won't appear here.
assert(layout.get_property<css::PropertyId::Display>().has_value());
do_render(painter, layout);
do_render(painter, layout, image_lookup);
}

for (auto const &child : layout.children) {
render_layout_impl(painter, child, clip);
render_layout_impl(painter, child, clip, image_lookup);
}
}

} // namespace

void render_layout(gfx::ICanvas &painter, layout::LayoutBox const &layout, std::optional<geom::Rect> const &clip) {
void render_layout(gfx::ICanvas &painter,
layout::LayoutBox const &layout,
std::optional<geom::Rect> const &clip,
ImageLookupFn const &image_lookup) {
static constexpr auto kGetBg = [](std::string_view xpath, layout::LayoutBox const &l) -> std::optional<gfx::Color> {
auto d = dom::nodes_by_xpath(l, xpath);
if (d.empty()) {
@@ -161,7 +193,7 @@ void render_layout(gfx::ICanvas &painter, layout::LayoutBox const &layout, std::
painter.clear(gfx::Color{255, 255, 255});
}

render_layout_impl(painter, layout, clip);
render_layout_impl(painter, layout, clip, image_lookup);
}

namespace debug {
20 changes: 18 additions & 2 deletions render/render.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021-2024 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2021-2025 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause

@@ -9,11 +9,27 @@
#include "gfx/icanvas.h"
#include "layout/layout_box.h"

#include <cstdint>
#include <functional>
#include <optional>
#include <span>
#include <string_view>

namespace render {

void render_layout(gfx::ICanvas &, layout::LayoutBox const &, std::optional<geom::Rect> const &clip = std::nullopt);
struct ImageView {
std::uint32_t width{};
std::uint32_t height{};
std::span<std::uint8_t const> rgba_data;
};

using ImageLookupFn = std::function<std::optional<ImageView>(std::string_view id)>;

void render_layout(
gfx::ICanvas &,
layout::LayoutBox const &,
std::optional<geom::Rect> const &clip = std::nullopt,
ImageLookupFn const & = [](auto) { return std::nullopt; });

Check warning on line 32 in render/render.h

Codecov / codecov/patch

render/render.h#L32

Added line #L32 was not covered by tests

namespace debug {
void render_layout_depth(gfx::ICanvas &, layout::LayoutBox const &);
42 changes: 41 additions & 1 deletion render/render_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2024 Robin Lindén <dev@robinlinden.eu>
// SPDX-FileCopyrightText: 2022-2025 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause

@@ -14,6 +14,8 @@
#include "layout/layout_box.h"
#include "style/styled_node.h"

#include <cstdint>
#include <optional>
#include <string_view>
#include <utility>
#include <vector>
@@ -175,6 +177,44 @@ int main() {
gfx::DrawRectCmd{expected_rect, expected_color, expected_borders}});
});

s.add_test("render img", [](etest::IActions &a) {
dom::Node dom = dom::Element{"img", {{"src", "meep.png"}}};
auto styled = style::StyledNode{.node = dom};
auto layout = layout::LayoutBox{
.node = &styled,
.dimensions = {{0, 0, 1, 3}},
};

std::vector<std::uint8_t> img{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
auto get_img_success = [&](auto) -> render::ImageView {
return {1, 3, img};
};
auto get_img_failure = [&](auto) -> std::optional<render::ImageView> {
return std::nullopt;
};

gfx::CanvasCommandSaver saver;
render::render_layout(saver, layout, {}, get_img_success);

// Success!
a.expect_eq(saver.take_commands(),
CanvasCommands{gfx::ClearCmd{{0xFF, 0xFF, 0xFF}}, gfx::DrawPixelsCmd{{0, 0, 1, 3}, img}});

// Failure: image not found
render::render_layout(saver, layout, {}, get_img_failure);
a.expect_eq(saver.take_commands(), CanvasCommands{gfx::ClearCmd{{0xFF, 0xFF, 0xFF}}});

// // Failure: missing src
dom = dom::Element{"img"};
render::render_layout(saver, layout, {}, get_img_success);
a.expect_eq(saver.take_commands(), CanvasCommands{gfx::ClearCmd{{0xFF, 0xFF, 0xFF}}});

// // Failure: not an img
dom = dom::Element{"div", {{"src", "meep.png"}}};
render::render_layout(saver, layout, {}, get_img_success);
a.expect_eq(saver.take_commands(), CanvasCommands{gfx::ClearCmd{{0xFF, 0xFF, 0xFF}}});
});

s.add_test("currentcolor", [](etest::IActions &a) {
dom::Node dom = dom::Element{"span", {}, {dom::Element{"span"}}};
auto const &children = std::get<dom::Element>(dom).children;