The rendering pipeline¶
Explanation: how a page becomes bytes on the wire, one stage at a time. Each stage is a pure function over values, which is why nearly all of it runs without a BBC Micro.
Document what is to be said
│ sextile.content.blocks
│ sextile.viewdata.typesetting
▼
Row (text, colour, indent)
│ sextile.formatting, sextile.layout
│ sextile.viewdata.canvas
▼
Frame (24 × 40 cells) what the screen shows
│ sextile.viewdata.frame
▼
bytes the wire stream
│ sextile.viewdata.encoding
A Document is structural, not typographic: Paragraph, Quote, Code and the
rest say what a thing is, and colour is spent telling a quotation from a listing
rather than on an italic. typesetting.rows_for flattens it into Rows — a
quotation cyan, a listing green, nesting indented — and leaves where a frame ends
to the layer that draws them.
A document, drawn¶
from sextile import Page, PageLayout, PageRequest, PageRouter, Sextile
from sextile.content.blocks import Code, Document, Paragraph
from sextile.formatting import Prose
from sextile.viewdata.typesetting import rows_for
_DOC = Document(blocks=(
Paragraph(("A page begins as a document.",)),
Code(("PRINT CHR$(141)",)),
))
router = PageRouter()
@router.page("1", name="page", title="Pipeline")
async def page(request: PageRequest) -> Page:
return PageLayout(title="PIPELINE", parts=[Prose(entries=rows_for(_DOC))]).build(request)
app = Sextile(name="Demo", pages=[*router])
PIPELINE 1a A page begins as a document. PRINT CHR$(141) 0 index
The same frame, on the wire¶
viewdata.frame is a fixed grid, not a stream of writes, and serialising it is:
hide the cursor, clear, home, then the cells that say something. The C0 range
holds two namespaces — a bare byte is screen or cursor control, ESC then byte
0x40a teletext attribute — kept straight inviewdata.encodingand measured in Viewdata encoding. The frame above goes down the wire as:
0000 14 0C 1E 1B 46 50 49 50 45 4C 49 4E 45 20 20 20 |....FPIPELINE |
0010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
0020 20 20 20 20 20 20 20 20 20 1B 47 31 61 1B 5A 1B | .G1a.Z.|
0030 54 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F |T...............|
0040 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F |................|
0050 7F 7F 7F 7F 7F 0D 0A 41 20 70 61 67 65 20 62 65 |.......A page be|
0060 67 69 6E 73 20 61 73 20 61 20 64 6F 63 75 6D 65 |gins as a docume|
0070 6E 74 2E 0D 0A 0D 0A 1B 42 50 52 49 4E 54 20 43 |nt......BPRINT C|
0080 48 52 24 28 31 34 31 29 0D 0A 0D 0A 0D 0A 0D 0A |HR$(141)........|
0090 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A |................|
00A0 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A 0D 0A 1B 5A 1B 54 |.............Z.T|
00B0 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F |................|
00C0 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F |................|
00D0 7F 7F 7F 7F 0D 0A 1B 43 30 20 69 6E 64 65 78 |.......C0 index|
Economy on the wire¶
Two measures cut a frame’s bytes without changing a pixel, both settled on a real screen and written up in Viewdata encoding:
A colour attribute occupies a cell, and
canvascounts it, so a row that changes colour twice has thirty-eight columns for text and no layer above has to know. It is why colour could not have been added later.Trailing blanks are not sent. The frame clears the screen first, so a space at the end of a row overwrites nothing; after the last row with anything on it, nothing is sent at all. Real pages lose between a third and three quarters of their bytes.