Skip to content

Commit

Permalink
vaev-layout: first steps of fragmenting
Browse files Browse the repository at this point in the history
Before there was no concept of fragments and the layout step was taking the
box tree as input and saving the used layout metrics into boxes themselves.

This commit introduces the concept of Fragments, Fragmentainers and
Fragmentation Context
(https://drafts.csswg.org/css-break-4/#fragmentation-model).

Currently, only pages, the block formatting context, and PDF under the 'print'
command are supported.

The commit mechanism was changed from an enum (yes/no) to support these new
concepts.
Now, since layouting the box tree generates the Fragment tree, a layout call
that should commit a Fragment for a Box into the Fragment tree will:
- create the said Fragment
- pass a reference to the said Fragment to it child call
- add the created Fragment to the list of children of its parent in the Fragment
tree by using the passed reference

To Fragment, breakpoints with appeal were implemented, following Chrome,
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/layout/block_fragmentation_tutorial.md
and its golden rule
> Break at the breakpoint with the highest appeal (primary criterion) that also
fits as much content as possible (secondary criterion).

Co-authored-by: Paulo Medeiros <[email protected]>
Co-authored-by: Nicolas Van Bossuyt <[email protected]>
  • Loading branch information
pauloamed and sleepy-monax committed Nov 9, 2024
1 parent 8214546 commit 4e51b4e
Show file tree
Hide file tree
Showing 27 changed files with 805 additions and 329 deletions.
83 changes: 59 additions & 24 deletions src/vaev-driver/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,26 +87,30 @@ RenderResult render(Markup::Document const &dom, Style::Media const &media, Vec2

start = Sys::now();

Layout::layout(
auto root = Layout::Frag();

tree.fc.createNextFragmentainer();
tree.fc.isDiscoveryMode = false;
auto outDiscovery = Layout::layout(
tree,
tree.root,
{
.commit = Layout::Commit::YES,
.fragment = &root,
.knownSize = {vp.small.width, NONE},
.availableSpace = {vp.small.width, 0_px},
.containingBlock = {vp.small.width, vp.small.height},
}
);
Layout::layoutPositioned(tree, tree.root, {vp.small.width, vp.small.height});

Layout::layoutPositioned(tree, root.children[0], {vp.small.width, vp.small.height});

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method

This comment has been minimized.

Copy link
@pauloamed

pauloamed Nov 9, 2024

Author Contributor

root.children[0] is the Frag for the root of the box tree.
root (frag) does not have a box attached to it and breaks the layout positioned method


auto sceneRoot = makeStrong<Scene::Stack>();

elapsed = Sys::now() - start;
logDebugIf(DEBUG_RENDER, "layout tree layout time: {}", elapsed);

auto paintStart = Sys::now();

Layout::paint(tree.root, *sceneRoot);
Layout::paint(root.children[0], *sceneRoot);
sceneRoot->prepare();

elapsed = Sys::now() - paintStart;
Expand All @@ -115,11 +119,12 @@ RenderResult render(Markup::Document const &dom, Style::Media const &media, Vec2
return {
std::move(stylebook),
makeStrong<Layout::Box>(std::move(tree.root)),
sceneRoot,
{sceneRoot},
makeStrong<Layout::Frag>(root)
};
}

RenderResult render(Markup::Document &dom, Style::Media const &media, Print::PaperStock paper) {
RenderResult print(Markup::Document &dom, Style::Media const &media, Vec2Px dimensions) {
Style::StyleBook stylebook;
stylebook.add(
fetchStylesheet("bundle://vaev-view/user-agent.css"_url)
Expand All @@ -134,36 +139,66 @@ RenderResult render(Markup::Document &dom, Style::Media const &media, Print::Pap
Style::Computer computer{media, stylebook};

Layout::Viewport vp{
.small = {
Px{paper.width},
Px{paper.height},
},
.small = dimensions,
};

Layout::Tree tree = {
Layout::build(computer, dom),
vp,
Layout::FragmentationContext(dimensions)
};

Layout::layout(
tree,
tree.root,
{
.commit = Layout::Commit::YES,
.knownSize = {vp.small.width, NONE},
.availableSpace = {vp.small.width, 0_px},
.containingBlock = {vp.small.width, vp.small.height},
auto root = Layout::Frag();

while (true) {
tree.fc.createNextFragmentainer();
tree.fc.isDiscoveryMode = true;
auto outDiscovery = Layout::layout(
tree,
tree.root,
{
.knownSize = {vp.small.width, NONE},
.availableSpace = {vp.small.width, 0_px},
.containingBlock = {vp.small.width, vp.small.height},
}
);

if (outDiscovery.completelyLaidOut) {
tree.fc.visitedWholeBox.put(&tree.root, true);
}
);

auto sceneRoot = makeStrong<Scene::Page>();
Layout::paint(tree.root, *sceneRoot);
sceneRoot->prepare();
tree.fc.pushFlagsVisitedWholeBox(tree.root);

tree.fc.isDiscoveryMode = false;
auto outReal = Layout::layout(
tree,
tree.root,
{
.fragment = &root,
.knownSize = {vp.small.width, NONE},
.availableSpace = {vp.small.width, 0_px},
.containingBlock = {vp.small.width, vp.small.height},
}
);

if (tree.fc.getStartPosition(tree.root) == 1)
break;
}

Vec<Strong<Scene::Node>> scenes;
for (auto &c : root.children) {
auto scenePage = makeStrong<Scene::Page>();
Layout::paint(c, *scenePage);
scenePage->prepare();

scenes.pushBack(scenePage);
}

return {
std::move(stylebook),
makeStrong<Layout::Box>(std::move(tree.root)),
sceneRoot,
scenes,
makeStrong<Layout::Frag>(root)
};
}

Expand Down
7 changes: 5 additions & 2 deletions src/vaev-driver/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <karm-scene/base.h>
#include <vaev-base/length.h>
#include <vaev-layout/box.h>
#include <vaev-layout/frag.h>
#include <vaev-layout/tree.h>
#include <vaev-markup/dom.h>
#include <vaev-style/media.h>

Expand All @@ -11,11 +13,12 @@ namespace Vaev::Driver {
struct RenderResult {
Style::StyleBook style;
Strong<Layout::Box> layout;
Strong<Scene::Node> scene;
Vec<Strong<Scene::Node>> scenes;
Strong<Layout::Frag> frag;
};

RenderResult render(Markup::Document const &dom, Style::Media const &media, Vec2Px viewport);

RenderResult render(Markup::Document &dom, Style::Media const &media, Print::PaperStock paper);
RenderResult print(Markup::Document &dom, Style::Media const &media, Vec2Px dimensions);

} // namespace Vaev::Driver
109 changes: 0 additions & 109 deletions src/vaev-layout/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,115 +22,6 @@ struct Viewport {
RectPx dynamic = small;
};

enum struct Commit {
NO, // No, only compute sizes
YES, // Yes, commit computed values to the tree
};

/// Input to the layout algorithm.

struct Input {
Commit commit = Commit::NO; //< Should the computed values be committed to the layout?
IntrinsicSize intrinsic = IntrinsicSize::AUTO;
Math::Vec2<Opt<Px>> knownSize = {};
Vec2Px position = {};
Vec2Px availableSpace = {};
Vec2Px containingBlock = {};

Input withCommit(Commit c) const {
auto copy = *this;
copy.commit = c;
return copy;
}

Input withIntrinsic(IntrinsicSize i) const {
auto copy = *this;
copy.intrinsic = i;
return copy;
}

Input withKnownSize(Math::Vec2<Opt<Px>> size) const {
auto copy = *this;
copy.knownSize = size;
return copy;
}

Input withPosition(Vec2Px pos) const {
auto copy = *this;
copy.position = pos;
return copy;
}

Input withAvailableSpace(Vec2Px space) const {
auto copy = *this;
copy.availableSpace = space;
return copy;
}

Input withContainingBlock(Vec2Px block) const {
auto copy = *this;
copy.containingBlock = block;
return copy;
}
};

/// Output of the layout algorithm.

struct Output {
Vec2Px size;

Px width() const {
return size.x;
}

Px height() const {
return size.y;
}

static Output fromSize(Vec2Px size) {
return Output{size};
}
};

/// Computed layout values.

struct Layout {
InsetsPx padding{};
InsetsPx borders{};
Vec2Px position; //< Position relative to the content box of the containing block
Vec2Px borderSize;
InsetsPx margin{};
RadiiPx radii{};
Px fontSize{16};

void repr(Io::Emit &e) const {
e("(layout paddings: {} borders: {} position: {} borderSize: {} margin: {} radii: {})",
padding, borders, position, borderSize, margin, radii);
}

Layout offseted(Vec2Px offset) const {
auto copy = *this;
copy.position = position + offset;
return copy;
}

RectPx borderBox() const {
return RectPx{position, borderSize};
}

RectPx paddingBox() const {
return borderBox().shrink(borders);
}

RectPx contentBox() const {
return paddingBox().shrink(padding);
}

RectPx marginBox() const {
return borderBox().grow(margin);
}
};

struct Box;

struct Tree;
Expand Down
Loading

0 comments on commit 4e51b4e

Please sign in to comment.