branding: one mark, and PSOGM draws it

Guru and SchCotie are gone. Shift is the only mark, and the crash screen draws
it where the face used to be.

Guru was an icon -- a pictogram with a face, denoting one state -- that lived
on the crash screen and nowhere else. Its argument was identity: `:(` is
recognisably Microsoft's, and a crash screen is the single screenshot most
likely to leave the machine, so it is the worst place to be wearing somebody
else's face. That part holds, and it is kept in the branding README because it
is the argument against any second mark. What does not follow is that the
answer is a *new* mark. Shift already signs the system, it is already what the
machine drew on the splash it started with, and a reader who has seen it twice
before the crash recognises it faster than a face they have never seen.

SchCotie was a mascot, and never existed: the file said so itself under "what
this does not cover". A brief for a character nobody is about to draw is not
worth carrying.

So `draw_guru()` and its SVG-space geometry leave panic.c, the two `guru.svg`
files are deleted, and PSOGM calls gfx_draw_logo() -- the same include/logo.h
mask the loader splash and the boot console composite. At its native 128 px:
there is no resampler in the crash path, and the face was scalable in a place
that only ever drew it at one size.

The name stays. "Purple Screen of Guru Meditation" is the Amiga nod, which
panic.c's own header already called out as a separate thing from the mark.

**And taking the screenshot to check it caught a regression from the TTF
console work.** `gfx_draw_char()` -- the pixel-positioned path, which only
PSOGM uses -- still walked `font_data[c][row]` for `row < CHAR_HEIGHT`.
font_data has exactly 16 rows per glyph; CHAR_HEIGHT is the *TrueType* cell
height now, about 21. So every line of the crash report drew five rows of the
next character's bitmap underneath itself, and it looked like each line had
been printed twice. The grid path had been updated when the cell size stopped
being a constant; this one had not, because nothing routine draws text in pixel
coordinates. Both go through one draw_glyph_px() now, and the bitmap's own
geometry is spelled BITMAP_W/BITMAP_H so it cannot be mistaken for a cell size
again.

Two stale claims corrected while in there, in panic.c's header and in
graphics.md: PSOGM has not dimmed-and-floated over the old screen for a while
-- it clears to full purple, and the file's own code comment says why -- and
there is no longer "a face that is obviously a cartoon".

The website's crash section shows the mark in flat white rather than its own
violets, which is what the kernel actually draws: PSOGM composites a coverage
mask in one colour, and those violets sit close enough to #4B1D9A to
half-disappear against it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-21 21:35:58 +02:00
commit 953f9c5c3c
11 changed files with 158 additions and 216 deletions

View file

@ -55,3 +55,27 @@ only ever seen when something is already wrong, and it quietly rots.
The serial copy is the one that survives, and it is what `tools/bootcheck.ps1` The serial copy is the one that survives, and it is what `tools/bootcheck.ps1`
reads. It is also all you get when the panic happens before there is a reads. It is also all you get when the panic happens before there is a
framebuffer. See [[schunix-verify-by-driving-shell]]. framebuffer. See [[schunix-verify-by-driving-shell]].
## 2026-09-21: the face is gone, and a bug it exposed
PSOGM draws **Shift**, the system logo, where the Guru face used to be —
`gfx_draw_logo()` from `include/logo.h`, the same mask the loader splash and
the boot console use, at its native 128 px. `draw_guru()` and its geometry
constants are deleted; so are `docs/branding/guru.svg` and the mascot brief.
The *name* "Purple Screen of Guru Meditation" stays — that is the Amiga nod,
which was always a separate thing from the mark.
Taking the screenshot to check it caught a real regression from the TTF
console work: **`gfx_draw_char()` still indexed the 8x16 bitmap with the
TrueType cell height.** `font_data` has 16 rows per glyph and `CHAR_HEIGHT`
became ~21, so every line of PSOGM drew five rows of the *next character's*
bitmap underneath itself — it looked like each line had been printed twice.
The grid path (`gfx_draw_cell`) had been updated; this one had not, because
nothing routine draws pixel-positioned text. Both now go through one
`draw_glyph_px()`, and the bitmap's own geometry is spelled `BITMAP_W/H` so it
cannot be confused with a cell size again.
**A crash screen is only checked by looking at it**, and nothing else in the
system exercises `gfx_text_at`. Screenshot it after any font or console change:
`echo c > /proc/sysrq`, then a QEMU monitor `screendump`.

View file

@ -109,7 +109,7 @@ good chance it is in there.
| | | | | |
|---|---| |---|---|
| [README](branding/README.md) | Shift the logo, Guru the icon, SchCotie the mascot: what each is for, the colour rule, and why a raster is committed next to its SVG. | | [README](branding/README.md) | Shift, the one mark: what it is for, the colour rule, why an icon and a mascot were removed, and why a raster is committed next to its SVG. |
## history/ — why it is the way it is ## history/ — why it is the way it is

View file

@ -1,12 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<title>Guru — the PSOGM face</title>
<desc>The crash-screen face. Chamfered corners quote the old SchOS letterforms.
Drawn here for reference only: kernel/core/panic.c builds it from primitives
rather than from a bitmap, so it scales with the display and costs nothing in
the kernel image.</desc>
<path fill="#7C5CFF" d="M58 24 L142 24 L176 58 L176 142 L142 176 L58 176 L24 142 L24 58 Z"/>
<path fill="#2B0F52" d="M74 82 h16 v22 h-16 Z"/>
<path fill="#2B0F52" d="M110 82 h16 v22 h-16 Z"/>
<path fill="#2B0F52" d="M72 128 h56 v11 h-56 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 655 B

View file

@ -11,7 +11,7 @@ docs.html the documentation reader
style.css the palette, transcribed from include/theme.h style.css the palette, transcribed from include/theme.h
app.js the front page: the boot log types, sections fade in app.js the front page: the boot log types, sections fade in
docs.js the Markdown renderer and the doc index docs.js the Markdown renderer and the doc index
Assets/ shift.svg (logo), uberblock.svg (SchFS), guru.svg (PSOGM), Assets/ shift.svg (logo), uberblock.svg (SchFS),
usb.svg (downloads), pride.svg usb.svg (downloads), pride.svg
``` ```

View file

@ -358,7 +358,7 @@
<section class="crash reveal"> <section class="crash reveal">
<div class="wrap"> <div class="wrap">
<img src="Assets/guru.svg" alt="Guru, the SchOS crash-screen icon"> <img src="Assets/shift.svg" alt="Shift, the SchOS mark, as the crash screen draws it">
<div> <div>
<p class="eyebrow">When it stops</p> <p class="eyebrow">When it stops</p>
<h2>PSOGM</h2> <h2>PSOGM</h2>

View file

@ -326,7 +326,16 @@ header.top {
align-items: start; align-items: start;
} }
@media (max-width: 44rem) { .crash .wrap { grid-template-columns: 1fr; } } @media (max-width: 44rem) { .crash .wrap { grid-template-columns: 1fr; } }
.crash img { width: 100%; max-width: 8rem; height: auto; } /* Flat white, not the logo's own violets. This is what the kernel actually
draws: PSOGM composites the coverage mask in one colour, so a full-colour
mark here would show something the crash screen never shows -- and those
violets are close enough to this purple to half-disappear against it. */
.crash img {
width: 100%;
max-width: 8rem;
height: auto;
filter: brightness(0) invert(1);
}
.crash .eyebrow { color: #C3AEEC; } .crash .eyebrow { color: #C3AEEC; }
.crash h2 { color: #fff; } .crash h2 { color: #fff; }
.crash p { color: #DBCEF5; } .crash p { color: #DBCEF5; }

View file

@ -198,14 +198,22 @@ matter more than the feature:
`kernel/core/panic.c`. The Purple Screen of Guru Meditation. Built around three `kernel/core/panic.c`. The Purple Screen of Guru Meditation. Built around three
decisions: decisions:
**It does not clear the screen.** Whatever was displayed stays, dimmed and **It takes the whole screen.** It used to dim what was there and float a panel
pushed back, with the report floating over it. A crash screen that wipes the over it, on the argument that a crash screen which wipes the display tells you
display tells you the machine is gone; one that leaves your work visible tells the machine is gone while one that leaves your work visible tells you it
you it stopped — which is both truer and much less alarming. stopped. That looked good and was wrong: the thing is called a Purple Screen,
and half a screen of abandoned work behind the report is a distraction at the
worst possible moment.
**It is not frightening.** No red, no capitals, no "FATAL". A calm sentence, a **It is not frightening.** No red, no capitals, no "FATAL". A calm sentence,
face that is obviously a cartoon, then the facts. Whoever is reading this is the Shift mark — the same one the machine drew on the splash it started with —
already having a bad time. then the facts. Whoever is reading this is already having a bad time.
The mark replaced **Guru**, a chamfered face drawn from primitives that lived
only here. Guru's argument was identity: `:(` is recognisably Microsoft's, and
a crash screen is the screenshot most likely to leave the machine. That holds.
What does not follow is that the answer is a *second* mark — see
[../branding/README.md](../branding/README.md).
**It says considerably more than it needs to.** Everything cheap to collect is **It says considerably more than it needs to.** Everything cheap to collect is
collected: stop code, fault address, which CPU, which process, how far into the collected: stop code, fault address, which CPU, which process, how far into the

View file

@ -1,18 +1,29 @@
# Branding # Branding
Three marks, three different jobs. They are not interchangeable and the One mark.
difference is not stylistic.
| | | | | | | |
|---|---|---| |---|---|---|
| **Shift** | *the logo* | An abstract mark. Non-human, non-animal. Signs the system. | | **Shift** | *the logo* | An abstract mark. Non-human, non-animal. Signs the system. |
| **Guru** | *the icon* | A pictogram. Denotes one state. Has a face; has no body. |
| **SchCotie** | *the mascot* | A character. Has a body, poses and expressions. |
The distinction that decides everything else: **a mascot has to be able to There were three. **Guru**, an icon — a pictogram with a face, denoting one
do things.** An icon works once, at one size, in one state. A character has to state — lived on the crash screen and nowhere else. **SchCotie**, a mascot —
work confused, pleased, asleep and from behind — which is why it costs more and a character with a body, poses and expressions — was a brief and was never
why it is drawn last. drawn. Both are gone for now, and may come back.
The reasoning that retired Guru is worth keeping, because it is the argument
against any second mark. Guru existed to give the crash screen an identity:
`:(` is recognisably Microsoft's, and a crash screen is the single screenshot
most likely to leave the machine, so it is the worst place to be wearing
somebody else's face. That part holds. What does not follow is that the answer
is a *new* mark. Shift already signs the system, it is already what the machine
drew on the splash it started with, and a reader who has seen it twice before
the crash recognises it faster than a face they have never seen.
SchCotie went for a simpler reason: **a mascot has to be able to do things.**
An icon works once, at one size, in one state. A character has to work
confused, pleased, asleep and from behind — which is why it costs more, and why
a brief for one is not worth carrying until somebody is going to draw it.
--- ---
@ -46,61 +57,8 @@ the kernel's name (**SchIFT**) as a shape.
- **With or without a wordmark.** Set "SchOS" beside it in **Roboto Bold**, - **With or without a wordmark.** Set "SchOS" beside it in **Roboto Bold**,
which is already in the tree (`ports/Roboto`), already Apache-2.0, and already which is already in the tree (`ports/Roboto`), already Apache-2.0, and already
in `NOTICE`. in `NOTICE`.
- Non-human and non-animal, permanently. The character work belongs to - Non-human and non-animal, permanently. If a character is ever drawn it gets
SchCotie; a logo that starts growing a face has taken the mascot's job. its own mark; a logo that starts growing a face has taken that job.
## Guru — the icon
`guru.svg`
The crash face. Mythic rather than cartoon — closer to a temple carving than a
character — square or nearly so, with an expression that stays in the
`:(` / `:|` register.
**Purple, and only purple, and only here.** `theme.h` states the rule it
enforces:
> normal states are calm and dark; only failure is loud. PSOGM keeps the
> full-bleed purple entirely to itself, which is what makes it mean something.
That purple is `PSOGM_BG #4B1D9A`, and it is deliberately **not** the accent
`#7C5CFF`. If Guru ever appears in accent violet on a splash, a sticker or a
README header, the deep purple stops meaning *the machine stopped* — and it is
the only colour in the system that means anything at all.
So: Guru does not leave the crash screen.
## SchCotie — the mascot
*Not drawn yet.*
A daemon: **the process that works while nobody is watching.** The horns are a
pun on the word; the meaning is the background job that keeps running after you
have walked away — which is `schinit` supervising, restarting what dies, awake
at 3am. Patient and watchful rather than mischievous.
Horns carry **X** — POSIX, Unix, X11 — one substitution reading three ways.
Two constraints that follow from the section above. A thin X is two diagonal
strokes and dissolves at small sizes, so it wants to be **solid wedges with the
X notched out as negative space**: the horn silhouette survives downscaling and
the X appears as you get closer. And it needs a **turnaround** — front, side,
three-quarter, matching heights on shared construction lines — because a
character drawn once at one angle only ever works at that angle.
### The licensing point, which is not optional here
**The BSD daemon is copyrighted.** Marshall Kirk McKusick holds it and the terms
are not a free licence. A character that reads as "Beastie, but purple, with X
horns" is a derivative-work argument, and this mark would be in every image
SchOS ships, beside a `NOTICE` file this project has been careful about.
The concept is not the problem. A daemon is a Greek *daimon* and a Unix
background process — neither is ownable. What belongs to Beastie is the
execution: the pitchfork, the sneakers, that red, that face, that pose. Horns, a
tail and a small imp are generic and thousands of years old.
**Draw from the idea. Do not have a picture of Beastie open while you do it.**
--- ---
@ -114,8 +72,18 @@ value it does not already define.
| `SCH_BG` | `#0C0E13` | Ground. Near-black with a blue cast, because pure black reads as "switched off". | | `SCH_BG` | `#0C0E13` | Ground. Near-black with a blue cast, because pure black reads as "switched off". |
| `SCH_FG` | `#E6E8EC` | Primary text | | `SCH_FG` | `#E6E8EC` | Primary text |
| `SCH_DIM` | `#707784` | Secondary | | `SCH_DIM` | `#707784` | Secondary |
| `SCH_ACCENT` | `#7C5CFF` | The one accent. Shift, SchCotie, anything calm. | | `SCH_ACCENT` | `#7C5CFF` | The one accent. Shift, and anything calm. |
| `PSOGM_BG` | `#4B1D9A` | **Failure only.** Guru's ground and nothing else's. | | `PSOGM_BG` | `#4B1D9A` | **Failure only.** The crash screen's ground and nothing else's. |
`PSOGM_BG` is deliberately **not** the accent. `theme.h` states the rule it
enforces:
> normal states are calm and dark; only failure is loud. PSOGM keeps the
> full-bleed purple entirely to itself, which is what makes it mean something.
If that purple ever appears on a splash, a sticker or a README header, it stops
meaning *the machine stopped* — and it is the only colour in the system that
means anything at all.
## File formats ## File formats
@ -154,7 +122,6 @@ shift.svg the logo, in colour
shift-mask.svg the same geometry in greys -- see below shift-mask.svg the same geometry in greys -- see below
shift-mask.png rasterised from it, committed on purpose shift-mask.png rasterised from it, committed on purpose
uberblock.svg SchFS uberblock.svg SchFS
guru.svg the crash icon
superseded-*.png the old wordmark, kept for reference only superseded-*.png the old wordmark, kept for reference only
``` ```
@ -189,8 +156,8 @@ If either is edited, edit both.
|---|---| |---|---|
| Loader splash | Shift, from `include/logo.h`, shaded per row by `metal()` in `boot/common/ui.c` — silver, not accent. | | Loader splash | Shift, from `include/logo.h`, shaded per row by `metal()` in `boot/common/ui.c` — silver, not accent. |
| Kernel console / early boot | Shift, same mask, composited flat in a theme colour by `gfx_draw_logo`. | | Kernel console / early boot | Shift, same mask, composited flat in a theme colour by `gfx_draw_logo`. |
| PSOGM | Guru, drawn from primitives by `draw_guru()` in `kernel/core/panic.c`. Never a bitmap: it scales with the display and costs nothing in the kernel image. | | PSOGM | Shift, the same mask again, by `gfx_draw_logo()` from `kernel/core/panic.c`. At its native 128 px — there is no resampler in the crash path, and the mark was only ever drawn at one size there anyway. |
| Serial crash report | `:(`, as text. Guru cannot be drawn in ASCII, and a mood marker in a log is not the icon. | | Serial crash report | `:(`, as text. The mark cannot be drawn in ASCII, and a mood marker in a log is not a mark. |
| SchFS | **Nowhere yet.** `uberblock.svg` has no consumer; it is waiting for `projects/schfs-linux/` to have a README worth heading. | | SchFS | **Nowhere yet.** `uberblock.svg` has no consumer; it is waiting for `projects/schfs-linux/` to have a README worth heading. |
One trap, since it has already been stepped in: `boot/common/ui.c` includes One trap, since it has already been stepped in: `boot/common/ui.c` includes
@ -207,10 +174,8 @@ loader must actually relink**, and that needs `clang` and `lld-link`.
has registered or defended. has registered or defended.
- **No usage rules for third parties** — nothing says what someone else may do - **No usage rules for third parties** — nothing says what someone else may do
with them. Worth writing before anything is published. with them. Worth writing before anything is published.
- **SchCotie does not exist.** Everything above it is a brief, not a - **There is no icon and no mascot.** Both were removed rather than left
description. half-specified. If either comes back it starts from the argument at the top
- **Guru is drawn but not wired in.** `panic.c` still renders `:(` from of this file, not from what used to be here.
primitives, and there is a real argument for keeping it — see the note in that
file about being "legible as a mood without being a picture of anything".
- **No wordmark lockup is specified.** "Roboto Bold beside it" is not a spacing - **No wordmark lockup is specified.** "Roboto Bold beside it" is not a spacing
rule, a size ratio or a clear-space margin, and it will want to be. rule, a size ratio or a clear-space margin, and it will want to be.

View file

@ -1,12 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<title>Guru — the PSOGM face</title>
<desc>The crash-screen face. Chamfered corners quote the old SchOS letterforms.
Drawn here for reference only: kernel/core/panic.c builds it from primitives
rather than from a bitmap, so it scales with the display and costs nothing in
the kernel image.</desc>
<path fill="#7C5CFF" d="M58 24 L142 24 L176 58 L176 142 L142 176 L58 176 L24 142 L24 58 Z"/>
<path fill="#2B0F52" d="M74 82 h16 v22 h-16 Z"/>
<path fill="#2B0F52" d="M110 82 h16 v22 h-16 Z"/>
<path fill="#2B0F52" d="M72 128 h56 v11 h-56 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 655 B

View file

@ -5,15 +5,16 @@
// The name is a nod to the Amiga's "Guru Meditation", but nothing else about // The name is a nod to the Amiga's "Guru Meditation", but nothing else about
// this is a nod to 1985. Three things it is built around: // this is a nod to 1985. Three things it is built around:
// //
// *It does not clear the screen.* Whatever was being displayed stays there, // *It takes the whole screen.* An earlier version dimmed what was there and
// dimmed and pushed back, with the report floating over it. A crash screen that // floated a panel over it, on the argument that a crash screen which wipes the
// wipes the display tells you the machine is gone; one that leaves your work // display tells you the machine is gone while one that leaves your work visible
// visible underneath tells you it stopped, which is both truer and much less // tells you it stopped. That looked good and was wrong: the thing is called a
// alarming. // Purple Screen, and half a screen of somebody's abandoned work behind the
// report is a distraction at the worst possible moment.
// //
// *It is not frightening.* No red, no capitals, no "FATAL". A calm sentence, a // *It is not frightening.* No red, no capitals, no "FATAL". A calm sentence,
// face that is obviously a cartoon, and then the facts. Whoever is reading this // the system's own mark, and then the facts. Whoever is reading this is
// is already having a bad time. // already having a bad time.
// //
// *It says considerably more than it needs to.* The screen is the only artefact // *It says considerably more than it needs to.* The screen is the only artefact
// of a kernel panic — there is no core dump and no log to go back to — so // of a kernel panic — there is no core dump and no log to go back to — so
@ -40,6 +41,7 @@
// same names but with different values, which meant theme.h described a crash // same names but with different values, which meant theme.h described a crash
// screen nobody had ever seen. // screen nobody had ever seen.
#include "theme.h" #include "theme.h"
#include "logo.h" /* LOGO_H -- the mark's height, for the layout below */
#include "qr.h" #include "qr.h"
extern uint ticks; extern uint ticks;
@ -87,80 +89,6 @@ put_hex(char *p, char *end, uint64 v, int digits)
return p; return p;
} }
/* ------------------------------------------------------------------ */
/* The face */
/* */
/* Guru: a chamfered head with two eyes and a flat mouth. See */
/* docs/branding/ -- it is the system's icon, and the only place it is */
/* allowed to appear is here. */
/* */
/* This replaced ":(", two dots and a parenthesis, whose argument was */
/* that it is "legible as a mood without being a picture of anything, */
/* and does not pretend the machine has feelings about what just */
/* happened." That reasoning was good and is recorded because it is */
/* the thing being traded away: a face is a louder claim than a */
/* punctuation mark, and it has to earn the volume. */
/* */
/* What it buys is identity. ":(" is Microsoft's, recognisably, and a */
/* crash screen is the single screenshot most likely to leave the */
/* machine -- so it is the worst place to be wearing somebody else's */
/* face. The octagon also quotes the chamfered corners of the old */
/* SchOS letterforms, which is the only part of that mark still in use. */
/* */
/* Still drawn from primitives rather than stored as a bitmap, for the */
/* original reason: it scales with the display and costs no space in a */
/* kernel image. Two rectangles and a run of spans -- no allocation, no */
/* locks, nothing that can fail halfway. */
/* ------------------------------------------------------------------ */
/* Geometry, in the 200-unit box docs/branding/guru.svg is drawn in, so the
* two cannot drift apart without somebody editing a number here on purpose.
* The head spans 24..176 of that box; everything is expressed against it. */
#define GURU_BOX_LO 24 /* head's left/top edge in the SVG box */
#define GURU_BOX_SPAN 152 /* head's width and height there */
#define GURU_CHAMFER 34 /* corner cut */
/* SVG coordinate -> pixel offset within a head `h` tall. */
static int
gmap(int v, int h)
{
return (v - GURU_BOX_LO) * h / GURU_BOX_SPAN;
}
static void
draw_guru(int x, int y, int h, uint32 color, uint32 bg)
{
/* The head, as horizontal spans. A scanline fill rather than a polygon
* routine: the shape is convex and its left edge is one subtraction, so
* this is the whole of it. */
for (int py = 0; py < h; py++) {
int sy = GURU_BOX_LO + py * GURU_BOX_SPAN / h; /* back to SVG space */
int inset = 0;
if (sy < GURU_BOX_LO + GURU_CHAMFER)
inset = (GURU_BOX_LO + GURU_CHAMFER) - sy;
else if (sy > GURU_BOX_LO + GURU_BOX_SPAN - GURU_CHAMFER)
inset = sy - (GURU_BOX_LO + GURU_BOX_SPAN - GURU_CHAMFER);
int x0 = gmap(GURU_BOX_LO + inset, h);
int x1 = gmap(GURU_BOX_LO + GURU_BOX_SPAN - inset, h);
if (x1 > x0)
gfx_fill_rect(x + x0, y + py, x1 - x0, 1, color);
}
/* The eyes and the mouth are knocked out of the head rather than drawn on
* top of it, which is what keeps them legible when the head is small: a
* hole in a solid reads at sizes a stroke does not. */
int ey = gmap(82, h), eh = gmap(104, h) - gmap(82, h);
int e1x = gmap(74, h), e2x = gmap(110, h);
int ew = gmap(90, h) - gmap(74, h);
gfx_fill_rect(x + e1x, y + ey, ew, eh, bg);
gfx_fill_rect(x + e2x, y + ey, ew, eh, bg);
int mx = gmap(72, h), mw = gmap(128, h) - gmap(72, h);
int my = gmap(128, h), mh = gmap(139, h) - gmap(128, h);
gfx_fill_rect(x + mx, y + my, mw, mh, bg);
}
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* The report */ /* The report */
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -458,9 +386,24 @@ paint(char *reason, addr_t *pcs, int npcs)
if (left > 110) left = 110; if (left > 110) left = 110;
int y = sh / 8; int y = sh / 8;
int face_h = 96; // Shift, the system's own mark, from the same include/logo.h the loader
draw_guru(left, y, face_h, PSOGM_FG, PSOGM_BG); // splash and the boot console draw. It replaces Guru, a chamfered face that
y += face_h + ch * 2; // lived only here and was drawn from primitives a few lines up this file.
//
// Guru was an *icon*: a pictogram denoting one state, with a face. The
// argument for it was identity -- ":(" is recognisably Microsoft's, and a
// crash screen is the single screenshot most likely to leave the machine, so
// it is the worst place to be wearing somebody else's face. That argument
// holds. It just does not follow that the answer is a second mark. The logo
// already signs the system, it is already the thing on the splash the
// machine started with, and a reader who has seen it twice before the crash
// recognises it faster than a face they have never seen.
//
// A fixed 128 px rather than scaled: the mask is 128x128 and there is no
// resampler here. The face was scalable and that bought nothing -- it was
// drawn at one size.
gfx_draw_logo(left, y, PSOGM_FG, PSOGM_BG);
y += LOGO_H + ch * 2;
// Left-aligned, not centred. Centred text reads as a poster; this is a // Left-aligned, not centred. Centred text reads as a poster; this is a
// report, and a report is read down the left edge. // report, and a report is read down the left edge.

View file

@ -39,8 +39,16 @@
// its own metrics. These start at the bitmap's size because the console has to // its own metrics. These start at the bitmap's size because the console has to
// work from the first panic onwards -- before gfx_font_init() has run there is // work from the first panic onwards -- before gfx_font_init() has run there is
// no font, and 8x16 against font_data is exactly what there always was. // no font, and 8x16 against font_data is exactly what there always was.
static int cell_w = 8; // The bitmap's own geometry, which is fixed forever: font_data is
static int cell_h = 16; // `uchar[256][16]` and each row is eight bits. Naming it separately from the
// cell size is not pedantry -- the two were the same number, then one of them
// started moving, and the code that kept indexing font_data by the moving one
// read straight off the end of every glyph.
#define BITMAP_W 8
#define BITMAP_H 16
static int cell_w = BITMAP_W;
static int cell_h = BITMAP_H;
#define CHAR_WIDTH cell_w #define CHAR_WIDTH cell_w
#define CHAR_HEIGHT cell_h #define CHAR_HEIGHT cell_h
@ -52,6 +60,7 @@ static struct font *cfont;
static int cfont_baseline; static int cfont_baseline;
static uint32 blend(uint32 bg, uint32 fg, uint a); static uint32 blend(uint32 bg, uint32 fg, uint a);
static void draw_glyph_px(int x, int y, uint32 cp, uint32 fg, uint32 bg);
// The scanout, as published by whichever driver owns the display. Zeroed until // The scanout, as published by whichever driver owns the display. Zeroed until
// one does, and every entry point checks `fb` before touching memory -- a // one does, and every entry point checks `fb` before touching memory -- a
@ -141,20 +150,10 @@ gfx_clear(uint32 color)
void void
gfx_draw_char(int x, int y, uchar c, uint32 color) gfx_draw_char(int x, int y, uchar c, uint32 color)
{ {
// No `c > 127` rejection any more: font_data holds 256 Latin-1 glyphs, and // Pixel-positioned rather than on the grid -- the crash screen lays itself
// a uchar cannot index past them. // out in pixels because it is a report, not a terminal. Same glyph path as
if (!fb) return; // the grid uses, which it did not used to be; see draw_glyph_px().
draw_glyph_px(x, y, c, color, gfx_bg);
for (int row = 0; row < CHAR_HEIGHT; row++) {
uchar row_data = font_data[c][row];
for (int col = 0; col < CHAR_WIDTH; col++) {
if (row_data & (0x80 >> col)) {
gfx_draw_pixel(x + col, y + row, color);
} else {
gfx_draw_pixel(x + col, y + row, gfx_bg);
}
}
}
} }
void void
@ -246,13 +245,21 @@ gfx_putc(int c, uint32 color)
// gfx_draw_char paints its background from the file-static gfx_bg, which is // gfx_draw_char paints its background from the file-static gfx_bg, which is
// right for a console that has one background and wrong for a terminal where // right for a console that has one background and wrong for a terminal where
// every cell carries its own. // every cell carries its own.
void // One glyph, at a pixel position, in a cell-sized box.
gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg) //
// Both the console grid and the pixel-positioned text in the crash screen come
// through here, which is the point: they used to be two implementations, and
// only one of them was updated when the cell size stopped being 8x16.
// gfx_draw_char() still walked `font_data[c][row]` for `row < CHAR_HEIGHT` --
// and CHAR_HEIGHT is the *TrueType* cell height now, while font_data has
// exactly 16 rows per glyph. Every line of PSOGM therefore drew five rows of
// the *next character's* bitmap underneath itself. It looked like each line
// had been printed twice.
static void
draw_glyph_px(int x, int y, uint32 cp, uint32 fg, uint32 bg)
{ {
if (!fb) if (!fb)
return; return;
int x = col * cell_w, y = row * cell_h;
if (x < 0 || y < 0 || if (x < 0 || y < 0 ||
x + cell_w > (int)scanout.width || y + cell_h > (int)scanout.height) x + cell_w > (int)scanout.width || y + cell_h > (int)scanout.height)
return; return;
@ -261,11 +268,15 @@ gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg)
// drawn as a space rather than as the wrong glyph -- `?` was at least honest // drawn as a space rather than as the wrong glyph -- `?` was at least honest
// about being a substitute, and a CP437 picture of something unrelated is // about being a substitute, and a CP437 picture of something unrelated is
// not. // not.
//
// BITMAP_W/H and not cell_w/h: the bitmap's geometry is a property of
// font_data, not of whatever the console decided a cell is. They agree
// whenever cfont is null, and the assert below is what keeps that true.
if (!cfont) { if (!cfont) {
uchar g = cp < 256 ? (uchar)cp : ' '; uchar g = cp < 256 ? (uchar)cp : ' ';
for (int r = 0; r < cell_h; r++) { for (int r = 0; r < BITMAP_H; r++) {
uchar bits = font_data[g][r]; uchar bits = font_data[g][r];
for (int c = 0; c < cell_w; c++) for (int c = 0; c < BITMAP_W; c++)
gfx_draw_pixel(x + c, y + r, (bits & (0x80 >> c)) ? fg : bg); gfx_draw_pixel(x + c, y + r, (bits & (0x80 >> c)) ? fg : bg);
} }
return; return;
@ -303,6 +314,12 @@ gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg)
} }
} }
void
gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg)
{
draw_glyph_px(col * cell_w, row * cell_h, cp, fg, bg);
}
// Bring the console typeface up. Called once, early, from the display path. // Bring the console typeface up. Called once, early, from the display path.
// //
// The font is linked into the kernel (see CONSFONT in the Makefile) because // The font is linked into the kernel (see CONSFONT in the Makefile) because