BRAIZE

A fatal exception 0E has occurred at 0028:C001D00D in VXD BRAIZE(01) + 0000B33F.

The current application will be terminated.

* Press any key to continue

* Press CTRL+ALT+DEL to restart the web. You will lose any unsaved code in all repositories.

Browser Engine Part 2: Layout + Paint.md
Back to guides

Part 2: Layout + Paint :tw_fire:

Now you have DOM + CSS. Time for pixels.

The beginner trick: make another tree.

DOM is page meaning. Layout is boxes on screen.

1. Layout Boxes Are Not DOM Nodes

Vertex makes this explicit in src/layout/box.h:

struct LayoutBox {
    const Node* node;
    ComputedStyle style;
    float x, y;
    float contentW, contentH;
    float marginTop, marginRight, marginBottom, marginLeft;
    float padTop, padRight, padBottom, padLeft;
    std::vector<std::unique_ptr<LayoutBox>> kids;
};

Beginner version:

LayoutBox
  source node
  style
  x, y
  width, height
  children

Do not draw while walking the DOM. First compute boxes. Then paint boxes. This saves pain. :thonk:

2. Start With Block Layout

Only support block elements first:

  • body
  • div
  • p
  • h1

Rule:

each block goes below the previous block

That is it.

Pseudo-code:

layout_block(node, x, y, available_width):
  box.x = x + margin_left
  box.y = y + margin_top
  box.width = style.width or available_width - margins - padding

  cursor_y = box.y + padding_top

  for child in node.children:
    child_box = layout_block(child, box.x + padding_left, cursor_y, box.width)
    cursor_y += child_box.height + child margins

  box.height = padding_top + children height + padding_bottom
  return box

For text, fake it at first:

text width = characters * font_size * 0.5
text height = font_size * 1.2

Vertex does the same kind of thing in tools/dump_layout.cpp with StubMeasure for offline testing:

float MeasureText(const std::wstring& s, const FontKey& f) override {
    return (float)s.size() * f.size * 0.5f;
}

That is perfect beginner energy. Fake measurement until real fonts matter. :adorpheus:

3. Print The Layout Tree

Vertex has tools/dump_layout.cpp, which parses a page, runs layout, and prints boxes. This is huge because you can debug layout without a window.

Make your own dump:

body x=8 y=8 w=784 h=92
  h1 x=8 y=8 w=784 h=38
    text "Hello Braize" x=8 y=8 w=192 h=38
  div.card x=8 y=46 w=300 h=36
    text "DOM and CSS work" x=20 y=58 w=144 h=19

If numbers look reasonable, painting will be easy.

4. Build A Paint List

Vertex has src/paint/display_list.h:

struct DisplayItem {
    std::string kind;
    std::string text;
    float x = 0;
    float y = 0;
    float width = 0;
    float height = 0;
};

Beginner version:

PaintCommand
  kind: rect/text
  x, y, width, height
  color
  text

When walking layout boxes:

if box has background:
  add rect command
if box is text:
  add text command
for child:
  paint child

5. Draw It

Use any drawing layer:

  • HTML Canvas
  • SDL
  • raylib
  • SFML
  • Win32/GDI
  • PNG output

PNG output totally counts. Window can come later.

Paint order for beginner engine:

background rect
text
children in order

Vertex has a render probe fixture at tests/fixtures/render_probe/stacking.in.html that checks paint order for positioned elements. You do not need stacking contexts yet, but tests like that are a good idea later.

6. What Your Demo Should Show

Make one screenshot with:

  • heading
  • paragraph
  • colored card
  • margin/padding visible
  • different text color
  • README command to run it

Example README section:

## Run

```bash
npm run render examples/hello.html
```

## Works

- HTML tags: body, h1, p, div
- CSS selectors: tag, class, id
- CSS properties: color, background, width, margin, padding, font-size
- Block layout
- Text and rectangle painting

## Does not work yet

- JavaScript
- images
- networking
- flexbox/grid
- forms

Tiny Stretch Goals

After this works:

  • text wrapping
  • borders
  • images
  • links
  • local file navigation
  • simple position: absolute

Do one at a time. Browsers are swamp monsters. :this-is-fine:

Back: Browser Engine Guide